@zlink-systems/framework 0.19.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Configuration/Builders.d.ts +0 -2
  3. package/dist/contracts/Configuration/Configs.d.ts +1 -0
  4. package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -2
  5. package/dist/contracts/Locations/Authority.d.ts +1 -7
  6. package/dist/runtime/actors/actor-authority-publication.js +25 -22
  7. package/dist/runtime/actors/actor-creation.js +30 -9
  8. package/dist/runtime/actors/actor-runtime-state.d.ts +3 -0
  9. package/dist/runtime/actors/index.d.ts +3 -0
  10. package/dist/runtime/actors/index.js +14 -3
  11. package/dist/runtime/admission.d.ts +2 -0
  12. package/dist/runtime/admission.js +11 -5
  13. package/dist/runtime/backend/contracts/index.d.ts +4 -1
  14. package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -0
  15. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
  16. package/dist/runtime/backend/node/node-raw-binding-port.d.ts +2 -1
  17. package/dist/runtime/backend/node/node-raw-binding-port.js +12 -5
  18. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +1 -1
  19. package/dist/runtime/backend/node/node-socket-backend-adapter.js +6 -0
  20. package/dist/runtime/channels/channel-socket-options.js +7 -0
  21. package/dist/runtime/channels/channel-socket-registry.js +3 -0
  22. package/dist/runtime/foundation/service-stateful-runtime.d.ts +1 -1
  23. package/dist/runtime/foundation/service-stateful-runtime.js +1 -5
  24. package/dist/runtime/host/actor-placement-coordinator.d.ts +14 -5
  25. package/dist/runtime/host/actor-placement-coordinator.js +159 -47
  26. package/dist/runtime/host/index.d.ts +2 -1
  27. package/dist/runtime/host/index.js +77 -120
  28. package/dist/runtime/host/location-runtime-owner.js +4 -2
  29. package/dist/runtime/host/remote-actor-join-receiver.js +12 -3
  30. package/dist/runtime/locations/creation-operation-id.d.ts +4 -0
  31. package/dist/runtime/locations/creation-operation-id.js +12 -0
  32. package/dist/runtime/locations/in-memory-authority-store.js +8 -22
  33. package/dist/runtime/locations/location-store-repository.js +36 -59
  34. package/dist/runtime/locations/opaque-record-key.d.ts +5 -0
  35. package/dist/runtime/locations/opaque-record-key.js +16 -0
  36. package/dist/runtime/locations/runtime.d.ts +7 -0
  37. package/dist/runtime/locations/runtime.js +130 -42
  38. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +1672 -0
  39. package/dist/runtime/protocol/service_wire_codec.generated.js +9169 -0
  40. package/dist/runtime/spots/spot-node-runtime-manager.js +2 -2
  41. package/package.json +2 -2
  42. package/src/contracts/Configuration/Builders.ts +0 -2
  43. package/src/contracts/Configuration/Configs.ts +1 -0
  44. package/src/contracts/Configuration/RegistrationTypes.ts +1 -2
  45. package/src/contracts/Locations/Authority.ts +1 -8
  46. package/src/runtime/actors/actor-authority-publication.ts +25 -22
  47. package/src/runtime/actors/actor-creation.ts +45 -11
  48. package/src/runtime/actors/actor-runtime-state.ts +4 -0
  49. package/src/runtime/actors/index.ts +32 -4
  50. package/src/runtime/admission.ts +8 -5
  51. package/src/runtime/backend/contracts/index.ts +4 -1
  52. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +2 -1
  53. package/src/runtime/backend/node/node-raw-binding-port.ts +16 -5
  54. package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -1
  55. package/src/runtime/backend/node/node-socket-backend-adapter.ts +7 -0
  56. package/src/runtime/channels/channel-socket-options.ts +9 -0
  57. package/src/runtime/channels/channel-socket-registry.ts +4 -0
  58. package/src/runtime/foundation/service-stateful-runtime.ts +2 -6
  59. package/src/runtime/host/actor-placement-coordinator.ts +216 -77
  60. package/src/runtime/host/index.ts +95 -135
  61. package/src/runtime/host/location-runtime-owner.ts +4 -2
  62. package/src/runtime/host/remote-actor-join-receiver.ts +15 -3
  63. package/src/runtime/locations/creation-operation-id.ts +11 -0
  64. package/src/runtime/locations/in-memory-authority-store.ts +7 -29
  65. package/src/runtime/locations/location-store-repository.ts +71 -69
  66. package/src/runtime/locations/opaque-record-key.ts +26 -0
  67. package/src/runtime/locations/runtime.ts +154 -60
  68. package/src/runtime/protocol/service_wire_codec.generated.ts +8302 -0
  69. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
  70. package/src/runtime/spots/spot-node-runtime-manager.ts +2 -2
