@zlink-systems/framework 0.32.0 → 0.32.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/runtime/actors/actor-context.js +1 -1
- package/dist/runtime/actors/actor-creation.js +1 -1
- package/dist/runtime/actors/actor-runtime-state.d.ts +2 -1
- package/dist/runtime/actors/actor-runtime-state.js +24 -8
- package/dist/runtime/actors/index.d.ts +3 -3
- package/dist/runtime/actors/index.js +19 -32
- package/dist/runtime/locations/actor-location-claims.js +9 -10
- package/package.json +2 -2
- package/src/runtime/actors/actor-context.ts +1 -1
- package/src/runtime/actors/actor-creation.ts +1 -1
- package/src/runtime/actors/actor-runtime-state.ts +27 -9
- package/src/runtime/actors/index.ts +30 -35
- package/src/runtime/locations/actor-location-claims.ts +14 -14
|
@@ -116,7 +116,7 @@ class ZLinkActorCreationCoordinator {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
else if (nodeRid !== undefined && updateLocation) {
|
|
119
|
-
await this.options.locationLifecycle?.setActorRef(actorType, actorId,
|
|
119
|
+
await this.options.locationLifecycle?.setActorRef(actorType, actorId, (0, actor_runtime_state_1.toFrameworkActorRef)(state.currentActorRef(), meshName), 0n);
|
|
120
120
|
}
|
|
121
121
|
return { status: 'created', actor, reply: response.reply };
|
|
122
122
|
}
|
|
@@ -76,6 +76,7 @@ export declare class ZLinkActorRuntimeState {
|
|
|
76
76
|
private spotGenerationValue;
|
|
77
77
|
private spotMembershipEpochValue;
|
|
78
78
|
private nativeActorRefValue;
|
|
79
|
+
private nativeActorDestroyed;
|
|
79
80
|
private entryNodeRidValue;
|
|
80
81
|
boundSessionValue: ZLinkRemoteBoundSessionTarget | undefined;
|
|
81
82
|
private remoteActorPacketTargetValue;
|
|
@@ -96,6 +97,7 @@ export declare class ZLinkActorRuntimeState {
|
|
|
96
97
|
get spotMembershipEpoch(): bigint;
|
|
97
98
|
get spotGeneration(): bigint | undefined;
|
|
98
99
|
get nativeActorRef(): ZLinkBackendActorRef | undefined;
|
|
100
|
+
currentActorRef(): ZLinkBackendActorRef;
|
|
99
101
|
get boundSessionBindingGeneration(): bigint;
|
|
100
102
|
get entryNodeRid(): RoutingId | undefined;
|
|
101
103
|
/** The one current Session binding of this Actor (Session–Actor binding §6). */
|
|
@@ -124,7 +126,6 @@ export declare class ZLinkActorRuntimeState {
|
|
|
124
126
|
setOwnerLeaseGeneration(generation: bigint): void;
|
|
125
127
|
getOrStartDestroy(entryNodeRid: RoutingId, destroy: (actorRef: ZLinkBackendActorRef | undefined) => Promise<void>): Promise<void>;
|
|
126
128
|
markNativeActorDestroyed(actorRef: ZLinkBackendActorRef): void;
|
|
127
|
-
clearFailedDestroy(task: Promise<void>): void;
|
|
128
129
|
ensureContext(createContext: () => ZLinkActorContext): ZLinkActorContext;
|
|
129
130
|
getOrStartCreation(actorType: string, failIfExists: boolean, createActor: () => Promise<ZLinkActorCreationAttemptResult>): ZLinkActorCreationOperation;
|
|
130
131
|
clearFailedCreation(task: Promise<ZLinkActorCreationAttemptResult>): boolean;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ZLinkActorRuntimeState = void 0;
|
|
4
4
|
exports.toFrameworkRoutingId = toFrameworkRoutingId;
|
|
5
5
|
exports.toFrameworkActorRef = toFrameworkActorRef;
|
|
6
|
+
const configuration_1 = require("../configuration");
|
|
6
7
|
const state_lane_1 = require("../execution/state-lane");
|
|
7
8
|
const framework_errors_internal_1 = require("../framework-errors-internal");
|
|
8
9
|
const routing_id_1 = require("../routing-id");
|
|
@@ -57,7 +58,9 @@ class ZLinkActorRuntimeState {
|
|
|
57
58
|
spotIdValue;
|
|
58
59
|
spotGenerationValue;
|
|
59
60
|
spotMembershipEpochValue = 0n;
|
|
61
|
+
// Preserve the exact incarnation until teardown finalization, even after native removal.
|
|
60
62
|
nativeActorRefValue;
|
|
63
|
+
nativeActorDestroyed = false;
|
|
61
64
|
entryNodeRidValue;
|
|
62
65
|
boundSessionValue;
|
|
63
66
|
remoteActorPacketTargetValue;
|
|
@@ -103,6 +106,12 @@ class ZLinkActorRuntimeState {
|
|
|
103
106
|
get nativeActorRef() {
|
|
104
107
|
return this.nativeActorRefValue;
|
|
105
108
|
}
|
|
109
|
+
currentActorRef() {
|
|
110
|
+
if (this.nativeActorRefValue === undefined) {
|
|
111
|
+
throw new configuration_1.ZLinkConfigurationException(`Actor '${this.actorId}' lifecycle identity is not initialized.`);
|
|
112
|
+
}
|
|
113
|
+
return this.nativeActorRefValue;
|
|
114
|
+
}
|
|
106
115
|
get boundSessionBindingGeneration() {
|
|
107
116
|
return this.boundSessionValue?.bindingGeneration ?? 0n;
|
|
108
117
|
}
|
|
@@ -194,20 +203,23 @@ class ZLinkActorRuntimeState {
|
|
|
194
203
|
!(0, routing_id_1.routingIdsEqual)(toFrameworkRoutingId(actorRef.nodeRid), entryNodeRid)) {
|
|
195
204
|
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorRouteNotFound, `Actor '${this.actorId}' is not owned by this Entry Spot.`);
|
|
196
205
|
}
|
|
197
|
-
const task = Promise.resolve()
|
|
206
|
+
const task = Promise.resolve()
|
|
207
|
+
.then(() => destroy(this.nativeActorDestroyed ? undefined : actorRef))
|
|
208
|
+
.catch((error) => {
|
|
209
|
+
// A caller may start another attempt only after this attempt has failed.
|
|
210
|
+
if (this.destroyTask === task) {
|
|
211
|
+
this.destroyTask = undefined;
|
|
212
|
+
this.movingValue = false;
|
|
213
|
+
}
|
|
214
|
+
throw error;
|
|
215
|
+
});
|
|
198
216
|
this.destroyTask = task;
|
|
199
217
|
return task;
|
|
200
218
|
}
|
|
201
219
|
markNativeActorDestroyed(actorRef) {
|
|
202
220
|
if (this.nativeActorRefValue === actorRef) {
|
|
203
|
-
this.
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
clearFailedDestroy(task) {
|
|
207
|
-
if (this.destroyTask === task) {
|
|
208
|
-
this.destroyTask = undefined;
|
|
221
|
+
this.nativeActorDestroyed = true;
|
|
209
222
|
}
|
|
210
|
-
this.movingValue = false;
|
|
211
223
|
}
|
|
212
224
|
ensureContext(createContext) {
|
|
213
225
|
this.context ??= createContext();
|
|
@@ -243,6 +255,7 @@ class ZLinkActorRuntimeState {
|
|
|
243
255
|
this.createRequestPayloadValue = undefined;
|
|
244
256
|
this.configured = false;
|
|
245
257
|
this.nativeActorRefValue = undefined;
|
|
258
|
+
this.nativeActorDestroyed = false;
|
|
246
259
|
this.entryNodeRidValue = undefined;
|
|
247
260
|
return true;
|
|
248
261
|
}
|
|
@@ -278,6 +291,7 @@ class ZLinkActorRuntimeState {
|
|
|
278
291
|
}
|
|
279
292
|
setNativeActorRef(actorRef) {
|
|
280
293
|
this.nativeActorRefValue = actorRef;
|
|
294
|
+
this.nativeActorDestroyed = false;
|
|
281
295
|
this.entryNodeRidValue ??= toFrameworkRoutingId(actorRef.nodeRid);
|
|
282
296
|
}
|
|
283
297
|
/** @internal Installs one authority-confirmed binding inside its Actor owner turn. */
|
|
@@ -344,6 +358,7 @@ class ZLinkActorRuntimeState {
|
|
|
344
358
|
this.spotGenerationValue = undefined;
|
|
345
359
|
this.spotMembershipEpochValue = 0n;
|
|
346
360
|
this.nativeActorRefValue = undefined;
|
|
361
|
+
this.nativeActorDestroyed = false;
|
|
347
362
|
this.entryNodeRidValue = undefined;
|
|
348
363
|
this.boundSessionValue = undefined;
|
|
349
364
|
this.remoteActorPacketTargetValue = undefined;
|
|
@@ -366,6 +381,7 @@ class ZLinkActorRuntimeState {
|
|
|
366
381
|
this.spotValue = undefined;
|
|
367
382
|
this.spotIdValue = undefined;
|
|
368
383
|
this.nativeActorRefValue = undefined;
|
|
384
|
+
this.nativeActorDestroyed = false;
|
|
369
385
|
// The arriving relocation installs the binding it carries.
|
|
370
386
|
this.boundSessionValue = undefined;
|
|
371
387
|
this.remoteActorPacketTargetValue = undefined;
|
|
@@ -64,7 +64,7 @@ export declare class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
64
64
|
private awaitActorDestroy;
|
|
65
65
|
getOrCreateActor(actorId: string, actorType: string, signal?: AbortSignal): Promise<ZLinkActor>;
|
|
66
66
|
getOrCreateWithNativeRef(actorId: string, actorType: string, actorRef: ZLinkBackendActorRef, createRequest?: unknown, signal?: AbortSignal): Promise<ZLinkActor>;
|
|
67
|
-
getOrCreateActorResult(actorId: string, actorType: string, createRequest?: unknown, signal?: AbortSignal): Promise<ZLinkActorLocalCreateResult
|
|
67
|
+
getOrCreateActorResult(actorId: string, actorType: string, createRequest?: unknown, signal?: AbortSignal): Promise<ZLinkActorLocalCreateResult<ActorRef | undefined>>;
|
|
68
68
|
createReservedActorResult(actorId: string, actorType: string, createRequest: unknown, signal?: AbortSignal, nativeActorRef?: ZLinkBackendActorRef): Promise<ZLinkActorLocalCreateResult>;
|
|
69
69
|
adoptCreatedAuthority(actorId: string, authorityOwnerGeneration: bigint, ownerLeaseGeneration: bigint): void;
|
|
70
70
|
getState(actorId: string): ZLinkActorRuntimeState | undefined;
|
|
@@ -77,10 +77,10 @@ export declare class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
77
77
|
private getOrCreateState;
|
|
78
78
|
private actorRefForState;
|
|
79
79
|
}
|
|
80
|
-
export type ZLinkActorLocalCreateResult = {
|
|
80
|
+
export type ZLinkActorLocalCreateResult<TActorRef extends ActorRef | undefined = ActorRef> = {
|
|
81
81
|
readonly status: 'existing' | 'created';
|
|
82
82
|
readonly actor: ZLinkActor;
|
|
83
|
-
readonly actorRef:
|
|
83
|
+
readonly actorRef: TActorRef;
|
|
84
84
|
readonly reply?: unknown;
|
|
85
85
|
} | {
|
|
86
86
|
readonly status: 'rejected';
|
|
@@ -17,7 +17,6 @@ const actor_creation_1 = require("./actor-creation");
|
|
|
17
17
|
const actor_runtime_state_1 = require("./actor-runtime-state");
|
|
18
18
|
const transferred_actor_rollback_1 = require("./transferred-actor-rollback");
|
|
19
19
|
const ACTOR_CREATION_RETRY_INTERVAL_MS = 5;
|
|
20
|
-
const LOCAL_DIAGNOSTIC_NODE_RID = 'local';
|
|
21
20
|
var actor_client_1 = require("./actor-client");
|
|
22
21
|
Object.defineProperty(exports, "DefaultZLinkActorClient", { enumerable: true, get: function () { return actor_client_1.DefaultZLinkActorClient; } });
|
|
23
22
|
var actor_context_1 = require("./actor-context");
|
|
@@ -100,7 +99,7 @@ class DefaultZLinkActorManager {
|
|
|
100
99
|
if (this.relocationStaged.has(actorId))
|
|
101
100
|
return undefined;
|
|
102
101
|
const state = this.states.get(actorId);
|
|
103
|
-
if (state?.actor !== undefined) {
|
|
102
|
+
if (state?.actor !== undefined && state.nativeActorRef !== undefined) {
|
|
104
103
|
return this.actorRefForState(state);
|
|
105
104
|
}
|
|
106
105
|
return await this.options.actorRefResolver?.resolveActorRef(actorId, signal);
|
|
@@ -111,7 +110,9 @@ class DefaultZLinkActorManager {
|
|
|
111
110
|
async findSpot(actorId, signal) {
|
|
112
111
|
(0, abort_1.throwIfAborted)(signal);
|
|
113
112
|
const state = this.states.get(actorId);
|
|
114
|
-
if (state?.actor === undefined ||
|
|
113
|
+
if (state?.actor === undefined ||
|
|
114
|
+
state.spotId === undefined ||
|
|
115
|
+
state.nativeActorRef === undefined)
|
|
115
116
|
return undefined;
|
|
116
117
|
const spotId = state.spotId;
|
|
117
118
|
const actorRef = this.actorRefForState(state);
|
|
@@ -162,13 +163,7 @@ class DefaultZLinkActorManager {
|
|
|
162
163
|
}
|
|
163
164
|
await this.finalizeDestroyedActor(actor.actorId, state);
|
|
164
165
|
}));
|
|
165
|
-
|
|
166
|
-
await this.awaitActorDestroy(state, destroyTask);
|
|
167
|
-
}
|
|
168
|
-
catch (error) {
|
|
169
|
-
state.clearFailedDestroy(destroyTask);
|
|
170
|
-
throw error;
|
|
171
|
-
}
|
|
166
|
+
await this.awaitActorDestroy(state, destroyTask);
|
|
172
167
|
return true;
|
|
173
168
|
}
|
|
174
169
|
async materializeTransferredActor(actorId, actorType, adapterKey, transferState, signal) {
|
|
@@ -235,9 +230,10 @@ class DefaultZLinkActorManager {
|
|
|
235
230
|
if (result.status === 'failed') {
|
|
236
231
|
throw actorApplicationFailure(actorId, result.error);
|
|
237
232
|
}
|
|
233
|
+
const actorRef = result.actorRef ?? this.actorRefForState(this.states.get(actorId));
|
|
238
234
|
return result.reply === undefined
|
|
239
|
-
? { status: result.status, actor:
|
|
240
|
-
: { status: result.status, actor:
|
|
235
|
+
? { status: result.status, actor: actorRef }
|
|
236
|
+
: { status: result.status, actor: actorRef, reply: result.reply };
|
|
241
237
|
}
|
|
242
238
|
ensureActorTypeBelongsToMesh(meshName, actorType) {
|
|
243
239
|
const registeredMesh = this.options.actorMeshNameProvider?.(actorType);
|
|
@@ -442,7 +438,13 @@ class DefaultZLinkActorManager {
|
|
|
442
438
|
}
|
|
443
439
|
async createReservedActorResult(actorId, actorType, createRequest, signal, nativeActorRef) {
|
|
444
440
|
this.rememberActorMeshFromType(actorId, actorType);
|
|
445
|
-
|
|
441
|
+
const result = await this.createOrGet(actorId, actorType, false, createRequest, signal, false, Registration_1.DEFAULT_REQUEST_TIMEOUT_MS, nativeActorRef);
|
|
442
|
+
if (result.status === 'failed' || result.status === 'rejected')
|
|
443
|
+
return result;
|
|
444
|
+
return {
|
|
445
|
+
...result,
|
|
446
|
+
actorRef: this.actorRefForState(this.states.get(actorId))
|
|
447
|
+
};
|
|
446
448
|
}
|
|
447
449
|
adoptCreatedAuthority(actorId, authorityOwnerGeneration, ownerLeaseGeneration) {
|
|
448
450
|
const state = this.states.get(actorId);
|
|
@@ -473,7 +475,7 @@ class DefaultZLinkActorManager {
|
|
|
473
475
|
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorRouteNotFound, `Actor '${actor.context.actorId}' must leave its current SPOT before destroy.`);
|
|
474
476
|
}
|
|
475
477
|
const destroyTask = state.getOrStartDestroy(entryNodeRid, (actorRef) => this.terminateActor(state, async () => {
|
|
476
|
-
const destroyedActorRef =
|
|
478
|
+
const destroyedActorRef = state.nativeActorRef === undefined ? undefined : this.actorRefForState(state);
|
|
477
479
|
if (actorRef !== undefined) {
|
|
478
480
|
await node.destroyActor(actorRef, 0, destroySignal);
|
|
479
481
|
state.markNativeActorDestroyed(actorRef);
|
|
@@ -483,13 +485,7 @@ class DefaultZLinkActorManager {
|
|
|
483
485
|
}
|
|
484
486
|
await this.finalizeDestroyedActor(actor.context.actorId, state);
|
|
485
487
|
}));
|
|
486
|
-
|
|
487
|
-
await this.awaitActorDestroy(state, destroyTask);
|
|
488
|
-
}
|
|
489
|
-
catch (error) {
|
|
490
|
-
state.clearFailedDestroy(destroyTask);
|
|
491
|
-
throw error;
|
|
492
|
-
}
|
|
488
|
+
await this.awaitActorDestroy(state, destroyTask);
|
|
493
489
|
}
|
|
494
490
|
async createOrGet(actorId, actorType, failIfExists, request, signal, claimLocation = true, timeoutMs = Registration_1.DEFAULT_REQUEST_TIMEOUT_MS, nativeActorRef) {
|
|
495
491
|
const deadline = performance.now() + timeoutMs;
|
|
@@ -533,7 +529,7 @@ class DefaultZLinkActorManager {
|
|
|
533
529
|
try {
|
|
534
530
|
const result = await operation.task;
|
|
535
531
|
if (result.status === 'created') {
|
|
536
|
-
const actorRef = this.actorRefForState(state);
|
|
532
|
+
const actorRef = state.nativeActorRef === undefined ? undefined : this.actorRefForState(state);
|
|
537
533
|
return {
|
|
538
534
|
status: operation.created ? 'created' : 'existing',
|
|
539
535
|
actor: result.actor,
|
|
@@ -602,16 +598,7 @@ class DefaultZLinkActorManager {
|
|
|
602
598
|
return state;
|
|
603
599
|
}
|
|
604
600
|
actorRefForState(state) {
|
|
605
|
-
|
|
606
|
-
if (nativeActorRef !== undefined) {
|
|
607
|
-
return (0, actor_runtime_state_1.toFrameworkActorRef)(nativeActorRef, state.meshName ?? '');
|
|
608
|
-
}
|
|
609
|
-
return {
|
|
610
|
-
actorId: state.actorId,
|
|
611
|
-
objectGeneration: 1n,
|
|
612
|
-
meshName: state.meshName ?? '',
|
|
613
|
-
nodeRid: this.options.actorCreatedNodeRidProvider?.() ?? LOCAL_DIAGNOSTIC_NODE_RID
|
|
614
|
-
};
|
|
601
|
+
return (0, actor_runtime_state_1.toFrameworkActorRef)(state.currentActorRef(), state.meshName ?? '');
|
|
615
602
|
}
|
|
616
603
|
}
|
|
617
604
|
exports.DefaultZLinkActorManager = DefaultZLinkActorManager;
|
|
@@ -311,24 +311,23 @@ class ZLinkActorLocationClaims {
|
|
|
311
311
|
const current = await store.readAuthority(key);
|
|
312
312
|
if (!matchesActorAuthority(current, actorRef, ownerToken))
|
|
313
313
|
return;
|
|
314
|
-
|
|
314
|
+
let result = await store.compareExchangeAuthority(key, current.storeVersion, {
|
|
315
315
|
kind: 'delete'
|
|
316
316
|
});
|
|
317
|
-
if (result.kind === 'deleted' ||
|
|
318
|
-
(result.kind === 'conflict' && result.current.kind === 'missing')) {
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
317
|
if (result.kind === 'conflict' &&
|
|
322
318
|
result.current.kind === 'snapshot' &&
|
|
323
319
|
matchesActorAuthority(result.current, actorRef, ownerToken)) {
|
|
324
|
-
|
|
320
|
+
result = await store.compareExchangeAuthority(key, result.current.storeVersion, {
|
|
325
321
|
kind: 'delete'
|
|
326
322
|
});
|
|
327
|
-
if (retry.kind === 'deleted' ||
|
|
328
|
-
(retry.kind === 'conflict' && retry.current.kind === 'missing')) {
|
|
329
|
-
return;
|
|
330
|
-
}
|
|
331
323
|
}
|
|
324
|
+
if (result.kind === 'deleted' ||
|
|
325
|
+
(result.kind === 'conflict' && result.current.kind === 'missing')) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
throw new Errors_1.ZLinkFrameworkException(result.kind === 'conflict'
|
|
329
|
+
? classifyMembershipAuthorityFailure(result.current, actorRef)
|
|
330
|
+
: Errors_1.ZLinkFrameworkErrorKind.InternalFailure, `Actor '${actorId}' authority release was rejected with status ${result.kind}.`);
|
|
332
331
|
}
|
|
333
332
|
}
|
|
334
333
|
exports.ZLinkActorLocationClaims = ZLinkActorLocationClaims;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zlink-systems/framework",
|
|
3
|
-
"version": "0.32.
|
|
3
|
+
"version": "0.32.1",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@opentelemetry/api": "^1.9.1",
|
|
21
21
|
"@opentelemetry/api-logs": "^0.221.0",
|
|
22
|
-
"@zlink-systems/stream-wire": "0.32.
|
|
22
|
+
"@zlink-systems/stream-wire": "0.32.1",
|
|
23
23
|
"@zlink-systems/zlink": "1.23.0"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
@@ -97,7 +97,7 @@ export class DefaultZLinkActorContext implements ZLinkActorContext {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
get objectGeneration(): bigint {
|
|
100
|
-
return this.state.
|
|
100
|
+
return this.state.currentActorRef().generation;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
get spotId(): SpotId | undefined {
|
|
@@ -211,7 +211,7 @@ export class ZLinkActorCreationCoordinator {
|
|
|
211
211
|
await this.options.locationLifecycle?.setActorRef(
|
|
212
212
|
actorType,
|
|
213
213
|
actorId,
|
|
214
|
-
|
|
214
|
+
toFrameworkActorRef(state.currentActorRef(), meshName),
|
|
215
215
|
0n
|
|
216
216
|
);
|
|
217
217
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
import type { Message } from '../../contracts/Common/Message';
|
|
11
11
|
import type { ZLinkBackendActorRef, ZLinkBackendMeshNode } from '../backend/contracts';
|
|
12
|
+
import { ZLinkConfigurationException } from '../configuration';
|
|
12
13
|
import { isStructuralGuardEnabled } from '../execution/state-lane';
|
|
13
14
|
import {
|
|
14
15
|
ZLinkFrameworkInternalErrorKind,
|
|
@@ -172,7 +173,9 @@ export class ZLinkActorRuntimeState {
|
|
|
172
173
|
private spotIdValue: RoutingId | undefined;
|
|
173
174
|
private spotGenerationValue: bigint | undefined;
|
|
174
175
|
private spotMembershipEpochValue = 0n;
|
|
176
|
+
// Preserve the exact incarnation until teardown finalization, even after native removal.
|
|
175
177
|
private nativeActorRefValue: ZLinkBackendActorRef | undefined;
|
|
178
|
+
private nativeActorDestroyed = false;
|
|
176
179
|
private entryNodeRidValue: RoutingId | undefined;
|
|
177
180
|
boundSessionValue: ZLinkRemoteBoundSessionTarget | undefined;
|
|
178
181
|
private remoteActorPacketTargetValue: ZLinkRemoteActorPacketTarget | undefined;
|
|
@@ -228,6 +231,15 @@ export class ZLinkActorRuntimeState {
|
|
|
228
231
|
return this.nativeActorRefValue;
|
|
229
232
|
}
|
|
230
233
|
|
|
234
|
+
currentActorRef(): ZLinkBackendActorRef {
|
|
235
|
+
if (this.nativeActorRefValue === undefined) {
|
|
236
|
+
throw new ZLinkConfigurationException(
|
|
237
|
+
`Actor '${this.actorId}' lifecycle identity is not initialized.`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
return this.nativeActorRefValue;
|
|
241
|
+
}
|
|
242
|
+
|
|
231
243
|
get boundSessionBindingGeneration(): bigint {
|
|
232
244
|
return this.boundSessionValue?.bindingGeneration ?? 0n;
|
|
233
245
|
}
|
|
@@ -355,24 +367,26 @@ export class ZLinkActorRuntimeState {
|
|
|
355
367
|
);
|
|
356
368
|
}
|
|
357
369
|
|
|
358
|
-
const task = Promise.resolve()
|
|
370
|
+
const task = Promise.resolve()
|
|
371
|
+
.then(() => destroy(this.nativeActorDestroyed ? undefined : actorRef))
|
|
372
|
+
.catch((error: unknown) => {
|
|
373
|
+
// A caller may start another attempt only after this attempt has failed.
|
|
374
|
+
if (this.destroyTask === task) {
|
|
375
|
+
this.destroyTask = undefined;
|
|
376
|
+
this.movingValue = false;
|
|
377
|
+
}
|
|
378
|
+
throw error;
|
|
379
|
+
});
|
|
359
380
|
this.destroyTask = task;
|
|
360
381
|
return task;
|
|
361
382
|
}
|
|
362
383
|
|
|
363
384
|
markNativeActorDestroyed(actorRef: ZLinkBackendActorRef): void {
|
|
364
385
|
if (this.nativeActorRefValue === actorRef) {
|
|
365
|
-
this.
|
|
386
|
+
this.nativeActorDestroyed = true;
|
|
366
387
|
}
|
|
367
388
|
}
|
|
368
389
|
|
|
369
|
-
clearFailedDestroy(task: Promise<void>): void {
|
|
370
|
-
if (this.destroyTask === task) {
|
|
371
|
-
this.destroyTask = undefined;
|
|
372
|
-
}
|
|
373
|
-
this.movingValue = false;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
390
|
ensureContext(createContext: () => ZLinkActorContext): ZLinkActorContext {
|
|
377
391
|
this.context ??= createContext();
|
|
378
392
|
return this.context;
|
|
@@ -425,6 +439,7 @@ export class ZLinkActorRuntimeState {
|
|
|
425
439
|
this.createRequestPayloadValue = undefined;
|
|
426
440
|
this.configured = false;
|
|
427
441
|
this.nativeActorRefValue = undefined;
|
|
442
|
+
this.nativeActorDestroyed = false;
|
|
428
443
|
this.entryNodeRidValue = undefined;
|
|
429
444
|
return true;
|
|
430
445
|
}
|
|
@@ -474,6 +489,7 @@ export class ZLinkActorRuntimeState {
|
|
|
474
489
|
|
|
475
490
|
setNativeActorRef(actorRef: ZLinkBackendActorRef): void {
|
|
476
491
|
this.nativeActorRefValue = actorRef;
|
|
492
|
+
this.nativeActorDestroyed = false;
|
|
477
493
|
this.entryNodeRidValue ??= toFrameworkRoutingId(actorRef.nodeRid);
|
|
478
494
|
}
|
|
479
495
|
|
|
@@ -556,6 +572,7 @@ export class ZLinkActorRuntimeState {
|
|
|
556
572
|
this.spotGenerationValue = undefined;
|
|
557
573
|
this.spotMembershipEpochValue = 0n;
|
|
558
574
|
this.nativeActorRefValue = undefined;
|
|
575
|
+
this.nativeActorDestroyed = false;
|
|
559
576
|
this.entryNodeRidValue = undefined;
|
|
560
577
|
this.boundSessionValue = undefined;
|
|
561
578
|
this.remoteActorPacketTargetValue = undefined;
|
|
@@ -578,6 +595,7 @@ export class ZLinkActorRuntimeState {
|
|
|
578
595
|
this.spotValue = undefined;
|
|
579
596
|
this.spotIdValue = undefined;
|
|
580
597
|
this.nativeActorRefValue = undefined;
|
|
598
|
+
this.nativeActorDestroyed = false;
|
|
581
599
|
// The arriving relocation installs the binding it carries.
|
|
582
600
|
this.boundSessionValue = undefined;
|
|
583
601
|
this.remoteActorPacketTargetValue = undefined;
|
|
@@ -40,7 +40,6 @@ import {
|
|
|
40
40
|
} from './actor-runtime-state';
|
|
41
41
|
import { ZLinkTransferredActorRollbackCoordinator } from './transferred-actor-rollback';
|
|
42
42
|
const ACTOR_CREATION_RETRY_INTERVAL_MS = 5;
|
|
43
|
-
const LOCAL_DIAGNOSTIC_NODE_RID = 'local';
|
|
44
43
|
|
|
45
44
|
export { DefaultZLinkActorClient, type ZLinkActorClientOptions } from './actor-client';
|
|
46
45
|
export { DefaultZLinkActorContext, ZLINK_ACTOR_JOIN_ENTRY_SPOT_RUNTIME } from './actor-context';
|
|
@@ -167,7 +166,7 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
167
166
|
throwIfAborted(signal);
|
|
168
167
|
if (this.relocationStaged.has(actorId)) return undefined;
|
|
169
168
|
const state = this.states.get(actorId);
|
|
170
|
-
if (state?.actor !== undefined) {
|
|
169
|
+
if (state?.actor !== undefined && state.nativeActorRef !== undefined) {
|
|
171
170
|
return this.actorRefForState(state);
|
|
172
171
|
}
|
|
173
172
|
return await this.options.actorRefResolver?.resolveActorRef(actorId, signal);
|
|
@@ -182,7 +181,12 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
182
181
|
async findSpot(actorId: string, signal?: AbortSignal): Promise<SpotRef | undefined> {
|
|
183
182
|
throwIfAborted(signal);
|
|
184
183
|
const state = this.states.get(actorId);
|
|
185
|
-
if (
|
|
184
|
+
if (
|
|
185
|
+
state?.actor === undefined ||
|
|
186
|
+
state.spotId === undefined ||
|
|
187
|
+
state.nativeActorRef === undefined
|
|
188
|
+
)
|
|
189
|
+
return undefined;
|
|
186
190
|
const spotId = state.spotId;
|
|
187
191
|
const actorRef = this.actorRefForState(state);
|
|
188
192
|
return {
|
|
@@ -249,12 +253,7 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
249
253
|
await this.finalizeDestroyedActor(actor.actorId, state);
|
|
250
254
|
})
|
|
251
255
|
);
|
|
252
|
-
|
|
253
|
-
await this.awaitActorDestroy(state, destroyTask);
|
|
254
|
-
} catch (error) {
|
|
255
|
-
state.clearFailedDestroy(destroyTask);
|
|
256
|
-
throw error;
|
|
257
|
-
}
|
|
256
|
+
await this.awaitActorDestroy(state, destroyTask);
|
|
258
257
|
return true;
|
|
259
258
|
}
|
|
260
259
|
|
|
@@ -362,9 +361,10 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
362
361
|
if (result.status === 'failed') {
|
|
363
362
|
throw actorApplicationFailure(actorId, result.error);
|
|
364
363
|
}
|
|
364
|
+
const actorRef = result.actorRef ?? this.actorRefForState(this.states.get(actorId)!);
|
|
365
365
|
return result.reply === undefined
|
|
366
|
-
? { status: result.status, actor:
|
|
367
|
-
: { status: result.status, actor:
|
|
366
|
+
? { status: result.status, actor: actorRef }
|
|
367
|
+
: { status: result.status, actor: actorRef, reply: result.reply };
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
private ensureActorTypeBelongsToMesh(meshName: string, actorType: string): void {
|
|
@@ -638,7 +638,7 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
638
638
|
actorType: string,
|
|
639
639
|
createRequest?: unknown,
|
|
640
640
|
signal?: AbortSignal
|
|
641
|
-
): Promise<ZLinkActorLocalCreateResult
|
|
641
|
+
): Promise<ZLinkActorLocalCreateResult<ActorRef | undefined>> {
|
|
642
642
|
this.rememberActorMeshFromType(actorId, actorType);
|
|
643
643
|
return await this.createOrGet(actorId, actorType, false, createRequest, signal);
|
|
644
644
|
}
|
|
@@ -651,7 +651,7 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
651
651
|
nativeActorRef?: ZLinkBackendActorRef
|
|
652
652
|
): Promise<ZLinkActorLocalCreateResult> {
|
|
653
653
|
this.rememberActorMeshFromType(actorId, actorType);
|
|
654
|
-
|
|
654
|
+
const result = await this.createOrGet(
|
|
655
655
|
actorId,
|
|
656
656
|
actorType,
|
|
657
657
|
false,
|
|
@@ -661,6 +661,11 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
661
661
|
DEFAULT_REQUEST_TIMEOUT_MS,
|
|
662
662
|
nativeActorRef
|
|
663
663
|
);
|
|
664
|
+
if (result.status === 'failed' || result.status === 'rejected') return result;
|
|
665
|
+
return {
|
|
666
|
+
...result,
|
|
667
|
+
actorRef: this.actorRefForState(this.states.get(actorId)!)
|
|
668
|
+
};
|
|
664
669
|
}
|
|
665
670
|
|
|
666
671
|
adoptCreatedAuthority(
|
|
@@ -712,7 +717,7 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
712
717
|
const destroyTask = state.getOrStartDestroy(entryNodeRid, (actorRef) =>
|
|
713
718
|
this.terminateActor(state, async () => {
|
|
714
719
|
const destroyedActorRef =
|
|
715
|
-
|
|
720
|
+
state.nativeActorRef === undefined ? undefined : this.actorRefForState(state);
|
|
716
721
|
if (actorRef !== undefined) {
|
|
717
722
|
await node.destroyActor(actorRef, 0, destroySignal);
|
|
718
723
|
state.markNativeActorDestroyed(actorRef);
|
|
@@ -728,12 +733,7 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
728
733
|
})
|
|
729
734
|
);
|
|
730
735
|
|
|
731
|
-
|
|
732
|
-
await this.awaitActorDestroy(state, destroyTask);
|
|
733
|
-
} catch (error) {
|
|
734
|
-
state.clearFailedDestroy(destroyTask);
|
|
735
|
-
throw error;
|
|
736
|
-
}
|
|
736
|
+
await this.awaitActorDestroy(state, destroyTask);
|
|
737
737
|
}
|
|
738
738
|
|
|
739
739
|
private async createOrGet(
|
|
@@ -745,7 +745,7 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
745
745
|
claimLocation = true,
|
|
746
746
|
timeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS,
|
|
747
747
|
nativeActorRef?: ZLinkBackendActorRef
|
|
748
|
-
): Promise<ZLinkActorLocalCreateResult
|
|
748
|
+
): Promise<ZLinkActorLocalCreateResult<ActorRef | undefined>> {
|
|
749
749
|
const deadline = performance.now() + timeoutMs;
|
|
750
750
|
const requestedMeshName = this.actorMeshNames.get(actorId);
|
|
751
751
|
for (;;) {
|
|
@@ -801,7 +801,8 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
801
801
|
try {
|
|
802
802
|
const result = await operation.task;
|
|
803
803
|
if (result.status === 'created') {
|
|
804
|
-
const actorRef =
|
|
804
|
+
const actorRef =
|
|
805
|
+
state.nativeActorRef === undefined ? undefined : this.actorRefForState(state);
|
|
805
806
|
return {
|
|
806
807
|
status: operation.created ? 'created' : 'existing',
|
|
807
808
|
actor: result.actor,
|
|
@@ -886,16 +887,7 @@ export class DefaultZLinkActorManager implements ZLinkActorManager {
|
|
|
886
887
|
}
|
|
887
888
|
|
|
888
889
|
private actorRefForState(state: ZLinkActorRuntimeState): ActorRef {
|
|
889
|
-
|
|
890
|
-
if (nativeActorRef !== undefined) {
|
|
891
|
-
return toFrameworkActorRef(nativeActorRef, state.meshName ?? '');
|
|
892
|
-
}
|
|
893
|
-
return {
|
|
894
|
-
actorId: state.actorId,
|
|
895
|
-
objectGeneration: 1n,
|
|
896
|
-
meshName: state.meshName ?? '',
|
|
897
|
-
nodeRid: this.options.actorCreatedNodeRidProvider?.() ?? LOCAL_DIAGNOSTIC_NODE_RID
|
|
898
|
-
};
|
|
890
|
+
return toFrameworkActorRef(state.currentActorRef(), state.meshName ?? '');
|
|
899
891
|
}
|
|
900
892
|
}
|
|
901
893
|
|
|
@@ -994,11 +986,11 @@ class ZLinkActorGetOrCreateCallRuntime
|
|
|
994
986
|
extends ZLinkActorCreateCallRuntime
|
|
995
987
|
implements ZLinkActorGetOrCreateCall {}
|
|
996
988
|
|
|
997
|
-
export type ZLinkActorLocalCreateResult =
|
|
989
|
+
export type ZLinkActorLocalCreateResult<TActorRef extends ActorRef | undefined = ActorRef> =
|
|
998
990
|
| {
|
|
999
991
|
readonly status: 'existing' | 'created';
|
|
1000
992
|
readonly actor: ZLinkActor;
|
|
1001
|
-
readonly actorRef:
|
|
993
|
+
readonly actorRef: TActorRef;
|
|
1002
994
|
readonly reply?: unknown;
|
|
1003
995
|
}
|
|
1004
996
|
| {
|
|
@@ -1010,7 +1002,10 @@ export type ZLinkActorLocalCreateResult =
|
|
|
1010
1002
|
readonly error: unknown;
|
|
1011
1003
|
};
|
|
1012
1004
|
|
|
1013
|
-
function requireCreatedActor(
|
|
1005
|
+
function requireCreatedActor(
|
|
1006
|
+
result: ZLinkActorLocalCreateResult<ActorRef | undefined>,
|
|
1007
|
+
actorId: string
|
|
1008
|
+
): ZLinkActor {
|
|
1014
1009
|
if (result.status === 'existing' || result.status === 'created') return result.actor;
|
|
1015
1010
|
if (result.status === 'failed') {
|
|
1016
1011
|
throw actorApplicationFailure(actorId, result.error);
|
|
@@ -426,30 +426,30 @@ export class ZLinkActorLocationClaims {
|
|
|
426
426
|
const current = await store.readAuthority(key);
|
|
427
427
|
if (!matchesActorAuthority(current, actorRef, ownerToken)) return;
|
|
428
428
|
|
|
429
|
-
|
|
429
|
+
let result = await store.compareExchangeAuthority(key, current.storeVersion, {
|
|
430
430
|
kind: 'delete'
|
|
431
431
|
});
|
|
432
|
-
if (
|
|
433
|
-
result.kind === 'deleted' ||
|
|
434
|
-
(result.kind === 'conflict' && result.current.kind === 'missing')
|
|
435
|
-
) {
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
432
|
if (
|
|
439
433
|
result.kind === 'conflict' &&
|
|
440
434
|
result.current.kind === 'snapshot' &&
|
|
441
435
|
matchesActorAuthority(result.current, actorRef, ownerToken)
|
|
442
436
|
) {
|
|
443
|
-
|
|
437
|
+
result = await store.compareExchangeAuthority(key, result.current.storeVersion, {
|
|
444
438
|
kind: 'delete'
|
|
445
439
|
});
|
|
446
|
-
if (
|
|
447
|
-
retry.kind === 'deleted' ||
|
|
448
|
-
(retry.kind === 'conflict' && retry.current.kind === 'missing')
|
|
449
|
-
) {
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
440
|
}
|
|
441
|
+
if (
|
|
442
|
+
result.kind === 'deleted' ||
|
|
443
|
+
(result.kind === 'conflict' && result.current.kind === 'missing')
|
|
444
|
+
) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
throw new ZLinkFrameworkException(
|
|
448
|
+
result.kind === 'conflict'
|
|
449
|
+
? classifyMembershipAuthorityFailure(result.current, actorRef)
|
|
450
|
+
: ZLinkFrameworkErrorKind.InternalFailure,
|
|
451
|
+
`Actor '${actorId}' authority release was rejected with status ${result.kind}.`
|
|
452
|
+
);
|
|
453
453
|
}
|
|
454
454
|
}
|
|
455
455
|
|