@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.
Files changed (70) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Configuration/Builders.d.ts +0 -2
  3. package/dist/contracts/Configuration/Configs.d.ts +1 -0
  4. package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -2
  5. package/dist/contracts/Locations/Authority.d.ts +1 -7
  6. package/dist/runtime/actors/actor-authority-publication.js +25 -22
  7. package/dist/runtime/actors/actor-creation.js +30 -9
  8. package/dist/runtime/actors/actor-runtime-state.d.ts +3 -0
  9. package/dist/runtime/actors/index.d.ts +3 -0
  10. package/dist/runtime/actors/index.js +14 -3
  11. package/dist/runtime/admission.d.ts +2 -0
  12. package/dist/runtime/admission.js +11 -5
  13. package/dist/runtime/backend/contracts/index.d.ts +4 -1
  14. package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -0
  15. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
  16. package/dist/runtime/backend/node/node-raw-binding-port.d.ts +2 -1
  17. package/dist/runtime/backend/node/node-raw-binding-port.js +12 -5
  18. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +1 -1
  19. package/dist/runtime/backend/node/node-socket-backend-adapter.js +6 -0
  20. package/dist/runtime/channels/channel-socket-options.js +7 -0
  21. package/dist/runtime/channels/channel-socket-registry.js +3 -0
  22. package/dist/runtime/foundation/service-stateful-runtime.d.ts +1 -1
  23. package/dist/runtime/foundation/service-stateful-runtime.js +1 -5
  24. package/dist/runtime/host/actor-placement-coordinator.d.ts +14 -5
  25. package/dist/runtime/host/actor-placement-coordinator.js +159 -47
  26. package/dist/runtime/host/index.d.ts +2 -1
  27. package/dist/runtime/host/index.js +77 -120
  28. package/dist/runtime/host/location-runtime-owner.js +4 -2
  29. package/dist/runtime/host/remote-actor-join-receiver.js +12 -3
  30. package/dist/runtime/locations/creation-operation-id.d.ts +4 -0
  31. package/dist/runtime/locations/creation-operation-id.js +12 -0
  32. package/dist/runtime/locations/in-memory-authority-store.js +8 -22
  33. package/dist/runtime/locations/location-store-repository.js +36 -59
  34. package/dist/runtime/locations/opaque-record-key.d.ts +5 -0
  35. package/dist/runtime/locations/opaque-record-key.js +16 -0
  36. package/dist/runtime/locations/runtime.d.ts +7 -0
  37. package/dist/runtime/locations/runtime.js +130 -42
  38. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +1672 -0
  39. package/dist/runtime/protocol/service_wire_codec.generated.js +9169 -0
  40. package/dist/runtime/spots/spot-node-runtime-manager.js +2 -2
  41. package/package.json +2 -2
  42. package/src/contracts/Configuration/Builders.ts +0 -2
  43. package/src/contracts/Configuration/Configs.ts +1 -0
  44. package/src/contracts/Configuration/RegistrationTypes.ts +1 -2
  45. package/src/contracts/Locations/Authority.ts +1 -8
  46. package/src/runtime/actors/actor-authority-publication.ts +25 -22
  47. package/src/runtime/actors/actor-creation.ts +45 -11
  48. package/src/runtime/actors/actor-runtime-state.ts +4 -0
  49. package/src/runtime/actors/index.ts +32 -4
  50. package/src/runtime/admission.ts +8 -5
  51. package/src/runtime/backend/contracts/index.ts +4 -1
  52. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +2 -1
  53. package/src/runtime/backend/node/node-raw-binding-port.ts +16 -5
  54. package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -1
  55. package/src/runtime/backend/node/node-socket-backend-adapter.ts +7 -0
  56. package/src/runtime/channels/channel-socket-options.ts +9 -0
  57. package/src/runtime/channels/channel-socket-registry.ts +4 -0
  58. package/src/runtime/foundation/service-stateful-runtime.ts +2 -6
  59. package/src/runtime/host/actor-placement-coordinator.ts +216 -77
  60. package/src/runtime/host/index.ts +95 -135
  61. package/src/runtime/host/location-runtime-owner.ts +4 -2
  62. package/src/runtime/host/remote-actor-join-receiver.ts +15 -3
  63. package/src/runtime/locations/creation-operation-id.ts +11 -0
  64. package/src/runtime/locations/in-memory-authority-store.ts +7 -29
  65. package/src/runtime/locations/location-store-repository.ts +71 -69
  66. package/src/runtime/locations/opaque-record-key.ts +26 -0
  67. package/src/runtime/locations/runtime.ts +154 -60
  68. package/src/runtime/protocol/service_wire_codec.generated.ts +8302 -0
  69. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
  70. 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 remote = await this.options.remoteCreate(target.meshName, String(target.nodeRid), actorCreateRecord(actorId, stableType, reserved.creating, target, deadlineUnixMs), Math.max(1, Math.ceil(deadlineMs - performance.now())));
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
- const terminal = encodeActorTerminal(record.correlation, local.result, local.actor);
155
- completion = await this.options.store.completeCreation({
156
- key,
157
- reservationId: record.reservation.reservationId,
158
- expectedStoreVersion: current.storeVersion.value,
159
- target: creationTarget(current),
160
- completion: local.result === 'created'
161
- ? {
162
- kind: 'created',
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
- }, signal);
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
- return actorResult(local.result, local.actor, local.reply);
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: 1n,
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 encodeActorTerminal(correlation, result, actor) {
336
- return Buffer.from(JSON.stringify({
337
- correlation: correlation.toString(),
338
- result,
339
- actor: actor === undefined
340
- ? undefined
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
- actorId: actor.actorId,
343
- generation: actor.objectGeneration.toString(),
344
- meshName: actor.meshName,
345
- nodeRid: String(actor.nodeRid)
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
- const ownerToken = runtime.currentOwnerToken;
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
- publishing = true;
1134
- const signal = this.executionState?.abortController.signal;
1135
- const recoverServices = async () => {
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
- if (typeof runtime.addOwnerLeaseRenewalFailedHandler === 'function') {
1207
- runtime.addOwnerLeaseRenewalFailedHandler(failureHandler);
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
- this.ownerLeaseFailureHandler = undefined;
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 !== undefined
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: String(entrySpot.routingId),
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
- if (await this.serviceRelocation.tryHandleControl(meshName, record, signal)) {
2068
- return;
2069
- }
2070
- if (record.kind === service_runtime_contracts_1.ReceiveKind.NodeRequest && record.parts.length === 1) {
2071
- const terminal = (0, actors_2.decodeRemoteActorSourceLeaveTerminal)(record.parts[0].data());
2072
- if (terminal !== undefined) {
2073
- if (await this.spotManager?.completeFormalSourceLeaveTerminal(terminal.actorId, terminal.transferId, terminal.succeeded) !== true) {
2074
- throw new ConfigurationException_1.ZLinkConfigurationException(`Actor '${terminal.actorId}' has no matching formal transfer terminal gate.`);
2075
- }
2076
- if (record.reply(Buffer.alloc(0)) !== runtime_values_1.SubmitResult.Ok) {
2077
- throw new ConfigurationException_1.ZLinkConfigurationException(`Actor '${terminal.actorId}' formal transfer terminal acknowledgement failed.`);
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
- const channelRuntime = this.channelRuntime;
2083
- if (channelRuntime === undefined) {
2084
- throw new ConfigurationException_1.ZLinkConfigurationException('MeshNode node-direct dispatch requires the channel runtime.');
2085
- }
2086
- return this.admission.run(meshName, 'RouteMesh node dispatch', () => channelRuntime.dispatchMeshRoute(meshName, record, signal));
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
- await channelRuntime.startLocationAutoConnect();
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
- return join.actorNodeGeneration !== undefined
130
- && join.expectedAuthorityOwnerGeneration !== undefined
131
- && join.expectedOwnerLeaseGeneration !== undefined;
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,4 @@
1
+ export declare function randomOperationId(entropy?: Uint8Array): {
2
+ readonly high: bigint;
3
+ readonly low: bigint;
4
+ };
@@ -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
+ }