@@ -13,6 +13,7 @@ import type {
13
13
  import { ZLinkSpotKind } from '../../contracts';
14
14
  import type {
15
15
  ZLinkAuthoritySnapshot,
16
+ ZLinkCreationOperationIdentity,
16
17
  ZLinkLocationOwnerToken,
17
18
  ZLinkObjectReserveResult
18
19
  } from '../locations/internal-location-contracts';
@@ -21,6 +22,7 @@ import type {
21
22
  ZLinkObjectCreationStore
22
23
  } from '../locations/internal-store-contracts';
23
24
  import { encodeAuthorityKey } from '../locations/authority-key-codec';
25
+ import { randomOperationId } from '../locations/creation-operation-id';
24
26
  import type {
25
27
  ServiceActorCreateRecord,
26
28
  ServiceUserSpotReservationFence
@@ -36,6 +38,18 @@ import {
36
38
  decodeLocationCreationContent,
37
39
  encodeLocationCreationContent
38
40
  } from './user-spot-creation-coordinator';
41
+ import {
42
+ decodeCreationOperationTerminalV1,
43
+ encodeCreationOperationTerminalV1,
44
+ enumWireFrameworkErrorCode,
45
+ enumWireRequestTerminalResult
46
+ } from '../protocol/service_wire_codec.generated';
47
+
48
+ const CREATION_TERMINAL_CODEC_CONTEXT = {
49
+ effectiveCompleteMessageBytesMinusActualEnvelopeOverhead: 1024 * 1024,
50
+ effectiveCompleteMessageBytes: 1024 * 1024,
51
+ runtimePredicates: {}
52
+ } as const;
39
53
 
40
54
  export interface ZLinkActorPlacementTarget {
41
55
  readonly meshName: string;
@@ -44,6 +58,8 @@ export interface ZLinkActorPlacementTarget {
44
58
  readonly entrySpotId: string;
45
59
  readonly owner: ZLinkLocationOwnerToken;
46
60
  readonly isLocal: boolean;
61
+ readonly sourceNodeRid: RoutingId;
62
+ readonly sourceNodeGeneration: bigint;
47
63
  }
48
64
 
49
65
  export interface ZLinkActorPlacementCoordinatorOptions {
@@ -57,7 +73,7 @@ export interface ZLinkActorPlacementCoordinatorOptions {
57
73
  readonly remoteCreate: (
58
74
  meshName: string,
59
75
  targetNodeRid: string,
60
- request: Omit<ServiceActorCreateRecord, 'kind' | 'correlation' | 'operation'>,
76
+ request: Omit<ServiceActorCreateRecord, 'kind' | 'correlation'>,
61
77
  timeoutMs: number
62
78
  ) => Promise<ServiceUserSpotOperationResult>;
63
79
  readonly decodeRemoteReply?: (payload: Uint8Array) => unknown;
@@ -81,6 +97,7 @@ export class ZLinkActorPlacementCoordinator {
81
97
  const contentReference = encodeLocationCreationContent(requestPayload);
82
98
  const requestSha256 = createHash('sha256').update(requestPayload).digest();
83
99
  const excluded = new Set<string>();
100
+ const operationId = randomOperationId();
84
101
  try {
85
102
  for (;;) {
86
103
  const target = await this.options.target(meshName, stableType, deadline.signal, excluded);
@@ -150,18 +167,39 @@ export class ZLinkActorPlacementCoordinator {
150
167
  true
151
168
  );
152
169
  }
153
- const remote = await this.options.remoteCreate(
154
- target.meshName,
155
- String(target.nodeRid),
156
- actorCreateRecord(
157
- actorId,
158
- stableType,
159
- reserved.creating,
160
- target,
161
- deadlineUnixMs
162
- ),
163
- Math.max(1, Math.ceil(deadlineMs - performance.now()))
164
- );
170
+ const operation: ZLinkCreationOperationIdentity = {
171
+ sourceNodeRid: target.sourceNodeRid,
172
+ sourceNodeGeneration: target.sourceNodeGeneration,
173
+ operationId
174
+ };
175
+ let remote: ServiceUserSpotOperationResult | undefined;
176
+ let transportFailure: unknown;
177
+ try {
178
+ remote = await this.options.remoteCreate(
179
+ target.meshName,
180
+ String(target.nodeRid),
181
+ actorCreateRecord(
182
+ actorId,
183
+ stableType,
184
+ reserved.creating,
185
+ target,
186
+ deadlineUnixMs,
187
+ operation
188
+ ),
189
+ Math.max(1, Math.ceil(deadlineMs - performance.now()))
190
+ );
191
+ } catch (error) {
192
+ transportFailure = error;
193
+ }
194
+ if (
195
+ remote === undefined || !isCompletedActorCreate(remote)
196
+ ) {
197
+ const retained = await this.options.store.readCreationTerminal(operation);
198
+ if (retained.kind === 'found') {
199
+ remote = remoteActorCreateResult(retained.terminalEnvelope, target);
200
+ }
201
+ }
202
+ if (remote === undefined) throw transportFailure;
165
203
  if (
166
204
  remote.terminalResult !== RequestResult.Ok
167
205
  || remote.failureCode !== 0
@@ -235,14 +273,22 @@ export class ZLinkActorPlacementCoordinator {
235
273
  requestPayload: Uint8Array,
236
274
  authority: ZLinkAuthoritySnapshot,
237
275
  signal: AbortSignal
238
- ) => Promise<{
239
- readonly result: 'created' | 'rejected';
240
- readonly actor?: ActorRef;
241
- readonly reply?: Uint8Array;
242
- readonly onPublished?: () => void;
243
- readonly entrySpotId?: string;
244
- readonly entrySpotGeneration?: bigint;
245
- }>,
276
+ ) => Promise<
277
+ | {
278
+ readonly result: 'created';
279
+ readonly actor: ActorRef;
280
+ readonly reply?: Uint8Array;
281
+ readonly onPublished?: () => void;
282
+ readonly entrySpotId: string;
283
+ readonly entrySpotGeneration: bigint;
284
+ }
285
+ | {
286
+ readonly result: 'rejected';
287
+ readonly reply?: Uint8Array;
288
+ readonly onPublished?: () => void;
289
+ }
290
+ | { readonly result: 'failed'; readonly error: unknown }
291
+ >,
246
292
  signal: AbortSignal
247
293
  ): Promise<ServiceUserSpotOperationResult> {
248
294
  const key = { kind: 'actor' as const, globalId: record.actorId };
@@ -277,39 +323,50 @@ export class ZLinkActorPlacementCoordinator {
277
323
  let completion: Awaited<ReturnType<ZLinkObjectCreationStore['completeCreation']>>;
278
324
  try {
279
325
  local = await materialize(requestPayload, current, signal);
280
- const terminal = encodeActorTerminal(
281
- record.correlation,
282
- local.result,
283
- local.actor
284
- );
285
- completion = await this.options.store.completeCreation({
286
- key,
287
- reservationId: record.reservation.reservationId,
288
- expectedStoreVersion: current.storeVersion.value,
289
- target: creationTarget(current),
290
- completion: local.result === 'created'
291
- ? {
292
- kind: 'created',
293
- readyPayload: encodeActorAuthorityIdentity({
294
- actorType: record.stableType,
295
- actor: local.actor!,
296
- meshName: current.allocation.descriptor.meshName,
297
- ownerNodeGeneration: current.allocation.descriptorLifecycleGeneration,
298
- owner: {
299
- ownerId: current.ownerId,
300
- leaseGeneration: current.ownerLeaseGeneration
301
- },
302
- spotId: local.entrySpotId!,
303
- spotGeneration: local.entrySpotGeneration!,
304
- spotKind: ZLinkSpotKind.Entry
305
- }),
306
- terminal: terminalPublication(record, terminal)
307
- }
308
- : {
309
- kind: 'rejected',
310
- terminal: terminalPublication(record, terminal)
311
- }
312
- }, signal);
326
+ if (local.result === 'failed') {
327
+ completion = await this.options.store.completeCreation({
328
+ key,
329
+ reservationId: record.reservation.reservationId,
330
+ expectedStoreVersion: current.storeVersion.value,
331
+ target: creationTarget(current),
332
+ completion: {
333
+ kind: 'failed',
334
+ terminal: terminalPublication(record, encodeActorTerminal({ result: 'failed' }))
335
+ }
336
+ }, signal);
337
+ } else {
338
+ const terminal = encodeActorTerminal(local.result === 'created'
339
+ ? { result: 'created', actor: local.actor, reply: local.reply }
340
+ : { result: 'rejected', reply: local.reply });
341
+ completion = await this.options.store.completeCreation({
342
+ key,
343
+ reservationId: record.reservation.reservationId,
344
+ expectedStoreVersion: current.storeVersion.value,
345
+ target: creationTarget(current),
346
+ completion: local.result === 'created'
347
+ ? {
348
+ kind: 'created',
349
+ readyPayload: encodeActorAuthorityIdentity({
350
+ actorType: record.stableType,
351
+ actor: local.actor,
352
+ meshName: current.allocation.descriptor.meshName,
353
+ ownerNodeGeneration: current.allocation.descriptorLifecycleGeneration,
354
+ owner: {
355
+ ownerId: current.ownerId,
356
+ leaseGeneration: current.ownerLeaseGeneration
357
+ },
358
+ spotId: local.entrySpotId,
359
+ spotGeneration: local.entrySpotGeneration,
360
+ spotKind: ZLinkSpotKind.Entry
361
+ }),
362
+ terminal: terminalPublication(record, terminal)
363
+ }
364
+ : {
365
+ kind: 'rejected',
366
+ terminal: terminalPublication(record, terminal)
367
+ }
368
+ }, signal);
369
+ }
313
370
  } catch (error) {
314
371
  const cleanup = createDeadline(1_000);
315
372
  try {
@@ -329,6 +386,15 @@ export class ZLinkActorPlacementCoordinator {
329
386
  }
330
387
  throw error;
331
388
  }
389
+ if (local.result === 'failed') {
390
+ if (completion.kind !== 'failed' && completion.kind !== 'alreadyCompleted') {
391
+ throw createInternalFrameworkException(
392
+ ZLinkFrameworkInternalErrorKind.ActorCreateFailed,
393
+ `Actor '${record.actorId}' failed creation did not record its terminal.`
394
+ );
395
+ }
396
+ throw local.error;
397
+ }
332
398
  if (
333
399
  local.result === 'created'
334
400
  ? completion.kind !== 'created' && completion.kind !== 'alreadyCompleted'
@@ -339,8 +405,11 @@ export class ZLinkActorPlacementCoordinator {
339
405
  `Actor '${record.actorId}' creation completion lost its reservation.`
340
406
  );
341
407
  }
342
- if (local.result === 'created') local.onPublished?.();
343
- return actorResult(local.result, local.actor, local.reply);
408
+ if (local.result === 'created') {
409
+ local.onPublished?.();
410
+ return actorResult('created', local.actor, local.reply);
411
+ }
412
+ return actorResult('rejected', undefined, local.reply);
344
413
  }
345
414
  }
346
415
 
@@ -404,13 +473,15 @@ function actorCreateRecord(
404
473
  stableType: string,
405
474
  snapshot: ZLinkAuthoritySnapshot,
406
475
  target: ZLinkActorPlacementTarget,
407
- deadlineUnixMs: number
408
- ): Omit<ServiceActorCreateRecord, 'kind' | 'correlation' | 'operation'> {
476
+ deadlineUnixMs: number,
477
+ operation: ZLinkCreationOperationIdentity
478
+ ): Omit<ServiceActorCreateRecord, 'kind' | 'correlation'> {
409
479
  const pending = snapshot.pendingCreation;
410
480
  if (pending === undefined) throw new Error('Actor reservation omitted Pending creation.');
411
481
  return {
412
- sourceNodeRid: '',
413
- sourceNodeGeneration: 1n,
482
+ sourceNodeRid: String(operation.sourceNodeRid),
483
+ sourceNodeGeneration: operation.sourceNodeGeneration,
484
+ operation: operation.operationId,
414
485
  actorId,
415
486
  stableType,
416
487
  reservation: reservationFence(snapshot, target, pending.reservationId),
@@ -509,23 +580,92 @@ function actorResult(
509
580
  };
510
581
  }
511
582
 
512
- function encodeActorTerminal(
513
- correlation: bigint,
514
- result: 'created' | 'rejected',
515
- actor?: ActorRef
516
- ): Buffer {
517
- return Buffer.from(JSON.stringify({
518
- correlation: correlation.toString(),
519
- result,
520
- actor: actor === undefined
521
- ? undefined
583
+ function isCompletedActorCreate(result: ServiceUserSpotOperationResult): boolean {
584
+ return result.terminalResult === RequestResult.Ok
585
+ && result.failureCode === 0
586
+ && result.tail?.kind === 'actorCreate';
587
+ }
588
+
589
+ type ActorTerminal =
590
+ | { readonly result: 'created'; readonly actor: ActorRef; readonly reply?: Uint8Array }
591
+ | { readonly result: 'rejected'; readonly reply?: Uint8Array }
592
+ | { readonly result: 'failed' };
593
+
594
+ function encodeActorTerminal(terminal: ActorTerminal): Buffer {
595
+ if (terminal.result === 'failed') {
596
+ return Buffer.from(encodeCreationOperationTerminalV1({
597
+ terminalResult: 'internalError',
598
+ failureCode: 'actorCreateFailed',
599
+ hasCreation: 'false',
600
+ hasApplicationPayload: 'false'
601
+ }, CREATION_TERMINAL_CODEC_CONTEXT));
602
+ }
603
+ return Buffer.from(encodeCreationOperationTerminalV1({
604
+ terminalResult: 'ok',
605
+ failureCode: 'none',
606
+ hasCreation: 'true',
607
+ creation: terminal.result === 'rejected'
608
+ ? { createResult: 'rejected' }
522
609
  : {
523
- actorId: actor.actorId,
524
- generation: actor.objectGeneration.toString(),
525
- meshName: actor.meshName,
526
- nodeRid: String(actor.nodeRid)
527
- }
528
- }));
610
+ createResult: 'created',
611
+ actor: {
612
+ actorId: terminal.actor.actorId,
613
+ objectGeneration: terminal.actor.objectGeneration
614
+ }
615
+ },
616
+ hasApplicationPayload: terminal.reply === undefined ? 'false' : 'true',
617
+ ...(terminal.reply === undefined
618
+ ? {}
619
+ : {
620
+ applicationPayload: {
621
+ packetName: 'ZLinkFrameworkActorCreateReply',
622
+ contentType: 'application/octet-stream',
623
+ payload: Buffer.from(terminal.reply)
624
+ }
625
+ })
626
+ }, CREATION_TERMINAL_CODEC_CONTEXT));
627
+ }
628
+
629
+ function remoteActorCreateResult(
630
+ terminalEnvelope: Uint8Array,
631
+ target: ZLinkActorPlacementTarget
632
+ ): ServiceUserSpotOperationResult {
633
+ const terminal = decodeCreationOperationTerminalV1(
634
+ terminalEnvelope,
635
+ CREATION_TERMINAL_CODEC_CONTEXT
636
+ );
637
+ const terminalResult = enumWireRequestTerminalResult(terminal.terminalResult);
638
+ const failureCode = enumWireFrameworkErrorCode(terminal.failureCode);
639
+ if (terminal.hasCreation === 'false' || terminal.creation === undefined) {
640
+ return { terminalResult, failureCode };
641
+ }
642
+ const creation = terminal.creation;
643
+ return {
644
+ terminalResult,
645
+ failureCode,
646
+ tail: {
647
+ kind: 'actorCreate',
648
+ createResult: creation.createResult,
649
+ ...(creation.createResult === 'rejected'
650
+ ? {}
651
+ : {
652
+ actor: {
653
+ actorId: creation.actor.actorId,
654
+ generation: creation.actor.objectGeneration,
655
+ nodeRid: String(target.nodeRid)
656
+ }
657
+ })
658
+ },
659
+ ...(terminal.applicationPayload === undefined
660
+ ? {}
661
+ : {
662
+ payload: {
663
+ packetName: terminal.applicationPayload.packetName,
664
+ contentType: terminal.applicationPayload.contentType,
665
+ payload: Buffer.from(terminal.applicationPayload.payload)
666
+ }
667
+ })
668
+ };
529
669
  }
530
670
 
531
671
  function terminalPublication(record: ServiceActorCreateRecord, terminalEnvelope: Uint8Array) {
@@ -536,7 +676,6 @@ function terminalPublication(record: ServiceActorCreateRecord, terminalEnvelope:
536
676
  operationId: record.operation
537
677
  },
538
678
  terminalEnvelope,
539
- terminalEnvelopeSha256: createHash('sha256').update(terminalEnvelope).digest(),
540
679
  operationDeadline: new Date(Number(record.deadlineUnixMs))
541
680
  };
542
681
  }