@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
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ZLinkLocationRuntime = exports.ZLinkOwnerCleanupError = void 0;
3
+ exports.ZLinkLocationRuntime = exports.ZLinkOwnerLeaseClaimError = exports.ZLinkOwnerCleanupError = void 0;
4
4
  const Options_1 = require("../../contracts/Locations/Options");
5
5
  const EndpointNotation_1 = require("../../contracts/Configuration/EndpointNotation");
6
6
  const node_crypto_1 = require("node:crypto");
@@ -17,6 +17,15 @@ class ZLinkOwnerCleanupError extends Error {
17
17
  }
18
18
  }
19
19
  exports.ZLinkOwnerCleanupError = ZLinkOwnerCleanupError;
20
+ class ZLinkOwnerLeaseClaimError extends Error {
21
+ kind;
22
+ constructor(kind) {
23
+ super(`Owner lease claim failed with '${kind}'.`);
24
+ this.kind = kind;
25
+ this.name = 'ZLinkOwnerLeaseClaimError';
26
+ }
27
+ }
28
+ exports.ZLinkOwnerLeaseClaimError = ZLinkOwnerLeaseClaimError;
20
29
  class ZLinkLocationRuntime {
21
30
  ownerId;
22
31
  options;
@@ -46,9 +55,9 @@ class ZLinkLocationRuntime {
46
55
  ownerLeaseRenewedAt;
47
56
  lastError;
48
57
  get ownerLeaseUsable() {
49
- return this.ownerLeaseHealthy
50
- && (this.ownerLeaseDeadlineMs === undefined
51
- || this.monotonicNowMs() < this.ownerLeaseDeadlineMs);
58
+ return this.ownerToken !== undefined
59
+ && this.ownerLeaseDeadlineMs !== undefined
60
+ && this.monotonicNowMs() < this.ownerLeaseDeadlineMs;
52
61
  }
53
62
  constructor(runtimeOptions) {
54
63
  this.stores = runtimeOptions.stores;
@@ -78,6 +87,10 @@ class ZLinkLocationRuntime {
78
87
  get currentOwnerToken() {
79
88
  return this.ownerToken;
80
89
  }
90
+ closeOwnerLeaseAdmission(error) {
91
+ this.ownerLeaseDeadlineMs = undefined;
92
+ this.recordFailure(errorMessage(error), 'owner_lease_recovery');
93
+ }
81
94
  async reclaimOwnerAuthorities(signal) {
82
95
  const owner = this.ownerToken;
83
96
  if (owner === undefined) {
@@ -166,37 +179,20 @@ class ZLinkLocationRuntime {
166
179
  this.started = true;
167
180
  this.ownerCleanupComplete = false;
168
181
  this.nodeRidValue = nodeRid;
169
- const claimStartedAtMs = this.monotonicNowMs();
170
- let claim;
171
182
  try {
172
- claim = await withTimeout((claimSignal) => this.stores.ownerLeaseStore.claimOwnerLease(this.ownerId, this.options.ownerLeaseTtlMs, claimSignal), this.options.ownerLeaseRenewTimeoutMs, signal);
173
- }
174
- catch (error) {
175
- this.ownerLeaseHealthy = false;
176
- this.ownerLeaseDeadlineMs = undefined;
177
- if (signal?.aborted === true)
178
- throw error;
179
- this.recordFailure(errorMessage(error), 'owner_lease_claim');
180
- // A Location host can still serve transport work while the Store is
181
- // unavailable. Keep the runtime in degraded mode so the heartbeat can
182
- // claim a lease after recovery; no descriptor or stateful authority is
183
- // published until that claim succeeds.
184
- this.nextLeaseRenewAtMs = this.monotonicNowMs();
183
+ const claimed = await this.renewOwnerLeaseOnce(signal);
184
+ if (claimed) {
185
+ this.scheduleHeartbeat();
186
+ return;
187
+ }
188
+ this.nextLeaseRenewAtMs = this.monotonicNowMs() + this.options.ownerLeaseRenewIntervalMs;
185
189
  this.scheduleHeartbeat();
186
- return;
187
190
  }
188
- const claimCompletedAtMs = this.monotonicNowMs();
189
- if (claim.kind !== 'claimed') {
191
+ catch (error) {
190
192
  this.started = false;
191
193
  this.ownerCleanupComplete = true;
192
- throw new Error(`Owner lease claim failed with '${claim.kind}'.`);
194
+ throw error;
193
195
  }
194
- this.ownerToken = claim.token;
195
- this.ownerLeaseHealthy = true;
196
- this.ownerLeaseRenewedAt = claim.storeNow;
197
- this.setOwnerLeaseDeadline(claimStartedAtMs, claimCompletedAtMs, claim.leaseExpiresAt, claim.storeNow);
198
- this.nextLeaseRenewAtMs = this.monotonicNowMs() + this.options.ownerLeaseRenewIntervalMs;
199
- this.scheduleHeartbeat();
200
196
  }
201
197
  async stop(signal) {
202
198
  if (!this.started && this.ownerCleanupComplete) {
@@ -242,6 +238,7 @@ class ZLinkLocationRuntime {
242
238
  if (this.ownerToken === undefined) {
243
239
  return await this.claimFreshOwnerLease(signal);
244
240
  }
241
+ const wasOwnerLeaseUsable = this.ownerLeaseUsable;
245
242
  try {
246
243
  const renewStartedAtMs = this.monotonicNowMs();
247
244
  const result = await withTimeout((renewSignal) => this.stores.ownerLeaseStore.renewOwnerLease(this.ownerToken, this.options.ownerLeaseTtlMs, renewSignal), this.options.ownerLeaseRenewTimeoutMs, signal);
@@ -266,8 +263,10 @@ class ZLinkLocationRuntime {
266
263
  this.ownerLeaseRenewedAt = result.storeNow;
267
264
  this.setOwnerLeaseDeadline(renewStartedAtMs, renewCompletedAtMs, result.leaseExpiresAt, result.storeNow);
268
265
  this.lastError = undefined;
269
- for (const handler of this.ownerLeaseRenewedHandlers)
270
- handler(result);
266
+ if (!wasOwnerLeaseUsable) {
267
+ for (const handler of this.ownerLeaseRenewedHandlers)
268
+ handler(result);
269
+ }
271
270
  return true;
272
271
  }
273
272
  catch (error) {
@@ -281,18 +280,37 @@ class ZLinkLocationRuntime {
281
280
  }
282
281
  }
283
282
  async claimFreshOwnerLease(signal) {
283
+ const claimStartedAtMs = this.monotonicNowMs();
284
+ const timeoutDeadline = performance.now() + this.options.ownerLeaseRenewTimeoutMs;
285
+ let claim;
286
+ try {
287
+ claim = await this.claimOwnerLeaseWithConfirmation(timeoutDeadline, signal);
288
+ }
289
+ catch (error) {
290
+ if (signal?.aborted === true)
291
+ throw error;
292
+ if (error instanceof ZLinkOwnerLeaseClaimError)
293
+ throw error;
294
+ this.recordOwnerLeaseRenewFailure();
295
+ this.recordFailure(errorMessage(error), 'owner_lease_claim');
296
+ return false;
297
+ }
298
+ const claimCompletedAtMs = this.monotonicNowMs();
299
+ if (claim.kind !== 'claimed') {
300
+ const error = new ZLinkOwnerLeaseClaimError(claim.kind);
301
+ this.recordFailure(error.message, 'owner_lease_claim');
302
+ throw error;
303
+ }
284
304
  try {
285
- const claimStartedAtMs = this.monotonicNowMs();
286
- const claim = await withTimeout((claimSignal) => this.stores.ownerLeaseStore.claimOwnerLease(this.ownerId, this.options.ownerLeaseTtlMs, claimSignal), this.options.ownerLeaseRenewTimeoutMs, signal);
287
- const claimCompletedAtMs = this.monotonicNowMs();
288
- if (claim.kind !== 'claimed') {
289
- this.recordFailure(`Owner lease claim failed with '${claim.kind}'.`, 'owner_lease_claim');
290
- return false;
291
- }
292
305
  if (!this.started) {
293
306
  // stop() may have completed while the Store claim was in flight. Do
294
307
  // not install a token into a stopped runtime or leave an orphan lease.
295
- await this.stores.ownerLeaseStore.releaseOwnerLease(claim.token).catch(() => undefined);
308
+ try {
309
+ await withTimeout(releaseSignal => this.stores.ownerLeaseStore.releaseOwnerLease(claim.token, releaseSignal), remainingTimeoutMs(timeoutDeadline));
310
+ }
311
+ catch (error) {
312
+ this.recordFailure(errorMessage(error), 'owner_lease_release');
313
+ }
296
314
  return false;
297
315
  }
298
316
  this.lastOwnerToken = this.ownerToken;
@@ -314,11 +332,60 @@ class ZLinkLocationRuntime {
314
332
  catch (error) {
315
333
  if (signal?.aborted === true)
316
334
  throw error;
317
- this.recordOwnerLeaseRenewFailure();
318
335
  this.recordFailure(errorMessage(error), 'owner_lease_claim');
319
336
  return false;
320
337
  }
321
338
  }
339
+ async claimOwnerLeaseWithConfirmation(timeoutDeadline, signal) {
340
+ signal?.throwIfAborted();
341
+ let claim;
342
+ try {
343
+ claim = await withTimeout((claimSignal) => this.stores.ownerLeaseStore.claimOwnerLease(this.ownerId, this.options.ownerLeaseTtlMs, claimSignal), remainingTimeoutMs(timeoutDeadline), signal);
344
+ }
345
+ catch (error) {
346
+ if (error instanceof OwnerLeaseRenewTimeoutError)
347
+ throw error;
348
+ const confirmed = await this.confirmOwnerLease(error, timeoutDeadline, signal);
349
+ if (confirmed !== undefined)
350
+ return confirmed;
351
+ throw error;
352
+ }
353
+ if (claim.kind !== 'conflict')
354
+ return claim;
355
+ const conflict = new ZLinkOwnerLeaseClaimError(claim.kind);
356
+ return await this.confirmOwnerLease(conflict, timeoutDeadline, signal) ?? claim;
357
+ }
358
+ async confirmOwnerLease(originalError, timeoutDeadline, signal) {
359
+ const confirmationTimeoutMs = remainingTimeoutMs(timeoutDeadline);
360
+ if (confirmationTimeoutMs <= 0)
361
+ throw originalError;
362
+ let confirmed;
363
+ try {
364
+ confirmed = await withTimeout(readSignal => this.stores.ownerLeaseStore.readOwnerLease(this.ownerId, readSignal), confirmationTimeoutMs);
365
+ }
366
+ catch (confirmationError) {
367
+ this.recordFailure(errorMessage(confirmationError), 'owner_lease_claim_confirmation');
368
+ throw originalError;
369
+ }
370
+ if (confirmed.kind !== 'found' || confirmed.token.ownerId !== this.ownerId) {
371
+ throw originalError;
372
+ }
373
+ if (signal?.aborted === true) {
374
+ try {
375
+ await withTimeout(releaseSignal => this.stores.ownerLeaseStore.releaseOwnerLease(confirmed.token, releaseSignal), remainingTimeoutMs(timeoutDeadline));
376
+ }
377
+ catch (releaseError) {
378
+ this.recordFailure(errorMessage(releaseError), 'owner_lease_release');
379
+ }
380
+ throw originalError;
381
+ }
382
+ return {
383
+ kind: 'claimed',
384
+ token: confirmed.token,
385
+ leaseExpiresAt: confirmed.leaseExpiresAt,
386
+ storeNow: confirmed.storeNow
387
+ };
388
+ }
322
389
  setOwnerLeaseDeadline(requestStartedAtMs, requestCompletedAtMs, leaseExpiresAt, storeNow) {
323
390
  const roundTripMs = Math.max(0, requestCompletedAtMs - requestStartedAtMs);
324
391
  const remainingTtlMs = Math.max(0, leaseExpiresAt.getTime() - storeNow.getTime());
@@ -771,22 +838,43 @@ async function withTimeout(action, timeoutMs, signal) {
771
838
  ? timeoutController.signal
772
839
  : AbortSignal.any([signal, timeoutController.signal]);
773
840
  let timeout;
841
+ let abort;
774
842
  const deadline = new Promise((_resolve, reject) => {
775
843
  timeout = setTimeout(() => {
776
- const error = new Error(`Owner lease renewal exceeded ${timeoutMs}ms.`);
844
+ const error = new OwnerLeaseRenewTimeoutError(timeoutMs);
777
845
  timeoutController.abort(error);
778
846
  reject(error);
779
847
  }, timeoutMs);
780
848
  timeout.unref();
781
849
  });
850
+ const cancellation = signal === undefined
851
+ ? undefined
852
+ : new Promise((_resolve, reject) => {
853
+ abort = () => reject(signal.reason);
854
+ signal.addEventListener('abort', abort, { once: true });
855
+ });
782
856
  try {
783
- return await Promise.race([action(operationSignal), deadline]);
857
+ signal?.throwIfAborted();
858
+ return await Promise.race(cancellation === undefined
859
+ ? [action(operationSignal), deadline]
860
+ : [action(operationSignal), deadline, cancellation]);
784
861
  }
785
862
  finally {
786
863
  if (timeout !== undefined)
787
864
  clearTimeout(timeout);
865
+ if (abort !== undefined)
866
+ signal?.removeEventListener('abort', abort);
867
+ }
868
+ }
869
+ class OwnerLeaseRenewTimeoutError extends Error {
870
+ constructor(timeoutMs) {
871
+ super(`Owner lease renewal exceeded ${timeoutMs}ms.`);
872
+ this.name = 'OwnerLeaseRenewTimeoutError';
788
873
  }
789
874
  }
875
+ function remainingTimeoutMs(timeoutDeadline) {
876
+ return Math.max(0, timeoutDeadline - performance.now());
877
+ }
790
878
  function errorMessage(error) {
791
879
  return error instanceof Error ? error.message : String(error);
792
880
  }