@zlink-systems/framework 0.19.0 → 0.21.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
|
@@ -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.
|
|
50
|
-
&&
|
|
51
|
-
|
|
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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
189
|
-
if (claim.kind !== 'claimed') {
|
|
191
|
+
catch (error) {
|
|
190
192
|
this.started = false;
|
|
191
193
|
this.ownerCleanupComplete = true;
|
|
192
|
-
throw
|
|
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
|
-
|
|
270
|
-
handler
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
}
|