@zlink-systems/framework 0.22.0 → 0.23.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/Dispatch/ZLinkDispatchOptions.d.ts +1 -0
- package/dist/contracts/Streams/IZLinkSession.d.ts +2 -1
- package/dist/runtime/actors/actor-context.js +4 -4
- package/dist/runtime/actors/actor-join-deferred-scope.d.ts +0 -1
- package/dist/runtime/actors/actor-join-deferred-scope.js +0 -18
- package/dist/runtime/actors/deferred-join-accepted-journal.js +2 -6
- package/dist/runtime/backend/contracts/index.d.ts +1 -0
- package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +3 -0
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +21 -8
- package/dist/runtime/channels/channel-dispatch-pipeline.js +2 -2
- package/dist/runtime/channels/channel-transports.js +3 -9
- package/dist/runtime/channels/dispatch-error-reporter.d.ts +1 -0
- package/dist/runtime/channels/dispatch-error-reporter.js +6 -38
- package/dist/runtime/diagnostics/dispatch-error-details.d.ts +6 -0
- package/dist/runtime/diagnostics/dispatch-error-details.js +55 -0
- package/dist/runtime/diagnostics/flow-context.d.ts +2 -1
- package/dist/runtime/diagnostics/flow-context.js +2 -2
- package/dist/runtime/diagnostics/message-flow.js +11 -3
- package/dist/runtime/foundation/actor-join-recovery-codec.js +0 -4
- package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +4 -2
- package/dist/runtime/foundation/raw-service-mesh-runtime.js +34 -9
- package/dist/runtime/foundation/service-session-binding-ingress-port.d.ts +1 -0
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +4 -0
- package/dist/runtime/foundation/service-stateful-runtime.js +46 -2
- package/dist/runtime/foundation/service-topology-registry.d.ts +1 -0
- package/dist/runtime/foundation/service-topology-registry.js +12 -4
- package/dist/runtime/host/index.js +1 -1
- package/dist/runtime/protocol/service_wire_pilot_codec.generated.js +20 -18
- package/dist/runtime/spots/index.js +1 -1
- package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +2 -1
- package/dist/runtime/spots/spot-actor-packet-dispatch.js +6 -1
- package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
- package/dist/runtime/spots/spot-node-runtime-manager.js +5 -1
- package/dist/runtime/spots/spot-route-packet-dispatch.js +1 -1
- package/dist/runtime/spots/spot-routed-bound-session-dispatch.js +1 -1
- package/dist/runtime/spots/spot-subscription-dispatch.js +1 -1
- package/dist/runtime/spots/spot-timer.js +1 -1
- package/dist/runtime/streams/actor-session-binding-registry.d.ts +21 -8
- package/dist/runtime/streams/actor-session-binding-registry.js +71 -42
- package/dist/runtime/streams/actor-session-binding-runtime-owner.d.ts +1 -0
- package/dist/runtime/streams/bound-actor-relay-sender.js +2 -1
- package/dist/runtime/streams/bound-session-response-target.js +8 -1
- package/dist/runtime/streams/bound-session-service.d.ts +1 -0
- package/dist/runtime/streams/bound-session-service.js +6 -3
- package/dist/runtime/streams/index.js +8 -1
- package/dist/runtime/streams/managed-stream.d.ts +3 -0
- package/dist/runtime/streams/managed-stream.js +20 -1
- package/dist/runtime/streams/protocol.d.ts +5 -1
- package/dist/runtime/streams/protocol.js +41 -4
- package/dist/runtime/streams/session-actor-coordinator.js +3 -4
- package/dist/runtime/streams/session-context.d.ts +9 -2
- package/dist/runtime/streams/session-context.js +20 -4
- package/dist/runtime/streams/session-local-actors.d.ts +3 -1
- package/dist/runtime/streams/session-local-actors.js +17 -2
- package/dist/runtime/streams/stream-frame-factory.d.ts +1 -1
- package/dist/runtime/streams/stream-frame-factory.js +3 -2
- package/dist/runtime/streams/stream-session-runtime.js +42 -12
- package/package.json +3 -3
- package/src/contracts/Dispatch/ZLinkDispatchOptions.ts +1 -0
- package/src/contracts/Streams/IZLinkSession.ts +2 -1
- package/src/runtime/actors/actor-context.ts +4 -4
- package/src/runtime/actors/actor-join-deferred-scope.ts +0 -32
- package/src/runtime/actors/deferred-join-accepted-journal.ts +2 -6
- package/src/runtime/backend/contracts/index.ts +4 -0
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +39 -11
- package/src/runtime/channels/channel-dispatch-pipeline.ts +4 -2
- package/src/runtime/channels/channel-transports.ts +3 -9
- package/src/runtime/channels/dispatch-error-reporter.ts +7 -45
- package/src/runtime/diagnostics/dispatch-error-details.ts +62 -0
- package/src/runtime/diagnostics/flow-context.ts +6 -4
- package/src/runtime/diagnostics/message-flow.ts +12 -3
- package/src/runtime/foundation/actor-join-recovery-codec.ts +0 -4
- package/src/runtime/foundation/raw-service-mesh-runtime.ts +54 -13
- package/src/runtime/foundation/service-session-binding-ingress-port.ts +1 -0
- package/src/runtime/foundation/service-stateful-runtime.ts +64 -5
- package/src/runtime/foundation/service-topology-registry.ts +18 -5
- package/src/runtime/host/index.ts +1 -1
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +18 -17
- package/src/runtime/spots/index.ts +2 -1
- package/src/runtime/spots/spot-actor-packet-dispatch.ts +20 -3
- package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
- package/src/runtime/spots/spot-node-runtime-manager.ts +5 -1
- package/src/runtime/spots/spot-route-packet-dispatch.ts +2 -1
- package/src/runtime/spots/spot-routed-bound-session-dispatch.ts +2 -1
- package/src/runtime/spots/spot-subscription-dispatch.ts +2 -1
- package/src/runtime/spots/spot-timer.ts +6 -1
- package/src/runtime/streams/actor-session-binding-registry.ts +150 -63
- package/src/runtime/streams/actor-session-binding-runtime-owner.ts +1 -0
- package/src/runtime/streams/bound-actor-relay-sender.ts +10 -2
- package/src/runtime/streams/bound-session-response-target.ts +10 -1
- package/src/runtime/streams/bound-session-service.ts +13 -3
- package/src/runtime/streams/index.ts +8 -1
- package/src/runtime/streams/managed-stream.ts +34 -12
- package/src/runtime/streams/protocol.ts +44 -5
- package/src/runtime/streams/session-actor-coordinator.ts +12 -4
- package/src/runtime/streams/session-context.ts +31 -4
- package/src/runtime/streams/session-local-actors.ts +19 -3
- package/src/runtime/streams/stream-frame-factory.ts +4 -2
- package/src/runtime/streams/stream-session-runtime.ts +58 -13
|
@@ -5,6 +5,7 @@ import type { ZLinkFlowOrigin } from '../../contracts';
|
|
|
5
5
|
export interface ZLinkFlowContextValue {
|
|
6
6
|
readonly flowId: string;
|
|
7
7
|
readonly flowOrigin: ZLinkFlowOrigin;
|
|
8
|
+
readonly streamSessionId?: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
const flowStorage = new AsyncLocalStorage<ZLinkFlowContextValue | undefined>();
|
|
@@ -71,14 +72,15 @@ export function runWithOutboundFlow<T>(enabled: boolean, callback: () => T): T {
|
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
export function createInboundFlow(
|
|
74
|
-
flowId
|
|
75
|
-
flowOrigin
|
|
76
|
-
createIfAbsent
|
|
75
|
+
flowId: string | undefined,
|
|
76
|
+
flowOrigin: ZLinkFlowOrigin | undefined,
|
|
77
|
+
createIfAbsent: boolean,
|
|
78
|
+
streamSessionId: string | undefined
|
|
77
79
|
): ZLinkFlowContextValue | undefined {
|
|
78
80
|
if (!createIfAbsent) {
|
|
79
81
|
return undefined;
|
|
80
82
|
}
|
|
81
|
-
return { flowId: flowId ?? createFlowId(), flowOrigin: flowOrigin ?? 'Inbound' };
|
|
83
|
+
return { flowId: flowId ?? createFlowId(), flowOrigin: flowOrigin ?? 'Inbound', streamSessionId };
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
function createFlowId(): string {
|
|
@@ -34,6 +34,7 @@ export const DEFAULT_ZLINK_DIAGNOSTICS: ZLinkDiagnosticsOptions = {
|
|
|
34
34
|
|
|
35
35
|
const telemetryLogger = openTelemetryLogs.getLogger('@zlink-systems/framework');
|
|
36
36
|
const telemetryTracer = openTelemetryTrace.getTracer('@zlink-systems/framework');
|
|
37
|
+
const ERROR_MESSAGE_MAX_LENGTH = 512;
|
|
37
38
|
let diagnosticsGeneration = 0n;
|
|
38
39
|
|
|
39
40
|
interface ZLinkTelemetryRecord {
|
|
@@ -68,6 +69,7 @@ interface ZLinkTelemetryRecord {
|
|
|
68
69
|
readonly instanceSpotType?: string;
|
|
69
70
|
readonly activationState?: string;
|
|
70
71
|
readonly actorId?: string;
|
|
72
|
+
readonly streamSessionId?: string;
|
|
71
73
|
readonly commandId?: number;
|
|
72
74
|
readonly messageSizeBytes?: number;
|
|
73
75
|
readonly durationSeconds?: number;
|
|
@@ -226,12 +228,13 @@ export class ZLinkMessageFlowTracer {
|
|
|
226
228
|
acceptedFlowId?: string,
|
|
227
229
|
acceptedFlowOrigin?: ZLinkFlowOrigin
|
|
228
230
|
): void {
|
|
229
|
-
const ambient =
|
|
231
|
+
const ambient = currentFlowContext();
|
|
230
232
|
const flowId = flowInput.flowId ?? acceptedFlowId ?? ambient?.flowId;
|
|
231
233
|
const flowOrigin = flowInput.flowOrigin ?? acceptedFlowOrigin ?? ambient?.flowOrigin;
|
|
232
234
|
const flow: ZLinkRuntimeMessageFlowEvent = {
|
|
233
235
|
...flowInput,
|
|
234
236
|
...(flowId === undefined ? {} : { flowId, flowOrigin }),
|
|
237
|
+
streamSessionId: flowInput.streamSessionId ?? ambient?.streamSessionId,
|
|
235
238
|
effectiveMode
|
|
236
239
|
};
|
|
237
240
|
this.tracedEvents += 1;
|
|
@@ -380,11 +383,12 @@ function toTelemetryRecord(
|
|
|
380
383
|
instanceSpotType: flow.instanceSpotType,
|
|
381
384
|
activationState: flow.activationState,
|
|
382
385
|
actorId: flow.actorId,
|
|
386
|
+
streamSessionId: flow.streamSessionId,
|
|
383
387
|
commandId: flow.commandId,
|
|
384
388
|
messageSizeBytes,
|
|
385
389
|
durationSeconds,
|
|
386
390
|
errorType: flow.errorType,
|
|
387
|
-
errorMessage:
|
|
391
|
+
errorMessage: flow.errorMessage,
|
|
388
392
|
errorCauseType: flow.errorCauseType,
|
|
389
393
|
errorCauseMessage: boundedText(flow.errorCauseMessage)
|
|
390
394
|
};
|
|
@@ -421,6 +425,7 @@ function structuredLogAttributes(record: ZLinkTelemetryRecord): Attributes {
|
|
|
421
425
|
instance_spot_type: record.instanceSpotType,
|
|
422
426
|
activation_state: record.activationState,
|
|
423
427
|
actor_id: record.actorId,
|
|
428
|
+
stream_session_id: record.streamSessionId,
|
|
424
429
|
correlation_id: record.correlationId,
|
|
425
430
|
flow_id: record.flowId,
|
|
426
431
|
flow_origin: record.flowOrigin,
|
|
@@ -456,11 +461,15 @@ function structuredLogBodyAttributes(record: ZLinkTelemetryRecord): Attributes {
|
|
|
456
461
|
instance_type: record.instanceSpotType,
|
|
457
462
|
activation_state: record.activationState,
|
|
458
463
|
actor: record.actorId,
|
|
464
|
+
session: record.streamSessionId,
|
|
459
465
|
corr: record.correlationId,
|
|
460
466
|
flow: record.flowId,
|
|
461
467
|
origin: record.flowOrigin,
|
|
462
468
|
outcome: record.outcome,
|
|
463
469
|
reason: record.reason,
|
|
470
|
+
action: record.action,
|
|
471
|
+
error_type: record.errorType,
|
|
472
|
+
error_message: record.errorMessage,
|
|
464
473
|
size: record.messageSizeBytes
|
|
465
474
|
});
|
|
466
475
|
}
|
|
@@ -571,7 +580,7 @@ function hashFlowId(flowId: string): number {
|
|
|
571
580
|
}
|
|
572
581
|
|
|
573
582
|
function boundedText(value: string | undefined): string | undefined {
|
|
574
|
-
return value === undefined ? undefined : value.slice(0,
|
|
583
|
+
return value === undefined ? undefined : value.slice(0, ERROR_MESSAGE_MAX_LENGTH);
|
|
575
584
|
}
|
|
576
585
|
|
|
577
586
|
function toSnakeCase(value: string): string {
|
|
@@ -10,7 +10,6 @@ import { decodeServiceWireFrozenRecord } from './service-stateful-wire-codec';
|
|
|
10
10
|
const RECOVERY_PACKET_NAME = '__zlink.actor.routed_join.recovery';
|
|
11
11
|
const RECOVERY_CONTENT_TYPE = 'application/x-zlink-actor-routed-join-recovery-v1';
|
|
12
12
|
const MAXIMUM_METADATA_BYTES = 256 * 1024;
|
|
13
|
-
const MAXIMUM_MESSAGE_BYTES = 1024 * 1024;
|
|
14
13
|
const FRAMEWORK_METADATA_UPPER_BOUND_BYTES = 64 * 1024;
|
|
15
14
|
const ACCEPTED_JOURNAL_UPPER_BOUND_BYTES = 16 * 1024 * 1024;
|
|
16
15
|
const SNAPSHOT_APPLICATION_STATE_RESERVATION_BYTES = 64 * 1024 * 1024;
|
|
@@ -124,9 +123,6 @@ export function encodeCanonicalActorJoinRecoverySavedWork(
|
|
|
124
123
|
const operationId = input.operationId;
|
|
125
124
|
const reply = operationId === undefined ? Buffer.alloc(0) : Buffer.from(input.reply ?? []);
|
|
126
125
|
const request = Buffer.from(input.request);
|
|
127
|
-
if (request.byteLength > MAXIMUM_MESSAGE_BYTES || reply.byteLength > MAXIMUM_MESSAGE_BYTES) {
|
|
128
|
-
throw new RangeError('Actor Join recovery message exceeds 1 MiB.');
|
|
129
|
-
}
|
|
130
126
|
const replyContentType =
|
|
131
127
|
operationId === undefined
|
|
132
128
|
? null
|
|
@@ -7,7 +7,7 @@ import type {
|
|
|
7
7
|
ZLinkRawReceivedRecord,
|
|
8
8
|
ZLinkRawRouterPort
|
|
9
9
|
} from '../backend/raw-binding-port';
|
|
10
|
-
import { RequestResult } from '../backend/runtime-values';
|
|
10
|
+
import { RequestResult, SubmitResult } from '../backend/runtime-values';
|
|
11
11
|
import type {
|
|
12
12
|
ApplicationJobPermitPort,
|
|
13
13
|
ApplicationJobQueuePort
|
|
@@ -63,6 +63,19 @@ export interface RawServiceRequestResult {
|
|
|
63
63
|
readonly payload?: ServiceApplicationPayload;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
type RawServiceChannelTargetSelection =
|
|
67
|
+
| { readonly kind: 'selected'; readonly peer: AdmittedServicePeer }
|
|
68
|
+
| {
|
|
69
|
+
readonly kind: 'noMember';
|
|
70
|
+
readonly submitResult: typeof SubmitResult.NotFound;
|
|
71
|
+
readonly requestResult: RawServiceRequestResult;
|
|
72
|
+
}
|
|
73
|
+
| {
|
|
74
|
+
readonly kind: 'knownButNotReady';
|
|
75
|
+
readonly submitResult: typeof SubmitResult.NotConnected;
|
|
76
|
+
readonly requestResult: RawServiceRequestResult;
|
|
77
|
+
};
|
|
78
|
+
|
|
66
79
|
/** An M6A application frame already owned by the Framework runtime. */
|
|
67
80
|
type ServiceApplicationPayloadInput = ServiceApplicationPayload | Buffer;
|
|
68
81
|
|
|
@@ -463,11 +476,10 @@ export class RawServiceMeshRuntime {
|
|
|
463
476
|
async sendToChannel(
|
|
464
477
|
channelName: string,
|
|
465
478
|
payload: ServiceApplicationPayloadInput
|
|
466
|
-
): Promise<
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
if (selected === undefined) return false;
|
|
479
|
+
): Promise<SubmitResult> {
|
|
480
|
+
const selection = this.selectChannelTarget(channelName);
|
|
481
|
+
if (selection.kind !== 'selected') return selection.submitResult;
|
|
482
|
+
const selected = selection.peer;
|
|
471
483
|
const applicationFrame = this.applicationFrame(payload);
|
|
472
484
|
if (selected.descriptor.nodeRoutingId === this.descriptor.nodeRoutingId) {
|
|
473
485
|
const applicationJobOwner = await this.reserveLocalIngress();
|
|
@@ -481,15 +493,17 @@ export class RawServiceMeshRuntime {
|
|
|
481
493
|
applicationJob
|
|
482
494
|
});
|
|
483
495
|
if (!accepted) applicationJob.close();
|
|
484
|
-
return accepted;
|
|
496
|
+
return accepted ? SubmitResult.Ok : SubmitResult.NotAdmitted;
|
|
485
497
|
} finally {
|
|
486
498
|
applicationJobOwner.close();
|
|
487
499
|
}
|
|
488
500
|
}
|
|
489
|
-
return this.send(selected.descriptor.nodeRoutingId, [
|
|
501
|
+
return (await this.send(selected.descriptor.nodeRoutingId, [
|
|
490
502
|
encodeChannelSendHeader(channelName),
|
|
491
503
|
applicationFrame
|
|
492
|
-
])
|
|
504
|
+
]))
|
|
505
|
+
? SubmitResult.Ok
|
|
506
|
+
: SubmitResult.NotConnected;
|
|
493
507
|
}
|
|
494
508
|
|
|
495
509
|
requestToNode(
|
|
@@ -504,13 +518,40 @@ export class RawServiceMeshRuntime {
|
|
|
504
518
|
channelName: string,
|
|
505
519
|
payload: ServiceApplicationPayloadInput,
|
|
506
520
|
timeoutMs: number
|
|
507
|
-
): PendingOperation<RawServiceRequestResult>
|
|
521
|
+
): PendingOperation<RawServiceRequestResult> {
|
|
522
|
+
const selection = this.selectChannelTarget(channelName);
|
|
523
|
+
if (selection.kind !== 'selected') {
|
|
524
|
+
const pending = this.operations.register(timeoutMs);
|
|
525
|
+
this.operations.complete(pending.id, selection.requestResult);
|
|
526
|
+
return pending;
|
|
527
|
+
}
|
|
528
|
+
return this.requestToTarget(
|
|
529
|
+
selection.peer.descriptor.nodeRoutingId,
|
|
530
|
+
payload,
|
|
531
|
+
timeoutMs,
|
|
532
|
+
channelName
|
|
533
|
+
);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
private selectChannelTarget(channelName: string): RawServiceChannelTargetSelection {
|
|
508
537
|
const selected = this.topology.selectChannel(channelName, (peer) =>
|
|
509
538
|
this.isLocalOrReadyPeer(peer.descriptor.nodeRoutingId)
|
|
510
539
|
);
|
|
511
|
-
return selected
|
|
512
|
-
|
|
513
|
-
|
|
540
|
+
if (selected !== undefined) return { kind: 'selected', peer: selected };
|
|
541
|
+
return this.topology.hasKnownChannelTarget(channelName)
|
|
542
|
+
? {
|
|
543
|
+
kind: 'knownButNotReady',
|
|
544
|
+
submitResult: SubmitResult.NotConnected,
|
|
545
|
+
requestResult: { terminalResult: RequestResult.NotConnected, failureCode: 0 }
|
|
546
|
+
}
|
|
547
|
+
: {
|
|
548
|
+
kind: 'noMember',
|
|
549
|
+
submitResult: SubmitResult.NotFound,
|
|
550
|
+
requestResult: {
|
|
551
|
+
terminalResult: RequestResult.NotFound,
|
|
552
|
+
failureCode: REQUEST_TARGET_NOT_FOUND_FAILURE_CODE
|
|
553
|
+
}
|
|
554
|
+
};
|
|
514
555
|
}
|
|
515
556
|
|
|
516
557
|
private isLocalOrReadyPeer(nodeRoutingId: string): boolean {
|
|
@@ -19,6 +19,7 @@ export interface ServiceSessionBindingAdmissionClaim {
|
|
|
19
19
|
export type ServiceSessionBindingAdmissionResult = 'passThrough' | 'retained' | 'rejected';
|
|
20
20
|
|
|
21
21
|
export interface ServiceSessionBindingIngressPort {
|
|
22
|
+
actorSlot(actorId: string, sessionRid: string): Promise<number | undefined>;
|
|
22
23
|
retainOutbound(
|
|
23
24
|
claim: ServiceSessionBindingAdmissionClaim,
|
|
24
25
|
delivery: ServiceSessionBindingRetainedDelivery
|
|
@@ -9,6 +9,12 @@ import {
|
|
|
9
9
|
translateWireReplyDecodeError
|
|
10
10
|
} from '../framework-errors-internal';
|
|
11
11
|
import { RequestResult, SubmitResult, isZLinkBackendResultError } from '../backend/runtime-values';
|
|
12
|
+
import type { ZLinkDispatchErrorReporter } from '../channels/dispatch-error-reporter';
|
|
13
|
+
import {
|
|
14
|
+
ZLinkRuntimeDispatchErrorAction as ZLinkDispatchErrorAction,
|
|
15
|
+
ZLinkDispatchErrorSurface,
|
|
16
|
+
ZLinkDispatchMessageKind
|
|
17
|
+
} from '../../contracts/Dispatch/ZLinkDispatchOptions';
|
|
12
18
|
import type {
|
|
13
19
|
RawServiceIngressRecord,
|
|
14
20
|
RawServiceMeshRuntime,
|
|
@@ -401,6 +407,8 @@ export class ServiceStatefulRuntime {
|
|
|
401
407
|
readonly kind: 'spot_multicast' | 'actor_control' | 'actor_binding';
|
|
402
408
|
readonly owner: string;
|
|
403
409
|
}) => void;
|
|
410
|
+
private dispatchErrors?: ZLinkDispatchErrorReporter;
|
|
411
|
+
private dispatchErrorMeshName?: string;
|
|
404
412
|
private readonly admittedUserSpotOperations = new Map<
|
|
405
413
|
string,
|
|
406
414
|
{
|
|
@@ -438,6 +446,11 @@ export class ServiceStatefulRuntime {
|
|
|
438
446
|
this.mailboxDropHandler = handler;
|
|
439
447
|
}
|
|
440
448
|
|
|
449
|
+
setDispatchErrorReporter(reporter: ZLinkDispatchErrorReporter, meshName: string): void {
|
|
450
|
+
this.dispatchErrors = reporter;
|
|
451
|
+
this.dispatchErrorMeshName = meshName;
|
|
452
|
+
}
|
|
453
|
+
|
|
441
454
|
createSpot(
|
|
442
455
|
routingId?: string,
|
|
443
456
|
kind: 'user' | 'instance' = 'user',
|
|
@@ -1229,17 +1242,41 @@ export class ServiceStatefulRuntime {
|
|
|
1229
1242
|
await this.enqueueLogicalMulticast(channelName, topic, sourceSpotId, payload);
|
|
1230
1243
|
const targets = this.raw.topology
|
|
1231
1244
|
.peers()
|
|
1232
|
-
.filter(
|
|
1233
|
-
peer
|
|
1234
|
-
|
|
1235
|
-
|
|
1245
|
+
.filter(
|
|
1246
|
+
(peer) =>
|
|
1247
|
+
peer.descriptor.channels.some(
|
|
1248
|
+
(channel) => channel.name === channelName && channel.weight > 0
|
|
1249
|
+
) && this.raw.isPeerRouteReady(peer.descriptor.nodeRoutingId)
|
|
1236
1250
|
);
|
|
1237
1251
|
const header = encodeLogicalMulticastHeader(channelName, topic, sourceSpotId);
|
|
1238
1252
|
const payloadFrame = encodeApplicationPayload(payload);
|
|
1253
|
+
const reporter = this.dispatchErrors;
|
|
1254
|
+
const captureFailures = reporter?.captureEnabled() === true;
|
|
1239
1255
|
for (const target of targets) {
|
|
1240
1256
|
// Remote admission ends at the source outbound transport queue. The
|
|
1241
1257
|
// receiver's Spot queue and handler completion are not publish results.
|
|
1242
|
-
await this.raw.sendService(target.descriptor.nodeRoutingId, [
|
|
1258
|
+
const accepted = await this.raw.sendService(target.descriptor.nodeRoutingId, [
|
|
1259
|
+
header,
|
|
1260
|
+
payloadFrame
|
|
1261
|
+
]);
|
|
1262
|
+
if (!accepted) {
|
|
1263
|
+
if (captureFailures) {
|
|
1264
|
+
reporter.report({
|
|
1265
|
+
surface: ZLinkDispatchErrorSurface.SpotRoute,
|
|
1266
|
+
messageKind: ZLinkDispatchMessageKind.Send,
|
|
1267
|
+
reason: serviceSpotPublishFailureReason(
|
|
1268
|
+
undefined,
|
|
1269
|
+
this.raw.isPeerRouteReady(target.descriptor.nodeRoutingId),
|
|
1270
|
+
this.closed
|
|
1271
|
+
),
|
|
1272
|
+
action: ZLinkDispatchErrorAction.Drop,
|
|
1273
|
+
meshName: this.dispatchErrorMeshName,
|
|
1274
|
+
channelName,
|
|
1275
|
+
topic,
|
|
1276
|
+
targetRid: target.descriptor.nodeRoutingId
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1243
1280
|
}
|
|
1244
1281
|
}
|
|
1245
1282
|
|
|
@@ -5251,6 +5288,28 @@ export class ServiceStatefulRuntime {
|
|
|
5251
5288
|
}
|
|
5252
5289
|
}
|
|
5253
5290
|
|
|
5291
|
+
function serviceSpotPublishFailureReason(
|
|
5292
|
+
result: number | undefined,
|
|
5293
|
+
routeReady: boolean,
|
|
5294
|
+
closed: boolean
|
|
5295
|
+
): 'stale_target' | 'backpressure' | 'shutdown' {
|
|
5296
|
+
if (closed || result === SubmitResult.Terminated) return 'shutdown';
|
|
5297
|
+
switch (result) {
|
|
5298
|
+
case SubmitResult.Backpressured:
|
|
5299
|
+
return 'backpressure';
|
|
5300
|
+
case SubmitResult.NotAdmitted:
|
|
5301
|
+
case SubmitResult.NotConnected:
|
|
5302
|
+
case SubmitResult.NotFound:
|
|
5303
|
+
case SubmitResult.InvalidHandle:
|
|
5304
|
+
return 'stale_target';
|
|
5305
|
+
default:
|
|
5306
|
+
// RawServiceMeshRuntime exposes failed Core submissions as false. A
|
|
5307
|
+
// ready route therefore identifies queue admission failure; a route
|
|
5308
|
+
// that became unready after selection is a stale target.
|
|
5309
|
+
return routeReady ? 'backpressure' : 'stale_target';
|
|
5310
|
+
}
|
|
5311
|
+
}
|
|
5312
|
+
|
|
5254
5313
|
function isEntrySpotFence(fence: ServiceDirectSpotRouteFence): boolean {
|
|
5255
5314
|
return (
|
|
5256
5315
|
fence.spot.spotId === fence.targetNodeRid &&
|
|
@@ -257,6 +257,13 @@ export class ServiceTopologyRegistry {
|
|
|
257
257
|
return descriptor === undefined ? undefined : cloneDescriptor(descriptor);
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
+
hasKnownChannelTarget(channelName: string): boolean {
|
|
261
|
+
requireText(channelName, 'channelName');
|
|
262
|
+
return [this.local, ...this.knownByRid.values()].some(
|
|
263
|
+
(descriptor) => selectableChannel(descriptor, channelName) !== undefined
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
260
267
|
private remember(descriptor: ServiceNodeDescriptor): void {
|
|
261
268
|
const current = this.knownByRid.get(descriptor.nodeRoutingId);
|
|
262
269
|
if (
|
|
@@ -285,7 +292,7 @@ export class ServiceTopologyRegistry {
|
|
|
285
292
|
return [local, ...this.peersByRid.values()]
|
|
286
293
|
.map((peer) => ({
|
|
287
294
|
peer: clonePeer(peer),
|
|
288
|
-
channel:
|
|
295
|
+
channel: selectableChannel(peer.descriptor, channelName)
|
|
289
296
|
}))
|
|
290
297
|
.filter(
|
|
291
298
|
(
|
|
@@ -293,10 +300,7 @@ export class ServiceTopologyRegistry {
|
|
|
293
300
|
): value is {
|
|
294
301
|
peer: AdmittedServicePeer;
|
|
295
302
|
channel: ServiceChannelDescriptor;
|
|
296
|
-
} =>
|
|
297
|
-
value.channel !== undefined &&
|
|
298
|
-
value.peer.descriptor.state === 'serving' &&
|
|
299
|
-
value.channel.weight > 0
|
|
303
|
+
} => value.channel !== undefined
|
|
300
304
|
);
|
|
301
305
|
},
|
|
302
306
|
(value) => value.channel.weight,
|
|
@@ -519,6 +523,15 @@ function findChannel(
|
|
|
519
523
|
return descriptor.channels.find((channel) => channel.name === channelName);
|
|
520
524
|
}
|
|
521
525
|
|
|
526
|
+
function selectableChannel(
|
|
527
|
+
descriptor: ServiceNodeDescriptor,
|
|
528
|
+
channelName: string
|
|
529
|
+
): ServiceChannelDescriptor | undefined {
|
|
530
|
+
if (descriptor.state !== 'serving') return undefined;
|
|
531
|
+
const channel = findChannel(descriptor, channelName);
|
|
532
|
+
return channel !== undefined && channel.weight > 0 ? channel : undefined;
|
|
533
|
+
}
|
|
534
|
+
|
|
522
535
|
function requireText(value: string, field: string): void {
|
|
523
536
|
if (value.length === 0 || value.includes('\0')) {
|
|
524
537
|
throw new TypeError(`${field} must be non-empty text without NUL.`);
|
|
@@ -3047,7 +3047,7 @@ export class ZLinkFrameworkRuntimeHost
|
|
|
3047
3047
|
const flow =
|
|
3048
3048
|
current?.flowOrigin === 'Lifecycle'
|
|
3049
3049
|
? current
|
|
3050
|
-
: createInboundFlow(undefined, 'Lifecycle', this.flowCreationEnabled());
|
|
3050
|
+
: createInboundFlow(undefined, 'Lifecycle', this.flowCreationEnabled(), undefined);
|
|
3051
3051
|
return runWithFlow(flow, operation);
|
|
3052
3052
|
}
|
|
3053
3053
|
|
|
@@ -6,14 +6,15 @@ export interface ServiceWireRouteFence { readonly id: string; readonly generatio
|
|
|
6
6
|
function u64(out: number[], value: bigint): void { if (value < 0n || value > 0xffffffffffffffffn) throw new RangeError("u64"); for (let i=7;i>=0;i--) out.push(Number((value >> BigInt(i*8)) & 255n)); }
|
|
7
7
|
function read64(bytes: Uint8Array, at: { value: number }): bigint { if (at.value + 8 > bytes.length) throw new RangeError("truncated u64"); let r=0n; for(let i=0;i<8;i++) r=(r<<8n)|BigInt(bytes[at.value++]); return r; }
|
|
8
8
|
function u32(out: number[], value: number): void { if(!Number.isInteger(value)||value<0||value>0xffffffff)throw new RangeError("u32");out.push(value>>>24,(value>>>16)&255,(value>>>8)&255,value&255); }
|
|
9
|
+
function appendBytes(out: number[], bytes: Uint8Array | readonly number[]): void { for (const byte of bytes) out.push(byte); }
|
|
9
10
|
function read32(bytes: Uint8Array, at: { value: number }): number { if(at.value+4>bytes.length)throw new RangeError("truncated u32");return ((bytes[at.value++]<<24)|(bytes[at.value++]<<16)|(bytes[at.value++]<<8)|bytes[at.value++])>>>0; }
|
|
10
|
-
function text8(out: number[], value: string): void { const b=text.encode(value); if(b.length===0 || b.length>255 || b.includes(0)) throw new RangeError("text8"); out.push(b.length
|
|
11
|
+
function text8(out: number[], value: string): void { const b=text.encode(value); if(b.length===0 || b.length>255 || b.includes(0)) throw new RangeError("text8"); out.push(b.length); appendBytes(out,b); }
|
|
11
12
|
function readText8(bytes: Uint8Array, at: { value: number }): string { const n=bytes[at.value++]; if(n===0 || at.value+n>bytes.length) throw new RangeError("truncated text8"); const b=bytes.slice(at.value,at.value+n); at.value+=n; if(b.includes(0))throw new RangeError("text8 NUL"); return decodeText.decode(b); }
|
|
12
|
-
function rid(out: number[], value: Uint8Array): void { if(value.length===0 || value.length>255) throw new RangeError("rid"); out.push(value.length
|
|
13
|
+
function rid(out: number[], value: Uint8Array): void { if(value.length===0 || value.length>255) throw new RangeError("rid"); out.push(value.length); appendBytes(out,value); }
|
|
13
14
|
function readRid(bytes: Uint8Array, at: { value: number }): Uint8Array { const n=bytes[at.value++]; if(n===0 || at.value+n>bytes.length) throw new RangeError("truncated rid"); const r=bytes.slice(at.value,at.value+n); at.value+=n; return r; }
|
|
14
15
|
function fence(out: number[], value: ServiceWireRouteFence): void { if(value.generation===0n||value.targetNodeGeneration===0n||value.expectedAuthorityOwnerGeneration===0n||value.expectedOwnerLeaseGeneration===0n)throw new RangeError("invalid fence");text8(out,value.id); u64(out,value.generation); rid(out,value.targetNodeRid); u64(out,value.targetNodeGeneration); u64(out,value.expectedAuthorityOwnerGeneration); u64(out,value.expectedOwnerLeaseGeneration); }
|
|
15
16
|
function readFence(bytes: Uint8Array, at: { value: number }): ServiceWireRouteFence { const value={ id:readText8(bytes,at), generation:read64(bytes,at), targetNodeRid:readRid(bytes,at), targetNodeGeneration:read64(bytes,at), expectedAuthorityOwnerGeneration:read64(bytes,at), expectedOwnerLeaseGeneration:read64(bytes,at) };if(value.generation===0n||value.targetNodeGeneration===0n||value.expectedAuthorityOwnerGeneration===0n||value.expectedOwnerLeaseGeneration===0n)throw new RangeError("invalid fence");return value; }
|
|
16
|
-
function encodePayload(value: ApplicationPayloadEnvelopeV1): Uint8Array { const body:number[]=[];text8(body,value.packetName);text8(body,value.contentType);u32(body,value.payload.length);body
|
|
17
|
+
function encodePayload(value: ApplicationPayloadEnvelopeV1): Uint8Array { const body:number[]=[];text8(body,value.packetName);text8(body,value.contentType);u32(body,value.payload.length);appendBytes(body,value.payload);const out=[1];u32(out,body.length);appendBytes(out,body);return Uint8Array.from(out); }
|
|
17
18
|
function decodePayload(bytes: Uint8Array): ApplicationPayloadEnvelopeV1 { const at={value:0};if(bytes[at.value++]!==1)throw new RangeError("payload version");const length=read32(bytes,at);if(length!==bytes.length-at.value)throw new RangeError("payload length");const end=bytes.length;const packetName=readText8(bytes,at);const contentType=readText8(bytes,at);const length2=read32(bytes,at);if(length2!==end-at.value)throw new RangeError("payload bytes");const payload=bytes.slice(at.value);at.value=end;if(at.value!==end)throw new RangeError("payload trailing");return {packetName,contentType,payload}; }
|
|
18
19
|
export function encodeActorJoin28(value: ActorJoin28): readonly Uint8Array[] { const out=[90,77,1,28,0]; if(value.correlation===0n) throw new RangeError("correlation"); u64(out,value.correlation); fence(out,value.actor); out.push(value.entry?1:0); fence(out,value.targetSpot); return value.payload===undefined?[Uint8Array.from(out)]:[Uint8Array.from(out),encodePayload(value.payload)]; }
|
|
19
20
|
export function decodeActorJoin28(frames: readonly Uint8Array[]): ActorJoin28 { if(frames.length<1||frames.length>2)throw new RangeError("actorJoin frames");const bytes=frames[0]!; const at={value:0}; if(bytes.length<5 || bytes[at.value++]!==90 || bytes[at.value++]!==77 || bytes[at.value++]!==1 || bytes[at.value++]!==28 || bytes[at.value++]!==0) throw new RangeError("actorJoin header"); const correlation=read64(bytes,at); const actor=readFence(bytes,at); const entry=bytes[at.value++]; const targetSpot=readFence(bytes,at); if(correlation===0n || entry>1 || at.value!==bytes.length) throw new RangeError("actorJoin body"); const payload=frames.length===2?decodePayload(frames[1]!):undefined; return payload===undefined?{ correlation, actor, entry:entry===1, targetSpot }:{ correlation, actor, entry:entry===1, targetSpot,payload }; }
|
|
@@ -22,16 +23,16 @@ export interface RelocationEnvelopeV1 { readonly relocationHigh: bigint; readonl
|
|
|
22
23
|
export interface RelocationDataChunkV1 { readonly order:number;readonly data:Uint8Array; }
|
|
23
24
|
export interface RelocationChunkEntryV1 { readonly order:number;readonly reference:string;readonly length:bigint;readonly checksumCrc32c:number; }
|
|
24
25
|
export interface RelocationManifestV1 { readonly logicalFormatVersion:1;readonly totalLength:bigint;readonly totalChecksumCrc32c:number;readonly inventoryDigestSha256:Uint8Array;readonly chunks:readonly RelocationChunkEntryV1[]; }
|
|
25
|
-
function bytes64(out:number[],v:Uint8Array):void{u64(out,BigInt(v.length));out
|
|
26
|
-
function body16(out:number[],write:(b:number[])=>void):void{const b:number[]=[];write(b);if(b.length>65535)throw new RangeError("body16");out.push(b.length>>>8,b.length&255
|
|
27
|
-
function body64(out:number[],write:(b:number[])=>void):void{const b:number[]=[];write(b);u64(out,BigInt(b.length));out
|
|
26
|
+
function bytes64(out:number[],v:Uint8Array):void{u64(out,BigInt(v.length));appendBytes(out,v);}
|
|
27
|
+
function body16(out:number[],write:(b:number[])=>void):void{const b:number[]=[];write(b);if(b.length>65535)throw new RangeError("body16");out.push(b.length>>>8,b.length&255);appendBytes(out,b);}
|
|
28
|
+
function body64(out:number[],write:(b:number[])=>void):void{const b:number[]=[];write(b);u64(out,BigInt(b.length));appendBytes(out,b);}
|
|
28
29
|
function sorted<T>(v:readonly T[],compare:(a:T,b:T)=>number,name:string):void{for(let i=1;i<v.length;i++)if(compare(v[i-1],v[i])>=0)throw new RangeError(name);}
|
|
29
30
|
function read8(bytes:Uint8Array,at:{value:number}):number{if(at.value>=bytes.length)throw new RangeError("truncated u8");return bytes[at.value++]!;}
|
|
30
31
|
function read16(bytes:Uint8Array,at:{value:number}):number{if(at.value+2>bytes.length)throw new RangeError("truncated u16");return (bytes[at.value++]!<<8)|bytes[at.value++]!;}
|
|
31
32
|
function take(bytes:Uint8Array,at:{value:number},n:number,name:string):Uint8Array<ArrayBuffer>{if(!Number.isSafeInteger(n)||n<0||at.value+n>bytes.length)throw new RangeError("truncated "+name);const v=Uint8Array.from(bytes.subarray(at.value,at.value+n));at.value+=n;return v;}
|
|
32
33
|
function nonzero64(bytes:Uint8Array,at:{value:number},name:string):bigint{const v=read64(bytes,at);if(v===0n)throw new RangeError(name);return v;}
|
|
33
34
|
function readText16(bytes:Uint8Array,at:{value:number},minimum:number,maximum:number,name:string):string{const n=read16(bytes,at);if(n<minimum||n>maximum)throw new RangeError(name);const b=take(bytes,at,n,name);if(b.includes(0))throw new RangeError(name+" NUL");return decodeText.decode(b);}
|
|
34
|
-
function writeText16(out:number[],value:string,minimum:number,maximum:number,name:string):void{const b=text.encode(value);if(b.length<minimum||b.length>maximum||b.includes(0))throw new RangeError(name);out.push(b.length>>>8,b.length&255
|
|
35
|
+
function writeText16(out:number[],value:string,minimum:number,maximum:number,name:string):void{const b=text.encode(value);if(b.length<minimum||b.length>maximum||b.includes(0))throw new RangeError(name);out.push(b.length>>>8,b.length&255);appendBytes(out,b);}
|
|
35
36
|
function boundedEnd(bytes:Uint8Array,at:{value:number},length:number,name:string):number{if(at.value+length>bytes.length)throw new RangeError("truncated "+name);return at.value+length;}
|
|
36
37
|
function exactEnd(at:{value:number},end:number,name:string):void{if(at.value!==end)throw new RangeError(name+" trailing");}
|
|
37
38
|
function skipRid(bytes:Uint8Array,at:{value:number}):void{const n=read8(bytes,at);if(n===0)throw new RangeError("rid minimumBytes");take(bytes,at,n,"rid");}
|
|
@@ -50,14 +51,14 @@ function skipFrozenRecord(bytes:Uint8Array,at:{value:number}):void{const kind=re
|
|
|
50
51
|
function compareUtf8(a:string,b:string):number{const x=text.encode(a),y=text.encode(b),n=Math.min(x.length,y.length);for(let i=0;i<n;i++)if(x[i]!==y[i])return x[i]!-y[i]!;return x.length-y.length;}
|
|
51
52
|
function writeRelocationObject(out:number[],value:RelocationObjectIdentity):void{const kind=value.objectKind==="actor"?1:value.objectKind==="userSpot"?2:3;out.push(kind);body16(out,b=>{if(value.objectKind==="actor"){text8(b,value.actorId);if(value.objectGeneration===0n||value.expectedAuthorityOwnerGeneration===0n)throw new RangeError("object");u64(b,value.objectGeneration);u64(b,value.expectedAuthorityOwnerGeneration);}else if(value.objectKind==="userSpot"){text8(b,value.spotId);if(value.objectGeneration===0n||value.expectedAuthorityOwnerGeneration===0n)throw new RangeError("object");u64(b,value.objectGeneration);u64(b,value.expectedAuthorityOwnerGeneration);}else{text8(b,value.instanceType);text8(b,value.spotId);if(value.objectGeneration===0n)throw new RangeError("object");u64(b,value.objectGeneration);}});}
|
|
52
53
|
function readRelocationObject(bytes:Uint8Array,at:{value:number}):RelocationObjectIdentity{const kind=read8(bytes,at),end=boundedEnd(bytes,at,read16(bytes,at),"relocation object");let value:RelocationObjectIdentity;if(kind===1)value={objectKind:"actor",actorId:readText8(bytes,at),objectGeneration:nonzero64(bytes,at,"object generation"),expectedAuthorityOwnerGeneration:nonzero64(bytes,at,"authority generation")};else if(kind===2)value={objectKind:"userSpot",spotId:readText8(bytes,at),objectGeneration:nonzero64(bytes,at,"object generation"),expectedAuthorityOwnerGeneration:nonzero64(bytes,at,"authority generation")};else if(kind===3)value={objectKind:"instanceSpot",instanceType:readText8(bytes,at),spotId:readText8(bytes,at),objectGeneration:nonzero64(bytes,at,"object generation")};else throw new RangeError("object kind");exactEnd(at,end,"relocation object");return value;}
|
|
53
|
-
export function encodeRelocationEnvelopeV1(value:RelocationEnvelopeV1):Uint8Array{const out:number[]=[];if(value.relocationHigh===0n&&value.relocationLow===0n)throw new RangeError("relocation");u64(out,value.relocationHigh);u64(out,value.relocationLow);writeRelocationObject(out,value.object);if(value.applicationVersion<0n||value.applicationVersion>9223372036854775807n)throw new RangeError("application version");u64(out,value.applicationVersion);sorted(value.applicationStates,(a,b)=>a.participantId<b.participantId?-1:a.participantId>b.participantId?1:0,"applicationStates");if(value.applicationStates.length===0)throw new RangeError("applicationStates");u32(out,value.applicationStates.length);for(const s of value.applicationStates){if(s.participantId===0n)throw new RangeError("participant");u64(out,s.participantId);out.push(s.hasState?1:0);body64(out,b=>{if(s.hasState)bytes64(b,s.payload);else if(s.payload.length!==0)throw new RangeError("absent state payload");});}sorted(value.savedWork,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"savedWork");u32(out,value.savedWork.length);for(const w of value.savedWork){if(w.participantId===0n||w.order===0n)throw new RangeError("saved work");const at={value:0};skipFrozenRecord(w.frozenRecord,at);exactEnd(at,w.frozenRecord.length,"frozen record");u64(out,w.participantId);u64(out,w.order);out
|
|
54
|
+
export function encodeRelocationEnvelopeV1(value:RelocationEnvelopeV1):Uint8Array{const out:number[]=[];if(value.relocationHigh===0n&&value.relocationLow===0n)throw new RangeError("relocation");u64(out,value.relocationHigh);u64(out,value.relocationLow);writeRelocationObject(out,value.object);if(value.applicationVersion<0n||value.applicationVersion>9223372036854775807n)throw new RangeError("application version");u64(out,value.applicationVersion);sorted(value.applicationStates,(a,b)=>a.participantId<b.participantId?-1:a.participantId>b.participantId?1:0,"applicationStates");if(value.applicationStates.length===0)throw new RangeError("applicationStates");u32(out,value.applicationStates.length);for(const s of value.applicationStates){if(s.participantId===0n)throw new RangeError("participant");u64(out,s.participantId);out.push(s.hasState?1:0);body64(out,b=>{if(s.hasState)bytes64(b,s.payload);else if(s.payload.length!==0)throw new RangeError("absent state payload");});}sorted(value.savedWork,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"savedWork");u32(out,value.savedWork.length);for(const w of value.savedWork){if(w.participantId===0n||w.order===0n)throw new RangeError("saved work");const at={value:0};skipFrozenRecord(w.frozenRecord,at);exactEnd(at,w.frozenRecord.length,"frozen record");u64(out,w.participantId);u64(out,w.order);appendBytes(out,w.frozenRecord);}sorted(value.timerRegistrations,(a,b)=>a.participantId===b.participantId?compareUtf8(a.name,b.name):a.participantId<b.participantId?-1:1,"timerRegistrations");u32(out,value.timerRegistrations.length);for(const t of value.timerRegistrations){if(t.participantId===0n||t.periodMilliseconds===0n||t.maxCatchUpTicks===0n||t.overrunPolicy<1||t.overrunPolicy>3)throw new RangeError("timer");u64(out,t.participantId);text8(out,t.name);text8(out,t.handlerType);u64(out,t.periodMilliseconds);out.push(t.overrunPolicy);u64(out,t.maxCatchUpTicks);out.push(t.stopOnUnhandledException?1:0);u64(out,t.lastCompletedDeliveryIndex);u64(out,t.lastCompletedScheduledIndex);u64(out,t.nextScheduledAtUnixMilliseconds);}sorted(value.pendingTimerTicks,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"pendingTimerTicks");u32(out,value.pendingTimerTicks.length);for(const p of value.pendingTimerTicks){if(p.participantId===0n||p.order===0n||p.deliveryIndex===0n||p.scheduledIndex===0n)throw new RangeError("pending timer");u64(out,p.participantId);u64(out,p.order);text8(out,p.timerName);u64(out,p.deliveryIndex);u64(out,p.scheduledIndex);u64(out,p.scheduledAtUnixMilliseconds);u64(out,p.skippedTicks);}if(out.length>274877906944)throw new RangeError("relocation logical bytes");return Uint8Array.from(out);}
|
|
54
55
|
export function decodeRelocationEnvelopeV1(chunks:readonly Uint8Array[]):RelocationEnvelopeV1{const n=chunks.reduce((s,c)=>s+c.length,0);if(n>274877906944)throw new RangeError("relocation logical bytes");const bytes=new Uint8Array(n);let offset=0;for(const c of chunks){bytes.set(c,offset);offset+=c.length;}const at={value:0},relocationHigh=read64(bytes,at),relocationLow=read64(bytes,at);if(relocationHigh===0n&&relocationLow===0n)throw new RangeError("relocation");const object=readRelocationObject(bytes,at),applicationVersion=read64(bytes,at);if(applicationVersion>9223372036854775807n)throw new RangeError("application version");const applicationStates:RelocationEnvelopeV1["applicationStates"][number][]=[];const stateCount=read32(bytes,at);if(stateCount===0)throw new RangeError("applicationStates");for(let i=0,count=stateCount;i<count;i++){const participantId=nonzero64(bytes,at,"participant"),has=read8(bytes,at),end=boundedEnd(bytes,at,Number(read64(bytes,at)),"application state");if(has>1)throw new RangeError("application state");let payload=new Uint8Array();if(has===1)payload=take(bytes,at,Number(read64(bytes,at)),"state payload");exactEnd(at,end,"application state");applicationStates.push({participantId,hasState:has===1,payload});}const participants=new Set(applicationStates.map((s)=>s.participantId));const savedWork:RelocationEnvelopeV1["savedWork"][number][]=[];for(let i=0,count=read32(bytes,at);i<count;i++){const participantId=nonzero64(bytes,at,"participant");if(!participants.has(participantId))throw new RangeError("saved work participant");const order=nonzero64(bytes,at,"order"),start=at.value;skipFrozenRecord(bytes,at);savedWork.push({participantId,order,frozenRecord:bytes.slice(start,at.value)});}const timerRegistrations:RelocationEnvelopeV1["timerRegistrations"][number][]=[],registeredTimerNames=new Map<bigint,Set<string>>();for(let i=0,count=read32(bytes,at);i<count;i++){const participantId=nonzero64(bytes,at,"participant");if(!participants.has(participantId))throw new RangeError("timer participant");const name=readText8(bytes,at),handlerType=readText8(bytes,at),periodMilliseconds=nonzero64(bytes,at,"period"),overrunPolicy=read8(bytes,at);if(overrunPolicy<1||overrunPolicy>3)throw new RangeError("overrun policy");const maxCatchUpTicks=nonzero64(bytes,at,"catch-up"),stop=read8(bytes,at);if(stop>1)throw new RangeError("stop flag");let names=registeredTimerNames.get(participantId);if(names===undefined){names=new Set<string>();registeredTimerNames.set(participantId,names);}names.add(name);timerRegistrations.push({participantId,name,handlerType,periodMilliseconds,overrunPolicy:overrunPolicy as 1|2|3,maxCatchUpTicks,stopOnUnhandledException:stop===1,lastCompletedDeliveryIndex:read64(bytes,at),lastCompletedScheduledIndex:read64(bytes,at),nextScheduledAtUnixMilliseconds:read64(bytes,at)});}const pendingTimerTicks:RelocationEnvelopeV1["pendingTimerTicks"][number][]=[];for(let i=0,count=read32(bytes,at);i<count;i++){const participantId=nonzero64(bytes,at,"participant");if(!participants.has(participantId))throw new RangeError("pending timer participant");const order=nonzero64(bytes,at,"order"),timerName=readText8(bytes,at);if(registeredTimerNames.get(participantId)?.has(timerName)!==true)throw new RangeError("pending timer registration");pendingTimerTicks.push({participantId,order,timerName,deliveryIndex:nonzero64(bytes,at,"delivery"),scheduledIndex:nonzero64(bytes,at,"scheduled"),scheduledAtUnixMilliseconds:read64(bytes,at),skippedTicks:read64(bytes,at)});}exactEnd(at,bytes.length,"relocation envelope");sorted(applicationStates,(a,b)=>a.participantId<b.participantId?-1:a.participantId>b.participantId?1:0,"applicationStates");sorted(savedWork,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"savedWork");sorted(timerRegistrations,(a,b)=>a.participantId===b.participantId?compareUtf8(a.name,b.name):a.participantId<b.participantId?-1:1,"timerRegistrations");sorted(pendingTimerTicks,(a,b)=>a.participantId===b.participantId?(a.order<b.order?-1:a.order>b.order?1:0):(a.participantId<b.participantId?-1:1),"pendingTimerTicks");return{relocationHigh,relocationLow,object,applicationVersion,applicationStates,savedWork,timerRegistrations,pendingTimerTicks};}
|
|
55
56
|
function crc32c(bytes:Uint8Array):number{let crc=0xffffffff;for(const octet of bytes){crc^=octet;for(let bit=0;bit<8;bit++)crc=(crc>>>1)^((crc&1)!==0?0x82f63b78:0);}return(~crc)>>>0;}
|
|
56
|
-
function encodeDurable(magic:readonly number[],body:readonly number[],maximum:number):Uint8Array{const out=[...magic,1,0,0];u32(out,body.length);out
|
|
57
|
+
function encodeDurable(magic:readonly number[],body:readonly number[],maximum:number):Uint8Array{const out=[...magic,1,0,0];u32(out,body.length);appendBytes(out,body);if(out.length+4>maximum)throw new RangeError("durable format bytes");u32(out,crc32c(Uint8Array.from(out)));return Uint8Array.from(out);}
|
|
57
58
|
function decodeDurable(bytes:Uint8Array,magic:readonly number[],maximum:number):Uint8Array{if(bytes.length>maximum||bytes.length<15||magic.some((v,i)=>bytes[i]!==v)||bytes[4]!==1||bytes[5]!==0||bytes[6]!==0)throw new RangeError("durable header");const at={value:7},length=read32(bytes,at),end=boundedEnd(bytes,at,length,"durable body");if(end+4!==bytes.length)throw new RangeError("durable length");const body=bytes.slice(at.value,end);at.value=end;const expected=read32(bytes,at);if(crc32c(bytes.slice(0,end))!==expected)throw new RangeError("durable checksum");return body;}
|
|
58
|
-
export function encodeRelocationDataChunkV1(value:RelocationDataChunkV1):Uint8Array{const body:number[]=[];u32(body,value.order);if(value.data.length>67108864)throw new RangeError("chunk data");u32(body,value.data.length);body
|
|
59
|
+
export function encodeRelocationDataChunkV1(value:RelocationDataChunkV1):Uint8Array{const body:number[]=[];u32(body,value.order);if(value.data.length>67108864)throw new RangeError("chunk data");u32(body,value.data.length);appendBytes(body,value.data);return encodeDurable([90,76,84,67],body,67108886);}
|
|
59
60
|
export function decodeRelocationDataChunkV1(bytes:Uint8Array):RelocationDataChunkV1{const body=decodeDurable(bytes,[90,76,84,67],67108886),at={value:0},order=read32(body,at),data=take(body,at,read32(body,at),"chunk data");exactEnd(at,body.length,"chunk");return{order,data};}
|
|
60
|
-
export function encodeRelocationManifestV1(value:RelocationManifestV1):Uint8Array{if(Number(value.logicalFormatVersion)!==1||value.totalLength>274877906944n||value.inventoryDigestSha256.length!==32||value.chunks.length>4096)throw new RangeError("manifest");sorted(value.chunks,(a,b)=>a.order-b.order,"manifest chunks");const body:number[]=[1];u64(body,value.totalLength);u32(body,value.totalChecksumCrc32c);body.push(32
|
|
61
|
+
export function encodeRelocationManifestV1(value:RelocationManifestV1):Uint8Array{if(Number(value.logicalFormatVersion)!==1||value.totalLength>274877906944n||value.inventoryDigestSha256.length!==32||value.chunks.length>4096)throw new RangeError("manifest");sorted(value.chunks,(a,b)=>a.order-b.order,"manifest chunks");const body:number[]=[1];u64(body,value.totalLength);u32(body,value.totalChecksumCrc32c);body.push(32);appendBytes(body,value.inventoryDigestSha256);u32(body,value.chunks.length);for(const chunk of value.chunks){if(chunk.length===0n)throw new RangeError("chunk length");u32(body,chunk.order);writeText16(body,chunk.reference,1,4096,"chunk reference");u64(body,chunk.length);u32(body,chunk.checksumCrc32c);}return encodeDurable([90,76,84,77],body,33554432);}
|
|
61
62
|
export function decodeRelocationManifestV1(bytes:Uint8Array):RelocationManifestV1{const body=decodeDurable(bytes,[90,76,84,77],33554432),at={value:0};if(read8(body,at)!==1)throw new RangeError("logical format version");const totalLength=read64(body,at);if(totalLength>274877906944n)throw new RangeError("logical length");const totalChecksumCrc32c=read32(body,at),digestLength=read8(body,at);if(digestLength!==32)throw new RangeError("inventory digest minimumBytes");const inventoryDigestSha256=take(body,at,digestLength,"inventory digest"),count=read32(body,at);if(count>4096)throw new RangeError("chunk count");const chunks:RelocationChunkEntryV1[]=[];for(let i=0;i<count;i++)chunks.push({order:read32(body,at),reference:readText16(body,at,1,4096,"chunk reference"),length:nonzero64(body,at,"chunk length"),checksumCrc32c:read32(body,at)});exactEnd(at,body.length,"manifest");sorted(chunks,(a,b)=>a.order-b.order,"manifest chunks");return{logicalFormatVersion:1,totalLength,totalChecksumCrc32c,inventoryDigestSha256,chunks};}
|
|
62
63
|
export interface LivenessProbe5 { readonly probeId: bigint; }
|
|
63
64
|
export function encodeLivenessProbe5(value: LivenessProbe5): Uint8Array { const out=[90,77,1,5,0]; if(value.probeId===0n) throw new RangeError("probeId"); u64(out,value.probeId); return Uint8Array.from(out); }
|
|
@@ -144,11 +145,11 @@ export function encodeRelocationFailed53(v:RelocationFailed53):Uint8Array{const
|
|
|
144
145
|
export function decodeRelocationFailed53(bytes:Uint8Array):RelocationFailed53{const at=b3ReadHeader(bytes,53,"relocationFailed"),v=b3ReadReadyBase(bytes,at),failureCode=read32(bytes,at);if(!b3FailureResults.has(failureCode))throw new RangeError("failure code");b3Finish(at,bytes,"relocationFailed");return{...v,failureCode};}
|
|
145
146
|
function b3WriteSourceBase(o:number[],v:RelocationData31|RelocationCutover34|RelocationState52):void{b3WriteId(o,v.relocation,"relocation");if(v.targetAttemptGeneration===0n)throw new RangeError("target attempt");u64(o,v.targetAttemptGeneration);b3WriteCoordinator(o,v.coordinator);b3WriteRole(o,v.senderRole);if(v.senderRole!=="source")throw new RangeError("sender role");writeRelocationObject(o,v.object);}
|
|
146
147
|
function b3ReadSourceBase(bytes:Uint8Array,at:{value:number}){const relocation=b3ReadId(bytes,at,"relocation"),targetAttemptGeneration=nonzero64(bytes,at,"target attempt"),coordinator=b3ReadCoordinator(bytes,at),senderRole=b3ReadRole(bytes,at);if(senderRole!=="source")throw new RangeError("sender role");return{relocation,targetAttemptGeneration,coordinator,senderRole,object:readRelocationObject(bytes,at)};}
|
|
147
|
-
export function encodeRelocationData31(v:RelocationData31):Uint8Array{const o=b3Header(31);b3WriteSourceBase(o,v);const at={value:0};skipFrozenRecord(v.record,at);exactEnd(at,v.record.length,"frozen record");o
|
|
148
|
+
export function encodeRelocationData31(v:RelocationData31):Uint8Array{const o=b3Header(31);b3WriteSourceBase(o,v);const at={value:0};skipFrozenRecord(v.record,at);exactEnd(at,v.record.length,"frozen record");appendBytes(o,v.record);return Uint8Array.from(o);}
|
|
148
149
|
export function decodeRelocationData31(bytes:Uint8Array):RelocationData31{const at=b3ReadHeader(bytes,31,"relocationData"),v=b3ReadSourceBase(bytes,at),start=at.value;skipFrozenRecord(bytes,at);const record=bytes.slice(start,at.value);b3Finish(at,bytes,"relocationData");return{...v,record};}
|
|
149
150
|
export function encodeRelocationCutover34(v:RelocationCutover34):Uint8Array{const o=b3Header(34);b3WriteSourceBase(o,v);if(v.boundaryRecordCount<0n||v.boundaryRecordCount>9223372036854775807n)throw new RangeError("boundary count");u64(o,v.boundaryRecordCount);u32(o,v.boundaryChecksumCrc32c);return Uint8Array.from(o);}
|
|
150
151
|
export function decodeRelocationCutover34(bytes:Uint8Array):RelocationCutover34{const at=b3ReadHeader(bytes,34,"relocationCutover"),v=b3ReadSourceBase(bytes,at),boundaryRecordCount=read64(bytes,at);if(boundaryRecordCount>9223372036854775807n)throw new RangeError("boundary count");const boundaryChecksumCrc32c=read32(bytes,at);b3Finish(at,bytes,"relocationCutover");return{...v,boundaryRecordCount,boundaryChecksumCrc32c};}
|
|
151
|
-
export function encodeRelocationState52(v:RelocationState52):Uint8Array{const o=b3Header(52);b3WriteSourceBase(o,v);if(v.chunkData.length>67108864)throw new RangeError("chunk data");u32(o,v.chunkOrdinal);u32(o,v.chunkData.length);o
|
|
152
|
+
export function encodeRelocationState52(v:RelocationState52):Uint8Array{const o=b3Header(52);b3WriteSourceBase(o,v);if(v.chunkData.length>67108864)throw new RangeError("chunk data");u32(o,v.chunkOrdinal);u32(o,v.chunkData.length);appendBytes(o,v.chunkData);return Uint8Array.from(o);}
|
|
152
153
|
export function decodeRelocationState52(bytes:Uint8Array):RelocationState52{const at=b3ReadHeader(bytes,52,"relocationState"),v=b3ReadSourceBase(bytes,at),chunkOrdinal=read32(bytes,at),length=read32(bytes,at);if(length>67108864)throw new RangeError("chunk data");const chunkData=take(bytes,at,length,"chunk data");b3Finish(at,bytes,"relocationState");return{...v,chunkOrdinal,chunkData};}
|
|
153
154
|
export function encodeRelocationPrepare40(v:RelocationPrepare40):Uint8Array{const o=b3Header(40);b3WriteId(o,v.relocation,"relocation");if(v.targetAttemptGeneration===0n||v.sourceNodeGeneration===0n)throw new RangeError("prepare");u64(o,v.targetAttemptGeneration);b3WriteCoordinator(o,v.coordinator);b3WriteTarget(o,v.target);b3WriteRole(o,v.initiatorRole);if(v.initiatorRole!=="source")throw new RangeError("initiator role");writeRelocationObject(o,v.object);rid(o,v.sourceNodeRid);u64(o,v.sourceNodeGeneration);if(v.payloadTotalLength<0n||v.payloadTotalLength>274877906944n||v.payloadChunkCount>4096||v.applicationVersion<0n||v.applicationVersion>9223372036854775807n)throw new RangeError("prepare bounds");u64(o,v.payloadTotalLength);u32(o,v.payloadChunkCount);u32(o,v.payloadChecksumCrc32c);u64(o,v.applicationVersion);return Uint8Array.from(o);}
|
|
154
155
|
export function decodeRelocationPrepare40(bytes:Uint8Array):RelocationPrepare40{const at=b3ReadHeader(bytes,40,"relocationPrepare"),relocation=b3ReadId(bytes,at,"relocation"),targetAttemptGeneration=nonzero64(bytes,at,"target attempt"),coordinator=b3ReadCoordinator(bytes,at),target=b3ReadTarget(bytes,at),initiatorRole=b3ReadRole(bytes,at);if(initiatorRole!=="source")throw new RangeError("initiator role");const object=readRelocationObject(bytes,at),sourceNodeRid=readRid(bytes,at),sourceNodeGeneration=nonzero64(bytes,at,"source node"),payloadTotalLength=read64(bytes,at),payloadChunkCount=read32(bytes,at),payloadChecksumCrc32c=read32(bytes,at),applicationVersion=read64(bytes,at);if(payloadTotalLength>274877906944n||payloadChunkCount>4096||applicationVersion>9223372036854775807n)throw new RangeError("prepare bounds");b3Finish(at,bytes,"relocationPrepare");return{relocation,targetAttemptGeneration,coordinator,target,initiatorRole,object,sourceNodeRid,sourceNodeGeneration,payloadTotalLength,payloadChunkCount,payloadChecksumCrc32c,applicationVersion};}
|
|
@@ -173,7 +174,7 @@ function b4ReadHeader(bytes:Uint8Array,id:number,name:string):{value:number}{con
|
|
|
173
174
|
function b4Finish(at:{value:number},bytes:Uint8Array,name:string):void{if(at.value!==bytes.length)throw new RangeError(name+" trailing");}
|
|
174
175
|
function b4Reservation(out:number[],v:ServiceWireObjectReservationFence):void{text8(out,v.reservationId);writeText16(out,v.expectedStoreVersion,1,4096,"store version");if(v.objectGeneration===0n||v.authorityOwnerGeneration===0n||v.targetNodeGeneration===0n||v.targetOwnerLeaseGeneration===0n||!Number.isInteger(v.pendingCapacityDelta)||v.pendingCapacityDelta<1||v.pendingCapacityDelta>0xffffffff)throw new RangeError("reservation");u64(out,v.objectGeneration);u64(out,v.authorityOwnerGeneration);rid(out,v.targetNodeRid);u64(out,v.targetNodeGeneration);text8(out,v.targetOwnerId);u64(out,v.targetOwnerLeaseGeneration);u32(out,v.pendingCapacityDelta);}
|
|
175
176
|
function b4ReadReservation(bytes:Uint8Array,at:{value:number}):ServiceWireObjectReservationFence{return{reservationId:readText8(bytes,at),expectedStoreVersion:readText16(bytes,at,1,4096,"store version"),objectGeneration:nonzero64(bytes,at,"object generation"),authorityOwnerGeneration:nonzero64(bytes,at,"authority generation"),targetNodeRid:readRid(bytes,at),targetNodeGeneration:nonzero64(bytes,at,"target node generation"),targetOwnerId:readText8(bytes,at),targetOwnerLeaseGeneration:nonzero64(bytes,at,"target lease generation"),pendingCapacityDelta:(()=>{const v=read32(bytes,at);if(v===0)throw new RangeError("pending capacity");return v;})()};}
|
|
176
|
-
function b4Close(out:number[],v:ServiceWireUserSpotCloseFenceV1):void{const body:number[]=[];text8(body,v.spotId);if(v.objectGeneration===0n||v.targetNodeGeneration===0n||v.expectedAuthorityOwnerGeneration===0n)throw new RangeError("close fence");u64(body,v.objectGeneration);rid(body,v.targetNodeRid);u64(body,v.targetNodeGeneration);u64(body,v.expectedAuthorityOwnerGeneration);writeText16(body,v.expectedStoreVersion,1,4096,"store version");out.push(1);if(body.length>65535)throw new RangeError("close fence");out.push(body.length>>>8,body.length&255
|
|
177
|
+
function b4Close(out:number[],v:ServiceWireUserSpotCloseFenceV1):void{const body:number[]=[];text8(body,v.spotId);if(v.objectGeneration===0n||v.targetNodeGeneration===0n||v.expectedAuthorityOwnerGeneration===0n)throw new RangeError("close fence");u64(body,v.objectGeneration);rid(body,v.targetNodeRid);u64(body,v.targetNodeGeneration);u64(body,v.expectedAuthorityOwnerGeneration);writeText16(body,v.expectedStoreVersion,1,4096,"store version");out.push(1);if(body.length>65535)throw new RangeError("close fence");out.push(body.length>>>8,body.length&255);appendBytes(out,body);}
|
|
177
178
|
function b4ReadClose(bytes:Uint8Array,at:{value:number}):ServiceWireUserSpotCloseFenceV1{if(read8(bytes,at)!==1)throw new RangeError("close fence version");const end=boundedEnd(bytes,at,read16(bytes,at),"close fence");const value={spotId:readText8(bytes,at),objectGeneration:nonzero64(bytes,at,"object generation"),targetNodeRid:readRid(bytes,at),targetNodeGeneration:nonzero64(bytes,at,"target node generation"),expectedAuthorityOwnerGeneration:nonzero64(bytes,at,"authority generation"),expectedStoreVersion:readText16(bytes,at,1,4096,"store version")};exactEnd(at,end,"close fence");return value;}
|
|
178
179
|
function b4CommandBase(out:number[],v:{correlation:bigint;operation:ServiceWireOperationId;sourceNodeRid:Uint8Array;sourceNodeGeneration:bigint}):void{if(v.correlation===0n||v.sourceNodeGeneration===0n)throw new RangeError("command base");u64(out,v.correlation);b3WriteId(out,v.operation,"operation");rid(out,v.sourceNodeRid);u64(out,v.sourceNodeGeneration);}
|
|
179
180
|
function b4ReadCommandBase(bytes:Uint8Array,at:{value:number}):{correlation:bigint;operation:ServiceWireOperationId;sourceNodeRid:Uint8Array;sourceNodeGeneration:bigint}{return{correlation:nonzero64(bytes,at,"correlation"),operation:b3ReadId(bytes,at,"operation"),sourceNodeRid:readRid(bytes,at),sourceNodeGeneration:nonzero64(bytes,at,"source node generation")};}
|
|
@@ -183,7 +184,7 @@ export function encodeUserSpotClose48(v:UserSpotClose48):Uint8Array{const out=b4
|
|
|
183
184
|
export function decodeUserSpotClose48(bytes:Uint8Array):UserSpotClose48{const at=b4ReadHeader(bytes,48,"userSpotClose"),base=b4ReadCommandBase(bytes,at),target=b4ReadClose(bytes,at),deadlineUnixMs=nonzero64(bytes,at,"deadline");b4Finish(at,bytes,"userSpotClose");return{...base,target,deadlineUnixMs};}
|
|
184
185
|
export function encodeActorCreate49(v:ActorCreate49):Uint8Array{const out=b4Header(49);b4CommandBase(out,v);text8(out,v.actorId);text8(out,v.stableType);b4Reservation(out,v.reservation);if(v.deadlineUnixMs===0n)throw new RangeError("deadline");u64(out,v.deadlineUnixMs);return Uint8Array.from(out);}
|
|
185
186
|
export function decodeActorCreate49(bytes:Uint8Array):ActorCreate49{const at=b4ReadHeader(bytes,49,"actorCreate"),base=b4ReadCommandBase(bytes,at),actorId=readText8(bytes,at),stableType=readText8(bytes,at),reservation=b4ReadReservation(bytes,at),deadlineUnixMs=nonzero64(bytes,at,"deadline");b4Finish(at,bytes,"actorCreate");return{...base,actorId,stableType,reservation,deadlineUnixMs};}
|
|
186
|
-
function b4ZljrPayload(v:ZljrRecordV1):Uint8Array{if(v.metadata.length>262144
|
|
187
|
-
function b4ReadZljrPayload(bytes:Uint8Array):{metadata:Uint8Array;request:Uint8Array;reply:Uint8Array}{const at={value:0};if(read32(bytes,at)!==0x5a4c4a52||read8(bytes,at)!==1)throw new RangeError("ZLJR header");const metadataLength=read32(bytes,at),requestLength=read32(bytes,at),replyLength=read32(bytes,at);if(metadataLength>262144
|
|
188
|
-
export function encodeZljrRecordV1(v:ZljrRecordV1):Uint8Array{if(v.source.nodeGeneration===0n||v.source.ownerLeaseGeneration===0n)throw new RangeError("ZLJR record");const source:number[]=[];rid(source,v.source.nodeRid);u64(source,v.source.nodeGeneration);text8(source,v.source.ownerId);u64(source,v.source.ownerLeaseGeneration);if(source.length>65535)throw new RangeError("ZLJR source");const payload=b4ZljrPayload(v),body:number[]=[];text8(body,"__zlink.actor.routed_join.recovery");text8(body,"application/x-zlink-actor-routed-join-recovery-v1");u32(body,payload.length);body
|
|
187
|
+
function b4ZljrPayload(v:ZljrRecordV1):Uint8Array{if(v.metadata.length>262144)throw new RangeError("ZLJR length");const out=[90,76,74,82,1];u32(out,v.metadata.length);u32(out,v.request.length);u32(out,v.reply.length);appendBytes(out,v.metadata);appendBytes(out,v.request);appendBytes(out,v.reply);return Uint8Array.from(out);}
|
|
188
|
+
function b4ReadZljrPayload(bytes:Uint8Array):{metadata:Uint8Array;request:Uint8Array;reply:Uint8Array}{const at={value:0};if(read32(bytes,at)!==0x5a4c4a52||read8(bytes,at)!==1)throw new RangeError("ZLJR header");const metadataLength=read32(bytes,at),requestLength=read32(bytes,at),replyLength=read32(bytes,at);if(metadataLength>262144)throw new RangeError("ZLJR length");const metadata=take(bytes,at,metadataLength,"ZLJR metadata"),request=take(bytes,at,requestLength,"ZLJR request"),reply=take(bytes,at,replyLength,"ZLJR reply");b4Finish(at,bytes,"ZLJR");return{metadata,request,reply};}
|
|
189
|
+
export function encodeZljrRecordV1(v:ZljrRecordV1):Uint8Array{if(v.source.nodeGeneration===0n||v.source.ownerLeaseGeneration===0n)throw new RangeError("ZLJR record");const source:number[]=[];rid(source,v.source.nodeRid);u64(source,v.source.nodeGeneration);text8(source,v.source.ownerId);u64(source,v.source.ownerLeaseGeneration);if(source.length>65535)throw new RangeError("ZLJR source");const payload=b4ZljrPayload(v),body:number[]=[];text8(body,"__zlink.actor.routed_join.recovery");text8(body,"application/x-zlink-actor-routed-join-recovery-v1");u32(body,payload.length);appendBytes(body,payload);const out=[1,1,source.length>>>8,source.length&255,...source,0];u64(out,v.operation.high);u64(out,v.operation.low);u32(out,0);out.push(0,0,1);u32(out,body.length);appendBytes(out,body);return Uint8Array.from(out);}
|
|
189
190
|
export function decodeZljrRecordV1(bytes:Uint8Array):ZljrRecordV1{const at={value:0};if(read8(bytes,at)!==1||read8(bytes,at)!==1)throw new RangeError("ZLJR frozen record");const sourceEnd=boundedEnd(bytes,at,read16(bytes,at),"ZLJR source"),nodeRid=readRid(bytes,at),nodeGeneration=nonzero64(bytes,at,"source node generation"),ownerId=readText8(bytes,at),ownerLeaseGeneration=nonzero64(bytes,at,"source owner lease");exactEnd(at,sourceEnd,"ZLJR source");if(read8(bytes,at)!==0)throw new RangeError("ZLJR metadata flag");const operation={high:read64(bytes,at),low:read64(bytes,at)};if(read32(bytes,at)!==0||read16(bytes,at)!==0||read8(bytes,at)!==1)throw new RangeError("ZLJR frozen record");const payloadEnd=boundedEnd(bytes,at,read32(bytes,at),"ZLJR payload"),packetName=readText8(bytes,at),contentType=readText8(bytes,at),payloadLength=read32(bytes,at);if(packetName!=="__zlink.actor.routed_join.recovery"||contentType!=="application/x-zlink-actor-routed-join-recovery-v1"||payloadLength!==payloadEnd-at.value)throw new RangeError("ZLJR payload");const payload=take(bytes,at,payloadLength,"ZLJR payload");exactEnd(at,payloadEnd,"ZLJR payload");b4Finish(at,bytes,"ZLJR record");return{source:{nodeRid,nodeGeneration,ownerId,ownerLeaseGeneration},operation,...b4ReadZljrPayload(payload)};}
|
|
@@ -1600,7 +1600,8 @@ export class DefaultZLinkSpotManager {
|
|
|
1600
1600
|
const inboundFlow = createInboundFlow(
|
|
1601
1601
|
envelope.header.flowId,
|
|
1602
1602
|
envelope.header.flowOrigin,
|
|
1603
|
-
this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true
|
|
1603
|
+
this.options.dispatchErrors?.flow.flowCreationEnabled() ?? true,
|
|
1604
|
+
undefined
|
|
1604
1605
|
);
|
|
1605
1606
|
await runWithFlow(inboundFlow, async () => {
|
|
1606
1607
|
const request = record.operationKind === OperationKind.InstanceSpotRequest;
|
|
@@ -3,7 +3,13 @@ import {
|
|
|
3
3
|
createInternalFrameworkException,
|
|
4
4
|
internalFrameworkErrorKind
|
|
5
5
|
} from '../framework-errors-internal';
|
|
6
|
-
import type {
|
|
6
|
+
import type {
|
|
7
|
+
ActorRef,
|
|
8
|
+
RoutingId,
|
|
9
|
+
ZLinkActor,
|
|
10
|
+
ZLinkMessageSerializer,
|
|
11
|
+
ZLinkSpot
|
|
12
|
+
} from '../../contracts';
|
|
7
13
|
import type { Message } from '../../contracts/Common/Message';
|
|
8
14
|
import { ZLinkFrameworkException, zlinkMessageMetadata } from '../../contracts';
|
|
9
15
|
import {
|
|
@@ -14,7 +20,8 @@ import {
|
|
|
14
20
|
ZLinkDispatchMessageKind
|
|
15
21
|
} from '../../contracts/Dispatch/ZLinkDispatchOptions';
|
|
16
22
|
import { flowIfEnabled } from '../diagnostics';
|
|
17
|
-
import { createInboundFlow, runWithFlow } from '../diagnostics/flow-context';
|
|
23
|
+
import { createInboundFlow, currentFlowContext, runWithFlow } from '../diagnostics/flow-context';
|
|
24
|
+
import { streamSessionIdFromRoutingId } from '../streams/managed-stream';
|
|
18
25
|
import type { ZLinkRemoteBoundSessionTarget } from '../actors';
|
|
19
26
|
import {
|
|
20
27
|
actorMessageFollowContext,
|
|
@@ -58,6 +65,7 @@ export interface ZLinkActorPacketDelivery {
|
|
|
58
65
|
readonly fallbackActorRef?: ActorRef;
|
|
59
66
|
readonly requestTerminal?: ZLinkActorRequestTerminal;
|
|
60
67
|
readonly messageFollowOrigin?: ZLinkMessageFollowOrigin;
|
|
68
|
+
readonly sourceSessionRid?: RoutingId;
|
|
61
69
|
}
|
|
62
70
|
|
|
63
71
|
/**
|
|
@@ -176,7 +184,16 @@ export class ZLinkSpotActorPacketDispatch {
|
|
|
176
184
|
throw error;
|
|
177
185
|
}
|
|
178
186
|
return runWithFlow(
|
|
179
|
-
createInboundFlow(
|
|
187
|
+
createInboundFlow(
|
|
188
|
+
header.flowId,
|
|
189
|
+
header.flowOrigin,
|
|
190
|
+
flowEnabled,
|
|
191
|
+
!flowEnabled
|
|
192
|
+
? undefined
|
|
193
|
+
: delivery.sourceSessionRid === undefined
|
|
194
|
+
? currentFlowContext()?.streamSessionId
|
|
195
|
+
: streamSessionIdFromRoutingId(delivery.sourceSessionRid)
|
|
196
|
+
),
|
|
180
197
|
async () => {
|
|
181
198
|
const messageKind =
|
|
182
199
|
header.kind === ZLinkStreamMessageKind.Request
|