@zlink-systems/framework 0.28.0 → 0.29.1
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/Configuration/Builders.d.ts +2 -0
- 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 -0
- package/dist/runtime/actors/actor-mailbox.d.ts +2 -2
- package/dist/runtime/actors/actor-mailbox.js +2 -2
- package/dist/runtime/actors/actor-runtime-contracts.d.ts +1 -0
- package/dist/runtime/actors/index.d.ts +2 -0
- package/dist/runtime/actors/index.js +21 -6
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +1 -3
- 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/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 +105 -145
- 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/handlers/handler-instance-scope.js +1 -1
- package/dist/runtime/host/actor-placement-coordinator.d.ts +2 -1
- package/dist/runtime/host/actor-placement-coordinator.js +2 -1
- package/dist/runtime/host/actor-runtime-options-factory.d.ts +2 -1
- package/dist/runtime/host/actor-runtime-options-factory.js +1 -0
- package/dist/runtime/host/index.d.ts +1 -1
- package/dist/runtime/host/index.js +3 -2
- 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/spot-address-transport.js +1 -1
- 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 +3 -1
- package/dist/runtime/locations/in-memory-authority-store.d.ts +3 -0
- package/dist/runtime/locations/in-memory-authority-store.js +47 -12
- package/dist/runtime/locations/in-memory-location-store.d.ts +2 -1
- package/dist/runtime/locations/in-memory-location-store.js +15 -10
- package/dist/runtime/locations/internal-location-contracts.d.ts +5 -0
- package/dist/runtime/locations/internal-store-contracts.d.ts +1 -1
- package/dist/runtime/locations/location-store-repository.d.ts +2 -0
- package/dist/runtime/locations/location-store-repository.js +130 -54
- package/dist/runtime/spots/spot-entry-activation.d.ts +1 -0
- package/dist/runtime/spots/spot-entry-activation.js +3 -0
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +1 -0
- package/dist/runtime/spots/spot-node-runtime-manager.js +6 -0
- package/dist/runtime/spots/spot-serial-executor.d.ts +1 -0
- package/dist/runtime/spots/spot-serial-executor.js +14 -2
- 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/index.d.ts +0 -1
- package/dist/runtime/streams/index.js +4 -3
- package/dist/runtime/streams/protocol.js +0 -9
- package/dist/runtime/streams/session-context.d.ts +0 -4
- package/dist/runtime/streams/session-context.js +0 -26
- 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/Configuration/Builders.ts +2 -0
- package/src/contracts/Configuration/InternalDefaults.ts +4 -0
- package/src/contracts/Configuration/RegistrationBuilders.ts +13 -0
- package/src/contracts/Configuration/RegistrationNormalizers.ts +25 -0
- package/src/contracts/Configuration/RegistrationTypes.ts +3 -0
- package/src/runtime/actors/actor-mailbox.ts +7 -2
- package/src/runtime/actors/actor-runtime-contracts.ts +4 -0
- package/src/runtime/actors/index.ts +77 -42
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -7
- package/src/runtime/execution/serial-execution-queue.ts +117 -29
- package/src/runtime/foundation/operation-registry.ts +16 -9
- 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 +152 -224
- package/src/runtime/foundation/service-stateful-wire-codec.ts +0 -25
- package/src/runtime/foundation/service-topology-registry.ts +30 -20
- package/src/runtime/handlers/handler-instance-scope.ts +2 -1
- package/src/runtime/host/actor-placement-coordinator.ts +8 -2
- package/src/runtime/host/actor-runtime-options-factory.ts +6 -0
- package/src/runtime/host/index.ts +6 -2
- package/src/runtime/host/instance-activation-authority.ts +58 -125
- package/src/runtime/host/spot-address-transport.ts +1 -1
- package/src/runtime/host/stateful-authority-route-runtime.ts +22 -9
- package/src/runtime/locations/domain-store-contract.ts +6 -1
- package/src/runtime/locations/in-memory-authority-store.ts +83 -12
- package/src/runtime/locations/in-memory-location-store.ts +22 -10
- package/src/runtime/locations/internal-location-contracts.ts +6 -0
- package/src/runtime/locations/internal-store-contracts.ts +1 -0
- package/src/runtime/locations/location-store-repository.ts +182 -69
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
- package/src/runtime/spots/spot-entry-activation.ts +4 -0
- package/src/runtime/spots/spot-node-runtime-manager.ts +7 -0
- package/src/runtime/spots/spot-serial-executor.ts +14 -5
- package/src/runtime/spots/spot-serial-turn-executor.ts +10 -0
- package/src/runtime/streams/index.ts +3 -1
- package/src/runtime/streams/protocol.ts +0 -13
- package/src/runtime/streams/session-context.ts +0 -32
- 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
|
@@ -58,7 +58,12 @@ export declare class ServiceTopologyRegistry {
|
|
|
58
58
|
selectChannel(channelName: string, isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
|
|
59
59
|
selectPlacement(isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
|
|
60
60
|
selectObjectPlacement(stableType: string, isReady?: (descriptor: ServiceNodeDescriptor) => boolean): ServiceNodeDescriptor | undefined;
|
|
61
|
+
selectObjectPlacementWithStatus(stableType: string, isReady?: (descriptor: ServiceNodeDescriptor) => boolean): {
|
|
62
|
+
readonly status: ServiceObjectPlacementStatus;
|
|
63
|
+
readonly descriptor?: ServiceNodeDescriptor;
|
|
64
|
+
};
|
|
61
65
|
objectPlacementStatus(stableType: string, isReady?: (descriptor: ServiceNodeDescriptor) => boolean): ServiceObjectPlacementStatus;
|
|
66
|
+
private objectPlacementCandidates;
|
|
62
67
|
instanceSpotPlacementTypes(): readonly string[];
|
|
63
68
|
private selectWeightedCycle;
|
|
64
69
|
private rebuildSelections;
|
|
@@ -181,21 +181,22 @@ class ServiceTopologyRegistry {
|
|
|
181
181
|
.map(clonePeer), (peer) => peer.descriptor.placementWeight, (peer) => peer.descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.descriptor.nodeRoutingId, right.descriptor.nodeRoutingId), isReady);
|
|
182
182
|
}
|
|
183
183
|
selectObjectPlacement(stableType, isReady = () => true) {
|
|
184
|
+
return this.selectObjectPlacementWithStatus(stableType, isReady).descriptor;
|
|
185
|
+
}
|
|
186
|
+
selectObjectPlacementWithStatus(stableType, isReady = () => true) {
|
|
184
187
|
requireText(stableType, 'stableType');
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
descriptor.activeCapacityUsed < descriptor.activeCapacityLimit &&
|
|
193
|
-
descriptor.pendingCapacityUsed < descriptor.pendingCapacityLimit &&
|
|
194
|
-
descriptor.protocolCapabilities.includes(capability))
|
|
195
|
-
.map(cloneDescriptor);
|
|
196
|
-
}, (descriptor) => descriptor.placementWeight, (descriptor) => descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.nodeRoutingId, right.nodeRoutingId), isReady);
|
|
188
|
+
const candidates = this.objectPlacementCandidates(stableType, isReady);
|
|
189
|
+
if (candidates.status !== 'available')
|
|
190
|
+
return { status: candidates.status };
|
|
191
|
+
const descriptor = this.selectWeightedCycle(`object:${stableType}`, () => this.objectPlacementCandidates(stableType).eligible.map(cloneDescriptor), (descriptor) => descriptor.placementWeight, (descriptor) => descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.nodeRoutingId, right.nodeRoutingId), isReady);
|
|
192
|
+
return descriptor === undefined
|
|
193
|
+
? { status: 'unavailable' }
|
|
194
|
+
: { status: 'available', descriptor };
|
|
197
195
|
}
|
|
198
196
|
objectPlacementStatus(stableType, isReady = () => true) {
|
|
197
|
+
return this.objectPlacementCandidates(stableType, isReady).status;
|
|
198
|
+
}
|
|
199
|
+
objectPlacementCandidates(stableType, isReady = () => true) {
|
|
199
200
|
requireText(stableType, 'stableType');
|
|
200
201
|
const capability = `object-type:${stableType}`;
|
|
201
202
|
const supported = [
|
|
@@ -205,15 +206,13 @@ class ServiceTopologyRegistry {
|
|
|
205
206
|
descriptor.objectRole === 'server' &&
|
|
206
207
|
descriptor.protocolCapabilities.includes(capability));
|
|
207
208
|
if (supported.length === 0)
|
|
208
|
-
return 'unsupported';
|
|
209
|
+
return { status: 'unsupported', eligible: [] };
|
|
209
210
|
const withCapacity = supported.filter((descriptor) => descriptor.activeCapacityUsed < descriptor.activeCapacityLimit &&
|
|
210
211
|
descriptor.pendingCapacityUsed < descriptor.pendingCapacityLimit);
|
|
211
212
|
if (withCapacity.length === 0)
|
|
212
|
-
return 'capacity';
|
|
213
|
+
return { status: 'capacity', eligible: [] };
|
|
213
214
|
const weighted = withCapacity.filter((descriptor) => descriptor.placementWeight > 0);
|
|
214
|
-
|
|
215
|
-
return 'unsupported';
|
|
216
|
-
return weighted.some(isReady) ? 'available' : 'unavailable';
|
|
215
|
+
return { status: weighted.some(isReady) ? 'available' : 'unavailable', eligible: weighted };
|
|
217
216
|
}
|
|
218
217
|
instanceSpotPlacementTypes() {
|
|
219
218
|
const prefix = 'instance-spot-type:';
|
|
@@ -158,7 +158,7 @@ class LifecycleHandlerInstanceScope {
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
async dispose(detachedTaskRunner) {
|
|
161
|
-
const disposeFromActiveInvocation = activeLifecycleScope.getStore() === this;
|
|
161
|
+
const disposeFromActiveInvocation = this.activeInvocations > 0 && activeLifecycleScope.getStore() === this;
|
|
162
162
|
const disposal = this.beginDisposeCore();
|
|
163
163
|
if (disposal.deferred !== undefined) {
|
|
164
164
|
startOutsideLifecycleInvocation(() => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ActorRef, RoutingId, ZLinkActorCreateResult } from '../../contracts';
|
|
2
|
-
import type { ZLinkAuthoritySnapshot, ZLinkLocationOwnerToken } from '../locations/internal-location-contracts';
|
|
2
|
+
import type { ZLinkAuthoritySnapshot, ZLinkLocationOwnerToken, ZLinkObjectReserveRequest } from '../locations/internal-location-contracts';
|
|
3
3
|
import type { ZLinkAuthorityStore, ZLinkObjectCreationStore } from '../locations/internal-store-contracts';
|
|
4
4
|
import type { ServiceActorCreateRecord } from '../foundation/service-stateful-wire-codec';
|
|
5
5
|
import type { ServiceUserSpotOperationResult } from '../foundation/service-stateful-runtime';
|
|
@@ -14,6 +14,7 @@ export interface ZLinkActorPlacementTarget {
|
|
|
14
14
|
readonly sourceNodeGeneration: bigint;
|
|
15
15
|
}
|
|
16
16
|
export interface ZLinkActorPlacementCoordinatorOptions {
|
|
17
|
+
readonly actorRelocationPolicy?: (meshName: string, stableType: string) => ZLinkObjectReserveRequest['actorRelocationPolicy'];
|
|
17
18
|
readonly store: ZLinkObjectCreationStore & ZLinkAuthorityStore;
|
|
18
19
|
readonly target: (meshName: string | undefined, stableType: string, signal?: AbortSignal, excludedNodeRids?: ReadonlySet<string>) => Promise<ZLinkActorPlacementTarget | undefined>;
|
|
19
20
|
readonly remoteCreate: (meshName: string, targetNodeRid: string, request: Omit<ServiceActorCreateRecord, 'kind' | 'correlation'>, timeoutMs: number) => Promise<ServiceUserSpotOperationResult>;
|
|
@@ -61,7 +61,8 @@ class ZLinkActorPlacementCoordinator {
|
|
|
61
61
|
},
|
|
62
62
|
target: reservationTarget(target),
|
|
63
63
|
creatingPayload,
|
|
64
|
-
capacity: { actors: 1, spots: 0 }
|
|
64
|
+
capacity: { actors: 1, spots: 0 },
|
|
65
|
+
actorRelocationPolicy: this.options.actorRelocationPolicy?.(target.meshName, stableType)
|
|
65
66
|
}, deadline.signal, BigInt(deadlineUnixMs));
|
|
66
67
|
const existing = existingActor(reserved, actorId, stableType);
|
|
67
68
|
if (existing !== undefined) {
|
|
@@ -15,6 +15,7 @@ import type { ZLinkRuntimeAdmissionGate } from '../admission';
|
|
|
15
15
|
import type { ZLinkRemoteActorPacketTarget } from '../actors';
|
|
16
16
|
import type { ZLinkActorJoinRelocation } from '../actors/actor-join-relocation';
|
|
17
17
|
export interface ZLinkActorRuntimeOptionsFactoryOptions {
|
|
18
|
+
readonly terminateActorActivation: (actor: ZLinkActor, terminal: () => Promise<void>) => Promise<void>;
|
|
18
19
|
readonly registration: ZLinkFrameworkRegistration;
|
|
19
20
|
/** Message flow tracer, so a failed deferred Join records its cause on the flow. */
|
|
20
21
|
readonly messageFlow?: () => import('../diagnostics/message-flow').ZLinkMessageFlowTracer | undefined;
|
|
@@ -61,7 +62,7 @@ export interface ZLinkActorRuntimeOptionsFactoryOptions {
|
|
|
61
62
|
export declare class ZLinkActorRuntimeOptionsFactory {
|
|
62
63
|
private readonly options;
|
|
63
64
|
constructor(options: ZLinkActorRuntimeOptionsFactoryOptions);
|
|
64
|
-
createActorManagerOptions(): Pick<ZLinkActorManagerOptions, 'joinCoordinator' | 'actorMeshNameProvider' | 'actorLeaveSpot' | 'messageSerializers' | 'nativeActorNode' | 'nativeActorNodeProvider' | 'nativeActorCompletionTableProvider' | 'actorCreatedNodeRidProvider' | 'actorRefResolver' | 'actorCreatedNotifier' | 'actorDestroyedCleanup' | 'publishActorAuthority' | 'locationLifecycle' | 'boundSessionFactory' | 'actorTransferRegistry' | 'shutdownSignal' | 'metrics' | 'admission'>;
|
|
65
|
+
createActorManagerOptions(): Pick<ZLinkActorManagerOptions, 'joinCoordinator' | 'actorMeshNameProvider' | 'actorLeaveSpot' | 'messageSerializers' | 'nativeActorNode' | 'nativeActorNodeProvider' | 'nativeActorCompletionTableProvider' | 'actorCreatedNodeRidProvider' | 'actorRefResolver' | 'actorCreatedNotifier' | 'actorDestroyedCleanup' | 'terminateActorActivation' | 'publishActorAuthority' | 'locationLifecycle' | 'boundSessionFactory' | 'actorTransferRegistry' | 'shutdownSignal' | 'metrics' | 'admission'>;
|
|
65
66
|
createActorClientOptions(): ConstructorParameters<typeof DefaultZLinkActorClient>[0];
|
|
66
67
|
private actorBelongsToMesh;
|
|
67
68
|
}
|
|
@@ -13,6 +13,7 @@ class ZLinkActorRuntimeOptionsFactory {
|
|
|
13
13
|
createActorManagerOptions() {
|
|
14
14
|
const actorTransferRegistry = this.options.actorTransferRegistry;
|
|
15
15
|
return {
|
|
16
|
+
terminateActorActivation: this.options.terminateActorActivation,
|
|
16
17
|
actorMeshNameProvider: this.options.actorMeshName,
|
|
17
18
|
actorLeaveSpot: (meshName, spotId, actor, signal) => {
|
|
18
19
|
const spotManager = this.options.spotManager();
|
|
@@ -155,7 +155,7 @@ export declare class ZLinkFrameworkRuntimeHost implements ZLinkFrameworkRuntimeL
|
|
|
155
155
|
private registerInstanceApplicationLifecycle;
|
|
156
156
|
setActorManager(actorManager: DefaultZLinkActorManager): void;
|
|
157
157
|
setSpotManager(spotManager: DefaultZLinkSpotManager): void;
|
|
158
|
-
createActorManagerOptions(): Pick<ZLinkActorManagerOptions, 'joinCoordinator' | 'actorMeshNameProvider' | 'actorLeaveSpot' | 'messageSerializers' | 'nativeActorNode' | 'nativeActorNodeProvider' | 'actorCreatedNodeRidProvider' | 'actorRefResolver' | 'actorCreatedNotifier' | 'actorDestroyedCleanup' | 'locationLifecycle' | 'boundSessionFactory' | 'shutdownSignal' | 'admission' | 'activationAdmission' | 'placementCreate'>;
|
|
158
|
+
createActorManagerOptions(): Pick<ZLinkActorManagerOptions, 'joinCoordinator' | 'actorMeshNameProvider' | 'actorLeaveSpot' | 'messageSerializers' | 'nativeActorNode' | 'nativeActorNodeProvider' | 'actorCreatedNodeRidProvider' | 'actorRefResolver' | 'actorCreatedNotifier' | 'actorDestroyedCleanup' | 'terminateActorActivation' | 'locationLifecycle' | 'boundSessionFactory' | 'shutdownSignal' | 'admission' | 'activationAdmission' | 'placementCreate'>;
|
|
159
159
|
createActorClientOptions(): ConstructorParameters<typeof DefaultZLinkActorClient>[0];
|
|
160
160
|
createLocationHandleResolver(): ZLinkStoreLocationResolvers | undefined;
|
|
161
161
|
createSpotManagerOptions(): Partial<ZLinkSpotManagerOptions>;
|
|
@@ -1511,6 +1511,8 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
1511
1511
|
}
|
|
1512
1512
|
this.actorPlacement ??= new actor_placement_coordinator_1.ZLinkActorPlacementCoordinator({
|
|
1513
1513
|
store: locationStore,
|
|
1514
|
+
actorRelocationPolicy: (meshName, stableType) => this.options.registration.spotNodes.get(meshName)?.actorFactoryRegistrations?.[stableType]
|
|
1515
|
+
?.relocation.kind,
|
|
1514
1516
|
remoteCreate: (meshName, targetNodeRid, request, timeoutMs) => {
|
|
1515
1517
|
const node = this.spotNodeRuntime?.meshNode(meshName);
|
|
1516
1518
|
if (node === undefined) {
|
|
@@ -1546,13 +1548,11 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
1546
1548
|
const descriptors = await (0, location_store_pages_1.listAllMeshNodeDescriptors)(locationStore, meshName, signal);
|
|
1547
1549
|
const selected = await selectReadyLocationPlacementDescriptor(descriptors, meshNode, signal, (descriptor) => {
|
|
1548
1550
|
const capability = descriptor.objectCapabilities.find((candidate) => candidate.objectKind === 'actor' && candidate.stableType === stableType);
|
|
1549
|
-
const actors = descriptor.populationCapacity.actors;
|
|
1550
1551
|
return (descriptor.state === contracts_1.ZLinkFrameworkRuntimeState.Serving &&
|
|
1551
1552
|
descriptor.objectRole === 'server' &&
|
|
1552
1553
|
descriptor.placementWeight > 0 &&
|
|
1553
1554
|
descriptor.entrySpotId !== undefined &&
|
|
1554
1555
|
excludedNodeRids?.has(String(descriptor.rid)) !== true &&
|
|
1555
|
-
(actors.limit === 0 || actors.active + actors.reserved < actors.limit) &&
|
|
1556
1556
|
capability !== undefined);
|
|
1557
1557
|
});
|
|
1558
1558
|
if (selected === undefined)
|
|
@@ -1832,6 +1832,7 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
1832
1832
|
}
|
|
1833
1833
|
actorRuntimeOptionsFactory() {
|
|
1834
1834
|
return new actor_runtime_options_factory_1.ZLinkActorRuntimeOptionsFactory({
|
|
1835
|
+
terminateActorActivation: (actor, terminal) => this.spotNodeRuntime?.terminateActorActivation(actor, terminal) ?? terminal(),
|
|
1835
1836
|
registration: this.options.registration,
|
|
1836
1837
|
messageFlow: () => this.createDispatchErrorReporter(this.runtimeOrPreStartErrorSink).flow,
|
|
1837
1838
|
routeTransport: this.routeTransport,
|
|
@@ -27,6 +27,5 @@ export declare class ZLinkInstanceActivationAuthority implements ServiceAsyncIns
|
|
|
27
27
|
abort(target: ServiceInstanceActivationTarget, reservation: ServiceInstanceActivationReservation): Promise<void>;
|
|
28
28
|
private requireOwner;
|
|
29
29
|
private deleteOrphan;
|
|
30
|
-
private awaitConcurrentActivation;
|
|
31
30
|
private requirePending;
|
|
32
31
|
}
|
|
@@ -11,7 +11,6 @@ const relocation_blob_1 = require("../locations/relocation-blob");
|
|
|
11
11
|
const service_instance_activation_recovery_codec_1 = require("../foundation/service-instance-activation-recovery-codec");
|
|
12
12
|
const CREATION_REQUEST_RETENTION_MS = 24 * 60 * 60 * 1_000;
|
|
13
13
|
const MAX_CREATION_REQUEST_BYTES = 1024 * 1024;
|
|
14
|
-
const ACTIVATION_JOIN_POLL_INTERVAL_MS = 10;
|
|
15
14
|
class ZLinkInstanceActivationAuthority {
|
|
16
15
|
options;
|
|
17
16
|
pending = new Map();
|
|
@@ -48,68 +47,64 @@ class ZLinkInstanceActivationAuthority {
|
|
|
48
47
|
}
|
|
49
48
|
let reserved;
|
|
50
49
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
50
|
+
reserved = await this.options.store.reserve({
|
|
51
|
+
key: { kind: 'instance_spot', globalId: target.targetSpotId },
|
|
52
|
+
intent: {
|
|
53
|
+
stableType: target.stableType,
|
|
54
|
+
requestContentReference: stored.reference.value,
|
|
55
|
+
requestSha256: requestHash,
|
|
56
|
+
requestEncodedSize: BigInt(requestBytes.byteLength)
|
|
57
|
+
},
|
|
58
|
+
target: {
|
|
59
|
+
meshName: this.options.meshName,
|
|
60
|
+
nodeRid: target.targetNodeRid,
|
|
61
|
+
nodeLifecycleGeneration: target.targetNodeGeneration,
|
|
62
|
+
owner
|
|
63
|
+
},
|
|
64
|
+
creatingPayload: (0, service_authority_payload_codec_1.encodeServiceInstanceAuthorityPayload)({
|
|
65
|
+
state: 'coldActivating',
|
|
66
|
+
stableType: target.stableType,
|
|
67
|
+
spotId: target.targetSpotId,
|
|
68
|
+
ownerId: owner.ownerId,
|
|
69
|
+
ownerLeaseGeneration: owner.leaseGeneration,
|
|
70
|
+
ownerMeshName: this.options.meshName,
|
|
71
|
+
ownerNodeRid: target.targetNodeRid,
|
|
72
|
+
ownerNodeGeneration: target.targetNodeGeneration
|
|
73
|
+
}),
|
|
74
|
+
capacity: {
|
|
75
|
+
actors: 0,
|
|
76
|
+
spots: 1,
|
|
77
|
+
spotType: {
|
|
78
|
+
objectKind: 'instance_spot',
|
|
68
79
|
stableType: target.stableType,
|
|
69
|
-
|
|
70
|
-
ownerId: owner.ownerId,
|
|
71
|
-
ownerLeaseGeneration: owner.leaseGeneration,
|
|
72
|
-
ownerMeshName: this.options.meshName,
|
|
73
|
-
ownerNodeRid: target.targetNodeRid,
|
|
74
|
-
ownerNodeGeneration: target.targetNodeGeneration
|
|
75
|
-
}),
|
|
76
|
-
capacity: {
|
|
77
|
-
actors: 0,
|
|
78
|
-
spots: 1,
|
|
79
|
-
spotType: {
|
|
80
|
-
objectKind: 'instance_spot',
|
|
81
|
-
stableType: target.stableType,
|
|
82
|
-
count: 1
|
|
83
|
-
}
|
|
80
|
+
count: 1
|
|
84
81
|
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
break;
|
|
88
|
-
this.options.metrics?.recordInstanceSpotClaimConflict(this.options.meshName, target.stableType, 'already_exists');
|
|
89
|
-
await this.deleteOrphan(stored.reference);
|
|
90
|
-
return readyExisting(reserved.current, target);
|
|
91
|
-
}
|
|
82
|
+
}
|
|
83
|
+
}, undefined, activation.deadlineUnixMs);
|
|
92
84
|
}
|
|
93
85
|
catch (error) {
|
|
94
86
|
await this.deleteOrphan(stored.reference);
|
|
95
87
|
throw error;
|
|
96
88
|
}
|
|
97
|
-
if (reserved.kind === 'conflict' &&
|
|
98
|
-
reserved.current.kind === 'snapshot' &&
|
|
99
|
-
reserved.current.allocation.objectKind === 'instance_spot' &&
|
|
100
|
-
reserved.current.allocation.stableType === target.stableType &&
|
|
101
|
-
reserved.current.allocation.state === 'reserved') {
|
|
102
|
-
this.options.metrics?.recordInstanceSpotClaimConflict(this.options.meshName, target.stableType, 'activation_in_progress');
|
|
103
|
-
await this.deleteOrphan(stored.reference);
|
|
104
|
-
return await this.awaitConcurrentActivation(target, activation.deadlineUnixMs);
|
|
105
|
-
}
|
|
106
89
|
if (reserved.kind !== 'reserved') {
|
|
107
|
-
|
|
90
|
+
const conflictReason = reserved.kind === 'alreadyExists'
|
|
91
|
+
? 'already_exists'
|
|
92
|
+
: reserved.kind === 'conflict' &&
|
|
93
|
+
reserved.current.kind === 'snapshot' &&
|
|
94
|
+
reserved.current.allocation.objectKind === 'instance_spot' &&
|
|
95
|
+
reserved.current.allocation.stableType === target.stableType &&
|
|
96
|
+
reserved.current.allocation.state === 'reserved'
|
|
97
|
+
? 'activation_in_progress'
|
|
98
|
+
: reserved.kind;
|
|
99
|
+
this.options.metrics?.recordInstanceSpotClaimConflict(this.options.meshName, target.stableType, conflictReason);
|
|
108
100
|
await this.deleteOrphan(stored.reference);
|
|
101
|
+
if (reserved.kind === 'typeMismatch') {
|
|
102
|
+
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.SpotTypeMismatch, `Instance activation reservation failed: ${reserved.kind}.`);
|
|
103
|
+
}
|
|
109
104
|
if (reserved.kind === 'placementCapacityExhausted') {
|
|
110
105
|
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.PlacementCapacityExhausted, `Instance Spot placement capacity is exhausted for '${String(target.targetSpotId)}'.`);
|
|
111
106
|
}
|
|
112
|
-
throw new
|
|
107
|
+
throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.Unavailable, `Instance activation reservation failed: ${reserved.kind}.`);
|
|
113
108
|
}
|
|
114
109
|
const reservation = {
|
|
115
110
|
attempt: reserved.creating.objectGeneration,
|
|
@@ -334,27 +329,6 @@ class ZLinkInstanceActivationAuthority {
|
|
|
334
329
|
// best-effort deletion fails.
|
|
335
330
|
}
|
|
336
331
|
}
|
|
337
|
-
async awaitConcurrentActivation(target, deadlineUnixMs) {
|
|
338
|
-
const key = authorityKey(target.targetSpotId);
|
|
339
|
-
const deadlineMs = performance.now() + Number(deadlineUnixMs - BigInt(Date.now()));
|
|
340
|
-
while (performance.now() <= deadlineMs) {
|
|
341
|
-
const current = await this.options.store.readAuthority(key);
|
|
342
|
-
if (current.kind === 'snapshot') {
|
|
343
|
-
if (current.allocation.objectKind !== 'instance_spot' ||
|
|
344
|
-
current.allocation.stableType !== target.stableType) {
|
|
345
|
-
throw new Error('Concurrent Instance activation resolved to a different Spot type.');
|
|
346
|
-
}
|
|
347
|
-
if (current.allocation.state === 'active') {
|
|
348
|
-
return readyExisting(current, target);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
throw new Error('Concurrent Instance activation ended before the Ready barrier.');
|
|
353
|
-
}
|
|
354
|
-
await waitForActivationJoin(deadlineMs);
|
|
355
|
-
}
|
|
356
|
-
throw new Error(`Concurrent Instance activation for '${target.targetSpotId}' did not reach Ready before deadline.`);
|
|
357
|
-
}
|
|
358
332
|
requirePending(reservation) {
|
|
359
333
|
const pending = this.pending.get(reservation.token);
|
|
360
334
|
if (pending === undefined || pending.creating.objectGeneration !== reservation.attempt) {
|
|
@@ -402,31 +376,6 @@ function readyRead(snapshot, target) {
|
|
|
402
376
|
}
|
|
403
377
|
return { kind: 'ready', route: routeFromSnapshot(snapshot), authority: snapshot };
|
|
404
378
|
}
|
|
405
|
-
function readyExisting(snapshot, target) {
|
|
406
|
-
const decoded = (0, service_authority_payload_codec_1.decodeServiceReadySpotAuthority)(snapshot.payload) ??
|
|
407
|
-
(0, service_authority_payload_codec_1.decodeServiceClosingSpotAuthority)(snapshot.payload);
|
|
408
|
-
if (decoded?.kind !== 'instance_spot' ||
|
|
409
|
-
decoded.stableType !== target.stableType ||
|
|
410
|
-
decoded.spotId !== target.targetSpotId ||
|
|
411
|
-
snapshot.allocation.objectKind !== 'instance_spot' ||
|
|
412
|
-
snapshot.allocation.state !== 'active' ||
|
|
413
|
-
decoded.ownerId !== snapshot.ownerId ||
|
|
414
|
-
decoded.ownerLeaseGeneration !== snapshot.ownerLeaseGeneration ||
|
|
415
|
-
decoded.ownerMeshName !== snapshot.allocation.descriptor.meshName ||
|
|
416
|
-
decoded.ownerNodeGeneration !== snapshot.allocation.descriptorLifecycleGeneration ||
|
|
417
|
-
!(0, routing_id_1.routingIdsEqual)(decoded.ownerNodeRid, snapshot.allocation.descriptor.rid)) {
|
|
418
|
-
throw new Error('Existing Instance authority is not a matching Ready allocation.');
|
|
419
|
-
}
|
|
420
|
-
return { kind: 'ready', route: routeFromSnapshot(snapshot), authority: snapshot };
|
|
421
|
-
}
|
|
422
|
-
async function waitForActivationJoin(deadlineMs) {
|
|
423
|
-
const remaining = deadlineMs - performance.now();
|
|
424
|
-
if (remaining <= 0)
|
|
425
|
-
return;
|
|
426
|
-
await new Promise((resolve) => {
|
|
427
|
-
setTimeout(resolve, Math.min(ACTIVATION_JOIN_POLL_INTERVAL_MS, remaining));
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
379
|
function requireCommitIdentity(target, reservation, creating, spot, meshName) {
|
|
431
380
|
if (creating.allocation.state !== 'reserved' ||
|
|
432
381
|
creating.allocation.objectKind !== 'instance_spot' ||
|
|
@@ -254,7 +254,7 @@ class ZLinkHostSpotAddressTransport {
|
|
|
254
254
|
if (stableType === undefined) {
|
|
255
255
|
if (distinctTypes.length === 0)
|
|
256
256
|
return { kind: 'unsupported' };
|
|
257
|
-
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.
|
|
257
|
+
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.InvalidOperation, 'Instance Spot type is required when multiple types are registered.');
|
|
258
258
|
}
|
|
259
259
|
if (canInspectPlacementTypes && !distinctTypes.includes(stableType)) {
|
|
260
260
|
return { kind: 'unsupported' };
|
|
@@ -28,6 +28,7 @@ export declare class ZLinkStatefulAuthorityRouteRuntime {
|
|
|
28
28
|
reconcile(signal?: AbortSignal, requireComplete?: boolean): Promise<void>;
|
|
29
29
|
private recoverInstanceActivation;
|
|
30
30
|
private recoverPendingInstanceActivation;
|
|
31
|
+
private clearRecoveryRoot;
|
|
31
32
|
private readRecoveryEnvelope;
|
|
32
33
|
private run;
|
|
33
34
|
private readCompleteSnapshot;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ZLinkStatefulAuthorityRouteRuntime = void 0;
|
|
4
|
+
const authority_key_codec_1 = require("../locations/authority-key-codec");
|
|
4
5
|
const node_crypto_1 = require("node:crypto");
|
|
5
6
|
const relocation_blob_1 = require("../locations/relocation-blob");
|
|
6
7
|
const routing_id_1 = require("../routing-id");
|
|
@@ -65,11 +66,19 @@ class ZLinkStatefulAuthorityRouteRuntime {
|
|
|
65
66
|
const previous = this.appliedByMesh.get(meshName) ?? new Map();
|
|
66
67
|
const status = sink.status();
|
|
67
68
|
for (const pending of snapshot.pending) {
|
|
68
|
-
if (pending.targetMeshName
|
|
69
|
-
|
|
69
|
+
if (pending.targetMeshName !== meshName)
|
|
70
|
+
continue;
|
|
71
|
+
if ((0, routing_id_1.routingIdsEqual)(status.routingId, pending.targetNodeRid) &&
|
|
70
72
|
status.lifecycleGeneration === pending.targetNodeGeneration) {
|
|
71
73
|
await this.recoverPendingInstanceActivation(sink, pending, signal);
|
|
72
74
|
}
|
|
75
|
+
else {
|
|
76
|
+
const release = this.options.creationStore?.releaseEndedReservation;
|
|
77
|
+
if (release === undefined)
|
|
78
|
+
throw new Error('Reservation lifecycle recovery requires a Location repository.');
|
|
79
|
+
if (await release.call(this.options.creationStore, (0, authority_key_codec_1.encodeAuthorityKey)('instance_spot', pending.spotId), pending.pending.storeVersion, signal))
|
|
80
|
+
await this.clearRecoveryRoot(pending.pending.requestReference, signal);
|
|
81
|
+
}
|
|
73
82
|
}
|
|
74
83
|
for (const [key, route] of current) {
|
|
75
84
|
if (route.meshName !== meshName)
|
|
@@ -207,19 +216,18 @@ class ZLinkStatefulAuthorityRouteRuntime {
|
|
|
207
216
|
}
|
|
208
217
|
}, signal);
|
|
209
218
|
if (aborted.kind === 'aborted' || aborted.kind === 'alreadyAborted') {
|
|
210
|
-
|
|
211
|
-
await this.options.relocationStore?.delete((0, relocation_blob_1.relocationBlobReference)(recovery.pending.requestReference));
|
|
212
|
-
}
|
|
213
|
-
catch {
|
|
214
|
-
// The authority no longer publishes this root. Retention owns a
|
|
215
|
-
// failed best-effort orphan cleanup.
|
|
216
|
-
}
|
|
219
|
+
await this.clearRecoveryRoot(recovery.pending.requestReference, signal);
|
|
217
220
|
}
|
|
218
221
|
return;
|
|
219
222
|
}
|
|
220
223
|
if ((await sink.recoverPendingInstanceActivation(envelope, recovery.pending, null)) === false)
|
|
221
224
|
return;
|
|
222
225
|
}
|
|
226
|
+
async clearRecoveryRoot(reference, signal) {
|
|
227
|
+
if (this.options.relocationStore === undefined)
|
|
228
|
+
throw new Error('Instance activation recovery requires a Relocation Store.');
|
|
229
|
+
await this.options.relocationStore.delete((0, relocation_blob_1.relocationBlobReference)(reference), signal);
|
|
230
|
+
}
|
|
223
231
|
async readRecoveryEnvelope(recovery, signal) {
|
|
224
232
|
const relocationStore = this.options.relocationStore;
|
|
225
233
|
if (relocationStore === undefined) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ZLinkLocationOwnerToken, ZLinkLocationPage, ZLinkLocationWriteIntent, ZLinkLocationWriteResult, ZLinkLocationWriteStatus, ZLinkMeshNodeDescriptor, ZLinkMeshNodeDescriptorKey, ZLinkClientServerServerDescriptor, ZLinkClientServerServerDescriptorKey, ZLinkFanoutPublisherDescriptor, ZLinkFanoutPublisherDescriptorKey, ZLinkOwnerLeaseClaimResult, ZLinkOwnerLeaseReadResult, ZLinkOwnerLeaseReleaseResult, ZLinkOwnerLeaseRenewResult, ZLinkPageRequest } from '../../contracts/Locations/Models';
|
|
2
|
-
import type { ZLinkAggregateAbortResult, ZLinkAggregateCommitResult, ZLinkAggregateFence, ZLinkAggregatePrepareRequest, ZLinkAggregatePrepareResult, ZLinkAuthorityCompareExchangeResult, ZLinkAuthorityKey, ZLinkAuthorityMutation, ZLinkAuthorityReadResult, ZLinkAuthorityScanCursor, ZLinkAuthorityScanResult, ZLinkAuthorityStoreVersion, ZLinkCreationOperationIdentity, ZLinkCreationTerminalReadResult, ZLinkObjectAbortRequest, ZLinkObjectAbortResult, ZLinkObjectCommitRequest, ZLinkObjectCommitResult, ZLinkObjectCreationCompleteRequest, ZLinkObjectCreationCompleteResult,
|
|
2
|
+
import type { ZLinkAggregateAbortResult, ZLinkAggregateCommitResult, ZLinkAggregateFence, ZLinkAggregatePrepareRequest, ZLinkAggregatePrepareResult, ZLinkAuthorityCompareExchangeResult, ZLinkAuthorityKey, ZLinkAuthorityMutation, ZLinkAuthorityReadResult, ZLinkAuthorityScanCursor, ZLinkAuthorityScanResult, ZLinkAuthorityStoreVersion, ZLinkCreationOperationIdentity, ZLinkCreationTerminalReadResult, ZLinkObjectAbortRequest, ZLinkObjectAbortResult, ZLinkObjectCommitRequest, ZLinkObjectCommitResult, ZLinkObjectCreationCompleteRequest, ZLinkObjectCreationCompleteResult, ZLinkObjectReserveResult } from '../../contracts/Locations/Authority';
|
|
3
|
+
import type { ZLinkObjectReserveRequest } from './internal-location-contracts';
|
|
3
4
|
/**
|
|
4
5
|
* Framework-private domain repository. Provider implementations never
|
|
5
6
|
* implement this interface; the framework maps it to the opaque Store SPI.
|
|
@@ -25,6 +26,7 @@ export interface ZLinkDomainLocationStore {
|
|
|
25
26
|
reserve(request: ZLinkObjectReserveRequest, signal?: AbortSignal, deadlineUnixMs?: bigint): Promise<ZLinkObjectReserveResult>;
|
|
26
27
|
commit(request: ZLinkObjectCommitRequest, signal?: AbortSignal, deadlineUnixMs?: bigint): Promise<ZLinkObjectCommitResult>;
|
|
27
28
|
completeCreation(request: ZLinkObjectCreationCompleteRequest, signal?: AbortSignal): Promise<ZLinkObjectCreationCompleteResult>;
|
|
29
|
+
releaseEndedReservation?(key: ZLinkAuthorityKey, expectedStoreVersion: string, signal?: AbortSignal): Promise<boolean>;
|
|
28
30
|
abort(request: ZLinkObjectAbortRequest, signal?: AbortSignal, deadlineUnixMs?: bigint): Promise<ZLinkObjectAbortResult>;
|
|
29
31
|
prepareAggregate(request: ZLinkAggregatePrepareRequest, signal?: AbortSignal): Promise<ZLinkAggregatePrepareResult>;
|
|
30
32
|
commitAggregate(fence: ZLinkAggregateFence, signal?: AbortSignal): Promise<ZLinkAggregateCommitResult>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ZLinkAggregateAbortResult, ZLinkAggregateCommitResult, ZLinkAggregateFence, ZLinkAggregatePrepareRequest, ZLinkAggregatePrepareResult, ZLinkAuthorityCompareExchangeResult, ZLinkAuthorityKey, ZLinkAuthorityMutation, ZLinkAuthorityReadResult, ZLinkAuthorityScanCursor, ZLinkAuthorityScanResult, ZLinkAuthorityStoreVersion, ZLinkCapacityVector, ZLinkLocationOwnerToken, ZLinkMeshNodeDescriptorKey, ZLinkObjectAbortRequest, ZLinkObjectAbortResult, ZLinkObjectCommitRequest, ZLinkObjectCommitResult, ZLinkObjectCreationCompleteRequest, ZLinkObjectCreationCompleteResult, ZLinkObjectReserveRequest, ZLinkObjectReserveResult, ZLinkCreationOperationIdentity, ZLinkCreationTerminalReadResult } from './internal-location-contracts';
|
|
2
2
|
export interface ZLinkInMemoryAuthorityValidation {
|
|
3
|
+
isOwnerLive(owner: ZLinkLocationOwnerToken): boolean;
|
|
3
4
|
isTargetLive(descriptor: ZLinkMeshNodeDescriptorKey, lifecycleGeneration: bigint, owner: ZLinkLocationOwnerToken): boolean;
|
|
4
5
|
placementCapacityAvailable?(descriptor: ZLinkMeshNodeDescriptorKey, requested: ZLinkCapacityVector, currentReserved: ZLinkCapacityVector, currentActive: ZLinkCapacityVector): boolean;
|
|
5
6
|
identityClaimed?(authorityKey: string): boolean;
|
|
@@ -26,6 +27,8 @@ export declare class ZLinkInMemoryAuthorityStore {
|
|
|
26
27
|
compareExchangeAuthority(key: ZLinkAuthorityKey, expectedStoreVersion: ZLinkAuthorityStoreVersion, mutation: ZLinkAuthorityMutation, signal?: AbortSignal): Promise<ZLinkAuthorityCompareExchangeResult>;
|
|
27
28
|
listAuthorities(prefix: string, cursor: ZLinkAuthorityScanCursor | undefined, limit: number, signal?: AbortSignal): Promise<ZLinkAuthorityScanResult>;
|
|
28
29
|
removeAllByOwner(owner: ZLinkLocationOwnerToken, signal?: AbortSignal): Promise<bigint>;
|
|
30
|
+
releaseEndedReservation(key: ZLinkAuthorityKey, expectedStoreVersion: string, signal?: AbortSignal): Promise<boolean>;
|
|
31
|
+
private tryReleaseEndedReservation;
|
|
29
32
|
reserve(request: ZLinkObjectReserveRequest, signal?: AbortSignal): Promise<ZLinkObjectReserveResult>;
|
|
30
33
|
commit(request: ZLinkObjectCommitRequest, signal?: AbortSignal): Promise<ZLinkObjectCommitResult>;
|
|
31
34
|
completeCreation(request: ZLinkObjectCreationCompleteRequest, signal?: AbortSignal): Promise<ZLinkObjectCreationCompleteResult>;
|
|
@@ -6,12 +6,15 @@ const stream_wire_1 = require("@zlink-systems/stream-wire");
|
|
|
6
6
|
const CoreTypes_1 = require("../../contracts/Common/CoreTypes");
|
|
7
7
|
const Stores_1 = require("../../contracts/Locations/Stores");
|
|
8
8
|
const node_crypto_1 = require("node:crypto");
|
|
9
|
+
const contracts_1 = require("../../contracts");
|
|
9
10
|
const actor_join_recovery_codec_1 = require("../foundation/actor-join-recovery-codec");
|
|
10
11
|
const authority_key_codec_1 = require("./authority-key-codec");
|
|
11
12
|
const opaque_record_key_1 = require("./opaque-record-key");
|
|
13
|
+
const service_relocation_runtime_1 = require("../foundation/service-relocation-runtime");
|
|
12
14
|
const MAX_GENERATION = 0x7fffffffffffffffn;
|
|
13
15
|
const CREATION_TERMINAL_RETENTION_MS = 5 * 60 * 1000;
|
|
14
16
|
const MAX_U64 = stream_wire_1.UINT64_MAX;
|
|
17
|
+
const relocationAuthorityCodec = new service_relocation_runtime_1.ServiceRelocationAuthorityPayloadCodec();
|
|
15
18
|
/**
|
|
16
19
|
* In-memory reference provider for the provider-owned authority state machine.
|
|
17
20
|
* Opaque payload bytes are copied but never parsed.
|
|
@@ -171,6 +174,46 @@ class ZLinkInMemoryAuthorityStore {
|
|
|
171
174
|
this.scanRevision++;
|
|
172
175
|
return BigInt(removed);
|
|
173
176
|
}
|
|
177
|
+
async releaseEndedReservation(key, expectedStoreVersion, signal) {
|
|
178
|
+
signal?.throwIfAborted();
|
|
179
|
+
return this.tryReleaseEndedReservation(key, expectedStoreVersion);
|
|
180
|
+
}
|
|
181
|
+
tryReleaseEndedReservation(key, expectedStoreVersion, request) {
|
|
182
|
+
const current = this.rows.get(key.value);
|
|
183
|
+
if (current === undefined ||
|
|
184
|
+
current.snapshot.storeVersion.value !== expectedStoreVersion ||
|
|
185
|
+
(current.snapshot.allocation.state === 'active'
|
|
186
|
+
? request?.key.kind !== 'actor'
|
|
187
|
+
: current.creation === undefined))
|
|
188
|
+
return false;
|
|
189
|
+
if (current.snapshot.allocation.state === 'active') {
|
|
190
|
+
for (const aggregate of this.aggregates.values()) {
|
|
191
|
+
if (aggregate.state === 'prepared' &&
|
|
192
|
+
aggregate.request.participants.some((participant) => participant.authorityKey.value === key.value))
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
if (relocationAuthorityCodec.read(current.snapshot.payload) !== undefined)
|
|
196
|
+
return false;
|
|
197
|
+
if (this.validation.isOwnerLive({
|
|
198
|
+
ownerId: current.snapshot.ownerId,
|
|
199
|
+
leaseGeneration: current.snapshot.ownerLeaseGeneration
|
|
200
|
+
}))
|
|
201
|
+
return false;
|
|
202
|
+
if (request?.actorRelocationPolicy !== 'disabled')
|
|
203
|
+
throw new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.Unavailable, 'Actor owner lease is unavailable.');
|
|
204
|
+
}
|
|
205
|
+
else if (this.validation.isTargetLive(current.snapshot.allocation.descriptor, current.snapshot.allocation.descriptorLifecycleGeneration, {
|
|
206
|
+
ownerId: current.snapshot.ownerId,
|
|
207
|
+
leaseGeneration: current.snapshot.ownerLeaseGeneration
|
|
208
|
+
}))
|
|
209
|
+
return false;
|
|
210
|
+
this.adjustCapacity(current.snapshot.allocation.state === 'active' ? this.activeCapacity : this.pendingCapacity, current.snapshot.allocation, -1);
|
|
211
|
+
this.rows.delete(key.value);
|
|
212
|
+
if (current.snapshot.allocation.state === 'reserved' && current.creation !== undefined)
|
|
213
|
+
this.creationTerminals.set(current.creation.reservationId, 'aborted');
|
|
214
|
+
this.scanRevision++;
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
174
217
|
async reserve(request, signal) {
|
|
175
218
|
signal?.throwIfAborted();
|
|
176
219
|
validateReserve(request);
|
|
@@ -184,18 +227,10 @@ class ZLinkInMemoryAuthorityStore {
|
|
|
184
227
|
current.snapshot.allocation.stableType !== request.intent.stableType) {
|
|
185
228
|
return { kind: 'typeMismatch', current: this.snapshot(current.snapshot) };
|
|
186
229
|
}
|
|
187
|
-
if (current.snapshot.
|
|
188
|
-
return
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
// Reclaim it so the key does not stay blocked forever.
|
|
192
|
-
if (this.isOwnerLive(current.snapshot) || current.creation === undefined) {
|
|
193
|
-
return { kind: 'conflict', current: this.read(key) };
|
|
194
|
-
}
|
|
195
|
-
this.adjustCapacity(this.pendingCapacity, current.snapshot.allocation, -1);
|
|
196
|
-
this.rows.delete(key);
|
|
197
|
-
this.creationTerminals.set(current.creation.reservationId, 'aborted');
|
|
198
|
-
this.scanRevision++;
|
|
230
|
+
if (!this.tryReleaseEndedReservation((0, authority_key_codec_1.encodeAuthorityKey)(request.key.kind, request.key.globalId), current.snapshot.storeVersion.value, request))
|
|
231
|
+
return current.snapshot.allocation.state === 'active'
|
|
232
|
+
? { kind: 'alreadyExists', current: this.snapshot(current.snapshot) }
|
|
233
|
+
: { kind: 'conflict', current: this.read(key) };
|
|
199
234
|
}
|
|
200
235
|
const target = creationTarget(request);
|
|
201
236
|
if (!this.validation.isTargetLive(target.descriptor, target.lifecycleGeneration, target.owner)) {
|
|
@@ -25,6 +25,7 @@ export declare class ZLinkInMemoryLocationStore implements ZLinkDomainLocationSt
|
|
|
25
25
|
commit(request: ZLinkObjectCommitRequest, signal?: AbortSignal): Promise<ZLinkObjectCommitResult>;
|
|
26
26
|
completeCreation(request: ZLinkObjectCreationCompleteRequest, signal?: AbortSignal): Promise<ZLinkObjectCreationCompleteResult>;
|
|
27
27
|
readCreationTerminal(operation: ZLinkCreationOperationIdentity, signal?: AbortSignal): Promise<ZLinkCreationTerminalReadResult>;
|
|
28
|
+
releaseEndedReservation(key: ZLinkAuthorityKey, expectedStoreVersion: string, signal?: AbortSignal): Promise<boolean>;
|
|
28
29
|
abort(request: ZLinkObjectAbortRequest, signal?: AbortSignal): Promise<ZLinkObjectAbortResult>;
|
|
29
30
|
prepareAggregate(request: ZLinkAggregatePrepareRequest, signal?: AbortSignal): Promise<ZLinkAggregatePrepareResult>;
|
|
30
31
|
commitAggregate(fence: ZLinkAggregateFence, signal?: AbortSignal): Promise<ZLinkAggregateCommitResult>;
|
|
@@ -71,7 +72,7 @@ export declare class ZLinkInMemoryLocationStore implements ZLinkDomainLocationSt
|
|
|
71
72
|
private transitionActorTransfer;
|
|
72
73
|
private remove;
|
|
73
74
|
private removeByOwner;
|
|
74
|
-
private
|
|
75
|
+
private liveOwnerLease;
|
|
75
76
|
private bump;
|
|
76
77
|
private removeFanoutByOwner;
|
|
77
78
|
private bumpScope;
|