@zlink-systems/framework 0.29.0 → 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.
Files changed (168) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Configuration/Builders.d.ts +1 -2
  3. package/dist/contracts/Configuration/Configs.d.ts +0 -5
  4. package/dist/contracts/Configuration/RegistrationBuilders.js +6 -4
  5. package/dist/contracts/Configuration/RegistrationTypes.d.ts +1 -6
  6. package/dist/contracts/Configuration/RegistrationValidators.js +4 -4
  7. package/dist/contracts/Configuration/index.d.ts +1 -1
  8. package/dist/internal.d.ts +1 -0
  9. package/dist/internal.js +3 -1
  10. package/dist/runtime/actors/actor-handoff.d.ts +3 -2
  11. package/dist/runtime/actors/actor-handoff.js +9 -4
  12. package/dist/runtime/actors/actor-runtime-contracts.d.ts +1 -0
  13. package/dist/runtime/actors/index.d.ts +2 -0
  14. package/dist/runtime/actors/index.js +21 -6
  15. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +3 -1
  16. package/dist/runtime/application-jobs/application-ingress-record-owner.js +6 -1
  17. package/dist/runtime/application-jobs/application-job-queue-scope.d.ts +3 -1
  18. package/dist/runtime/application-jobs/application-job-queue-scope.js +5 -0
  19. package/dist/runtime/application-jobs/contracts.d.ts +5 -1
  20. package/dist/runtime/backend/contracts/index.d.ts +10 -19
  21. package/dist/runtime/backend/mesh-completion-table.d.ts +6 -4
  22. package/dist/runtime/backend/mesh-completion-table.js +28 -18
  23. package/dist/runtime/backend/mesh-dispatch-pump.js +21 -25
  24. package/dist/runtime/backend/node/node-backend-adapter-factory.js +6 -1
  25. package/dist/runtime/backend/node/node-backend-adapter-support.d.ts +3 -3
  26. package/dist/runtime/backend/node/node-backend-adapter-support.js +24 -29
  27. package/dist/runtime/backend/node/node-mesh-backend-adapter.d.ts +1 -9
  28. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +1 -1
  29. package/dist/runtime/backend/node/node-raw-binding-port.js +11 -6
  30. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +10 -12
  31. package/dist/runtime/backend/node/node-raw-mesh-backend.js +64 -98
  32. package/dist/runtime/backend/node/node-socket-backend-adapter.js +10 -5
  33. package/dist/runtime/backend/raw-binding-port.d.ts +9 -2
  34. package/dist/runtime/channels/channel-receive-loops.js +3 -3
  35. package/dist/runtime/channels/channel-runtime-lifecycle.js +2 -1
  36. package/dist/runtime/channels/channel-socket-registry.d.ts +10 -3
  37. package/dist/runtime/channels/channel-socket-registry.js +59 -82
  38. package/dist/runtime/diagnostics/message-flow.d.ts +1 -0
  39. package/dist/runtime/diagnostics/message-flow.js +3 -3
  40. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +15 -1
  41. package/dist/runtime/foundation/raw-service-mesh-runtime.js +193 -64
  42. package/dist/runtime/foundation/service-liveness-registry.d.ts +20 -8
  43. package/dist/runtime/foundation/service-liveness-registry.js +57 -44
  44. package/dist/runtime/foundation/service-mailbox.d.ts +3 -0
  45. package/dist/runtime/foundation/service-mailbox.js +2 -1
  46. package/dist/runtime/foundation/service-stateful-runtime.d.ts +7 -6
  47. package/dist/runtime/foundation/service-stateful-runtime.js +60 -41
  48. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +5 -2
  49. package/dist/runtime/foundation/service-stateful-wire-codec.js +43 -61
  50. package/dist/runtime/foundation/service-topology-registry.d.ts +5 -3
  51. package/dist/runtime/foundation/service-topology-registry.js +9 -4
  52. package/dist/runtime/handlers/handler-instance-scope.js +1 -1
  53. package/dist/runtime/host/actor-packet-relay.js +7 -7
  54. package/dist/runtime/host/actor-placement-coordinator.d.ts +2 -1
  55. package/dist/runtime/host/actor-placement-coordinator.js +2 -1
  56. package/dist/runtime/host/actor-runtime-options-factory.d.ts +2 -1
  57. package/dist/runtime/host/actor-runtime-options-factory.js +2 -1
  58. package/dist/runtime/host/application-job-queue.d.ts +11 -4
  59. package/dist/runtime/host/application-job-queue.js +66 -10
  60. package/dist/runtime/host/entry-actor-runtime.js +1 -1
  61. package/dist/runtime/host/index.d.ts +1 -1
  62. package/dist/runtime/host/index.js +18 -13
  63. package/dist/runtime/host/instance-activation-authority.d.ts +2 -1
  64. package/dist/runtime/host/instance-activation-authority.js +89 -25
  65. package/dist/runtime/host/service-relocation-host-runtime.js +2 -2
  66. package/dist/runtime/host/spot-address-transport.d.ts +1 -0
  67. package/dist/runtime/host/spot-address-transport.js +25 -12
  68. package/dist/runtime/host/spot-runtime-options-factory.js +2 -2
  69. package/dist/runtime/host/user-spot-creation-coordinator.d.ts +1 -0
  70. package/dist/runtime/host/user-spot-creation-coordinator.js +1 -0
  71. package/dist/runtime/locations/domain-store-contract.d.ts +3 -2
  72. package/dist/runtime/locations/in-memory-authority-store.d.ts +5 -2
  73. package/dist/runtime/locations/in-memory-authority-store.js +107 -14
  74. package/dist/runtime/locations/in-memory-location-store.d.ts +3 -3
  75. package/dist/runtime/locations/in-memory-location-store.js +31 -13
  76. package/dist/runtime/locations/internal-location-contracts.d.ts +8 -0
  77. package/dist/runtime/locations/location-store-repository.d.ts +4 -2
  78. package/dist/runtime/locations/location-store-repository.js +209 -33
  79. package/dist/runtime/locations/resolvers.d.ts +2 -1
  80. package/dist/runtime/locations/resolvers.js +10 -1
  81. package/dist/runtime/messaging/submission-result.d.ts +1 -1
  82. package/dist/runtime/messaging/submission-result.js +2 -2
  83. package/dist/runtime/spots/formal-remote-actor-admission-registry.d.ts +2 -0
  84. package/dist/runtime/spots/index.d.ts +4 -3
  85. package/dist/runtime/spots/index.js +14 -14
  86. package/dist/runtime/spots/spot-activation.d.ts +2 -1
  87. package/dist/runtime/spots/spot-activation.js +4 -4
  88. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +2 -1
  89. package/dist/runtime/spots/spot-actor-admission-coordinator.js +11 -10
  90. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +4 -1
  91. package/dist/runtime/spots/spot-actor-packet-dispatch.js +7 -5
  92. package/dist/runtime/spots/spot-actor-packet-drain.js +2 -0
  93. package/dist/runtime/spots/spot-actor-packet-relay-dispatch.js +1 -0
  94. package/dist/runtime/spots/spot-entry-activation.d.ts +1 -0
  95. package/dist/runtime/spots/spot-entry-activation.js +6 -1
  96. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +3 -1
  97. package/dist/runtime/spots/spot-node-runtime-manager.js +10 -11
  98. package/dist/runtime/spots/spot-routing-internal.d.ts +6 -0
  99. package/dist/runtime/spots/spot-runtime-ports.d.ts +2 -1
  100. package/dist/runtime/spots/spot-serial-executor.d.ts +1 -0
  101. package/dist/runtime/spots/spot-serial-executor.js +10 -1
  102. package/dist/runtime/streams/stream-session-runtime.js +1 -1
  103. package/package.json +3 -3
  104. package/src/contracts/Configuration/Builders.ts +1 -2
  105. package/src/contracts/Configuration/Configs.ts +0 -6
  106. package/src/contracts/Configuration/RegistrationBuilders.ts +8 -12
  107. package/src/contracts/Configuration/RegistrationTypes.ts +1 -6
  108. package/src/contracts/Configuration/RegistrationValidators.ts +9 -10
  109. package/src/contracts/Configuration/index.ts +1 -6
  110. package/src/internal.ts +1 -0
  111. package/src/runtime/actors/actor-handoff.ts +13 -4
  112. package/src/runtime/actors/actor-runtime-contracts.ts +4 -0
  113. package/src/runtime/actors/index.ts +77 -42
  114. package/src/runtime/application-jobs/application-ingress-record-owner.ts +12 -2
  115. package/src/runtime/application-jobs/application-job-queue-scope.ts +6 -1
  116. package/src/runtime/application-jobs/contracts.ts +14 -1
  117. package/src/runtime/backend/contracts/index.ts +10 -18
  118. package/src/runtime/backend/mesh-completion-table.ts +36 -19
  119. package/src/runtime/backend/mesh-dispatch-pump.ts +19 -22
  120. package/src/runtime/backend/node/node-backend-adapter-factory.ts +12 -5
  121. package/src/runtime/backend/node/node-backend-adapter-support.ts +31 -29
  122. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +3 -10
  123. package/src/runtime/backend/node/node-raw-binding-port.ts +19 -10
  124. package/src/runtime/backend/node/node-raw-mesh-backend.ts +81 -111
  125. package/src/runtime/backend/node/node-socket-backend-adapter.ts +12 -8
  126. package/src/runtime/backend/raw-binding-port.ts +12 -2
  127. package/src/runtime/channels/channel-receive-loops.ts +3 -3
  128. package/src/runtime/channels/channel-runtime-lifecycle.ts +7 -1
  129. package/src/runtime/channels/channel-socket-registry.ts +84 -130
  130. package/src/runtime/diagnostics/message-flow.ts +1 -1
  131. package/src/runtime/foundation/raw-service-mesh-runtime.ts +268 -70
  132. package/src/runtime/foundation/service-liveness-registry.ts +68 -59
  133. package/src/runtime/foundation/service-mailbox.ts +7 -1
  134. package/src/runtime/foundation/service-stateful-runtime.ts +109 -77
  135. package/src/runtime/foundation/service-stateful-wire-codec.ts +55 -79
  136. package/src/runtime/foundation/service-topology-registry.ts +13 -7
  137. package/src/runtime/handlers/handler-instance-scope.ts +2 -1
  138. package/src/runtime/host/actor-packet-relay.ts +7 -0
  139. package/src/runtime/host/actor-placement-coordinator.ts +8 -2
  140. package/src/runtime/host/actor-runtime-options-factory.ts +7 -0
  141. package/src/runtime/host/application-job-queue.ts +77 -10
  142. package/src/runtime/host/entry-actor-runtime.ts +1 -0
  143. package/src/runtime/host/index.ts +30 -7
  144. package/src/runtime/host/instance-activation-authority.ts +132 -27
  145. package/src/runtime/host/service-relocation-host-runtime.ts +2 -0
  146. package/src/runtime/host/spot-address-transport.ts +45 -22
  147. package/src/runtime/host/spot-runtime-options-factory.ts +2 -0
  148. package/src/runtime/host/user-spot-creation-coordinator.ts +8 -0
  149. package/src/runtime/locations/domain-store-contract.ts +6 -2
  150. package/src/runtime/locations/in-memory-authority-store.ts +153 -12
  151. package/src/runtime/locations/in-memory-location-store.ts +39 -13
  152. package/src/runtime/locations/internal-location-contracts.ts +10 -0
  153. package/src/runtime/locations/location-store-repository.ts +295 -36
  154. package/src/runtime/locations/resolvers.ts +25 -2
  155. package/src/runtime/messaging/submission-result.ts +2 -2
  156. package/src/runtime/spots/formal-remote-actor-admission-registry.ts +2 -0
  157. package/src/runtime/spots/index.ts +16 -0
  158. package/src/runtime/spots/spot-activation.ts +5 -0
  159. package/src/runtime/spots/spot-actor-admission-coordinator.ts +13 -0
  160. package/src/runtime/spots/spot-actor-packet-dispatch.ts +10 -0
  161. package/src/runtime/spots/spot-actor-packet-drain.ts +2 -0
  162. package/src/runtime/spots/spot-actor-packet-relay-dispatch.ts +1 -0
  163. package/src/runtime/spots/spot-entry-activation.ts +7 -0
  164. package/src/runtime/spots/spot-node-runtime-manager.ts +14 -12
  165. package/src/runtime/spots/spot-routing-internal.ts +14 -0
  166. package/src/runtime/spots/spot-runtime-ports.ts +2 -0
  167. package/src/runtime/spots/spot-serial-executor.ts +10 -4
  168. package/src/runtime/streams/stream-session-runtime.ts +1 -1
