@zlink-systems/framework 0.18.1 → 0.20.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/Configuration/Builders.d.ts +0 -2
- package/dist/contracts/Configuration/Configs.d.ts +1 -0
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -2
- package/dist/contracts/Locations/Authority.d.ts +1 -7
- package/dist/runtime/actors/actor-authority-publication.js +25 -22
- package/dist/runtime/actors/actor-creation.js +30 -9
- package/dist/runtime/actors/actor-runtime-state.d.ts +3 -0
- package/dist/runtime/actors/index.d.ts +3 -0
- package/dist/runtime/actors/index.js +14 -3
- package/dist/runtime/admission.d.ts +2 -0
- package/dist/runtime/admission.js +11 -5
- package/dist/runtime/backend/contracts/index.d.ts +4 -1
- package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -0
- package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
- package/dist/runtime/backend/node/node-raw-binding-port.d.ts +2 -1
- package/dist/runtime/backend/node/node-raw-binding-port.js +12 -5
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +1 -1
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +6 -0
- package/dist/runtime/channels/channel-socket-options.js +7 -0
- package/dist/runtime/channels/channel-socket-registry.js +3 -0
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +1 -1
- package/dist/runtime/foundation/service-stateful-runtime.js +1 -5
- package/dist/runtime/host/actor-placement-coordinator.d.ts +14 -5
- package/dist/runtime/host/actor-placement-coordinator.js +159 -47
- package/dist/runtime/host/index.d.ts +2 -1
- package/dist/runtime/host/index.js +77 -120
- package/dist/runtime/host/location-runtime-owner.js +4 -2
- package/dist/runtime/host/remote-actor-join-receiver.js +12 -3
- package/dist/runtime/locations/creation-operation-id.d.ts +4 -0
- package/dist/runtime/locations/creation-operation-id.js +12 -0
- package/dist/runtime/locations/in-memory-authority-store.js +8 -22
- package/dist/runtime/locations/location-store-repository.js +36 -59
- package/dist/runtime/locations/opaque-record-key.d.ts +5 -0
- package/dist/runtime/locations/opaque-record-key.js +16 -0
- package/dist/runtime/locations/runtime.d.ts +7 -0
- package/dist/runtime/locations/runtime.js +130 -42
- package/dist/runtime/protocol/service_wire_codec.generated.d.ts +1672 -0
- package/dist/runtime/protocol/service_wire_codec.generated.js +9169 -0
- package/dist/runtime/spots/spot-node-runtime-manager.js +2 -2
- package/package.json +2 -2
- package/src/contracts/Configuration/Builders.ts +0 -2
- package/src/contracts/Configuration/Configs.ts +1 -0
- package/src/contracts/Configuration/RegistrationTypes.ts +1 -2
- package/src/contracts/Locations/Authority.ts +1 -8
- package/src/runtime/actors/actor-authority-publication.ts +25 -22
- package/src/runtime/actors/actor-creation.ts +45 -11
- package/src/runtime/actors/actor-runtime-state.ts +4 -0
- package/src/runtime/actors/index.ts +32 -4
- package/src/runtime/admission.ts +8 -5
- package/src/runtime/backend/contracts/index.ts +4 -1
- package/src/runtime/backend/node/node-mesh-backend-adapter.ts +2 -1
- package/src/runtime/backend/node/node-raw-binding-port.ts +16 -5
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -1
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +7 -0
- package/src/runtime/channels/channel-socket-options.ts +9 -0
- package/src/runtime/channels/channel-socket-registry.ts +4 -0
- package/src/runtime/foundation/service-stateful-runtime.ts +2 -6
- package/src/runtime/host/actor-placement-coordinator.ts +216 -77
- package/src/runtime/host/index.ts +95 -135
- package/src/runtime/host/location-runtime-owner.ts +4 -2
- package/src/runtime/host/remote-actor-join-receiver.ts +15 -3
- package/src/runtime/locations/creation-operation-id.ts +11 -0
- package/src/runtime/locations/in-memory-authority-store.ts +7 -29
- package/src/runtime/locations/location-store-repository.ts +71 -69
- package/src/runtime/locations/opaque-record-key.ts +26 -0
- package/src/runtime/locations/runtime.ts +154 -60
- package/src/runtime/protocol/service_wire_codec.generated.ts +8302 -0
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
- package/src/runtime/spots/spot-node-runtime-manager.ts +2 -2
|
@@ -6,8 +6,15 @@ const node_crypto_1 = require("node:crypto");
|
|
|
6
6
|
const runtime_values_1 = require("../backend/runtime-values");
|
|
7
7
|
const contracts_1 = require("../../contracts");
|
|
8
8
|
const authority_key_codec_1 = require("../locations/authority-key-codec");
|
|
9
|
+
const creation_operation_id_1 = require("../locations/creation-operation-id");
|
|
9
10
|
const actor_authority_publication_1 = require("../actors/actor-authority-publication");
|
|
10
11
|
const user_spot_creation_coordinator_1 = require("./user-spot-creation-coordinator");
|
|
12
|
+
const service_wire_codec_generated_1 = require("../protocol/service_wire_codec.generated");
|
|
13
|
+
const CREATION_TERMINAL_CODEC_CONTEXT = {
|
|
14
|
+
effectiveCompleteMessageBytesMinusActualEnvelopeOverhead: 1024 * 1024,
|
|
15
|
+
effectiveCompleteMessageBytes: 1024 * 1024,
|
|
16
|
+
runtimePredicates: {}
|
|
17
|
+
};
|
|
11
18
|
class ZLinkActorPlacementCoordinator {
|
|
12
19
|
options;
|
|
13
20
|
constructor(options) {
|
|
@@ -20,6 +27,7 @@ class ZLinkActorPlacementCoordinator {
|
|
|
20
27
|
const contentReference = (0, user_spot_creation_coordinator_1.encodeLocationCreationContent)(requestPayload);
|
|
21
28
|
const requestSha256 = (0, node_crypto_1.createHash)('sha256').update(requestPayload).digest();
|
|
22
29
|
const excluded = new Set();
|
|
30
|
+
const operationId = (0, creation_operation_id_1.randomOperationId)();
|
|
23
31
|
try {
|
|
24
32
|
for (;;) {
|
|
25
33
|
const target = await this.options.target(meshName, stableType, deadline.signal, excluded);
|
|
@@ -75,7 +83,27 @@ class ZLinkActorPlacementCoordinator {
|
|
|
75
83
|
if (reserved.kind !== 'reserved') {
|
|
76
84
|
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorCreateFailed, `Actor '${actorId}' reservation failed with '${reserved.kind}'.`, true);
|
|
77
85
|
}
|
|
78
|
-
const
|
|
86
|
+
const operation = {
|
|
87
|
+
sourceNodeRid: target.sourceNodeRid,
|
|
88
|
+
sourceNodeGeneration: target.sourceNodeGeneration,
|
|
89
|
+
operationId
|
|
90
|
+
};
|
|
91
|
+
let remote;
|
|
92
|
+
let transportFailure;
|
|
93
|
+
try {
|
|
94
|
+
remote = await this.options.remoteCreate(target.meshName, String(target.nodeRid), actorCreateRecord(actorId, stableType, reserved.creating, target, deadlineUnixMs, operation), Math.max(1, Math.ceil(deadlineMs - performance.now())));
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
transportFailure = error;
|
|
98
|
+
}
|
|
99
|
+
if (remote === undefined || !isCompletedActorCreate(remote)) {
|
|
100
|
+
const retained = await this.options.store.readCreationTerminal(operation);
|
|
101
|
+
if (retained.kind === 'found') {
|
|
102
|
+
remote = remoteActorCreateResult(retained.terminalEnvelope, target);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (remote === undefined)
|
|
106
|
+
throw transportFailure;
|
|
79
107
|
if (remote.terminalResult !== runtime_values_1.RequestResult.Ok
|
|
80
108
|
|| remote.failureCode !== 0) {
|
|
81
109
|
// Classify the (terminal, fine failure code) pair via the shared
|
|
@@ -151,35 +179,51 @@ class ZLinkActorPlacementCoordinator {
|
|
|
151
179
|
let completion;
|
|
152
180
|
try {
|
|
153
181
|
local = await materialize(requestPayload, current, signal);
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
readyPayload: (0, actor_authority_publication_1.encodeActorAuthorityIdentity)({
|
|
164
|
-
actorType: record.stableType,
|
|
165
|
-
actor: local.actor,
|
|
166
|
-
meshName: current.allocation.descriptor.meshName,
|
|
167
|
-
ownerNodeGeneration: current.allocation.descriptorLifecycleGeneration,
|
|
168
|
-
owner: {
|
|
169
|
-
ownerId: current.ownerId,
|
|
170
|
-
leaseGeneration: current.ownerLeaseGeneration
|
|
171
|
-
},
|
|
172
|
-
spotId: local.entrySpotId,
|
|
173
|
-
spotGeneration: local.entrySpotGeneration,
|
|
174
|
-
spotKind: contracts_1.ZLinkSpotKind.Entry
|
|
175
|
-
}),
|
|
176
|
-
terminal: terminalPublication(record, terminal)
|
|
177
|
-
}
|
|
178
|
-
: {
|
|
179
|
-
kind: 'rejected',
|
|
180
|
-
terminal: terminalPublication(record, terminal)
|
|
182
|
+
if (local.result === 'failed') {
|
|
183
|
+
completion = await this.options.store.completeCreation({
|
|
184
|
+
key,
|
|
185
|
+
reservationId: record.reservation.reservationId,
|
|
186
|
+
expectedStoreVersion: current.storeVersion.value,
|
|
187
|
+
target: creationTarget(current),
|
|
188
|
+
completion: {
|
|
189
|
+
kind: 'failed',
|
|
190
|
+
terminal: terminalPublication(record, encodeActorTerminal({ result: 'failed' }))
|
|
181
191
|
}
|
|
182
|
-
|
|
192
|
+
}, signal);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
const terminal = encodeActorTerminal(local.result === 'created'
|
|
196
|
+
? { result: 'created', actor: local.actor, reply: local.reply }
|
|
197
|
+
: { result: 'rejected', reply: local.reply });
|
|
198
|
+
completion = await this.options.store.completeCreation({
|
|
199
|
+
key,
|
|
200
|
+
reservationId: record.reservation.reservationId,
|
|
201
|
+
expectedStoreVersion: current.storeVersion.value,
|
|
202
|
+
target: creationTarget(current),
|
|
203
|
+
completion: local.result === 'created'
|
|
204
|
+
? {
|
|
205
|
+
kind: 'created',
|
|
206
|
+
readyPayload: (0, actor_authority_publication_1.encodeActorAuthorityIdentity)({
|
|
207
|
+
actorType: record.stableType,
|
|
208
|
+
actor: local.actor,
|
|
209
|
+
meshName: current.allocation.descriptor.meshName,
|
|
210
|
+
ownerNodeGeneration: current.allocation.descriptorLifecycleGeneration,
|
|
211
|
+
owner: {
|
|
212
|
+
ownerId: current.ownerId,
|
|
213
|
+
leaseGeneration: current.ownerLeaseGeneration
|
|
214
|
+
},
|
|
215
|
+
spotId: local.entrySpotId,
|
|
216
|
+
spotGeneration: local.entrySpotGeneration,
|
|
217
|
+
spotKind: contracts_1.ZLinkSpotKind.Entry
|
|
218
|
+
}),
|
|
219
|
+
terminal: terminalPublication(record, terminal)
|
|
220
|
+
}
|
|
221
|
+
: {
|
|
222
|
+
kind: 'rejected',
|
|
223
|
+
terminal: terminalPublication(record, terminal)
|
|
224
|
+
}
|
|
225
|
+
}, signal);
|
|
226
|
+
}
|
|
183
227
|
}
|
|
184
228
|
catch (error) {
|
|
185
229
|
const cleanup = createDeadline(1_000);
|
|
@@ -199,14 +243,22 @@ class ZLinkActorPlacementCoordinator {
|
|
|
199
243
|
}
|
|
200
244
|
throw error;
|
|
201
245
|
}
|
|
246
|
+
if (local.result === 'failed') {
|
|
247
|
+
if (completion.kind !== 'failed' && completion.kind !== 'alreadyCompleted') {
|
|
248
|
+
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorCreateFailed, `Actor '${record.actorId}' failed creation did not record its terminal.`);
|
|
249
|
+
}
|
|
250
|
+
throw local.error;
|
|
251
|
+
}
|
|
202
252
|
if (local.result === 'created'
|
|
203
253
|
? completion.kind !== 'created' && completion.kind !== 'alreadyCompleted'
|
|
204
254
|
: completion.kind !== 'rejected' && completion.kind !== 'alreadyCompleted') {
|
|
205
255
|
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorCreateFailed, `Actor '${record.actorId}' creation completion lost its reservation.`);
|
|
206
256
|
}
|
|
207
|
-
if (local.result === 'created')
|
|
257
|
+
if (local.result === 'created') {
|
|
208
258
|
local.onPublished?.();
|
|
209
|
-
|
|
259
|
+
return actorResult('created', local.actor, local.reply);
|
|
260
|
+
}
|
|
261
|
+
return actorResult('rejected', undefined, local.reply);
|
|
210
262
|
}
|
|
211
263
|
}
|
|
212
264
|
exports.ZLinkActorPlacementCoordinator = ZLinkActorPlacementCoordinator;
|
|
@@ -253,13 +305,14 @@ function withActorAuthorityFence(actor, authority) {
|
|
|
253
305
|
});
|
|
254
306
|
return actor;
|
|
255
307
|
}
|
|
256
|
-
function actorCreateRecord(actorId, stableType, snapshot, target, deadlineUnixMs) {
|
|
308
|
+
function actorCreateRecord(actorId, stableType, snapshot, target, deadlineUnixMs, operation) {
|
|
257
309
|
const pending = snapshot.pendingCreation;
|
|
258
310
|
if (pending === undefined)
|
|
259
311
|
throw new Error('Actor reservation omitted Pending creation.');
|
|
260
312
|
return {
|
|
261
|
-
sourceNodeRid:
|
|
262
|
-
sourceNodeGeneration:
|
|
313
|
+
sourceNodeRid: String(operation.sourceNodeRid),
|
|
314
|
+
sourceNodeGeneration: operation.sourceNodeGeneration,
|
|
315
|
+
operation: operation.operationId,
|
|
263
316
|
actorId,
|
|
264
317
|
stableType,
|
|
265
318
|
reservation: reservationFence(snapshot, target, pending.reservationId),
|
|
@@ -332,19 +385,79 @@ function actorResult(createResult, actor, reply) {
|
|
|
332
385
|
})
|
|
333
386
|
};
|
|
334
387
|
}
|
|
335
|
-
function
|
|
336
|
-
return
|
|
337
|
-
|
|
338
|
-
result
|
|
339
|
-
|
|
340
|
-
|
|
388
|
+
function isCompletedActorCreate(result) {
|
|
389
|
+
return result.terminalResult === runtime_values_1.RequestResult.Ok
|
|
390
|
+
&& result.failureCode === 0
|
|
391
|
+
&& result.tail?.kind === 'actorCreate';
|
|
392
|
+
}
|
|
393
|
+
function encodeActorTerminal(terminal) {
|
|
394
|
+
if (terminal.result === 'failed') {
|
|
395
|
+
return Buffer.from((0, service_wire_codec_generated_1.encodeCreationOperationTerminalV1)({
|
|
396
|
+
terminalResult: 'internalError',
|
|
397
|
+
failureCode: 'actorCreateFailed',
|
|
398
|
+
hasCreation: 'false',
|
|
399
|
+
hasApplicationPayload: 'false'
|
|
400
|
+
}, CREATION_TERMINAL_CODEC_CONTEXT));
|
|
401
|
+
}
|
|
402
|
+
return Buffer.from((0, service_wire_codec_generated_1.encodeCreationOperationTerminalV1)({
|
|
403
|
+
terminalResult: 'ok',
|
|
404
|
+
failureCode: 'none',
|
|
405
|
+
hasCreation: 'true',
|
|
406
|
+
creation: terminal.result === 'rejected'
|
|
407
|
+
? { createResult: 'rejected' }
|
|
341
408
|
: {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
409
|
+
createResult: 'created',
|
|
410
|
+
actor: {
|
|
411
|
+
actorId: terminal.actor.actorId,
|
|
412
|
+
objectGeneration: terminal.actor.objectGeneration
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
hasApplicationPayload: terminal.reply === undefined ? 'false' : 'true',
|
|
416
|
+
...(terminal.reply === undefined
|
|
417
|
+
? {}
|
|
418
|
+
: {
|
|
419
|
+
applicationPayload: {
|
|
420
|
+
packetName: 'ZLinkFrameworkActorCreateReply',
|
|
421
|
+
contentType: 'application/octet-stream',
|
|
422
|
+
payload: Buffer.from(terminal.reply)
|
|
423
|
+
}
|
|
424
|
+
})
|
|
425
|
+
}, CREATION_TERMINAL_CODEC_CONTEXT));
|
|
426
|
+
}
|
|
427
|
+
function remoteActorCreateResult(terminalEnvelope, target) {
|
|
428
|
+
const terminal = (0, service_wire_codec_generated_1.decodeCreationOperationTerminalV1)(terminalEnvelope, CREATION_TERMINAL_CODEC_CONTEXT);
|
|
429
|
+
const terminalResult = (0, service_wire_codec_generated_1.enumWireRequestTerminalResult)(terminal.terminalResult);
|
|
430
|
+
const failureCode = (0, service_wire_codec_generated_1.enumWireFrameworkErrorCode)(terminal.failureCode);
|
|
431
|
+
if (terminal.hasCreation === 'false' || terminal.creation === undefined) {
|
|
432
|
+
return { terminalResult, failureCode };
|
|
433
|
+
}
|
|
434
|
+
const creation = terminal.creation;
|
|
435
|
+
return {
|
|
436
|
+
terminalResult,
|
|
437
|
+
failureCode,
|
|
438
|
+
tail: {
|
|
439
|
+
kind: 'actorCreate',
|
|
440
|
+
createResult: creation.createResult,
|
|
441
|
+
...(creation.createResult === 'rejected'
|
|
442
|
+
? {}
|
|
443
|
+
: {
|
|
444
|
+
actor: {
|
|
445
|
+
actorId: creation.actor.actorId,
|
|
446
|
+
generation: creation.actor.objectGeneration,
|
|
447
|
+
nodeRid: String(target.nodeRid)
|
|
448
|
+
}
|
|
449
|
+
})
|
|
450
|
+
},
|
|
451
|
+
...(terminal.applicationPayload === undefined
|
|
452
|
+
? {}
|
|
453
|
+
: {
|
|
454
|
+
payload: {
|
|
455
|
+
packetName: terminal.applicationPayload.packetName,
|
|
456
|
+
contentType: terminal.applicationPayload.contentType,
|
|
457
|
+
payload: Buffer.from(terminal.applicationPayload.payload)
|
|
458
|
+
}
|
|
459
|
+
})
|
|
460
|
+
};
|
|
348
461
|
}
|
|
349
462
|
function terminalPublication(record, terminalEnvelope) {
|
|
350
463
|
return {
|
|
@@ -354,7 +467,6 @@ function terminalPublication(record, terminalEnvelope) {
|
|
|
354
467
|
operationId: record.operation
|
|
355
468
|
},
|
|
356
469
|
terminalEnvelope,
|
|
357
|
-
terminalEnvelopeSha256: (0, node_crypto_1.createHash)('sha256').update(terminalEnvelope).digest(),
|
|
358
470
|
operationDeadline: new Date(Number(record.deadlineUnixMs))
|
|
359
471
|
};
|
|
360
472
|
}
|
|
@@ -63,7 +63,6 @@ export declare class ZLinkFrameworkRuntimeHost implements ZLinkFrameworkRuntimeL
|
|
|
63
63
|
private spotManager?;
|
|
64
64
|
private ownerLeaseRecoveryRuntime?;
|
|
65
65
|
private ownerLeaseRecoveryHandler?;
|
|
66
|
-
private ownerLeaseFailureHandler?;
|
|
67
66
|
private registerUserSpotHandlers?;
|
|
68
67
|
private readonly destroyedActorRefs;
|
|
69
68
|
private readonly runtimeEventPublisher;
|
|
@@ -128,10 +127,12 @@ export declare class ZLinkFrameworkRuntimeHost implements ZLinkFrameworkRuntimeL
|
|
|
128
127
|
private runtimeMetricMeshSnapshots;
|
|
129
128
|
private stopCore;
|
|
130
129
|
private installOwnerLeaseRecoveryPublication;
|
|
130
|
+
private resumeOwnerLeaseOwnedWork;
|
|
131
131
|
private createStatefulAuthorityRoutes;
|
|
132
132
|
private requiresStatefulAuthorityRuntime;
|
|
133
133
|
private installLocationBackedAuthorities;
|
|
134
134
|
private removeOwnerLeaseRecoveryPublication;
|
|
135
|
+
private ownerAdmissionOpen;
|
|
135
136
|
onApplicationBootstrap(): Promise<void>;
|
|
136
137
|
onApplicationShutdown(): Promise<void>;
|
|
137
138
|
private performMeshDrain;
|
|
@@ -97,7 +97,6 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
97
97
|
spotManager;
|
|
98
98
|
ownerLeaseRecoveryRuntime;
|
|
99
99
|
ownerLeaseRecoveryHandler;
|
|
100
|
-
ownerLeaseFailureHandler;
|
|
101
100
|
registerUserSpotHandlers;
|
|
102
101
|
destroyedActorRefs = new Map();
|
|
103
102
|
runtimeEventPublisher;
|
|
@@ -105,7 +104,7 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
105
104
|
applicationJobQueue;
|
|
106
105
|
capacityStatus;
|
|
107
106
|
metricRegistrations = [];
|
|
108
|
-
admission = new admission_1.ZLinkRuntimeAdmissionGate();
|
|
107
|
+
admission = new admission_1.ZLinkRuntimeAdmissionGate(() => this.ownerAdmissionOpen());
|
|
109
108
|
cachedLocationSpotRouteResolver;
|
|
110
109
|
actorClientLocationResolver;
|
|
111
110
|
// Shared, runtime-mutable message-flow mode cell — installed once so
|
|
@@ -638,6 +637,14 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
638
637
|
: contracts_1.ZLinkFrameworkRelocationReason.RuntimeNotReady
|
|
639
638
|
});
|
|
640
639
|
}
|
|
640
|
+
if (!this.admission.acceptsNewWork) {
|
|
641
|
+
return Promise.resolve({
|
|
642
|
+
mode: options.mode,
|
|
643
|
+
effectiveTargetApplicationVersion,
|
|
644
|
+
outcome: contracts_1.ZLinkFrameworkRelocationOutcome.Blocked,
|
|
645
|
+
reason: contracts_1.ZLinkFrameworkRelocationReason.StoreUnavailable
|
|
646
|
+
});
|
|
647
|
+
}
|
|
641
648
|
if (hasUnsupportedManualTopology(this.options.registration)) {
|
|
642
649
|
return Promise.resolve({
|
|
643
650
|
mode: options.mode,
|
|
@@ -1116,96 +1123,40 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
1116
1123
|
}
|
|
1117
1124
|
}
|
|
1118
1125
|
installOwnerLeaseRecoveryPublication(runtime, spotNodeRuntime, channelRuntime) {
|
|
1119
|
-
let publishing = false;
|
|
1120
|
-
let lastPublishedOwnerToken = runtime.currentOwnerToken;
|
|
1121
|
-
let recoveryRequired = false;
|
|
1122
|
-
let recoveringServices;
|
|
1123
|
-
let stoppingServices;
|
|
1124
|
-
let transportFence = Promise.resolve();
|
|
1125
1126
|
const handler = () => {
|
|
1126
|
-
|
|
1127
|
-
if (publishing
|
|
1128
|
-
|| ownerToken === undefined
|
|
1129
|
-
|| (ownerToken === lastPublishedOwnerToken && !recoveryRequired))
|
|
1130
|
-
return;
|
|
1131
|
-
if (runtime.ownerLeaseUsable === false)
|
|
1127
|
+
if (!this.ownerAdmissionOpen())
|
|
1132
1128
|
return;
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
if (stoppingServices !== undefined) {
|
|
1137
|
-
await stoppingServices;
|
|
1138
|
-
stoppingServices = undefined;
|
|
1139
|
-
}
|
|
1140
|
-
if (this.statefulAuthorityRoutes !== undefined)
|
|
1141
|
-
return;
|
|
1142
|
-
const store = this.locationOwner.currentStores?.locationStore;
|
|
1143
|
-
if (store === undefined || this.executionState === undefined)
|
|
1144
|
-
return;
|
|
1145
|
-
if (recoveringServices !== undefined)
|
|
1146
|
-
return await recoveringServices;
|
|
1147
|
-
recoveringServices = (async () => {
|
|
1148
|
-
if (this.requiresStatefulAuthorityRuntime()) {
|
|
1149
|
-
const routes = this.createStatefulAuthorityRoutes(store, spotNodeRuntime);
|
|
1150
|
-
await routes.start(this.executionState?.abortController.signal);
|
|
1151
|
-
this.statefulAuthorityRoutes = routes;
|
|
1152
|
-
}
|
|
1153
|
-
})();
|
|
1154
|
-
try {
|
|
1155
|
-
await recoveringServices;
|
|
1156
|
-
}
|
|
1157
|
-
finally {
|
|
1158
|
-
recoveringServices = undefined;
|
|
1159
|
-
}
|
|
1160
|
-
};
|
|
1161
|
-
void transportFence
|
|
1162
|
-
.then(() => spotNodeRuntime.publishMeshNodeState(contracts_1.ZLinkFrameworkRuntimeState.Preparing, signal))
|
|
1163
|
-
.then(() => recoverServices())
|
|
1164
|
-
.then(() => {
|
|
1165
|
-
const currentOwnerToken = runtime.currentOwnerToken;
|
|
1166
|
-
if (runtime.ownerLeaseUsable === false
|
|
1167
|
-
|| currentOwnerToken === undefined
|
|
1168
|
-
|| currentOwnerToken.ownerId !== ownerToken.ownerId
|
|
1169
|
-
|| currentOwnerToken.leaseGeneration !== ownerToken.leaseGeneration) {
|
|
1170
|
-
throw new Error('Owner lease changed during recovery.');
|
|
1171
|
-
}
|
|
1172
|
-
return spotNodeRuntime.publishMeshNodeState(this.runtimeState, signal);
|
|
1173
|
-
})
|
|
1174
|
-
.then(() => spotNodeRuntime.startLocationAutoConnect(signal))
|
|
1175
|
-
.then(() => channelRuntime.reclaimLocationOwnerRows(signal))
|
|
1176
|
-
.then(() => this.locationOwner.currentLifecycle?.reclaimOwnerRows() ?? Promise.resolve())
|
|
1177
|
-
.then(() => {
|
|
1178
|
-
lastPublishedOwnerToken = ownerToken;
|
|
1179
|
-
recoveryRequired = false;
|
|
1180
|
-
publishing = false;
|
|
1181
|
-
}, error => {
|
|
1129
|
+
void this.resumeOwnerLeaseOwnedWork(runtime, spotNodeRuntime, channelRuntime)
|
|
1130
|
+
.catch(error => {
|
|
1131
|
+
runtime.closeOwnerLeaseAdmission(error);
|
|
1182
1132
|
this.runtimeOrPreStartErrorSink.reportRuntimeTaskException('owner lease recovery', error);
|
|
1183
|
-
publishing = false;
|
|
1184
1133
|
});
|
|
1185
1134
|
};
|
|
1186
1135
|
this.ownerLeaseRecoveryRuntime = runtime;
|
|
1187
1136
|
this.ownerLeaseRecoveryHandler = handler;
|
|
1188
|
-
const failureHandler = () => {
|
|
1189
|
-
recoveryRequired = true;
|
|
1190
|
-
if (runtime.ownerLeaseUsable)
|
|
1191
|
-
return;
|
|
1192
|
-
// A Store failure invalidates new ownership and discovery work, but it
|
|
1193
|
-
// must not tear down transports that were already established. The
|
|
1194
|
-
// auto-connect reconciler applies storeFailureGraceMs to the last
|
|
1195
|
-
// complete descriptor set; fencing here would bypass that contract and
|
|
1196
|
-
// make existing requests fail during the grace window.
|
|
1197
|
-
const routes = this.statefulAuthorityRoutes;
|
|
1198
|
-
if (routes === undefined)
|
|
1199
|
-
return;
|
|
1200
|
-
this.statefulAuthorityRoutes = undefined;
|
|
1201
|
-
stoppingServices = routes.stop();
|
|
1202
|
-
void stoppingServices.catch(error => this.runtimeOrPreStartErrorSink.reportRuntimeTaskException('stateful authority route fencing', error));
|
|
1203
|
-
};
|
|
1204
|
-
this.ownerLeaseFailureHandler = failureHandler;
|
|
1205
1137
|
runtime.addOwnerLeaseRenewedHandler(handler);
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1138
|
+
}
|
|
1139
|
+
async resumeOwnerLeaseOwnedWork(runtime, spotNodeRuntime, channelRuntime) {
|
|
1140
|
+
if (!this.ownerAdmissionOpen())
|
|
1141
|
+
return;
|
|
1142
|
+
const signal = this.executionState?.abortController.signal;
|
|
1143
|
+
await spotNodeRuntime.publishMeshNodeState(contracts_1.ZLinkFrameworkRuntimeState.Preparing, signal);
|
|
1144
|
+
const store = this.locationOwner.currentStores?.locationStore;
|
|
1145
|
+
if (this.statefulAuthorityRoutes === undefined
|
|
1146
|
+
&& store !== undefined
|
|
1147
|
+
&& this.executionState !== undefined
|
|
1148
|
+
&& this.requiresStatefulAuthorityRuntime()) {
|
|
1149
|
+
const routes = this.createStatefulAuthorityRoutes(store, spotNodeRuntime);
|
|
1150
|
+
await routes.start(signal);
|
|
1151
|
+
this.statefulAuthorityRoutes = routes;
|
|
1152
|
+
}
|
|
1153
|
+
if (runtime.ownerLeaseUsable === false)
|
|
1154
|
+
return;
|
|
1155
|
+
await spotNodeRuntime.publishMeshNodeState(this.runtimeState, signal);
|
|
1156
|
+
await spotNodeRuntime.startLocationAutoConnect(signal);
|
|
1157
|
+
await channelRuntime.startLocationAutoConnect(signal);
|
|
1158
|
+
await channelRuntime.reclaimLocationOwnerRows(signal);
|
|
1159
|
+
await this.locationOwner.currentLifecycle?.reclaimOwnerRows();
|
|
1209
1160
|
}
|
|
1210
1161
|
createStatefulAuthorityRoutes(locationStore, spotNodeRuntime) {
|
|
1211
1162
|
return new stateful_authority_route_runtime_1.ZLinkStatefulAuthorityRouteRuntime({
|
|
@@ -1268,14 +1219,14 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
1268
1219
|
if (this.ownerLeaseRecoveryRuntime !== undefined && this.ownerLeaseRecoveryHandler !== undefined) {
|
|
1269
1220
|
this.ownerLeaseRecoveryRuntime.removeOwnerLeaseRenewedHandler(this.ownerLeaseRecoveryHandler);
|
|
1270
1221
|
}
|
|
1271
|
-
if (this.ownerLeaseRecoveryRuntime !== undefined && this.ownerLeaseFailureHandler !== undefined) {
|
|
1272
|
-
if (typeof this.ownerLeaseRecoveryRuntime.removeOwnerLeaseRenewalFailedHandler === 'function') {
|
|
1273
|
-
this.ownerLeaseRecoveryRuntime.removeOwnerLeaseRenewalFailedHandler(this.ownerLeaseFailureHandler);
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
1222
|
this.ownerLeaseRecoveryRuntime = undefined;
|
|
1277
1223
|
this.ownerLeaseRecoveryHandler = undefined;
|
|
1278
|
-
|
|
1224
|
+
}
|
|
1225
|
+
ownerAdmissionOpen() {
|
|
1226
|
+
const runtime = this.locationOwner.currentRuntime;
|
|
1227
|
+
if (runtime !== undefined)
|
|
1228
|
+
return runtime.ownerLeaseUsable;
|
|
1229
|
+
return this.locationOwner.currentStores?.locationStore === undefined;
|
|
1279
1230
|
}
|
|
1280
1231
|
async onApplicationBootstrap() {
|
|
1281
1232
|
await this.start();
|
|
@@ -1478,15 +1429,7 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
1478
1429
|
detachedTaskRunner: this.detachedTaskRunner(),
|
|
1479
1430
|
metrics: this.metrics,
|
|
1480
1431
|
admission: this.admission,
|
|
1481
|
-
statefulExecutionAllowed: () =>
|
|
1482
|
-
const locationRuntime = this.locationOwner.currentRuntime;
|
|
1483
|
-
if (locationRuntime !== undefined)
|
|
1484
|
-
return locationRuntime.ownerLeaseUsable;
|
|
1485
|
-
// A host configured with a Location Store must not execute stateful
|
|
1486
|
-
// timers while its lease runtime is unavailable. Hosts without a
|
|
1487
|
-
// Location Store have no owner lease to fence.
|
|
1488
|
-
return this.locationOwner.currentStores?.locationStore === undefined;
|
|
1489
|
-
}
|
|
1432
|
+
statefulExecutionAllowed: () => this.ownerAdmissionOpen()
|
|
1490
1433
|
}).create(this.actorTransferRuntime),
|
|
1491
1434
|
activationConcurrencyLimitProvider: (meshName) => this.options.registration.spotNodes.get(meshName)?.activationConcurrencyLimit ?? 128,
|
|
1492
1435
|
onInstanceActivationConcurrencyChanged: (meshName) => {
|
|
@@ -1549,17 +1492,21 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
1549
1492
|
if (selected === undefined)
|
|
1550
1493
|
return undefined;
|
|
1551
1494
|
const localStatus = meshNode?.status();
|
|
1495
|
+
if (localStatus === undefined) {
|
|
1496
|
+
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ObjectClientNotConfigured, `RouteMesh '${meshName}' has no local client node.`);
|
|
1497
|
+
}
|
|
1552
1498
|
return {
|
|
1553
1499
|
meshName,
|
|
1554
1500
|
nodeRid: selected.rid,
|
|
1555
1501
|
nodeGeneration: selected.lifecycleGeneration,
|
|
1502
|
+
sourceNodeRid: String(localStatus.routingId),
|
|
1503
|
+
sourceNodeGeneration: localStatus.lifecycleGeneration,
|
|
1556
1504
|
entrySpotId: selected.entrySpotId,
|
|
1557
1505
|
owner: {
|
|
1558
1506
|
ownerId: selected.ownerId,
|
|
1559
1507
|
leaseGeneration: selected.leaseGeneration
|
|
1560
1508
|
},
|
|
1561
|
-
isLocal: localStatus
|
|
1562
|
-
&& String(localStatus.routingId) === String(selected.rid)
|
|
1509
|
+
isLocal: String(localStatus.routingId) === String(selected.rid)
|
|
1563
1510
|
&& localStatus.lifecycleGeneration === selected.lifecycleGeneration
|
|
1564
1511
|
};
|
|
1565
1512
|
}
|
|
@@ -1723,12 +1670,20 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
1723
1670
|
}
|
|
1724
1671
|
return await actorPlacement.handleRemoteCreate(record, async (requestPayload, authority, createSignal) => {
|
|
1725
1672
|
const request = (0, creation_payload_codec_1.decodeFrameworkCreationPayload)(requestPayload, this.options.registration.messageSerializers);
|
|
1673
|
+
const entrySpotId = this.spotNodeRuntime
|
|
1674
|
+
?.meshNodeDescriptor(meshName)?.entrySpotId;
|
|
1675
|
+
if (entrySpotId === undefined) {
|
|
1676
|
+
throw new ConfigurationException_1.ZLinkConfigurationException(`Remote Actor creation requires the '${meshName}' MeshNode Entry Spot descriptor.`);
|
|
1677
|
+
}
|
|
1726
1678
|
const entrySpot = node.entrySpot().status();
|
|
1727
1679
|
const nativeActorRef = node.restoreActorAuthority?.(record.actorId, record.stableType, authority.objectGeneration, authority.authorityOwnerGeneration, String(entrySpot.routingId), entrySpot.lifecycleGeneration, 1n);
|
|
1728
1680
|
if (nativeActorRef === undefined) {
|
|
1729
1681
|
throw new ConfigurationException_1.ZLinkConfigurationException('Remote Actor creation requires native authority restoration support.');
|
|
1730
1682
|
}
|
|
1731
1683
|
const local = await actors.createReservedActorResult(record.actorId, record.stableType, request, createSignal, nativeActorRef);
|
|
1684
|
+
if (local.status === 'failed') {
|
|
1685
|
+
return { result: 'failed', error: local.error };
|
|
1686
|
+
}
|
|
1732
1687
|
if (local.status === 'rejected') {
|
|
1733
1688
|
let reply;
|
|
1734
1689
|
if (local.reply !== undefined) {
|
|
@@ -1765,7 +1720,7 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
1765
1720
|
...local.actorRef,
|
|
1766
1721
|
meshName
|
|
1767
1722
|
},
|
|
1768
|
-
entrySpotId
|
|
1723
|
+
entrySpotId,
|
|
1769
1724
|
entrySpotGeneration: entrySpot.lifecycleGeneration,
|
|
1770
1725
|
...(reply === undefined ? {} : { reply }),
|
|
1771
1726
|
onPublished: () => actors.adoptCreatedAuthority(record.actorId, authority.authorityOwnerGeneration, authority.ownerLeaseGeneration)
|
|
@@ -2064,26 +2019,28 @@ class ZLinkFrameworkRuntimeHost {
|
|
|
2064
2019
|
}
|
|
2065
2020
|
case service_runtime_contracts_1.ReceiveKind.NodeSend:
|
|
2066
2021
|
case service_runtime_contracts_1.ReceiveKind.NodeRequest: {
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
if (
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2022
|
+
return this.admission.run(meshName, 'RouteMesh node dispatch', async () => {
|
|
2023
|
+
if (await this.serviceRelocation.tryHandleControl(meshName, record, signal)) {
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
if (record.kind === service_runtime_contracts_1.ReceiveKind.NodeRequest && record.parts.length === 1) {
|
|
2027
|
+
const terminal = (0, actors_2.decodeRemoteActorSourceLeaveTerminal)(record.parts[0].data());
|
|
2028
|
+
if (terminal !== undefined) {
|
|
2029
|
+
if (await this.spotManager?.completeFormalSourceLeaveTerminal(terminal.actorId, terminal.transferId, terminal.succeeded) !== true) {
|
|
2030
|
+
throw new ConfigurationException_1.ZLinkConfigurationException(`Actor '${terminal.actorId}' has no matching formal transfer terminal gate.`);
|
|
2031
|
+
}
|
|
2032
|
+
if (record.reply(Buffer.alloc(0)) !== runtime_values_1.SubmitResult.Ok) {
|
|
2033
|
+
throw new ConfigurationException_1.ZLinkConfigurationException(`Actor '${terminal.actorId}' formal transfer terminal acknowledgement failed.`);
|
|
2034
|
+
}
|
|
2035
|
+
return;
|
|
2078
2036
|
}
|
|
2079
|
-
return Promise.resolve();
|
|
2080
2037
|
}
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2038
|
+
const channelRuntime = this.channelRuntime;
|
|
2039
|
+
if (channelRuntime === undefined) {
|
|
2040
|
+
throw new ConfigurationException_1.ZLinkConfigurationException('MeshNode node-direct dispatch requires the channel runtime.');
|
|
2041
|
+
}
|
|
2042
|
+
return await channelRuntime.dispatchMeshRoute(meshName, record, signal);
|
|
2043
|
+
});
|
|
2087
2044
|
}
|
|
2088
2045
|
case service_runtime_contracts_1.ReceiveKind.SpotSend:
|
|
2089
2046
|
case service_runtime_contracts_1.ReceiveKind.SpotRequest:
|
|
@@ -143,9 +143,11 @@ class ZLinkLocationRuntimeOwner {
|
|
|
143
143
|
return runtime;
|
|
144
144
|
}
|
|
145
145
|
spotNodeRuntime.configureLocationAutoConnect(runtime, stores, this.options.registration.locations.options, this.currentEvents);
|
|
146
|
-
await spotNodeRuntime.startLocationAutoConnect();
|
|
147
146
|
channelRuntime.configureLocationAutoConnect(runtime, stores, this.options.registration.locations.options, this.currentEvents);
|
|
148
|
-
|
|
147
|
+
if (runtime.ownerLeaseUsable) {
|
|
148
|
+
await spotNodeRuntime.startLocationAutoConnect();
|
|
149
|
+
await channelRuntime.startLocationAutoConnect();
|
|
150
|
+
}
|
|
149
151
|
return runtime;
|
|
150
152
|
}
|
|
151
153
|
clearForStop() {
|
|
@@ -126,9 +126,18 @@ class ZLinkRemoteActorJoinReceiver {
|
|
|
126
126
|
}
|
|
127
127
|
exports.ZLinkRemoteActorJoinReceiver = ZLinkRemoteActorJoinReceiver;
|
|
128
128
|
function hasAuthorityFence(join) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
const fields = [
|
|
130
|
+
join.actorNodeGeneration,
|
|
131
|
+
join.expectedAuthorityOwnerGeneration,
|
|
132
|
+
join.expectedOwnerLeaseGeneration
|
|
133
|
+
];
|
|
134
|
+
if (fields.every(value => value === undefined)) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
if (fields.some(value => value === undefined)) {
|
|
138
|
+
throw (0, framework_errors_internal_1.createInternalFrameworkException)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.RequestProtocolError, `Actor '${join.actorId}' Join carries an incomplete Authority fence.`);
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
132
141
|
}
|
|
133
142
|
function decodeJoinAuthorityFence(join) {
|
|
134
143
|
let objectGeneration;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.randomOperationId = randomOperationId;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
function randomOperationId(entropy = (0, node_crypto_1.randomBytes)(16)) {
|
|
6
|
+
if (entropy.byteLength !== 16)
|
|
7
|
+
throw new RangeError('Operation ID entropy must be 16 bytes.');
|
|
8
|
+
const bytes = Buffer.from(entropy);
|
|
9
|
+
const high = bytes.readBigUInt64BE(0);
|
|
10
|
+
const low = bytes.readBigUInt64BE(8);
|
|
11
|
+
return high === 0n && low === 0n ? { high, low: 1n } : { high, low };
|
|
12
|
+
}
|