@zlink-systems/framework 0.29.1 → 0.30.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 +1 -2
- package/dist/contracts/Configuration/Configs.d.ts +0 -5
- package/dist/contracts/Configuration/RegistrationBuilders.js +6 -4
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -6
- package/dist/contracts/Configuration/RegistrationValidators.js +4 -4
- package/dist/contracts/Configuration/index.d.ts +1 -1
- package/dist/internal.d.ts +1 -0
- package/dist/internal.js +3 -1
- package/dist/runtime/actors/actor-handoff.d.ts +3 -2
- package/dist/runtime/actors/actor-handoff.js +9 -4
- package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +3 -1
- package/dist/runtime/application-jobs/application-ingress-record-owner.js +6 -1
- package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +3 -1
- package/dist/runtime/application-jobs/application-job-queue-scope.js +5 -0
- package/dist/runtime/application-jobs/contracts.d.ts +5 -1
- package/dist/runtime/backend/contracts/index.d.ts +10 -19
- package/dist/runtime/backend/mesh-completion-table.d.ts +6 -4
- package/dist/runtime/backend/mesh-completion-table.js +28 -18
- package/dist/runtime/backend/mesh-dispatch-pump.js +21 -25
- package/dist/runtime/backend/node/node-backend-adapter-factory.js +6 -1
- package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +3 -3
- package/dist/runtime/backend/node/node-backend-adapter-support.js +24 -29
- package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -9
- package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
- package/dist/runtime/backend/node/node-raw-binding-port.js +11 -6
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +10 -12
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +64 -98
- package/dist/runtime/backend/node/node-socket-backend-adapter.js +10 -5
- package/dist/runtime/backend/raw-binding-port.d.ts +9 -2
- package/dist/runtime/channels/channel-receive-loops.js +3 -3
- package/dist/runtime/channels/channel-runtime-lifecycle.js +2 -1
- package/dist/runtime/channels/channel-socket-registry.d.ts +10 -3
- package/dist/runtime/channels/channel-socket-registry.js +59 -82
- package/dist/runtime/diagnostics/message-flow.d.ts +1 -0
- package/dist/runtime/diagnostics/message-flow.js +3 -3
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +15 -1
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +193 -64
- package/dist/runtime/foundation/service-liveness-registry.d.ts +20 -8
- package/dist/runtime/foundation/service-liveness-registry.js +57 -44
- package/dist/runtime/foundation/service-mailbox.d.ts +3 -0
- package/dist/runtime/foundation/service-mailbox.js +2 -1
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +7 -6
- package/dist/runtime/foundation/service-stateful-runtime.js +34 -23
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +5 -2
- package/dist/runtime/foundation/service-stateful-wire-codec.js +43 -61
- package/dist/runtime/foundation/service-topology-registry.d.ts +5 -3
- package/dist/runtime/foundation/service-topology-registry.js +9 -4
- package/dist/runtime/host/actor-packet-relay.js +7 -7
- package/dist/runtime/host/actor-runtime-options-factory.js +1 -1
- package/dist/runtime/host/application-job-queue.d.ts +11 -4
- package/dist/runtime/host/application-job-queue.js +66 -10
- package/dist/runtime/host/entry-actor-runtime.js +1 -1
- package/dist/runtime/host/index.js +15 -11
- package/dist/runtime/host/instance-activation-authority.d.ts +2 -1
- package/dist/runtime/host/instance-activation-authority.js +89 -25
- package/dist/runtime/host/service-relocation-host-runtime.js +2 -2
- package/dist/runtime/host/spot-address-transport.d.ts +1 -0
- package/dist/runtime/host/spot-address-transport.js +25 -12
- package/dist/runtime/host/spot-runtime-options-factory.js +2 -2
- package/dist/runtime/host/user-spot-creation-coordinator.d.ts +1 -0
- package/dist/runtime/host/user-spot-creation-coordinator.js +1 -0
- package/dist/runtime/locations/domain-store-contract.d.ts +2 -2
- package/dist/runtime/locations/in-memory-authority-store.d.ts +4 -2
- package/dist/runtime/locations/in-memory-authority-store.js +80 -8
- package/dist/runtime/locations/in-memory-location-store.d.ts +2 -2
- package/dist/runtime/locations/in-memory-location-store.js +19 -3
- package/dist/runtime/locations/internal-location-contracts.d.ts +3 -0
- package/dist/runtime/locations/location-store-repository.d.ts +3 -2
- package/dist/runtime/locations/location-store-repository.js +165 -22
- package/dist/runtime/locations/resolvers.d.ts +2 -1
- package/dist/runtime/locations/resolvers.js +10 -1
- package/dist/runtime/messaging/submission-result.d.ts +1 -1
- package/dist/runtime/messaging/submission-result.js +2 -2
- package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +2 -0
- package/dist/runtime/spots/index.d.ts +4 -3
- package/dist/runtime/spots/index.js +14 -14
- package/dist/runtime/spots/spot-activation.d.ts +2 -1
- package/dist/runtime/spots/spot-activation.js +4 -4
- package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +2 -1
- package/dist/runtime/spots/spot-actor-admission-coordinator.js +11 -10
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +4 -1
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +7 -5
- package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
- package/dist/runtime/spots/spot-actor-packet-relay-dispatch.js +1 -0
- package/dist/runtime/spots/spot-entry-activation.js +3 -1
- package/dist/runtime/spots/spot-node-runtime-manager.d.ts +2 -1
- package/dist/runtime/spots/spot-node-runtime-manager.js +4 -11
- package/dist/runtime/spots/spot-routing-internal.d.ts +6 -0
- package/dist/runtime/spots/spot-runtime-ports.d.ts +2 -1
- package/dist/runtime/streams/stream-session-runtime.js +1 -1
- package/package.json +3 -3
- package/src/contracts/Configuration/Builders.ts +1 -2
- package/src/contracts/Configuration/Configs.ts +0 -6
- package/src/contracts/Configuration/RegistrationBuilders.ts +8 -12
- package/src/contracts/Configuration/RegistrationTypes.ts +1 -6
- package/src/contracts/Configuration/RegistrationValidators.ts +9 -10
- package/src/contracts/Configuration/index.ts +1 -6
- package/src/internal.ts +1 -0
- package/src/runtime/actors/actor-handoff.ts +13 -4
- package/src/runtime/application-jobs/application-ingress-record-owner.ts +12 -2
- package/src/runtime/application-jobs/application-job-queue-scope.ts +6 -1
- package/src/runtime/application-jobs/contracts.ts +14 -1
- package/src/runtime/backend/contracts/index.ts +10 -18
- package/src/runtime/backend/mesh-completion-table.ts +36 -19
- package/src/runtime/backend/mesh-dispatch-pump.ts +19 -22
- package/src/runtime/backend/node/node-backend-adapter-factory.ts +12 -5
- package/src/runtime/backend/node/node-backend-adapter-support.ts +31 -29
- package/src/runtime/backend/node/node-mesh-backend-adapter.ts +3 -10
- package/src/runtime/backend/node/node-raw-binding-port.ts +19 -10
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +81 -111
- package/src/runtime/backend/node/node-socket-backend-adapter.ts +12 -8
- package/src/runtime/backend/raw-binding-port.ts +12 -2
- package/src/runtime/channels/channel-receive-loops.ts +3 -3
- package/src/runtime/channels/channel-runtime-lifecycle.ts +7 -1
- package/src/runtime/channels/channel-socket-registry.ts +84 -130
- package/src/runtime/diagnostics/message-flow.ts +1 -1
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +268 -70
- package/src/runtime/foundation/service-liveness-registry.ts +68 -59
- package/src/runtime/foundation/service-mailbox.ts +7 -1
- package/src/runtime/foundation/service-stateful-runtime.ts +61 -35
- package/src/runtime/foundation/service-stateful-wire-codec.ts +55 -79
- package/src/runtime/foundation/service-topology-registry.ts +13 -7
- package/src/runtime/host/actor-packet-relay.ts +7 -0
- package/src/runtime/host/actor-runtime-options-factory.ts +1 -0
- package/src/runtime/host/application-job-queue.ts +77 -10
- package/src/runtime/host/entry-actor-runtime.ts +1 -0
- package/src/runtime/host/index.ts +24 -5
- package/src/runtime/host/instance-activation-authority.ts +132 -27
- package/src/runtime/host/service-relocation-host-runtime.ts +2 -0
- package/src/runtime/host/spot-address-transport.ts +45 -22
- package/src/runtime/host/spot-runtime-options-factory.ts +2 -0
- package/src/runtime/host/user-spot-creation-coordinator.ts +8 -0
- package/src/runtime/locations/domain-store-contract.ts +6 -2
- package/src/runtime/locations/in-memory-authority-store.ts +113 -9
- package/src/runtime/locations/in-memory-location-store.ts +25 -3
- package/src/runtime/locations/internal-location-contracts.ts +4 -0
- package/src/runtime/locations/location-store-repository.ts +220 -15
- package/src/runtime/locations/resolvers.ts +25 -2
- package/src/runtime/messaging/submission-result.ts +2 -2
- package/src/runtime/spots/formal-remote-actor-admission-registry.ts +2 -0
- package/src/runtime/spots/index.ts +16 -0
- package/src/runtime/spots/spot-activation.ts +5 -0
- package/src/runtime/spots/spot-actor-admission-coordinator.ts +13 -0
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +10 -0
- package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
- package/src/runtime/spots/spot-actor-packet-relay-dispatch.ts +1 -0
- package/src/runtime/spots/spot-entry-activation.ts +3 -0
- package/src/runtime/spots/spot-node-runtime-manager.ts +7 -12
- package/src/runtime/spots/spot-routing-internal.ts +14 -0
- package/src/runtime/spots/spot-runtime-ports.ts +2 -0
- package/src/runtime/streams/stream-session-runtime.ts +1 -1
|
@@ -100,6 +100,8 @@ export type RawServiceIngressHandler = (
|
|
|
100
100
|
) => RawServicePumpResult | undefined | Promise<RawServicePumpResult | undefined>;
|
|
101
101
|
|
|
102
102
|
export interface RawServiceMeshRuntimeOptions {
|
|
103
|
+
readonly onReceiveReady?: () => void;
|
|
104
|
+
readonly infrastructureTaskRunner?: import('../spots/spot-actor-join-dispatch').ZLinkDetachedTaskRunner;
|
|
103
105
|
readonly onPendingOperationsChanged?: () => void;
|
|
104
106
|
readonly descriptor: ServiceNodeDescriptor;
|
|
105
107
|
readonly resolveAdvertisedEndpoint?: (boundEndpoint: string) => string;
|
|
@@ -170,6 +172,19 @@ export class RawServiceMeshRuntime {
|
|
|
170
172
|
private readonly applicationJobQueue: ApplicationJobQueuePort;
|
|
171
173
|
private readonly peerAdmissionSealed?: () => boolean;
|
|
172
174
|
private readonly applicationJobStop = new AbortController();
|
|
175
|
+
private receiveAcquisition?: Promise<ApplicationJobPermitPort>;
|
|
176
|
+
private receivePermit?: ApplicationJobPermitPort;
|
|
177
|
+
private readonly controlSends = new Map<
|
|
178
|
+
string,
|
|
179
|
+
{
|
|
180
|
+
readonly task: Promise<boolean>;
|
|
181
|
+
next?: () => boolean | Promise<boolean>;
|
|
182
|
+
current?: () => boolean;
|
|
183
|
+
}
|
|
184
|
+
>();
|
|
185
|
+
private infrastructureFailure?: { readonly error: unknown };
|
|
186
|
+
private readonly onReceiveReady?: () => void;
|
|
187
|
+
private readonly infrastructureTaskRunner?: RawServiceMeshRuntimeOptions['infrastructureTaskRunner'];
|
|
173
188
|
private readonly onPeerNotRequired?: RawServiceMeshRuntimeOptions['onPeerNotRequired'];
|
|
174
189
|
private readonly onPeerDisconnected?: RawServiceMeshRuntimeOptions['onPeerDisconnected'];
|
|
175
190
|
private readonly onProtocolError?: RawServiceMeshRuntimeOptions['onProtocolError'];
|
|
@@ -183,6 +198,8 @@ export class RawServiceMeshRuntime {
|
|
|
183
198
|
private closed = false;
|
|
184
199
|
|
|
185
200
|
constructor(options: RawServiceMeshRuntimeOptions) {
|
|
201
|
+
this.onReceiveReady = options.onReceiveReady;
|
|
202
|
+
this.infrastructureTaskRunner = options.infrastructureTaskRunner;
|
|
186
203
|
this.operations = new OperationRegistry(undefined, options.onPendingOperationsChanged);
|
|
187
204
|
this.descriptor = options.descriptor;
|
|
188
205
|
this.topology = new ServiceTopologyRegistry(options.descriptor);
|
|
@@ -371,7 +388,7 @@ export class RawServiceMeshRuntime {
|
|
|
371
388
|
for (const listener of this.peerConnectionIntentRemoved) listener(nodeRoutingId);
|
|
372
389
|
}
|
|
373
390
|
|
|
374
|
-
|
|
391
|
+
announcePeer(nodeRoutingId: string): Promise<boolean> {
|
|
375
392
|
const expected = this.expectedPeers.get(nodeRoutingId);
|
|
376
393
|
const generation = this.selectedRoutes.get(nodeRoutingId);
|
|
377
394
|
if (
|
|
@@ -380,17 +397,27 @@ export class RawServiceMeshRuntime {
|
|
|
380
397
|
expected.helloSubmittedGeneration === generation ||
|
|
381
398
|
this.peerAdmissionSealed?.() === true
|
|
382
399
|
)
|
|
383
|
-
return false;
|
|
384
|
-
const
|
|
385
|
-
encodeRouteMeshAdmission(M6aServiceWireCommand.hello, this.topology.localDescriptor())
|
|
386
|
-
]);
|
|
387
|
-
if (
|
|
388
|
-
accepted &&
|
|
400
|
+
return Promise.resolve(false);
|
|
401
|
+
const current = () =>
|
|
389
402
|
this.expectedPeers.get(nodeRoutingId) === expected &&
|
|
390
|
-
this.selectedRoutes.get(nodeRoutingId) === generation
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
403
|
+
this.selectedRoutes.get(nodeRoutingId) === generation;
|
|
404
|
+
return Promise.resolve(
|
|
405
|
+
this.startControlSend(
|
|
406
|
+
nodeRoutingId,
|
|
407
|
+
M6aServiceWireCommand.hello,
|
|
408
|
+
() =>
|
|
409
|
+
this.submitControl(
|
|
410
|
+
nodeRoutingId,
|
|
411
|
+
[
|
|
412
|
+
encodeRouteMeshAdmission(M6aServiceWireCommand.hello, this.topology.localDescriptor())
|
|
413
|
+
],
|
|
414
|
+
() => {
|
|
415
|
+
if (current()) expected.helloSubmittedGeneration = generation;
|
|
416
|
+
}
|
|
417
|
+
),
|
|
418
|
+
current
|
|
419
|
+
)
|
|
420
|
+
);
|
|
394
421
|
}
|
|
395
422
|
|
|
396
423
|
isPeerRouteReady(nodeRoutingId: string, lifecycleGeneration?: bigint): boolean {
|
|
@@ -400,8 +427,7 @@ export class RawServiceMeshRuntime {
|
|
|
400
427
|
return (
|
|
401
428
|
peer !== undefined &&
|
|
402
429
|
(lifecycleGeneration === undefined ||
|
|
403
|
-
peer.descriptor.lifecycleGeneration === lifecycleGeneration)
|
|
404
|
-
this.liveness.isReady(nodeRoutingId, peer.connectionId)
|
|
430
|
+
peer.descriptor.lifecycleGeneration === lifecycleGeneration)
|
|
405
431
|
);
|
|
406
432
|
}
|
|
407
433
|
|
|
@@ -548,7 +574,7 @@ export class RawServiceMeshRuntime {
|
|
|
548
574
|
signal === undefined
|
|
549
575
|
? this.applicationJobStop.signal
|
|
550
576
|
: AbortSignal.any([this.applicationJobStop.signal, signal]);
|
|
551
|
-
const permit = await this.applicationJobQueue.acquire(waitSignal);
|
|
577
|
+
const permit = await this.applicationJobQueue.acquire(waitSignal, 'local');
|
|
552
578
|
return ApplicationIngressRecordOwner.create(this.applicationJobQueue, permit, { close() {} });
|
|
553
579
|
}
|
|
554
580
|
|
|
@@ -561,7 +587,13 @@ export class RawServiceMeshRuntime {
|
|
|
561
587
|
parts: readonly Uint8Array[],
|
|
562
588
|
timeoutMs: number
|
|
563
589
|
): Promise<readonly Buffer[]> {
|
|
564
|
-
|
|
590
|
+
const admission = this.topology.peer(targetNodeRoutingId)?.liveness;
|
|
591
|
+
return this.requireStarted().request(
|
|
592
|
+
targetNodeRoutingId,
|
|
593
|
+
parts,
|
|
594
|
+
timeoutMs,
|
|
595
|
+
admission?.recordReceived
|
|
596
|
+
);
|
|
565
597
|
}
|
|
566
598
|
|
|
567
599
|
replyService(
|
|
@@ -607,6 +639,18 @@ export class RawServiceMeshRuntime {
|
|
|
607
639
|
nowMs = performance.now(),
|
|
608
640
|
observe?: RawServicePumpObserver
|
|
609
641
|
): Promise<RawServicePumpResult> {
|
|
642
|
+
const result = await this.receiveOne(nowMs, observe);
|
|
643
|
+
const acquisition = this.receiveAcquisition;
|
|
644
|
+
if (acquisition === undefined) return result;
|
|
645
|
+
// A single receive operation may await its FIFO grant. Infrastructure
|
|
646
|
+
// rounds use receiveOne directly and end while that grant is pending.
|
|
647
|
+
try {
|
|
648
|
+
await acquisition;
|
|
649
|
+
} catch (error) {
|
|
650
|
+
if (this.closed) return 'noData';
|
|
651
|
+
throw error;
|
|
652
|
+
}
|
|
653
|
+
if (this.closed) return 'noData';
|
|
610
654
|
return this.receiveOne(nowMs, observe);
|
|
611
655
|
}
|
|
612
656
|
|
|
@@ -616,6 +660,7 @@ export class RawServiceMeshRuntime {
|
|
|
616
660
|
|
|
617
661
|
/** Returns whether a receive budget ended before no-data; idle ticks only maintain peers. */
|
|
618
662
|
async pumpBatch(receiveReady = true, routeReady = false): Promise<boolean> {
|
|
663
|
+
this.observeInfrastructureFailure();
|
|
619
664
|
if (routeReady) await this.observeSelectedRoutes();
|
|
620
665
|
const startedAtMs = performance.now();
|
|
621
666
|
let messages = 0;
|
|
@@ -639,8 +684,10 @@ export class RawServiceMeshRuntime {
|
|
|
639
684
|
break;
|
|
640
685
|
}
|
|
641
686
|
if (!this.closed) {
|
|
642
|
-
|
|
643
|
-
|
|
687
|
+
for (const nodeRoutingId of this.expectedPeers.keys()) {
|
|
688
|
+
if (this.topology.peer(nodeRoutingId) === undefined) await this.announcePeer(nodeRoutingId);
|
|
689
|
+
}
|
|
690
|
+
this.tickLiveness();
|
|
644
691
|
}
|
|
645
692
|
return receiveReady && messages > 0;
|
|
646
693
|
}
|
|
@@ -650,13 +697,40 @@ export class RawServiceMeshRuntime {
|
|
|
650
697
|
observe?: RawServicePumpObserver
|
|
651
698
|
): Promise<RawServicePumpResult> {
|
|
652
699
|
const router = this.requireStarted();
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
permit
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
700
|
+
this.observeInfrastructureFailure();
|
|
701
|
+
if (this.receivePermit === undefined && this.receiveAcquisition === undefined) {
|
|
702
|
+
let resolveAcquisition!: (permit: ApplicationJobPermitPort) => void;
|
|
703
|
+
let rejectAcquisition!: (error: unknown) => void;
|
|
704
|
+
const acquisition = new Promise<ApplicationJobPermitPort>((resolve, reject) => {
|
|
705
|
+
resolveAcquisition = resolve;
|
|
706
|
+
rejectAcquisition = reject;
|
|
707
|
+
});
|
|
708
|
+
const permit = this.applicationJobQueue.acquireOrResume(
|
|
709
|
+
(permit) => {
|
|
710
|
+
this.receiveAcquisition = undefined;
|
|
711
|
+
if (this.closed) permit.releaseAfterInternalProcessing();
|
|
712
|
+
else {
|
|
713
|
+
this.receivePermit = permit;
|
|
714
|
+
this.onReceiveReady?.();
|
|
715
|
+
}
|
|
716
|
+
resolveAcquisition(permit);
|
|
717
|
+
},
|
|
718
|
+
rejectAcquisition,
|
|
719
|
+
this.applicationJobStop.signal,
|
|
720
|
+
'remote'
|
|
721
|
+
);
|
|
722
|
+
if (permit !== undefined) this.receivePermit = permit;
|
|
723
|
+
else {
|
|
724
|
+
this.receiveAcquisition = acquisition;
|
|
725
|
+
acquisition.catch((error) => {
|
|
726
|
+
this.receiveAcquisition = undefined;
|
|
727
|
+
if (!this.applicationJobStop.signal.aborted) this.reportInfrastructureFailure(error);
|
|
728
|
+
});
|
|
729
|
+
}
|
|
659
730
|
}
|
|
731
|
+
const permit = this.receivePermit;
|
|
732
|
+
this.receivePermit = undefined;
|
|
733
|
+
if (permit === undefined) return 'noData';
|
|
660
734
|
if (this.closed) {
|
|
661
735
|
permit.releaseAfterInternalProcessing();
|
|
662
736
|
return 'noData';
|
|
@@ -744,7 +818,7 @@ export class RawServiceMeshRuntime {
|
|
|
744
818
|
(received.parts.length === 1 && received.parts[0]!.byteLength === 0)
|
|
745
819
|
) {
|
|
746
820
|
if (this.peerAdmissionSealed?.() === true) return 'dropped';
|
|
747
|
-
return (await this.
|
|
821
|
+
return (await this.sendControl(received.sourceRid, [
|
|
748
822
|
encodeRouteMeshAdmission(M6aServiceWireCommand.hello, this.topology.localDescriptor())
|
|
749
823
|
]))
|
|
750
824
|
? 'infrastructure'
|
|
@@ -772,17 +846,12 @@ export class RawServiceMeshRuntime {
|
|
|
772
846
|
(expected.meshName !== descriptor.meshName ||
|
|
773
847
|
expected.nodeRoutingId !== descriptor.nodeRoutingId)
|
|
774
848
|
) {
|
|
775
|
-
await this.
|
|
849
|
+
await this.sendControl(received.sourceRid, [
|
|
776
850
|
encodeReject(enumWireRejectReason('identityMismatch'))
|
|
777
851
|
]);
|
|
778
852
|
return 'infrastructure';
|
|
779
853
|
}
|
|
780
|
-
const result = this.admitPeer(
|
|
781
|
-
descriptor,
|
|
782
|
-
routeConnectionId(received.routeGeneration),
|
|
783
|
-
nowMs,
|
|
784
|
-
expected
|
|
785
|
-
);
|
|
854
|
+
const result = this.admitPeer(descriptor, received.routeGeneration, nowMs, expected);
|
|
786
855
|
if (result !== 'admitted' && result !== 'notRequired') {
|
|
787
856
|
const detail =
|
|
788
857
|
result === 'invalidDescriptor'
|
|
@@ -796,7 +865,7 @@ export class RawServiceMeshRuntime {
|
|
|
796
865
|
`RouteMesh admission rejected peer '${received.sourceRid}' as ${result}` +
|
|
797
866
|
(detail === undefined ? '.' : ` (${detail}).`)
|
|
798
867
|
);
|
|
799
|
-
await this.
|
|
868
|
+
await this.sendControl(received.sourceRid, [encodeReject(admissionReason(result))]);
|
|
800
869
|
return 'infrastructure';
|
|
801
870
|
}
|
|
802
871
|
if (result === 'admitted') this.selectBilateralConnection(descriptor);
|
|
@@ -804,7 +873,7 @@ export class RawServiceMeshRuntime {
|
|
|
804
873
|
// Hello supplies only the sender's descriptor. Even NotRequired
|
|
805
874
|
// peers must receive ours before the intent owner closes transport;
|
|
806
875
|
// submitting a response does not guarantee delivery before close.
|
|
807
|
-
await this.
|
|
876
|
+
await this.sendControl(received.sourceRid, [
|
|
808
877
|
encodeRouteMeshAdmission(M6aServiceWireCommand.admit, this.topology.localDescriptor())
|
|
809
878
|
]);
|
|
810
879
|
} else if (result === 'notRequired') {
|
|
@@ -819,6 +888,8 @@ export class RawServiceMeshRuntime {
|
|
|
819
888
|
}
|
|
820
889
|
const peer = this.topology.peer(received.sourceRid);
|
|
821
890
|
if (peer === undefined) return 'protocolError';
|
|
891
|
+
if (peer.liveness !== undefined && peer.liveness.connectionId === received.routeGeneration)
|
|
892
|
+
this.liveness.recordReceived(peer.liveness, nowMs);
|
|
822
893
|
if (
|
|
823
894
|
header.command === M6aServiceWireCommand.livenessProbe ||
|
|
824
895
|
header.command === M6aServiceWireCommand.livenessAck
|
|
@@ -828,11 +899,11 @@ export class RawServiceMeshRuntime {
|
|
|
828
899
|
if (record.command === M6aServiceWireCommand.livenessProbe) {
|
|
829
900
|
const ack = this.liveness.acknowledgeProbe(
|
|
830
901
|
received.sourceRid,
|
|
831
|
-
|
|
902
|
+
received.routeGeneration,
|
|
832
903
|
record.probeId
|
|
833
904
|
);
|
|
834
905
|
if (ack === undefined) return 'protocolError';
|
|
835
|
-
const sent = await this.
|
|
906
|
+
const sent = await this.sendControl(received.sourceRid, [
|
|
836
907
|
livenessCodec.encodeLivenessRecord({
|
|
837
908
|
command: M6aServiceWireCommand.livenessAck,
|
|
838
909
|
probeId: record.probeId
|
|
@@ -840,7 +911,12 @@ export class RawServiceMeshRuntime {
|
|
|
840
911
|
]);
|
|
841
912
|
if (!sent) return 'dropped';
|
|
842
913
|
} else {
|
|
843
|
-
this.liveness.acknowledge(
|
|
914
|
+
this.liveness.acknowledge(
|
|
915
|
+
received.sourceRid,
|
|
916
|
+
received.routeGeneration,
|
|
917
|
+
record.probeId,
|
|
918
|
+
nowMs
|
|
919
|
+
);
|
|
844
920
|
}
|
|
845
921
|
return 'infrastructure';
|
|
846
922
|
}
|
|
@@ -950,21 +1026,24 @@ export class RawServiceMeshRuntime {
|
|
|
950
1026
|
return this.operations.size;
|
|
951
1027
|
}
|
|
952
1028
|
|
|
953
|
-
|
|
1029
|
+
tickLiveness(nowMs = performance.now()): ServiceLivenessTick {
|
|
954
1030
|
const result = this.liveness.tick(nowMs);
|
|
955
1031
|
this.requireStarted();
|
|
956
1032
|
for (const probe of result.probes) {
|
|
957
|
-
|
|
1033
|
+
const parts = [
|
|
958
1034
|
livenessCodec.encodeLivenessRecord({
|
|
959
1035
|
command: M6aServiceWireCommand.livenessProbe,
|
|
960
1036
|
probeId: probe.probeId
|
|
961
1037
|
})
|
|
962
|
-
]
|
|
1038
|
+
];
|
|
1039
|
+
this.startControlSend(probe.nodeRoutingId, M6aServiceWireCommand.livenessProbe, () =>
|
|
1040
|
+
this.submitControl(probe.nodeRoutingId, parts)
|
|
1041
|
+
);
|
|
963
1042
|
}
|
|
964
1043
|
for (const nodeRoutingId of result.timedOutNodes) {
|
|
965
1044
|
const peer = this.topology.peer(nodeRoutingId);
|
|
966
1045
|
if (peer === undefined) continue;
|
|
967
|
-
|
|
1046
|
+
this.requireStarted().disconnectRid!(nodeRoutingId);
|
|
968
1047
|
this.removePeer(peer);
|
|
969
1048
|
}
|
|
970
1049
|
return result;
|
|
@@ -990,7 +1069,7 @@ export class RawServiceMeshRuntime {
|
|
|
990
1069
|
const expected = this.expectedPeers.get(nodeRoutingId);
|
|
991
1070
|
if (expected !== undefined) delete expected.helloSubmittedGeneration;
|
|
992
1071
|
const peer = this.topology.peer(nodeRoutingId);
|
|
993
|
-
if (peer
|
|
1072
|
+
if (peer?.liveness?.connectionId === generation) {
|
|
994
1073
|
this.removePeer(peer);
|
|
995
1074
|
}
|
|
996
1075
|
}
|
|
@@ -1008,9 +1087,13 @@ export class RawServiceMeshRuntime {
|
|
|
1008
1087
|
|
|
1009
1088
|
close(): void {
|
|
1010
1089
|
if (this.closed) return;
|
|
1090
|
+
this.closed = true;
|
|
1011
1091
|
this.applicationJobStop.abort(
|
|
1012
1092
|
new Error('Raw service runtime application job admission stopped.')
|
|
1013
1093
|
);
|
|
1094
|
+
this.receivePermit?.releaseAfterInternalProcessing();
|
|
1095
|
+
this.receivePermit = undefined;
|
|
1096
|
+
for (const send of this.controlSends.values()) delete send.next;
|
|
1014
1097
|
this.mailbox.close();
|
|
1015
1098
|
this.operations.close('Raw service runtime closed.');
|
|
1016
1099
|
this.serviceIngress = undefined;
|
|
@@ -1022,7 +1105,6 @@ export class RawServiceMeshRuntime {
|
|
|
1022
1105
|
if (host !== undefined) host.close();
|
|
1023
1106
|
this.router = undefined;
|
|
1024
1107
|
this.host = undefined;
|
|
1025
|
-
this.closed = true;
|
|
1026
1108
|
}
|
|
1027
1109
|
|
|
1028
1110
|
private requestToTarget(
|
|
@@ -1107,7 +1189,8 @@ export class RawServiceMeshRuntime {
|
|
|
1107
1189
|
})().catch((error) => this.operations.fail(pending.id, error));
|
|
1108
1190
|
return pending;
|
|
1109
1191
|
}
|
|
1110
|
-
|
|
1192
|
+
const selectedPeer = this.topology.peer(selectedTargetNodeRoutingId);
|
|
1193
|
+
if (selectedPeer === undefined) {
|
|
1111
1194
|
// Logical target selection is the requester's decision: an RID that is
|
|
1112
1195
|
// not an admitted peer is not a target. Whether an admitted target's
|
|
1113
1196
|
// route can carry the request is Core's REQUEST result.
|
|
@@ -1118,11 +1201,14 @@ export class RawServiceMeshRuntime {
|
|
|
1118
1201
|
return pending;
|
|
1119
1202
|
}
|
|
1120
1203
|
const parts = [header, encodedPayload];
|
|
1121
|
-
let router: ZLinkRawRouterPort;
|
|
1122
1204
|
let request: Promise<readonly Uint8Array[]>;
|
|
1123
1205
|
try {
|
|
1124
|
-
|
|
1125
|
-
|
|
1206
|
+
request = this.requireStarted().request(
|
|
1207
|
+
selectedTargetNodeRoutingId,
|
|
1208
|
+
parts,
|
|
1209
|
+
timeoutMs,
|
|
1210
|
+
selectedPeer.liveness?.recordReceived
|
|
1211
|
+
);
|
|
1126
1212
|
} catch (error) {
|
|
1127
1213
|
this.operations.fail(pending.id, error);
|
|
1128
1214
|
return pending;
|
|
@@ -1182,7 +1268,7 @@ export class RawServiceMeshRuntime {
|
|
|
1182
1268
|
|
|
1183
1269
|
private admitPeer(
|
|
1184
1270
|
descriptor: ServiceNodeDescriptor,
|
|
1185
|
-
connectionId: string,
|
|
1271
|
+
connectionId: string | bigint,
|
|
1186
1272
|
nowMs: number,
|
|
1187
1273
|
expected?: {
|
|
1188
1274
|
readonly endpoint?: string;
|
|
@@ -1202,12 +1288,18 @@ export class RawServiceMeshRuntime {
|
|
|
1202
1288
|
`RouteMesh peer '${descriptor.nodeRoutingId}' descriptor revision is stale or conflicting.`
|
|
1203
1289
|
);
|
|
1204
1290
|
}
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
this.liveness.
|
|
1209
|
-
|
|
1210
|
-
|
|
1291
|
+
const topologyConnectionId =
|
|
1292
|
+
typeof connectionId === 'bigint' ? `route:${connectionId}` : connectionId;
|
|
1293
|
+
const result = this.topology.admit(descriptor, topologyConnectionId, expected, () => {
|
|
1294
|
+
const admission = this.liveness.admit(descriptor.nodeRoutingId, connectionId, nowMs);
|
|
1295
|
+
this.liveness.recordReceived(admission, nowMs);
|
|
1296
|
+
return admission;
|
|
1297
|
+
});
|
|
1298
|
+
if (result === 'notRequired' && previous !== undefined) {
|
|
1299
|
+
this.liveness.disconnect(
|
|
1300
|
+
descriptor.nodeRoutingId,
|
|
1301
|
+
previous.liveness?.connectionId ?? previous.connectionId
|
|
1302
|
+
);
|
|
1211
1303
|
}
|
|
1212
1304
|
return result;
|
|
1213
1305
|
}
|
|
@@ -1244,7 +1336,10 @@ export class RawServiceMeshRuntime {
|
|
|
1244
1336
|
|
|
1245
1337
|
private removePeer(peer: AdmittedServicePeer): void {
|
|
1246
1338
|
this.topology.disconnect(peer.descriptor.nodeRoutingId, peer.connectionId);
|
|
1247
|
-
this.liveness.disconnect(
|
|
1339
|
+
this.liveness.disconnect(
|
|
1340
|
+
peer.descriptor.nodeRoutingId,
|
|
1341
|
+
peer.liveness?.connectionId ?? peer.connectionId
|
|
1342
|
+
);
|
|
1248
1343
|
this.onPeerDisconnected?.(
|
|
1249
1344
|
peer.descriptor.nodeRoutingId,
|
|
1250
1345
|
peer.descriptor.advertisedEndpoint,
|
|
@@ -1257,18 +1352,129 @@ export class RawServiceMeshRuntime {
|
|
|
1257
1352
|
return this.router;
|
|
1258
1353
|
}
|
|
1259
1354
|
|
|
1260
|
-
private async
|
|
1355
|
+
private async sendControl(
|
|
1356
|
+
targetNodeRoutingId: string,
|
|
1357
|
+
parts: readonly Uint8Array[]
|
|
1358
|
+
): Promise<boolean> {
|
|
1359
|
+
return this.startControlSend(targetNodeRoutingId, decodeHeader(parts[0]!).command, () =>
|
|
1360
|
+
this.submitControl(targetNodeRoutingId, parts)
|
|
1361
|
+
);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
private submitControl(
|
|
1365
|
+
target: string,
|
|
1366
|
+
parts: readonly Uint8Array[],
|
|
1367
|
+
accepted?: () => void
|
|
1368
|
+
): boolean | Promise<boolean> {
|
|
1369
|
+
let submission;
|
|
1261
1370
|
try {
|
|
1262
|
-
|
|
1263
|
-
return true;
|
|
1371
|
+
submission = this.requireStarted().submitSend(target, parts);
|
|
1264
1372
|
} catch (error) {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1373
|
+
return this.sendFailure(error);
|
|
1374
|
+
}
|
|
1375
|
+
if (submission.result === SubmitResult.Ok) {
|
|
1376
|
+
accepted?.();
|
|
1377
|
+
return true;
|
|
1378
|
+
}
|
|
1379
|
+
return submission.admitted.then(
|
|
1380
|
+
() => {
|
|
1381
|
+
accepted?.();
|
|
1382
|
+
return true;
|
|
1383
|
+
},
|
|
1384
|
+
(error) => this.sendFailure(error)
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
private startControlSend(
|
|
1389
|
+
target: string,
|
|
1390
|
+
command: number,
|
|
1391
|
+
attempt: () => boolean | Promise<boolean>,
|
|
1392
|
+
current?: () => boolean
|
|
1393
|
+
): boolean {
|
|
1394
|
+
const key = JSON.stringify([target, command]);
|
|
1395
|
+
const existing = this.controlSends.get(key);
|
|
1396
|
+
if (
|
|
1397
|
+
existing !== undefined &&
|
|
1398
|
+
(command !== M6aServiceWireCommand.hello || existing.current?.() === true)
|
|
1399
|
+
) {
|
|
1400
|
+
if (command !== M6aServiceWireCommand.hello) {
|
|
1401
|
+
existing.next = attempt;
|
|
1402
|
+
existing.current = current;
|
|
1269
1403
|
}
|
|
1270
|
-
|
|
1404
|
+
return true;
|
|
1405
|
+
}
|
|
1406
|
+
const first = attempt();
|
|
1407
|
+
if (typeof first === 'boolean') return first;
|
|
1408
|
+
const owner = this;
|
|
1409
|
+
const state: {
|
|
1410
|
+
task: Promise<boolean>;
|
|
1411
|
+
next?: () => boolean | Promise<boolean>;
|
|
1412
|
+
current?: () => boolean;
|
|
1413
|
+
} = {
|
|
1414
|
+
current,
|
|
1415
|
+
task: first.then(function drain(accepted): boolean | Promise<boolean> {
|
|
1416
|
+
if (!owner.closed && state.next !== undefined) {
|
|
1417
|
+
const next = state.next;
|
|
1418
|
+
delete state.next;
|
|
1419
|
+
return Promise.resolve(next()).then(drain);
|
|
1420
|
+
}
|
|
1421
|
+
if (owner.controlSends.get(key) === state) owner.controlSends.delete(key);
|
|
1422
|
+
return accepted;
|
|
1423
|
+
})
|
|
1424
|
+
};
|
|
1425
|
+
this.controlSends.set(key, state);
|
|
1426
|
+
state.task.then(
|
|
1427
|
+
() => {},
|
|
1428
|
+
(error) => {
|
|
1429
|
+
if (this.controlSends.get(key) === state) this.controlSends.delete(key);
|
|
1430
|
+
if (this.infrastructureTaskRunner === undefined) this.reportInfrastructureFailure(error);
|
|
1431
|
+
}
|
|
1432
|
+
);
|
|
1433
|
+
this.infrastructureTaskRunner?.runDetached('RouteMesh control SEND', async () => {
|
|
1434
|
+
await state.task;
|
|
1435
|
+
});
|
|
1436
|
+
return true;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
private reportInfrastructureFailure(error: unknown): void {
|
|
1440
|
+
this.infrastructureFailure = {
|
|
1441
|
+
error:
|
|
1442
|
+
this.infrastructureFailure === undefined
|
|
1443
|
+
? error
|
|
1444
|
+
: new AggregateError(
|
|
1445
|
+
[this.infrastructureFailure.error, error],
|
|
1446
|
+
'RouteMesh infrastructure failed.'
|
|
1447
|
+
)
|
|
1448
|
+
};
|
|
1449
|
+
this.onReceiveReady?.();
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
private observeInfrastructureFailure(): void {
|
|
1453
|
+
const failure = this.infrastructureFailure;
|
|
1454
|
+
this.infrastructureFailure = undefined;
|
|
1455
|
+
if (failure !== undefined) throw failure.error;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
private send(targetNodeRoutingId: string, parts: readonly Uint8Array[]): Promise<boolean> {
|
|
1459
|
+
return this.requireStarted()
|
|
1460
|
+
.send(targetNodeRoutingId, parts)
|
|
1461
|
+
.then(
|
|
1462
|
+
() => {
|
|
1463
|
+
return true;
|
|
1464
|
+
},
|
|
1465
|
+
(error) => {
|
|
1466
|
+
return this.sendFailure(error);
|
|
1467
|
+
}
|
|
1468
|
+
);
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
private sendFailure(error: unknown): false {
|
|
1472
|
+
if (isZLinkBackendResultError(error) && error.operation === 'submit') {
|
|
1473
|
+
const terminal = submitToRequestResult(error.result, error.phase);
|
|
1474
|
+
if (terminal === RequestResult.NotConnected || terminal === RequestResult.NotFound)
|
|
1475
|
+
return false;
|
|
1271
1476
|
}
|
|
1477
|
+
throw error;
|
|
1272
1478
|
}
|
|
1273
1479
|
}
|
|
1274
1480
|
|
|
@@ -1309,14 +1515,6 @@ function isAlreadyDisconnectedError(error: unknown): boolean {
|
|
|
1309
1515
|
return (error as { readonly nativeErrno?: unknown }).nativeErrno === nativeErrnoValues.ENOENT;
|
|
1310
1516
|
}
|
|
1311
1517
|
|
|
1312
|
-
/**
|
|
1313
|
-
* An admission belongs to one Core selected route. Its connection identity is
|
|
1314
|
-
* that route's generation, an opaque token compared only for equality.
|
|
1315
|
-
*/
|
|
1316
|
-
function routeConnectionId(routeGeneration: bigint): string {
|
|
1317
|
-
return `route:${routeGeneration.toString()}`;
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
1518
|
function describeInvalidAdmissionDescriptor(
|
|
1321
1519
|
descriptor: ServiceNodeDescriptor,
|
|
1322
1520
|
expected: ServicePeerAdmissionExpectation | undefined,
|