@@ -14,6 +14,7 @@ import {
14
14
  encodeInstanceRouteV1,
15
15
  encodeSpotRequestCommand,
16
16
  encodeSpotSendCommand,
17
+ type InstanceRouteV1,
17
18
  type ServiceWireDecoderContext
18
19
  } from '../protocol/service_wire_codec.generated';
19
20
  import {
@@ -82,8 +83,6 @@ import {
82
83
  import { APPLICATION_PAYLOAD_VERSION, ServiceWireProtocolError } from './service-wire-m6a-codec';
83
84
  const MESSAGE_FOLLOW_MAX_BODY_BYTES = 16 * 1024 * 1024;
84
85
  const MESSAGE_FOLLOW_VERSION = 1;
85
- const INSTANCE_ROUTE_VERSION = 1;
86
- const INSTANCE_ACTIVATION_VERSION = 2;
87
86
 
88
87
  const PREFIX_SIZE = SERVICE_WIRE_PREFIX_SIZE;
89
88
  const MAGIC_0 = SERVICE_WIRE_MAGIC[0];
@@ -946,6 +945,10 @@ export interface ServiceInstanceActivationTarget {
946
945
  readonly descriptorVersion: string;
947
946
  }
948
947
 
948
+ export interface ServiceInstanceColdActivationTarget extends ServiceInstanceActivationTarget {
949
+ readonly targetMeshName: string;
950
+ }
951
+
949
952
  export interface ServiceUserSpotReservationFence {
950
953
  readonly reservationId: string;
951
954
  readonly expectedStoreVersion: string;
@@ -1105,7 +1108,7 @@ export type ServiceStatefulWireRecord =
1105
1108
  | {
1106
1109
  readonly kind: 'instanceSpot';
1107
1110
  readonly activation: 'missing';
1108
- readonly target: ServiceInstanceActivationTarget;
1111
+ readonly target: ServiceInstanceColdActivationTarget;
1109
1112
  readonly sourceNodeGeneration: bigint;
1110
1113
  readonly sourceNodeRid: string;
1111
1114
  readonly sourceSpotId?: string;
@@ -1435,7 +1438,7 @@ export function encodeInstanceSpotHeader(
1435
1438
  }
1436
1439
 
1437
1440
  export function encodeInstanceSpotActivationHeader(
1438
- target: ServiceInstanceActivationTarget,
1441
+ target: ServiceInstanceColdActivationTarget,
1439
1442
  sourceNodeGeneration: bigint,
1440
1443
  sourceNodeRid: string,
1441
1444
  sourceSpotId: string | undefined,
@@ -1454,25 +1457,28 @@ export function encodeInstanceSpotActivationHeader(
1454
1457
  if (operationKind === 'send' && replyRouteId !== undefined) {
1455
1458
  throw new RangeError('Instance Spot send must not carry a reply route.');
1456
1459
  }
1457
- const targetBody = concat(
1458
- rid(target.targetNodeRid, 'targetNodeRid'),
1459
- u64(target.targetNodeGeneration),
1460
- rid(target.targetSpotId, 'targetSpotId'),
1461
- text16(target.stableType, 'stableType'),
1462
- text16(target.descriptorVersion, 'descriptorVersion')
1460
+ const encodedRoute = encodeInstanceRouteV1(
1461
+ {
1462
+ routeKind: 'coldActivation',
1463
+ targetNodeRid: toGeneratedRoutingId(target.targetNodeRid, 'targetNodeRid'),
1464
+ targetNodeGeneration: target.targetNodeGeneration,
1465
+ targetSpotId: target.targetSpotId,
1466
+ targetMeshName: target.targetMeshName,
1467
+ stableType: target.stableType,
1468
+ targetDescriptorVersion: target.descriptorVersion,
1469
+ deadlineUnixMs
1470
+ },
1471
+ STATEFUL_MESSAGE_CONTEXT
1463
1472
  );
1464
1473
  return concat(
1465
1474
  prefix(M6bServiceWireCommand.instanceSpot, hasMetadata ? M6bServiceWireFlag.metadata : 0),
1466
- Buffer.of(INSTANCE_ACTIVATION_VERSION),
1467
- u16(targetBody.byteLength),
1468
- targetBody,
1475
+ encodedRoute,
1469
1476
  u64Any(sourceNodeGeneration),
1470
1477
  rid(sourceNodeRid, 'sourceNodeRid'),
1471
1478
  optionalRid(sourceSpotId),
1472
1479
  Buffer.of(operationKind === 'send' ? 1 : 2),
1473
1480
  u64Any(operation.high),
1474
1481
  u64Any(operation.low),
1475
- u64(deadlineUnixMs),
1476
1482
  ...(operationKind === 'request' ? [u64(requirePositive(replyRouteId, 'replyRouteId'))] : [])
1477
1483
  );
1478
1484
  }
@@ -1741,53 +1747,31 @@ export function decodeStatefulHeader(
1741
1747
  fail(`Invalid command flags '${command.flags}'.`);
1742
1748
  }
1743
1749
  const routeStart = reader.offset;
1744
- const version = reader.u8('instanceRoute.version');
1745
- if (version !== INSTANCE_ROUTE_VERSION && version !== INSTANCE_ACTIVATION_VERSION)
1746
- fail('Unsupported Instance route version.');
1750
+ reader.u8('instanceRoute.kind');
1747
1751
  const routeLength = reader.u16('instanceRoute.length');
1748
1752
  const routeEnd = reader.offset + routeLength;
1749
- const generatedReadyRoute =
1750
- version === INSTANCE_ROUTE_VERSION
1751
- ? decodeInstanceRouteV1(
1752
- reader.bytes.subarray(routeStart, routeEnd),
1753
- STATEFUL_MESSAGE_CONTEXT
1754
- )
1755
- : undefined;
1756
- if (generatedReadyRoute !== undefined && generatedReadyRoute.routeKind !== 'ready') {
1757
- fail('Invalid Ready Instance route kind.');
1758
- }
1759
- const commonTarget =
1760
- generatedReadyRoute?.routeKind === 'ready'
1761
- ? {
1762
- targetNodeRid: fromGeneratedRoutingId(
1763
- generatedReadyRoute.targetNodeRid,
1764
- 'targetNodeRid'
1765
- ),
1766
- targetNodeGeneration: generatedReadyRoute.targetNodeGeneration,
1767
- targetSpotId: generatedReadyRoute.targetSpotId
1768
- }
1769
- : {
1770
- targetNodeRid: reader.rid('targetNodeRid'),
1771
- targetNodeGeneration: reader.nonZeroU64('targetNodeGeneration'),
1772
- targetSpotId: reader.rid('targetSpotId')
1773
- };
1774
- const route: ServiceInstanceRouteFence | undefined =
1775
- generatedReadyRoute?.routeKind === 'ready'
1776
- ? {
1777
- ...commonTarget,
1778
- ...generatedReadyRoute.authority
1779
- }
1753
+ const generatedRoute = decodeCanonicalInstanceRoute(
1754
+ reader.bytes.subarray(routeStart, routeEnd)
1755
+ );
1756
+ const commonTarget = {
1757
+ targetNodeRid: fromGeneratedRoutingId(generatedRoute.targetNodeRid, 'targetNodeRid'),
1758
+ targetNodeGeneration: generatedRoute.targetNodeGeneration,
1759
+ targetSpotId: generatedRoute.targetSpotId
1760
+ };
1761
+ const route =
1762
+ generatedRoute.routeKind === 'ready'
1763
+ ? { ...commonTarget, ...generatedRoute.authority }
1780
1764
  : undefined;
1781
- const target: ServiceInstanceActivationTarget | undefined =
1782
- version === INSTANCE_ACTIVATION_VERSION
1765
+ const target =
1766
+ generatedRoute.routeKind === 'coldActivation'
1783
1767
  ? {
1784
1768
  ...commonTarget,
1785
- stableType: reader.text16('stableType'),
1786
- descriptorVersion: reader.text16('descriptorVersion')
1769
+ targetMeshName: generatedRoute.targetMeshName,
1770
+ stableType: generatedRoute.stableType,
1771
+ descriptorVersion: generatedRoute.targetDescriptorVersion
1787
1772
  }
1788
1773
  : undefined;
1789
- if (generatedReadyRoute !== undefined) reader.offset = routeEnd;
1790
- if (reader.offset !== routeEnd) fail('Invalid Instance route body length.');
1774
+ reader.offset = routeEnd;
1791
1775
  const sourceNodeGeneration = reader.nonZeroU64('sourceNodeGeneration');
1792
1776
  const sourceNodeRid = reader.rid('sourceNodeRid');
1793
1777
  const sourceSpotId = reader.optionalRid('sourceSpotId');
@@ -1799,21 +1783,21 @@ export function decodeStatefulHeader(
1799
1783
  };
1800
1784
  const operationKind = operationValue === 1 ? ('send' as const) : ('request' as const);
1801
1785
  if (
1802
- version === INSTANCE_ROUTE_VERSION &&
1786
+ generatedRoute.routeKind === 'ready' &&
1803
1787
  ((operationKind === 'send' && (operation.high !== 0n || operation.low !== 0n)) ||
1804
1788
  (operationKind === 'request' && operation.high === 0n && operation.low === 0n))
1805
1789
  ) {
1806
1790
  fail('Invalid Instance operation identity.');
1807
1791
  }
1808
1792
  if (
1809
- version === INSTANCE_ACTIVATION_VERSION &&
1793
+ generatedRoute.routeKind === 'coldActivation' &&
1810
1794
  operation.high === 0n &&
1811
1795
  operation.low === 0n
1812
1796
  ) {
1813
1797
  fail('Instance activation requires a non-zero operation identity.');
1814
1798
  }
1815
1799
  const deadlineUnixMs =
1816
- version === INSTANCE_ACTIVATION_VERSION ? reader.nonZeroU64('deadlineUnixMs') : undefined;
1800
+ generatedRoute.routeKind === 'coldActivation' ? generatedRoute.deadlineUnixMs : undefined;
1817
1801
  const replyRouteId =
1818
1802
  operationKind === 'request' ? reader.nonZeroU64('replyRouteId') : undefined;
1819
1803
  reader.end();
@@ -1826,12 +1810,12 @@ export function decodeStatefulHeader(
1826
1810
  operation,
1827
1811
  ...(replyRouteId === undefined ? {} : { replyRouteId })
1828
1812
  };
1829
- return version === INSTANCE_ROUTE_VERSION
1813
+ return generatedRoute.routeKind === 'ready'
1830
1814
  ? {
1831
1815
  ...common,
1832
1816
  activation: 'ready',
1833
1817
  route: route!,
1834
- instanceIntent: generatedReadyRoute!.instanceIntent === 'true'
1818
+ instanceIntent: generatedRoute.instanceIntent === 'true'
1835
1819
  }
1836
1820
  : {
1837
1821
  ...common,
@@ -2387,11 +2371,6 @@ function text8(value: string, name: string): Buffer {
2387
2371
  return sized8(value, name);
2388
2372
  }
2389
2373
 
2390
- function text16(value: string, name: string): Buffer {
2391
- const bytes = encodeCanonicalServiceWireText(value, name, 0xffff, outOfRange);
2392
- return concat(u16(bytes.byteLength), bytes);
2393
- }
2394
-
2395
2374
  function optionalRid(value: string | undefined): Buffer {
2396
2375
  return value === undefined ? Buffer.of(0) : rid(value, 'sourceSpotId');
2397
2376
  }
@@ -2448,6 +2427,15 @@ function concat(...parts: readonly Uint8Array[]): Buffer {
2448
2427
  return Buffer.concat(parts.map((part) => Buffer.from(part)));
2449
2428
  }
2450
2429
 
2430
+ function decodeCanonicalInstanceRoute(bytes: Uint8Array): InstanceRouteV1 {
2431
+ try {
2432
+ return decodeInstanceRouteV1(bytes, STATEFUL_MESSAGE_CONTEXT);
2433
+ } catch (error) {
2434
+ if (error instanceof RangeError) throw new ServiceWireProtocolError(error.message);
2435
+ throw error;
2436
+ }
2437
+ }
2438
+
2451
2439
  function fail(message: string): never {
2452
2440
  throw new ServiceWireProtocolError(message);
2453
2441
  }
@@ -3093,21 +3081,9 @@ class FrozenReader {
3093
3081
 
3094
3082
  private instanceRoute(): void {
3095
3083
  const start = this.offset;
3096
- const kind = this.u8('instanceRouteKind');
3097
- if (kind < 1 || kind > 2) fail('Invalid Instance route kind.');
3098
- const body = this.body16('Instance route');
3099
- if (kind === INSTANCE_ROUTE_VERSION) {
3100
- decodeInstanceRouteV1(this.bytes.subarray(start, this.offset), STATEFUL_MESSAGE_CONTEXT);
3101
- return;
3102
- }
3103
- body.rid8('targetNodeRid');
3104
- body.nonZeroU64('targetNodeGeneration');
3105
- body.text8('targetSpotId');
3106
- body.text8('targetMeshName');
3107
- body.text8('stableType');
3108
- body.text8('targetDescriptorVersion');
3109
- body.nonZeroU64('deadlineUnixMs');
3110
- body.end('Instance route');
3084
+ this.u8('instanceRouteKind');
3085
+ this.body16('Instance route');
3086
+ decodeCanonicalInstanceRoute(this.bytes.subarray(start, this.offset));
3111
3087
  }
3112
3088
 
3113
3089
  private relocationObject(): void {
@@ -1,4 +1,5 @@
1
1
  import { ZLINK_MAX_IDENTITY_TEXT_BYTES } from '../../contracts/Common/CoreTypes';
2
+ import type { ServiceLivenessConnection } from './service-liveness-registry';
2
3
  import {
3
4
  ZLINK_MAX_PUBLIC_WEIGHT,
4
5
  isValidCapacity,
@@ -43,7 +44,8 @@ export interface AdmittedServicePeer {
43
44
  readonly descriptor: ServiceNodeDescriptor;
44
45
  // The Core selected-route generation this admission belongs to (Core ROUTER
45
46
  // §10.1), as an opaque token compared only for equality.
46
- readonly connectionId: string;
47
+ readonly connectionId: string | bigint;
48
+ readonly liveness?: ServiceLivenessConnection;
47
49
  }
48
50
 
49
51
  export interface ServicePeerAdmissionExpectation {
@@ -103,12 +105,14 @@ export class ServiceTopologyRegistry {
103
105
 
104
106
  admit(
105
107
  descriptor: ServiceNodeDescriptor,
106
- connectionId: string,
107
- expected?: ServicePeerAdmissionExpectation
108
+ connectionId: string | bigint,
109
+ expected?: ServicePeerAdmissionExpectation,
110
+ admitLiveness?: () => ServiceLivenessConnection
108
111
  ): PeerAdmissionResult {
109
112
  try {
110
113
  validateDescriptor(descriptor);
111
- requireText(connectionId, 'connectionId');
114
+ if (typeof connectionId === 'string') requireText(connectionId, 'connectionId');
115
+ else if (connectionId === 0n) throw new TypeError('connectionId must be nonzero.');
112
116
  } catch (error) {
113
117
  return 'invalidDescriptor';
114
118
  }
@@ -154,14 +158,15 @@ export class ServiceTopologyRegistry {
154
158
  // that route replaces the admission of a route it replaced.
155
159
  this.peersByRid.set(descriptor.nodeRoutingId, {
156
160
  descriptor: cloneDescriptor(descriptor),
157
- connectionId
161
+ connectionId,
162
+ liveness: admitLiveness?.() ?? current?.liveness
158
163
  });
159
164
  this.remember(descriptor);
160
165
  this.rebuildSelections();
161
166
  return 'admitted';
162
167
  }
163
168
 
164
- disconnect(nodeRoutingId: string, connectionId: string): boolean {
169
+ disconnect(nodeRoutingId: string, connectionId: string | bigint): boolean {
165
170
  const current = this.peersByRid.get(nodeRoutingId);
166
171
  if (current === undefined || current.connectionId !== connectionId) return false;
167
172
  this.peersByRid.delete(nodeRoutingId);
@@ -525,7 +530,8 @@ function cloneDescriptor(descriptor: ServiceNodeDescriptor): ServiceNodeDescript
525
530
  function clonePeer(peer: AdmittedServicePeer): AdmittedServicePeer {
526
531
  return {
527
532
  descriptor: cloneDescriptor(peer.descriptor),
528
- connectionId: peer.connectionId
533
+ connectionId: peer.connectionId,
534
+ liveness: peer.liveness
529
535
  };
530
536
  }
531
537
 
@@ -212,7 +212,8 @@ class LifecycleHandlerInstanceScope {
212
212
  }
213
213
 
214
214
  async dispose(detachedTaskRunner: ZLinkDetachedTaskRunner): Promise<void> {
215
- const disposeFromActiveInvocation = activeLifecycleScope.getStore() === this;
215
+ const disposeFromActiveInvocation =
216
+ this.activeInvocations > 0 && activeLifecycleScope.getStore() === this;
216
217
  const disposal = this.beginDisposeCore();
217
218
  if (disposal.deferred !== undefined) {
218
219
  startOutsideLifecycleInvocation(() => {
@@ -303,6 +303,7 @@ export class ZLinkActorPacketRelay {
303
303
  const dispatch =
304
304
  state?.spotId === undefined
305
305
  ? this.requireSpotNodeRuntime().dispatchEntryActorPacket(
306
+ 'remote',
306
307
  relay.actorId,
307
308
  [header, body],
308
309
  false,
@@ -310,6 +311,7 @@ export class ZLinkActorPacketRelay {
310
311
  fallbackActorRef
311
312
  )
312
313
  : this.requireSpotManager().dispatchRoutedActorPacket(
314
+ 'remote',
313
315
  state.spotId,
314
316
  relay.actorId,
315
317
  [header, body],
@@ -356,6 +358,7 @@ export class ZLinkActorPacketRelay {
356
358
  const dispatch =
357
359
  state?.spotId === undefined
358
360
  ? this.requireSpotNodeRuntime().dispatchEntryActorPacket(
361
+ 'remote',
359
362
  relay.actorId,
360
363
  [header, body],
361
364
  false,
@@ -363,6 +366,7 @@ export class ZLinkActorPacketRelay {
363
366
  fallbackActorRef
364
367
  )
365
368
  : this.requireSpotManager().dispatchRoutedActorPacket(
369
+ 'remote',
366
370
  state.spotId,
367
371
  relay.actorId,
368
372
  [header, body],
@@ -423,6 +427,7 @@ export class ZLinkActorPacketRelay {
423
427
  const response =
424
428
  state?.spotId === undefined
425
429
  ? await this.requireSpotNodeRuntime().dispatchEntryActorPacket(
430
+ 'remote',
426
431
  relay.actorId,
427
432
  [header, body],
428
433
  relay.returnResponse === true,
@@ -430,6 +435,7 @@ export class ZLinkActorPacketRelay {
430
435
  fallbackActorRef
431
436
  )
432
437
  : await this.requireSpotManager().dispatchRoutedActorPacket(
438
+ 'remote',
433
439
  state.spotId,
434
440
  relay.actorId,
435
441
  [header, body],
@@ -892,6 +898,7 @@ export class ZLinkActorPacketRelay {
892
898
  this.options.detachedTaskRunner.runDetached('local actor packet relay', async () => {
893
899
  try {
894
900
  const response = await this.requireSpotManager().dispatchRoutedActorPacket(
901
+ 'local',
895
902
  spotId,
896
903
  actor.actorId,
897
904
  [header, body],
@@ -11,7 +11,8 @@ import type {
11
11
  ZLinkAuthoritySnapshot,
12
12
  ZLinkCreationOperationIdentity,
13
13
  ZLinkLocationOwnerToken,
14
- ZLinkObjectReserveResult
14
+ ZLinkObjectReserveResult,
15
+ ZLinkObjectReserveRequest
15
16
  } from '../locations/internal-location-contracts';
16
17
  import type {
17
18
  ZLinkAuthorityStore,
@@ -57,6 +58,10 @@ export interface ZLinkActorPlacementTarget {
57
58
  }
58
59
 
59
60
  export interface ZLinkActorPlacementCoordinatorOptions {
61
+ readonly actorRelocationPolicy?: (
62
+ meshName: string,
63
+ stableType: string
64
+ ) => ZLinkObjectReserveRequest['actorRelocationPolicy'];
60
65
  readonly store: ZLinkObjectCreationStore & ZLinkAuthorityStore;
61
66
  readonly target: (
62
67
  meshName: string | undefined,
@@ -130,7 +135,8 @@ export class ZLinkActorPlacementCoordinator {
130
135
  },
131
136
  target: reservationTarget(target),
132
137
  creatingPayload,
133
- capacity: { actors: 1, spots: 0 }
138
+ capacity: { actors: 1, spots: 0 },
139
+ actorRelocationPolicy: this.options.actorRelocationPolicy?.(target.meshName, stableType)
134
140
  },
135
141
  deadline.signal,
136
142
  BigInt(deadlineUnixMs)
@@ -34,6 +34,10 @@ import type { ZLinkRemoteActorPacketTarget } from '../actors';
34
34
  import type { ZLinkActorJoinRelocation } from '../actors/actor-join-relocation';
35
35
 
36
36
  export interface ZLinkActorRuntimeOptionsFactoryOptions {
37
+ readonly terminateActorActivation: (
38
+ actor: ZLinkActor,
39
+ terminal: () => Promise<void>
40
+ ) => Promise<void>;
37
41
  readonly registration: ZLinkFrameworkRegistration;
38
42
  /** Message flow tracer, so a failed deferred Join records its cause on the flow. */
39
43
  readonly messageFlow?: () =>
@@ -101,6 +105,7 @@ export class ZLinkActorRuntimeOptionsFactory {
101
105
  | 'actorRefResolver'
102
106
  | 'actorCreatedNotifier'
103
107
  | 'actorDestroyedCleanup'
108
+ | 'terminateActorActivation'
104
109
  | 'publishActorAuthority'
105
110
  | 'locationLifecycle'
106
111
  | 'boundSessionFactory'
@@ -111,6 +116,7 @@ export class ZLinkActorRuntimeOptionsFactory {
111
116
  > {
112
117
  const actorTransferRegistry = this.options.actorTransferRegistry;
113
118
  return {
119
+ terminateActorActivation: this.options.terminateActorActivation,
114
120
  actorMeshNameProvider: this.options.actorMeshName,
115
121
  actorLeaveSpot: (meshName, spotId, actor, signal) => {
116
122
  const spotManager = this.options.spotManager();
@@ -274,6 +280,7 @@ export class ZLinkActorRuntimeOptionsFactory {
274
280
  this.actorBelongsToMesh(meshName, actorId) &&
275
281
  !this.options.actorHandoff.isProvisional(actorId)
276
282
  ? this.options.actorHandoff.capture(
283
+ 'local',
277
284
  actorId,
278
285
  parts,
279
286
  returnResponse,
@@ -1,3 +1,5 @@
1
+ import { SeverityNumber } from '@opentelemetry/api-logs';
2
+ import { telemetryLogger } from '../diagnostics/message-flow';
1
3
  import { MILLISECONDS_PER_SECOND } from '../diagnostics/runtime-metrics';
2
4
  import { ZLinkApplicationJobQueueProfile } from '../../contracts/Dispatch';
3
5
  import {
@@ -11,6 +13,7 @@ import {
11
13
  import { readFileSync } from 'node:fs';
12
14
  import { availableParallelism } from 'node:os';
13
15
  import type {
16
+ ApplicationJobOrigin,
14
17
  ApplicationJobPermitPort,
15
18
  ApplicationJobQueuePort
16
19
  } from '../application-jobs/contracts';
@@ -122,7 +125,10 @@ export function resolveApplicationJobQueueConfiguration(
122
125
  export class ApplicationJobQueuePermit implements ApplicationJobPermitPort {
123
126
  private state: 'reserved' | 'queued' | 'released' = 'reserved';
124
127
 
125
- constructor(private readonly owner: ApplicationJobQueue) {}
128
+ constructor(
129
+ private readonly owner: ApplicationJobQueue,
130
+ readonly origin: ApplicationJobOrigin
131
+ ) {}
126
132
 
127
133
  markApplicationQueued(): void {
128
134
  if (this.state !== 'reserved') {
@@ -149,6 +155,7 @@ export class ApplicationJobQueuePermit implements ApplicationJobPermitPort {
149
155
  }
150
156
 
151
157
  interface CapacityWaiter {
158
+ readonly origin: ApplicationJobOrigin;
152
159
  readonly resolve: (permit: ApplicationJobQueuePermit) => void;
153
160
  readonly reject: (reason: unknown) => void;
154
161
  readonly signal?: AbortSignal;
@@ -164,6 +171,8 @@ interface CapacityWaiter {
164
171
  * directly to the oldest live waiter without allocating the configured limit.
165
172
  */
166
173
  export class ApplicationJobQueue implements ApplicationJobQueuePort {
174
+ private localWaiters = 0;
175
+ private localBacklogLogged = false;
167
176
  private reservedSupplyPermits = 0n;
168
177
  private queuedApplicationJobs = 0n;
169
178
  private peakPermitsInUse = 0n;
@@ -191,7 +200,8 @@ export class ApplicationJobQueue implements ApplicationJobQueuePort {
191
200
  constructor(
192
201
  private readonly configuration: ApplicationJobQueueConfiguration,
193
202
  private readonly nowMs: () => number = () => performance.now(),
194
- private readonly handlerStartGate?: () => boolean
203
+ private readonly handlerStartGate?: () => boolean,
204
+ private readonly loggerFailureReporter?: (error: unknown) => void
195
205
  ) {
196
206
  this.receiveFlowController = new ApplicationJobReceiveFlowController(this, (state) => state);
197
207
  }
@@ -242,16 +252,33 @@ export class ApplicationJobQueue implements ApplicationJobQueuePort {
242
252
  this.receiveFlowController.unregister(target);
243
253
  }
244
254
 
245
- acquire(signal?: AbortSignal): Promise<ApplicationJobQueuePermit> {
255
+ acquire(
256
+ signal: AbortSignal | undefined,
257
+ origin: ApplicationJobOrigin
258
+ ): Promise<ApplicationJobQueuePermit> {
259
+ return new Promise((resolve, reject) => {
260
+ const permit = this.acquireOrResume(resolve, reject, signal, origin);
261
+ if (permit !== undefined) resolve(permit);
262
+ });
263
+ }
264
+
265
+ acquireOrResume(
266
+ resume: (permit: ApplicationJobQueuePermit) => void,
267
+ reject: (reason: unknown) => void,
268
+ signal: AbortSignal | undefined,
269
+ origin: ApplicationJobOrigin
270
+ ): ApplicationJobQueuePermit | undefined {
246
271
  if (signal?.aborted === true) {
247
- return Promise.reject(abortReason(signal));
272
+ reject(abortReason(signal));
273
+ return undefined;
248
274
  }
249
275
  if (this.waiters.length === 0 && this.permitsInUse() < this.limit()) {
250
- return Promise.resolve(this.reserve());
276
+ return this.reserve(origin);
251
277
  }
252
278
 
253
- return new Promise<ApplicationJobQueuePermit>((resolve, reject) => {
279
+ new Promise<ApplicationJobQueuePermit>((resolve, reject) => {
254
280
  const waiter: CapacityWaiter = {
281
+ origin,
255
282
  resolve,
256
283
  reject,
257
284
  signal,
@@ -262,9 +289,14 @@ export class ApplicationJobQueue implements ApplicationJobQueuePort {
262
289
  };
263
290
  this.waiters.push(waiter);
264
291
  this.capacityWaitCount += 1n;
292
+ const warningCount = this.updateLocalWaiters(origin, 1);
265
293
  signal?.addEventListener('abort', waiter.abort, { once: true });
266
294
  this.drainWaiters();
267
- });
295
+ if (warningCount !== undefined) this.logLocalBacklog(warningCount);
296
+ })
297
+ .then(resume)
298
+ .catch(reject);
299
+ return undefined;
268
300
  }
269
301
 
270
302
  snapshot(): ApplicationJobQueueSnapshot {
@@ -356,12 +388,15 @@ export class ApplicationJobQueue implements ApplicationJobQueuePort {
356
388
  return this.reservedSupplyPermits + this.queuedApplicationJobs;
357
389
  }
358
390
 
359
- private reserve(evaluatePressure = true): ApplicationJobQueuePermit {
391
+ private reserve(
392
+ origin: ApplicationJobOrigin,
393
+ evaluatePressure = true
394
+ ): ApplicationJobQueuePermit {
360
395
  this.reservedSupplyPermits += 1n;
361
396
  const current = this.permitsInUse();
362
397
  if (current > this.peakPermitsInUse) this.peakPermitsInUse = current;
363
398
  if (evaluatePressure) this.evaluatePressure();
364
- return new ApplicationJobQueuePermit(this);
399
+ return new ApplicationJobQueuePermit(this, origin);
365
400
  }
366
401
 
367
402
  private drainWaiters(): void {
@@ -371,7 +406,7 @@ export class ApplicationJobQueue implements ApplicationJobQueuePort {
371
406
  waiter.active = false;
372
407
  waiter.signal?.removeEventListener('abort', waiter.abort);
373
408
  this.recordCompletedWait(waiter);
374
- waiter.resolve(this.reserve(false));
409
+ waiter.resolve(this.reserve(waiter.origin, false));
375
410
  }
376
411
  }
377
412
 
@@ -387,11 +422,43 @@ export class ApplicationJobQueue implements ApplicationJobQueuePort {
387
422
  }
388
423
 
389
424
  private recordCompletedWait(waiter: CapacityWaiter): void {
425
+ this.updateLocalWaiters(waiter.origin, -1);
390
426
  if (waiter.metricsEpoch !== this.metricsEpoch) return;
391
427
  const durationMs = Math.max(0, this.nowMs() - waiter.startedAtMs);
392
428
  this.capacityWaitDurationSeconds += durationMs / MILLISECONDS_PER_SECOND;
393
429
  }
394
430
 
431
+ private updateLocalWaiters(origin: ApplicationJobOrigin, delta: number): number | undefined {
432
+ if (origin !== 'local') return undefined;
433
+ this.localWaiters += delta;
434
+ if (this.localWaiters === 0) this.localBacklogLogged = false;
435
+ if (!this.localBacklogLogged && this.localWaiters > Number(this.limit())) {
436
+ this.localBacklogLogged = true;
437
+ return this.localWaiters;
438
+ }
439
+ return undefined;
440
+ }
441
+
442
+ private logLocalBacklog(localWaiters: number): void {
443
+ try {
444
+ telemetryLogger.emit({
445
+ eventName: 'zlink.runtime.host.local_job_backlog_exceeded',
446
+ timestamp: new Date(),
447
+ severityNumber: SeverityNumber.WARN,
448
+ severityText: 'WARN',
449
+ body: 'Same-host jobs waiting for a host permit exceeded the application job queue maximum.',
450
+ attributes: {
451
+ source_kind: 'host',
452
+ source_name: 'zlink.runtime.host',
453
+ local_waiters: localWaiters,
454
+ effective_maximum: Number(this.limit())
455
+ }
456
+ });
457
+ } catch (error) {
458
+ this.loggerFailureReporter?.(error);
459
+ }
460
+ }
461
+
395
462
  private evaluatePressure(): void {
396
463
  const permitsInUse = this.permitsInUse();
397
464
  const next =
@@ -95,6 +95,7 @@ export class ZLinkEntryActorRuntimeService implements ZLinkEntryActorRuntime {
95
95
  return {
96
96
  handled: true,
97
97
  response: await spotManager.dispatchRoutedActorPacket(
98
+ 'remote',
98
99
  spotId,
99
100
  actorId,
100
101
  parts,