@zlink-systems/framework 0.27.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Actors/ZLinkActorClient.d.ts +1 -1
- package/dist/contracts/Channels/Calls.d.ts +1 -1
- package/dist/contracts/Configuration/Builders.d.ts +2 -1
- package/dist/contracts/Configuration/Configs.d.ts +0 -1
- package/dist/contracts/Configuration/InternalDefaults.d.ts +3 -0
- package/dist/contracts/Configuration/InternalDefaults.js +4 -1
- package/dist/contracts/Configuration/RegistrationBuilders.js +9 -0
- package/dist/contracts/Configuration/RegistrationNormalizers.d.ts +1 -0
- package/dist/contracts/Configuration/RegistrationNormalizers.js +16 -0
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +3 -5
- package/dist/contracts/Configuration/RegistrationValidators.js +0 -2
- package/dist/contracts/Spots/Contracts.d.ts +1 -1
- package/dist/contracts/Streams/BoundSessionContracts.d.ts +1 -1
- package/dist/contracts/Streams/IZLinkSession.d.ts +2 -3
- package/dist/contracts/Streams/IZLinkSessionActor.d.ts +2 -2
- package/dist/runtime/actors/actor-client.js +7 -10
- package/dist/runtime/actors/actor-mailbox.d.ts +2 -2
- package/dist/runtime/actors/actor-mailbox.js +2 -2
- package/dist/runtime/actors/actor-request-deadline.d.ts +1 -3
- package/dist/runtime/actors/actor-request-deadline.js +4 -29
- package/dist/runtime/actors/actor-routed-join-transport.d.ts +2 -3
- package/dist/runtime/backend/contracts/index.d.ts +1 -4
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +2 -0
- package/dist/runtime/backend/node/node-backend-adapter-support.js +35 -4
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.d.ts +1 -1
- package/dist/runtime/backend/node/node-dealer-channel-client-transport.js +1 -2
- package/dist/runtime/backend/node/node-raw-binding-port.js +10 -27
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +1 -3
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +1 -1
- package/dist/runtime/channels/channel-clients.js +8 -10
- package/dist/runtime/channels/channel-envelope.d.ts +1 -1
- package/dist/runtime/channels/channel-envelope.js +4 -10
- package/dist/runtime/channels/channel-outbound-operations.d.ts +2 -2
- package/dist/runtime/channels/channel-outbound-operations.js +20 -36
- package/dist/runtime/channels/channel-runtime-manager.d.ts +4 -4
- package/dist/runtime/channels/channel-runtime-manager.js +8 -8
- package/dist/runtime/channels/channel-socket-options.js +0 -10
- package/dist/runtime/channels/channel-socket-registry.d.ts +1 -1
- package/dist/runtime/channels/channel-socket-registry.js +3 -14
- package/dist/runtime/channels/channel-transports.d.ts +10 -14
- package/dist/runtime/channels/channel-transports.js +11 -14
- package/dist/runtime/channels/local-spot-route-transport.d.ts +1 -1
- package/dist/runtime/channels/local-spot-route-transport.js +1 -3
- package/dist/runtime/channels/router-socket-spot-route-transport.d.ts +1 -1
- package/dist/runtime/channels/router-socket-spot-route-transport.js +2 -2
- package/dist/runtime/channels/source-spot-router.d.ts +1 -1
- package/dist/runtime/channels/source-spot-router.js +2 -4
- package/dist/runtime/channels/spot-node-route-transport.d.ts +1 -1
- package/dist/runtime/channels/spot-node-route-transport.js +2 -3
- package/dist/runtime/channels/spot-route-bridge-transport.d.ts +1 -1
- package/dist/runtime/channels/spot-route-bridge-transport.js +2 -2
- package/dist/runtime/channels/spot-route-dispatch-strategy.d.ts +2 -4
- package/dist/runtime/channels/spot-route-dispatch-strategy.js +11 -12
- package/dist/runtime/execution/serial-execution-queue.d.ts +12 -3
- package/dist/runtime/execution/serial-execution-queue.js +116 -28
- package/dist/runtime/foundation/operation-registry.d.ts +3 -2
- package/dist/runtime/foundation/operation-registry.js +5 -1
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +17 -11
- package/dist/runtime/foundation/service-authority-payload-codec.d.ts +0 -2
- package/dist/runtime/foundation/service-authority-payload-codec.js +0 -43
- package/dist/runtime/foundation/service-stateful-registry.d.ts +4 -3
- package/dist/runtime/foundation/service-stateful-registry.js +5 -2
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +1 -8
- package/dist/runtime/foundation/service-stateful-runtime.js +78 -126
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +0 -11
- package/dist/runtime/foundation/service-stateful-wire-codec.js +1 -23
- package/dist/runtime/foundation/service-topology-registry.d.ts +5 -0
- package/dist/runtime/foundation/service-topology-registry.js +16 -17
- package/dist/runtime/host/actor-packet-relay.d.ts +2 -2
- package/dist/runtime/host/actor-packet-relay.js +8 -10
- package/dist/runtime/host/index.js +1 -15
- package/dist/runtime/host/instance-activation-authority.d.ts +0 -1
- package/dist/runtime/host/instance-activation-authority.js +46 -97
- package/dist/runtime/host/remote-bound-session-relay.js +8 -8
- package/dist/runtime/host/spot-address-transport.d.ts +1 -9
- package/dist/runtime/host/spot-address-transport.js +47 -87
- package/dist/runtime/host/stateful-authority-route-runtime.d.ts +1 -0
- package/dist/runtime/host/stateful-authority-route-runtime.js +17 -9
- package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
- package/dist/runtime/locations/in-memory-authority-store.d.ts +2 -0
- package/dist/runtime/locations/in-memory-authority-store.js +22 -8
- package/dist/runtime/locations/in-memory-location-store.d.ts +1 -0
- package/dist/runtime/locations/in-memory-location-store.js +3 -0
- package/dist/runtime/locations/internal-store-contracts.d.ts +1 -1
- package/dist/runtime/locations/location-store-repository.d.ts +1 -0
- package/dist/runtime/locations/location-store-repository.js +98 -53
- package/dist/runtime/messaging/request-deadline.d.ts +3 -0
- package/dist/runtime/messaging/request-deadline.js +39 -0
- package/dist/runtime/messaging/submission-result.d.ts +2 -1
- package/dist/runtime/messaging/submission-result.js +11 -4
- package/dist/runtime/spots/spot-outbound.d.ts +3 -5
- package/dist/runtime/spots/spot-outbound.js +12 -15
- package/dist/runtime/spots/spot-serial-executor.js +4 -1
- package/dist/runtime/spots/spot-serial-turn-executor.d.ts +3 -0
- package/dist/runtime/spots/spot-serial-turn-executor.js +5 -0
- package/dist/runtime/streams/bound-actor-relay-sender.d.ts +2 -2
- package/dist/runtime/streams/bound-actor-relay-sender.js +14 -14
- package/dist/runtime/streams/bound-session-service.js +1 -1
- package/dist/runtime/streams/index.d.ts +2 -3
- package/dist/runtime/streams/index.js +6 -5
- package/dist/runtime/streams/managed-stream.d.ts +2 -2
- package/dist/runtime/streams/managed-stream.js +3 -5
- package/dist/runtime/streams/native-fallback-bound-session.js +13 -16
- package/dist/runtime/streams/protocol.js +0 -9
- package/dist/runtime/streams/session-calls.d.ts +5 -7
- package/dist/runtime/streams/session-calls.js +6 -23
- package/dist/runtime/streams/session-context.d.ts +4 -8
- package/dist/runtime/streams/session-context.js +3 -32
- package/dist/runtime/streams/session-serial-executor.d.ts +0 -1
- package/dist/runtime/streams/session-serial-executor.js +0 -3
- package/dist/runtime/streams/stream-message-utils.d.ts +0 -1
- package/dist/runtime/streams/stream-message-utils.js +0 -20
- package/dist/runtime/streams/stream-session-runtime.d.ts +6 -3
- package/dist/runtime/streams/stream-session-runtime.js +40 -85
- package/package.json +3 -3
- package/src/contracts/Actors/ZLinkActorClient.ts +1 -1
- package/src/contracts/Channels/Calls.ts +1 -1
- package/src/contracts/Configuration/Builders.ts +2 -1
- package/src/contracts/Configuration/Configs.ts +0 -1
- package/src/contracts/Configuration/InternalDefaults.ts +4 -0
- package/src/contracts/Configuration/RegistrationBuilders.ts +13 -3
- package/src/contracts/Configuration/RegistrationNormalizers.ts +25 -0
- package/src/contracts/Configuration/RegistrationTypes.ts +3 -5
- package/src/contracts/Configuration/RegistrationValidators.ts +0 -4
- package/src/contracts/Spots/Contracts.ts +1 -1
- package/src/contracts/Streams/BoundSessionContracts.ts +1 -1
- package/src/contracts/Streams/IZLinkSession.ts +2 -3
- package/src/contracts/Streams/IZLinkSessionActor.ts +2 -6
- package/src/runtime/actors/actor-client.ts +9 -15
- package/src/runtime/actors/actor-mailbox.ts +7 -2
- package/src/runtime/actors/actor-request-deadline.ts +4 -49
- package/src/runtime/actors/actor-routed-join-transport.ts +3 -5
- package/src/runtime/backend/contracts/index.ts +1 -8
- package/src/runtime/backend/node/node-backend-adapter-support.ts +45 -5
- package/src/runtime/backend/node/node-dealer-channel-client-transport.ts +0 -2
- package/src/runtime/backend/node/node-raw-binding-port.ts +15 -41
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -7
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +1 -1
- package/src/runtime/channels/channel-clients.ts +9 -19
- package/src/runtime/channels/channel-envelope.ts +16 -28
- package/src/runtime/channels/channel-outbound-operations.ts +36 -38
- package/src/runtime/channels/channel-runtime-manager.ts +6 -28
- package/src/runtime/channels/channel-socket-options.ts +0 -13
- package/src/runtime/channels/channel-socket-registry.ts +3 -17
- package/src/runtime/channels/channel-transports.ts +7 -33
- package/src/runtime/channels/local-spot-route-transport.ts +1 -5
- package/src/runtime/channels/router-socket-spot-route-transport.ts +2 -6
- package/src/runtime/channels/source-spot-router.ts +2 -9
- package/src/runtime/channels/spot-node-route-transport.ts +2 -7
- package/src/runtime/channels/spot-route-bridge-transport.ts +5 -12
- package/src/runtime/channels/spot-route-dispatch-strategy.ts +11 -30
- package/src/runtime/execution/serial-execution-queue.ts +117 -29
- package/src/runtime/foundation/operation-registry.ts +16 -9
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +17 -14
- package/src/runtime/foundation/service-authority-payload-codec.ts +0 -46
- package/src/runtime/foundation/service-stateful-registry.ts +10 -5
- package/src/runtime/foundation/service-stateful-runtime.ts +103 -181
- package/src/runtime/foundation/service-stateful-wire-codec.ts +0 -25
- package/src/runtime/foundation/service-topology-registry.ts +30 -20
- package/src/runtime/host/actor-packet-relay.ts +8 -15
- package/src/runtime/host/index.ts +2 -26
- package/src/runtime/host/instance-activation-authority.ts +58 -125
- package/src/runtime/host/remote-bound-session-relay.ts +8 -16
- package/src/runtime/host/spot-address-transport.ts +107 -166
- package/src/runtime/host/stateful-authority-route-runtime.ts +22 -9
- package/src/runtime/locations/domain-store-contract.ts +5 -0
- package/src/runtime/locations/in-memory-authority-store.ts +42 -8
- package/src/runtime/locations/in-memory-location-store.ts +8 -0
- package/src/runtime/locations/internal-store-contracts.ts +1 -0
- package/src/runtime/locations/location-store-repository.ts +130 -68
- package/src/runtime/messaging/request-deadline.ts +58 -0
- package/src/runtime/messaging/submission-result.ts +18 -4
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
- package/src/runtime/spots/spot-outbound.ts +14 -24
- package/src/runtime/spots/spot-serial-executor.ts +4 -1
- package/src/runtime/spots/spot-serial-turn-executor.ts +10 -0
- package/src/runtime/streams/bound-actor-relay-sender.ts +12 -26
- package/src/runtime/streams/bound-session-service.ts +1 -1
- package/src/runtime/streams/index.ts +5 -5
- package/src/runtime/streams/managed-stream.ts +3 -13
- package/src/runtime/streams/native-fallback-bound-session.ts +13 -25
- package/src/runtime/streams/protocol.ts +0 -13
- package/src/runtime/streams/session-calls.ts +8 -34
- package/src/runtime/streams/session-context.ts +6 -47
- package/src/runtime/streams/session-serial-executor.ts +0 -8
- package/src/runtime/streams/stream-message-utils.ts +1 -27
- package/src/runtime/streams/stream-session-runtime.ts +54 -97
- package/dist/runtime/streams/session-requests.d.ts +0 -25
- package/dist/runtime/streams/session-requests.js +0 -101
- package/src/runtime/streams/session-requests.ts +0 -110
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ZlinkStreamContentType } from '@zlink-systems/stream-wire';
|
|
2
2
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
|
-
import { ZLinkFrameworkException } from '../../contracts';
|
|
3
|
+
import { ZLinkFrameworkErrorKind, ZLinkFrameworkException } from '../../contracts';
|
|
4
4
|
import type { ZLinkAuthoritySnapshot } from '../locations/internal-location-contracts';
|
|
5
5
|
import { decodeServiceClosingSpotAuthority } from './service-authority-payload-codec';
|
|
6
6
|
import {
|
|
@@ -280,9 +280,10 @@ export type ServiceInstanceAuthorityRead =
|
|
|
280
280
|
readonly authority?: ZLinkAuthoritySnapshot;
|
|
281
281
|
};
|
|
282
282
|
|
|
283
|
-
export type ServiceInstanceAuthorityReserve =
|
|
284
|
-
|
|
285
|
-
|
|
283
|
+
export type ServiceInstanceAuthorityReserve = {
|
|
284
|
+
readonly kind: 'reserved';
|
|
285
|
+
readonly reservation: ServiceInstanceActivationReservation;
|
|
286
|
+
};
|
|
286
287
|
|
|
287
288
|
/**
|
|
288
289
|
* Synchronous authority port used by the raw ingress turn. An asynchronous
|
|
@@ -337,13 +338,6 @@ export interface ServiceAsyncInstanceActivationAuthority {
|
|
|
337
338
|
): Promise<void>;
|
|
338
339
|
}
|
|
339
340
|
|
|
340
|
-
export class ServiceInstanceActivationRedirectError extends Error {
|
|
341
|
-
constructor(readonly route: ServiceInstanceRouteFence) {
|
|
342
|
-
super(`Instance Spot '${route.targetSpotId}' is owned by '${route.targetNodeRid}'.`);
|
|
343
|
-
this.name = 'ServiceInstanceActivationRedirectError';
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
341
|
export interface ServiceUserSpotOperationResult {
|
|
348
342
|
readonly terminalResult: number;
|
|
349
343
|
readonly failureCode: number;
|
|
@@ -980,7 +974,7 @@ export class ServiceStatefulRuntime {
|
|
|
980
974
|
const committed = await authority.commit(target, reservation, spot);
|
|
981
975
|
if (committed.kind !== 'committed') {
|
|
982
976
|
this.registry.closeSpot(spot.ref);
|
|
983
|
-
throw
|
|
977
|
+
throw activationRefused();
|
|
984
978
|
}
|
|
985
979
|
return this.finishRecoveredInstanceActivation(envelope, committed.route, expectedCurrentRoute);
|
|
986
980
|
}
|
|
@@ -2069,6 +2063,12 @@ export class ServiceStatefulRuntime {
|
|
|
2069
2063
|
metadata ? record.parts[1] : undefined
|
|
2070
2064
|
);
|
|
2071
2065
|
}
|
|
2066
|
+
if (
|
|
2067
|
+
decoded.kind === 'instanceSpot' &&
|
|
2068
|
+
(decoded.operationKind === 'request') !== (record.requestSequence !== undefined)
|
|
2069
|
+
) {
|
|
2070
|
+
return 'protocolError';
|
|
2071
|
+
}
|
|
2072
2072
|
const hasPayload = [
|
|
2073
2073
|
'spotSend',
|
|
2074
2074
|
'spotRequest',
|
|
@@ -2131,6 +2131,14 @@ export class ServiceStatefulRuntime {
|
|
|
2131
2131
|
canonicalPayload
|
|
2132
2132
|
);
|
|
2133
2133
|
} catch (error) {
|
|
2134
|
+
if (
|
|
2135
|
+
decoded.kind === 'instanceSpot' &&
|
|
2136
|
+
decoded.activation === 'missing' &&
|
|
2137
|
+
(error instanceof ZLinkFrameworkException || error instanceof ServiceStaleGenerationError)
|
|
2138
|
+
) {
|
|
2139
|
+
this.finishMissingInstanceActivationFailure(record, decoded, error);
|
|
2140
|
+
return 'infrastructure';
|
|
2141
|
+
}
|
|
2134
2142
|
const correlation = statefulCorrelation(decoded);
|
|
2135
2143
|
if (correlation !== undefined) {
|
|
2136
2144
|
const result = failure(error);
|
|
@@ -2243,7 +2251,7 @@ export class ServiceStatefulRuntime {
|
|
|
2243
2251
|
sourceSpotId: record.sourceSpotId,
|
|
2244
2252
|
targetSpot: record.target.spot,
|
|
2245
2253
|
...(record.kind === 'spotRequest'
|
|
2246
|
-
? { reply: this.replyPort(ingress, record.correlation
|
|
2254
|
+
? { reply: this.replyPort(ingress, record.correlation) }
|
|
2247
2255
|
: {})
|
|
2248
2256
|
}
|
|
2249
2257
|
);
|
|
@@ -2291,7 +2299,7 @@ export class ServiceStatefulRuntime {
|
|
|
2291
2299
|
originalReplyRouteId: ingress.requestSequence ?? 0n
|
|
2292
2300
|
},
|
|
2293
2301
|
...(record.kind === 'actorRequest'
|
|
2294
|
-
? { reply: this.replyPort(ingress, record.correlation
|
|
2302
|
+
? { reply: this.replyPort(ingress, record.correlation) }
|
|
2295
2303
|
: {})
|
|
2296
2304
|
}
|
|
2297
2305
|
);
|
|
@@ -2344,7 +2352,8 @@ export class ServiceStatefulRuntime {
|
|
|
2344
2352
|
ingress: RawServiceIngressRecord,
|
|
2345
2353
|
record: Extract<ServiceStatefulWireRecord, { readonly kind: 'instanceSpot' }>,
|
|
2346
2354
|
payloadFrame: Buffer,
|
|
2347
|
-
metadataFrame?: Buffer
|
|
2355
|
+
metadataFrame?: Buffer,
|
|
2356
|
+
localReply?: NonNullable<ServiceStatefulMailboxData['reply']>
|
|
2348
2357
|
): RawServicePumpResult {
|
|
2349
2358
|
if (record.activation === 'missing' && this.asyncInstanceAuthority !== undefined) {
|
|
2350
2359
|
const ownedIngress = shareIngressOwnership(ingress);
|
|
@@ -2352,7 +2361,7 @@ export class ServiceStatefulRuntime {
|
|
|
2352
2361
|
ownedIngress,
|
|
2353
2362
|
record,
|
|
2354
2363
|
payloadFrame,
|
|
2355
|
-
|
|
2364
|
+
localReply,
|
|
2356
2365
|
metadataFrame
|
|
2357
2366
|
).finally(() => ownedIngress.applicationJobOwner?.close());
|
|
2358
2367
|
return 'infrastructure';
|
|
@@ -2384,7 +2393,7 @@ export class ServiceStatefulRuntime {
|
|
|
2384
2393
|
record,
|
|
2385
2394
|
payloadFrame,
|
|
2386
2395
|
spot,
|
|
2387
|
-
|
|
2396
|
+
localReply,
|
|
2388
2397
|
this.instanceApplicationTerminalCompletion(applicationTarget),
|
|
2389
2398
|
metadataFrame
|
|
2390
2399
|
);
|
|
@@ -2440,7 +2449,10 @@ export class ServiceStatefulRuntime {
|
|
|
2440
2449
|
}
|
|
2441
2450
|
const directReply =
|
|
2442
2451
|
record.operationKind === 'request'
|
|
2443
|
-
? (localReply ??
|
|
2452
|
+
? (localReply ??
|
|
2453
|
+
(ingress.requestSequence === undefined
|
|
2454
|
+
? undefined
|
|
2455
|
+
: this.replyPort(ingress, record.replyRouteId)))
|
|
2444
2456
|
: undefined;
|
|
2445
2457
|
const applicationTarget =
|
|
2446
2458
|
applicationTargetOverride ??
|
|
@@ -2475,7 +2487,7 @@ export class ServiceStatefulRuntime {
|
|
|
2475
2487
|
...(metadataFrame === undefined ? {} : { applicationMetadata: metadataFrame }),
|
|
2476
2488
|
...(terminalCompletion === undefined ? {} : { onTerminalCompletion: terminalCompletion }),
|
|
2477
2489
|
...(onHandlerTurnStarted === undefined ? {} : { onHandlerTurnStarted }),
|
|
2478
|
-
...(record.operationKind === 'request'
|
|
2490
|
+
...(record.operationKind === 'request' && directReply !== undefined
|
|
2479
2491
|
? {
|
|
2480
2492
|
reply:
|
|
2481
2493
|
onTerminalCompletion === undefined
|
|
@@ -2530,30 +2542,7 @@ export class ServiceStatefulRuntime {
|
|
|
2530
2542
|
throw new Error('Activated Instance message was not admitted to the local queue.');
|
|
2531
2543
|
}
|
|
2532
2544
|
} catch (error) {
|
|
2533
|
-
|
|
2534
|
-
if (error instanceof ServiceInstanceActivationRedirectError && !this.closed) {
|
|
2535
|
-
try {
|
|
2536
|
-
await this.relayMissingInstanceActivation(
|
|
2537
|
-
record,
|
|
2538
|
-
payloadFrame,
|
|
2539
|
-
error.route,
|
|
2540
|
-
ingress,
|
|
2541
|
-
localReply,
|
|
2542
|
-
metadataFrame
|
|
2543
|
-
);
|
|
2544
|
-
return;
|
|
2545
|
-
} catch (relayError) {
|
|
2546
|
-
terminalError = relayError;
|
|
2547
|
-
}
|
|
2548
|
-
}
|
|
2549
|
-
if (record.operationKind !== 'request') return;
|
|
2550
|
-
const result = failure(terminalError);
|
|
2551
|
-
if (localReply !== undefined) {
|
|
2552
|
-
localReply(result.terminalResult, result.failureCode);
|
|
2553
|
-
} else {
|
|
2554
|
-
if (record.replyRouteId === undefined) return;
|
|
2555
|
-
this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
|
|
2556
|
-
}
|
|
2545
|
+
this.finishMissingInstanceActivationFailure(ingress, record, error, localReply);
|
|
2557
2546
|
}
|
|
2558
2547
|
}
|
|
2559
2548
|
|
|
@@ -2604,7 +2593,12 @@ export class ServiceStatefulRuntime {
|
|
|
2604
2593
|
throw new Error('Rematerialized Instance message was not admitted to the local queue.');
|
|
2605
2594
|
}
|
|
2606
2595
|
} catch (error) {
|
|
2607
|
-
if (
|
|
2596
|
+
if (
|
|
2597
|
+
record.operationKind !== 'request' ||
|
|
2598
|
+
record.replyRouteId === undefined ||
|
|
2599
|
+
ingress.requestSequence === undefined
|
|
2600
|
+
)
|
|
2601
|
+
return;
|
|
2608
2602
|
const result = failure(error);
|
|
2609
2603
|
this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
|
|
2610
2604
|
}
|
|
@@ -2664,10 +2658,11 @@ export class ServiceStatefulRuntime {
|
|
|
2664
2658
|
|
|
2665
2659
|
private instanceApplicationTerminalCompletion(
|
|
2666
2660
|
target: ServiceInstanceApplicationTarget
|
|
2667
|
-
): () => Promise<void> {
|
|
2661
|
+
): (() => Promise<void>) | undefined {
|
|
2662
|
+
const lifecycle = this.instanceApplicationLifecycle;
|
|
2663
|
+
if (lifecycle === undefined) return undefined;
|
|
2668
2664
|
return async () => {
|
|
2669
|
-
const authorityReleased =
|
|
2670
|
-
(await this.instanceApplicationLifecycle?.completeTerminal(target)) ?? false;
|
|
2665
|
+
const authorityReleased = await lifecycle.completeTerminal(target);
|
|
2671
2666
|
if (!authorityReleased) return;
|
|
2672
2667
|
const current = this.instanceIntents.get(target.targetSpotId);
|
|
2673
2668
|
if (
|
|
@@ -2731,91 +2726,39 @@ export class ServiceStatefulRuntime {
|
|
|
2731
2726
|
for (const waiter of waiters) waiter();
|
|
2732
2727
|
}
|
|
2733
2728
|
|
|
2734
|
-
private
|
|
2729
|
+
private finishMissingInstanceActivationFailure(
|
|
2730
|
+
ingress: RawServiceIngressRecord,
|
|
2735
2731
|
record: Extract<
|
|
2736
2732
|
ServiceStatefulWireRecord,
|
|
2737
2733
|
{ readonly kind: 'instanceSpot'; readonly activation: 'missing' }
|
|
2738
2734
|
>,
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
const redirectedTarget: ServiceInstanceActivationTarget = {
|
|
2750
|
-
...record.target,
|
|
2751
|
-
targetNodeRid: route.targetNodeRid,
|
|
2752
|
-
targetNodeGeneration: route.targetNodeGeneration,
|
|
2753
|
-
targetSpotId: route.targetSpotId
|
|
2754
|
-
};
|
|
2755
|
-
if (record.operationKind === 'send') {
|
|
2756
|
-
const submitted = await this.submitOneWay(
|
|
2757
|
-
route.targetNodeRid,
|
|
2758
|
-
instanceOperationParts(
|
|
2759
|
-
[
|
|
2760
|
-
encodeInstanceSpotActivationHeader(
|
|
2761
|
-
redirectedTarget,
|
|
2762
|
-
this.nodeGeneration,
|
|
2763
|
-
this.nodeRid,
|
|
2764
|
-
record.sourceSpotId,
|
|
2765
|
-
'send',
|
|
2766
|
-
record.operation,
|
|
2767
|
-
record.deadlineUnixMs,
|
|
2768
|
-
undefined,
|
|
2769
|
-
metadataFrame !== undefined
|
|
2770
|
-
),
|
|
2771
|
-
payloadFrame
|
|
2772
|
-
],
|
|
2773
|
-
metadataFrame
|
|
2774
|
-
)
|
|
2775
|
-
);
|
|
2776
|
-
if (submitted !== SubmitResult.Ok) {
|
|
2777
|
-
throw new Error(`Instance activation redirect was not admitted: ${submitted}.`);
|
|
2735
|
+
error: unknown,
|
|
2736
|
+
localReply?: NonNullable<ServiceStatefulMailboxData['reply']>
|
|
2737
|
+
): void {
|
|
2738
|
+
if (record.operationKind === 'request') {
|
|
2739
|
+
if (record.deadlineUnixMs <= BigInt(Date.now())) return;
|
|
2740
|
+
const result = failure(error);
|
|
2741
|
+
if (localReply !== undefined) {
|
|
2742
|
+
localReply(result.terminalResult, result.failureCode);
|
|
2743
|
+
} else if (ingress.requestSequence !== undefined && record.replyRouteId !== undefined) {
|
|
2744
|
+
this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
|
|
2778
2745
|
}
|
|
2779
2746
|
return;
|
|
2780
2747
|
}
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
record.deadlineUnixMs,
|
|
2796
|
-
pending.id,
|
|
2797
|
-
metadataFrame !== undefined
|
|
2798
|
-
),
|
|
2799
|
-
payloadFrame
|
|
2800
|
-
],
|
|
2801
|
-
metadataFrame
|
|
2802
|
-
),
|
|
2803
|
-
remainingMs,
|
|
2804
|
-
'instanceSpotRequest'
|
|
2805
|
-
);
|
|
2806
|
-
const result = await pending.promise;
|
|
2807
|
-
if (localReply !== undefined) {
|
|
2808
|
-
localReply(result.terminalResult, result.failureCode, result.payload);
|
|
2809
|
-
return;
|
|
2810
|
-
}
|
|
2811
|
-
if (record.replyRouteId !== undefined) {
|
|
2812
|
-
this.replyWire(
|
|
2813
|
-
ingress,
|
|
2814
|
-
record.replyRouteId,
|
|
2815
|
-
result.terminalResult,
|
|
2816
|
-
result.failureCode,
|
|
2817
|
-
result.payload
|
|
2818
|
-
);
|
|
2748
|
+
const reporter = this.dispatchErrors;
|
|
2749
|
+
if (reporter?.captureEnabled() === true) {
|
|
2750
|
+
reporter.report({
|
|
2751
|
+
surface: ZLinkDispatchErrorSurface.InstanceSpot,
|
|
2752
|
+
messageKind: ZLinkDispatchMessageKind.Send,
|
|
2753
|
+
reason: ZLinkDispatchErrorReason.StaleTarget,
|
|
2754
|
+
action: ZLinkDispatchErrorAction.Drop,
|
|
2755
|
+
meshName: this.dispatchErrorMeshName,
|
|
2756
|
+
sourceRid: record.sourceNodeRid,
|
|
2757
|
+
spotId: record.target.targetSpotId,
|
|
2758
|
+
instanceSpotType: record.target.stableType,
|
|
2759
|
+
activationState: 'activating',
|
|
2760
|
+
error
|
|
2761
|
+
});
|
|
2819
2762
|
}
|
|
2820
2763
|
}
|
|
2821
2764
|
|
|
@@ -2971,13 +2914,10 @@ export class ServiceStatefulRuntime {
|
|
|
2971
2914
|
return local;
|
|
2972
2915
|
}
|
|
2973
2916
|
if (current.kind === 'ready') {
|
|
2974
|
-
throw
|
|
2917
|
+
throw activationRefused();
|
|
2975
2918
|
}
|
|
2976
2919
|
|
|
2977
2920
|
const reserved = authority.reserve(target, record.operation, record.deadlineUnixMs);
|
|
2978
|
-
if (reserved.kind === 'ready') {
|
|
2979
|
-
throw new ServiceInstanceActivationRedirectError(reserved.route);
|
|
2980
|
-
}
|
|
2981
2921
|
|
|
2982
2922
|
let activation: { readonly spot: ServiceSpotState; readonly created: boolean };
|
|
2983
2923
|
try {
|
|
@@ -2994,7 +2934,7 @@ export class ServiceStatefulRuntime {
|
|
|
2994
2934
|
const committed = authority.commit(target, reserved.reservation, activation.spot);
|
|
2995
2935
|
if (committed.kind === 'lost') {
|
|
2996
2936
|
if (activation.created) this.registry.closeSpot(activation.spot.ref);
|
|
2997
|
-
throw
|
|
2937
|
+
throw activationRefused();
|
|
2998
2938
|
}
|
|
2999
2939
|
if (!routeMatchesLocal(committed.route, activation.spot, this.nodeRid, this.nodeGeneration)) {
|
|
3000
2940
|
if (activation.created) this.registry.closeSpot(activation.spot.ref);
|
|
@@ -3073,14 +3013,14 @@ export class ServiceStatefulRuntime {
|
|
|
3073
3013
|
}
|
|
3074
3014
|
};
|
|
3075
3015
|
}
|
|
3016
|
+
if (current.kind === 'ready' && current.route.targetNodeRid !== this.nodeRid) {
|
|
3017
|
+
throw activationRefused();
|
|
3018
|
+
}
|
|
3076
3019
|
if (
|
|
3077
3020
|
current.kind === 'ready' &&
|
|
3078
3021
|
current.authority !== undefined &&
|
|
3079
3022
|
decodeServiceClosingSpotAuthority(current.authority.payload) !== undefined
|
|
3080
3023
|
) {
|
|
3081
|
-
if (current.route.targetNodeRid !== this.nodeRid) {
|
|
3082
|
-
throw new ServiceInstanceActivationRedirectError(current.route);
|
|
3083
|
-
}
|
|
3084
3024
|
if (
|
|
3085
3025
|
current.route.targetNodeGeneration !== this.nodeGeneration ||
|
|
3086
3026
|
(local !== undefined &&
|
|
@@ -3095,17 +3035,11 @@ export class ServiceStatefulRuntime {
|
|
|
3095
3035
|
};
|
|
3096
3036
|
}
|
|
3097
3037
|
if (!this.acceptsLocalMissingInstancePlacement()) {
|
|
3098
|
-
if (current.kind === 'ready' && current.route.targetNodeRid !== this.nodeRid) {
|
|
3099
|
-
throw new ServiceInstanceActivationRedirectError(current.route);
|
|
3100
|
-
}
|
|
3101
3038
|
// A draining or retiring node must not create a successor generation
|
|
3102
3039
|
// from a stale client route while relocation is publishing its target.
|
|
3103
3040
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3104
3041
|
}
|
|
3105
3042
|
if (current.kind === 'ready') {
|
|
3106
|
-
if (current.route.targetNodeRid !== this.nodeRid) {
|
|
3107
|
-
throw new ServiceInstanceActivationRedirectError(current.route);
|
|
3108
|
-
}
|
|
3109
3043
|
if (current.route.targetNodeGeneration !== this.nodeGeneration) {
|
|
3110
3044
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3111
3045
|
}
|
|
@@ -3148,16 +3082,9 @@ export class ServiceStatefulRuntime {
|
|
|
3148
3082
|
await lifecycle.discard(target);
|
|
3149
3083
|
materialized = lifecycle.isMaterialized(target);
|
|
3150
3084
|
}
|
|
3151
|
-
// The
|
|
3152
|
-
//
|
|
3153
|
-
//
|
|
3154
|
-
// A newer reservation can also become visible after an explicit close
|
|
3155
|
-
// releases authority but before local disposal removes the old
|
|
3156
|
-
// projection. The close gate serializes that replacement; rejecting it
|
|
3157
|
-
// here turns a valid next-generation Instance intent into a stale-route
|
|
3158
|
-
// failure.
|
|
3159
|
-
// If disposal did not remove an orphaned materialization, keep fencing
|
|
3160
|
-
// it instead of admitting work into two application generations.
|
|
3085
|
+
// The local projection does not decide reservation ownership. Preserve
|
|
3086
|
+
// the Creating fence for Reserve, which refuses a competing attempt.
|
|
3087
|
+
// Keep fencing an orphan that disposal did not remove.
|
|
3161
3088
|
if (
|
|
3162
3089
|
(!joinsCreating && current.kind !== 'missing') ||
|
|
3163
3090
|
(!joinsCreating && materialized !== false && !materializing)
|
|
@@ -3186,9 +3113,6 @@ export class ServiceStatefulRuntime {
|
|
|
3186
3113
|
} finally {
|
|
3187
3114
|
deadline.close();
|
|
3188
3115
|
}
|
|
3189
|
-
if (reserved.kind === 'ready') {
|
|
3190
|
-
throw new ServiceInstanceActivationRedirectError(reserved.route);
|
|
3191
|
-
}
|
|
3192
3116
|
if (this.closed || record.deadlineUnixMs < BigInt(Date.now())) {
|
|
3193
3117
|
await authority.abort(target, reserved.reservation);
|
|
3194
3118
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
@@ -3224,7 +3148,7 @@ export class ServiceStatefulRuntime {
|
|
|
3224
3148
|
if (committed.kind === 'lost') {
|
|
3225
3149
|
if (activation.created) this.registry.closeSpot(activation.spot.ref);
|
|
3226
3150
|
await this.instanceApplicationLifecycle?.discard(target);
|
|
3227
|
-
throw
|
|
3151
|
+
throw activationRefused();
|
|
3228
3152
|
}
|
|
3229
3153
|
if (!routeMatchesLocal(committed.route, activation.spot, this.nodeRid, this.nodeGeneration)) {
|
|
3230
3154
|
if (activation.created) this.registry.closeSpot(activation.spot.ref);
|
|
@@ -3269,7 +3193,7 @@ export class ServiceStatefulRuntime {
|
|
|
3269
3193
|
const confirmed = await authority.read(target);
|
|
3270
3194
|
if (confirmed.kind !== 'ready' || !sameInstanceRoute(confirmed.route, route)) {
|
|
3271
3195
|
if (confirmed.kind === 'ready' && confirmed.route.targetNodeRid !== this.nodeRid) {
|
|
3272
|
-
throw
|
|
3196
|
+
throw activationRefused();
|
|
3273
3197
|
}
|
|
3274
3198
|
throw new ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
3275
3199
|
}
|
|
@@ -3404,7 +3328,7 @@ export class ServiceStatefulRuntime {
|
|
|
3404
3328
|
kindData: control,
|
|
3405
3329
|
...(canonicalApplicationPayload === undefined ? {} : { canonicalApplicationPayload }),
|
|
3406
3330
|
reply: (terminalResult, failureCode, replyPayload, tail) => {
|
|
3407
|
-
return this.replyPort(ingress, record.correlation
|
|
3331
|
+
return this.replyPort(ingress, record.correlation)(
|
|
3408
3332
|
terminalResult,
|
|
3409
3333
|
failureCode,
|
|
3410
3334
|
replyPayload,
|
|
@@ -4037,14 +3961,12 @@ export class ServiceStatefulRuntime {
|
|
|
4037
3961
|
|
|
4038
3962
|
private replyPort(
|
|
4039
3963
|
ingress: RawServiceIngressRecord,
|
|
4040
|
-
correlation: bigint | undefined
|
|
4041
|
-
operationKind: 'spotRequest' | 'actorRequest' | 'actorJoin' | 'instanceSpotRequest'
|
|
3964
|
+
correlation: bigint | undefined
|
|
4042
3965
|
): NonNullable<ServiceStatefulMailboxData['reply']> {
|
|
4043
3966
|
if (correlation === undefined)
|
|
4044
3967
|
throw new ServiceWireProtocolError('Request correlation is missing.');
|
|
4045
3968
|
return (terminalResult, failureCode, payload, tail) => {
|
|
4046
3969
|
this.replyWire(ingress, correlation, terminalResult, failureCode, payload, tail);
|
|
4047
|
-
void operationKind;
|
|
4048
3970
|
return true;
|
|
4049
3971
|
};
|
|
4050
3972
|
}
|
|
@@ -4057,10 +3979,11 @@ export class ServiceStatefulRuntime {
|
|
|
4057
3979
|
payload?: ServiceApplicationPayload,
|
|
4058
3980
|
tail?: ServiceStatefulReplyTail
|
|
4059
3981
|
): void {
|
|
4060
|
-
|
|
3982
|
+
const parts = [
|
|
4061
3983
|
encodeStatefulReply(correlation, terminalResult, failureCode, tail),
|
|
4062
3984
|
...(payload === undefined ? [] : [encodeApplicationPayload(payload)])
|
|
4063
|
-
]
|
|
3985
|
+
];
|
|
3986
|
+
this.raw.replyService(ingress, parts);
|
|
4064
3987
|
}
|
|
4065
3988
|
|
|
4066
3989
|
private handleUserSpotOperation(
|
|
@@ -4763,23 +4686,17 @@ export class ServiceStatefulRuntime {
|
|
|
4763
4686
|
return;
|
|
4764
4687
|
}
|
|
4765
4688
|
if (decoded.kind === 'instanceSpot') {
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
targetSpot: activation.ref,
|
|
4778
|
-
reply: localReply
|
|
4779
|
-
});
|
|
4780
|
-
} catch (error) {
|
|
4781
|
-
this.operations.reply(pending.id, failure(error));
|
|
4782
|
-
}
|
|
4689
|
+
const hasMetadata = (ingress.flags & M6bServiceWireFlag.metadata) !== 0;
|
|
4690
|
+
const metadataFrame = hasMetadata
|
|
4691
|
+
? validateServiceMetadataFrame(ingress.parts[1]!)
|
|
4692
|
+
: undefined;
|
|
4693
|
+
this.enqueueInstanceSpot(
|
|
4694
|
+
ingress,
|
|
4695
|
+
decoded,
|
|
4696
|
+
ingress.parts[hasMetadata ? 2 : 1]!,
|
|
4697
|
+
metadataFrame,
|
|
4698
|
+
localReply
|
|
4699
|
+
);
|
|
4783
4700
|
return;
|
|
4784
4701
|
}
|
|
4785
4702
|
this.operations.reply(pending.id, {
|
|
@@ -4790,7 +4707,7 @@ export class ServiceStatefulRuntime {
|
|
|
4790
4707
|
}
|
|
4791
4708
|
|
|
4792
4709
|
private completeRemoteReply(
|
|
4793
|
-
pending: ServiceStatefulPendingOperation,
|
|
4710
|
+
pending: Pick<ServiceStatefulPendingOperation, 'id'>,
|
|
4794
4711
|
targetNodeRid: string,
|
|
4795
4712
|
operationKind:
|
|
4796
4713
|
| 'spotRequest'
|
|
@@ -5038,9 +4955,7 @@ export class ServiceStatefulRuntime {
|
|
|
5038
4955
|
...(wire.correlation === undefined ? {} : { correlation: wire.correlation }),
|
|
5039
4956
|
sourceSpotId: wire.sourceSpotId,
|
|
5040
4957
|
targetSpot: wire.target.spot,
|
|
5041
|
-
...(wire.kind === 'spotRequest'
|
|
5042
|
-
? { reply: this.replyPort(ingress, wire.correlation, 'spotRequest') }
|
|
5043
|
-
: {})
|
|
4958
|
+
...(wire.kind === 'spotRequest' ? { reply: this.replyPort(ingress, wire.correlation) } : {})
|
|
5044
4959
|
}
|
|
5045
4960
|
);
|
|
5046
4961
|
}
|
|
@@ -5523,6 +5438,13 @@ function actorLocation(actor: ServiceActorState): ActorLocation {
|
|
|
5523
5438
|
};
|
|
5524
5439
|
}
|
|
5525
5440
|
|
|
5441
|
+
function activationRefused(): ZLinkFrameworkException {
|
|
5442
|
+
return new ZLinkFrameworkException(
|
|
5443
|
+
ZLinkFrameworkErrorKind.Unavailable,
|
|
5444
|
+
'Instance cold activation authority was refused.'
|
|
5445
|
+
);
|
|
5446
|
+
}
|
|
5447
|
+
|
|
5526
5448
|
function failure(error: unknown): ServiceStatefulResult {
|
|
5527
5449
|
if (error instanceof ServiceStaleGenerationError) {
|
|
5528
5450
|
return internalFrameworkWireReply(ZLinkFrameworkInternalErrorKind.ActorLocationStale);
|
|
@@ -167,8 +167,6 @@ export type ServiceWireRelocationObject =
|
|
|
167
167
|
readonly objectGeneration: bigint;
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
-
/** Shared CRC-32C wire bounds for the direct relocation payload transfer. */
|
|
171
|
-
export const RELOCATION_PAYLOAD_TOTAL_LENGTH_MAX = 274_877_906_944n;
|
|
172
170
|
export const RELOCATION_PAYLOAD_CHUNK_COUNT_MAX = 4096;
|
|
173
171
|
export { RELOCATION_STATE_CHUNK_DATA_MAX_BYTES } from '../../contracts/Configuration/InternalDefaults';
|
|
174
172
|
|
|
@@ -2658,29 +2656,6 @@ export function decodeServiceWireFrozenRecord(bytes: Uint8Array): ServiceWireFro
|
|
|
2658
2656
|
return { ...record, canonicalBytes: Buffer.from(bytes) };
|
|
2659
2657
|
}
|
|
2660
2658
|
|
|
2661
|
-
/**
|
|
2662
|
-
* Consumes exactly one canonical frozen record at `offset`. Relocation
|
|
2663
|
-
* saved-work is a vector of back-to-back records, so its boundary is the
|
|
2664
|
-
* record grammar itself rather than an enclosing length field.
|
|
2665
|
-
*/
|
|
2666
|
-
export function decodeServiceWireFrozenRecordPrefix(
|
|
2667
|
-
bytes: Uint8Array,
|
|
2668
|
-
offset = 0
|
|
2669
|
-
): { readonly record: ServiceWireFrozenRecord; readonly length: number } {
|
|
2670
|
-
if (!Number.isSafeInteger(offset) || offset < 0 || offset >= bytes.byteLength) {
|
|
2671
|
-
fail('Frozen record offset is outside the encoded bytes.');
|
|
2672
|
-
}
|
|
2673
|
-
const reader = new FrozenReader(bytes.subarray(offset));
|
|
2674
|
-
const record = decodeServiceWireFrozenRecordFrom(reader);
|
|
2675
|
-
return {
|
|
2676
|
-
record: {
|
|
2677
|
-
...record,
|
|
2678
|
-
canonicalBytes: Buffer.from(bytes.subarray(offset, offset + reader.position))
|
|
2679
|
-
},
|
|
2680
|
-
length: reader.position
|
|
2681
|
-
};
|
|
2682
|
-
}
|
|
2683
|
-
|
|
2684
2659
|
function decodeServiceWireFrozenRecordFrom(reader: FrozenReader): ServiceWireFrozenRecord {
|
|
2685
2660
|
const recordKind = reader.u8('recordKind');
|
|
2686
2661
|
if (recordKind < 1 || recordKind > 14) fail('Invalid frozen record kind.');
|
|
@@ -305,35 +305,46 @@ export class ServiceTopologyRegistry {
|
|
|
305
305
|
stableType: string,
|
|
306
306
|
isReady: (descriptor: ServiceNodeDescriptor) => boolean = () => true
|
|
307
307
|
): ServiceNodeDescriptor | undefined {
|
|
308
|
+
return this.selectObjectPlacementWithStatus(stableType, isReady).descriptor;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
selectObjectPlacementWithStatus(
|
|
312
|
+
stableType: string,
|
|
313
|
+
isReady: (descriptor: ServiceNodeDescriptor) => boolean = () => true
|
|
314
|
+
): {
|
|
315
|
+
readonly status: ServiceObjectPlacementStatus;
|
|
316
|
+
readonly descriptor?: ServiceNodeDescriptor;
|
|
317
|
+
} {
|
|
308
318
|
requireText(stableType, 'stableType');
|
|
309
|
-
|
|
319
|
+
const candidates = this.objectPlacementCandidates(stableType, isReady);
|
|
320
|
+
if (candidates.status !== 'available') return { status: candidates.status };
|
|
321
|
+
const descriptor = this.selectWeightedCycle(
|
|
310
322
|
`object:${stableType}`,
|
|
311
|
-
() =>
|
|
312
|
-
const capability = `object-type:${stableType}`;
|
|
313
|
-
return [this.local, ...this.peersByRid.values()]
|
|
314
|
-
.map((value) => ('descriptor' in value ? value.descriptor : value))
|
|
315
|
-
.filter(
|
|
316
|
-
(descriptor) =>
|
|
317
|
-
descriptor.state === 'serving' &&
|
|
318
|
-
descriptor.objectRole === 'server' &&
|
|
319
|
-
descriptor.placementWeight > 0 &&
|
|
320
|
-
descriptor.activeCapacityUsed < descriptor.activeCapacityLimit &&
|
|
321
|
-
descriptor.pendingCapacityUsed < descriptor.pendingCapacityLimit &&
|
|
322
|
-
descriptor.protocolCapabilities.includes(capability)
|
|
323
|
-
)
|
|
324
|
-
.map(cloneDescriptor);
|
|
325
|
-
},
|
|
323
|
+
() => this.objectPlacementCandidates(stableType).eligible.map(cloneDescriptor),
|
|
326
324
|
(descriptor) => descriptor.placementWeight,
|
|
327
325
|
(descriptor) => descriptor.nodeRoutingId,
|
|
328
326
|
(left, right) => compareOrdinal(left.nodeRoutingId, right.nodeRoutingId),
|
|
329
327
|
isReady
|
|
330
328
|
);
|
|
329
|
+
return descriptor === undefined
|
|
330
|
+
? { status: 'unavailable' }
|
|
331
|
+
: { status: 'available', descriptor };
|
|
331
332
|
}
|
|
332
333
|
|
|
333
334
|
objectPlacementStatus(
|
|
334
335
|
stableType: string,
|
|
335
336
|
isReady: (descriptor: ServiceNodeDescriptor) => boolean = () => true
|
|
336
337
|
): ServiceObjectPlacementStatus {
|
|
338
|
+
return this.objectPlacementCandidates(stableType, isReady).status;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private objectPlacementCandidates(
|
|
342
|
+
stableType: string,
|
|
343
|
+
isReady: (descriptor: ServiceNodeDescriptor) => boolean = () => true
|
|
344
|
+
): {
|
|
345
|
+
readonly status: ServiceObjectPlacementStatus;
|
|
346
|
+
readonly eligible: readonly ServiceNodeDescriptor[];
|
|
347
|
+
} {
|
|
337
348
|
requireText(stableType, 'stableType');
|
|
338
349
|
const capability = `object-type:${stableType}`;
|
|
339
350
|
const supported = [
|
|
@@ -345,16 +356,15 @@ export class ServiceTopologyRegistry {
|
|
|
345
356
|
descriptor.objectRole === 'server' &&
|
|
346
357
|
descriptor.protocolCapabilities.includes(capability)
|
|
347
358
|
);
|
|
348
|
-
if (supported.length === 0) return 'unsupported';
|
|
359
|
+
if (supported.length === 0) return { status: 'unsupported', eligible: [] };
|
|
349
360
|
const withCapacity = supported.filter(
|
|
350
361
|
(descriptor) =>
|
|
351
362
|
descriptor.activeCapacityUsed < descriptor.activeCapacityLimit &&
|
|
352
363
|
descriptor.pendingCapacityUsed < descriptor.pendingCapacityLimit
|
|
353
364
|
);
|
|
354
|
-
if (withCapacity.length === 0) return 'capacity';
|
|
365
|
+
if (withCapacity.length === 0) return { status: 'capacity', eligible: [] };
|
|
355
366
|
const weighted = withCapacity.filter((descriptor) => descriptor.placementWeight > 0);
|
|
356
|
-
|
|
357
|
-
return weighted.some(isReady) ? 'available' : 'unavailable';
|
|
367
|
+
return { status: weighted.some(isReady) ? 'available' : 'unavailable', eligible: weighted };
|
|
358
368
|
}
|
|
359
369
|
|
|
360
370
|
instanceSpotPlacementTypes(): readonly string[] {
|