@zlink-systems/framework 0.31.0 → 0.32.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 (171) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/contracts/Actors/ZLinkActor.d.ts +2 -0
  3. package/dist/contracts/Actors/ZLinkSessionBindingIdentity.d.ts +6 -0
  4. package/dist/contracts/Actors/ZLinkSessionBindingIdentity.js +2 -0
  5. package/dist/contracts/Actors/index.d.ts +1 -0
  6. package/dist/contracts/Actors/index.js +1 -0
  7. package/dist/contracts/Common/payload-slab.d.ts +4 -0
  8. package/dist/contracts/Common/payload-slab.js +30 -0
  9. package/dist/contracts/Handlers/Contexts.d.ts +1 -0
  10. package/dist/internal.d.ts +9 -0
  11. package/dist/internal.js +23 -1
  12. package/dist/runtime/actors/actor-client.d.ts +1 -1
  13. package/dist/runtime/actors/actor-client.js +32 -23
  14. package/dist/runtime/actors/actor-mailbox.d.ts +1 -1
  15. package/dist/runtime/actors/actor-runtime-state.d.ts +1 -1
  16. package/dist/runtime/actors/spot-actor-dispatch.d.ts +4 -2
  17. package/dist/runtime/actors/spot-actor-dispatch.js +2 -1
  18. package/dist/runtime/application-jobs/application-ingress-record-owner.d.ts +1 -0
  19. package/dist/runtime/application-jobs/application-ingress-record-owner.js +3 -0
  20. package/dist/runtime/backend/contracts/index.d.ts +2 -0
  21. package/dist/runtime/backend/mesh-completion-table.d.ts +1 -0
  22. package/dist/runtime/backend/mesh-completion-table.js +1 -0
  23. package/dist/runtime/backend/node/node-mesh-backend-adapter.js +5 -1
  24. package/dist/runtime/backend/node/node-raw-binding-port.d.ts +7 -2
  25. package/dist/runtime/backend/node/node-raw-binding-port.js +17 -11
  26. package/dist/runtime/backend/node/node-raw-mesh-backend.d.ts +2 -2
  27. package/dist/runtime/backend/node/node-raw-mesh-backend.js +15 -8
  28. package/dist/runtime/channels/channel-dispatch-pipeline.d.ts +7 -6
  29. package/dist/runtime/channels/channel-dispatch-pipeline.js +1 -0
  30. package/dist/runtime/channels/channel-dispatchers.d.ts +7 -3
  31. package/dist/runtime/channels/channel-dispatchers.js +27 -19
  32. package/dist/runtime/channels/channel-envelope.js +543 -23
  33. package/dist/runtime/channels/channel-receive-loops.d.ts +9 -6
  34. package/dist/runtime/channels/channel-receive-loops.js +59 -18
  35. package/dist/runtime/channels/channel-runtime-lifecycle.d.ts +22 -3
  36. package/dist/runtime/channels/channel-socket-registry.d.ts +22 -4
  37. package/dist/runtime/diagnostics/runtime-metrics.d.ts +1 -1
  38. package/dist/runtime/diagnostics/runtime-metrics.js +2 -1
  39. package/dist/runtime/execution/serial-execution-queue.d.ts +26 -13
  40. package/dist/runtime/foundation/raw-service-mesh-runtime.d.ts +27 -11
  41. package/dist/runtime/foundation/raw-service-mesh-runtime.js +29 -25
  42. package/dist/runtime/foundation/service-liveness-registry.d.ts +1 -1
  43. package/dist/runtime/foundation/service-mailbox.d.ts +3 -1
  44. package/dist/runtime/foundation/service-mailbox.js +4 -1
  45. package/dist/runtime/foundation/service-runtime-contracts.d.ts +3 -1
  46. package/dist/runtime/foundation/service-stateful-registry.d.ts +2 -0
  47. package/dist/runtime/foundation/service-stateful-registry.js +28 -12
  48. package/dist/runtime/foundation/service-stateful-runtime.d.ts +51 -19
  49. package/dist/runtime/foundation/service-stateful-runtime.js +54 -5
  50. package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +3 -0
  51. package/dist/runtime/foundation/service-stateful-wire-codec.js +15 -0
  52. package/dist/runtime/foundation/service-wire-constants.generated.d.ts +1 -0
  53. package/dist/runtime/foundation/service-wire-constants.generated.js +12 -0
  54. package/dist/runtime/foundation/service-wire-m6a-codec.d.ts +2 -0
  55. package/dist/runtime/foundation/service-wire-m6a-codec.js +17 -7
  56. package/dist/runtime/host/actor-placement-coordinator.d.ts +1 -1
  57. package/dist/runtime/host/actor-transfer-runtime.d.ts +3 -3
  58. package/dist/runtime/host/application-job-queue.d.ts +1 -1
  59. package/dist/runtime/host/index.d.ts +89 -36
  60. package/dist/runtime/host/index.js +101 -7
  61. package/dist/runtime/host/location-runtime-owner.d.ts +2 -2
  62. package/dist/runtime/host/service-relocation-host-runtime.d.ts +181 -44
  63. package/dist/runtime/host/service-relocation-host-runtime.js +47 -13
  64. package/dist/runtime/host/spot-address-transport.js +50 -42
  65. package/dist/runtime/locations/auto-connect-loop.d.ts +0 -1
  66. package/dist/runtime/locations/auto-connect-loop.js +25 -19
  67. package/dist/runtime/locations/auto-connect-planner.js +2 -5
  68. package/dist/runtime/locations/auto-connect-reconciler.d.ts +1 -0
  69. package/dist/runtime/locations/auto-connect-reconciler.js +35 -23
  70. package/dist/runtime/locations/in-memory-location-store.d.ts +0 -2
  71. package/dist/runtime/locations/in-memory-location-store.js +1 -38
  72. package/dist/runtime/locations/location-store-repository.d.ts +0 -1
  73. package/dist/runtime/locations/location-store-repository.js +13 -115
  74. package/dist/runtime/locations/runtime.d.ts +3 -3
  75. package/dist/runtime/locations/runtime.js +12 -16
  76. package/dist/runtime/messaging/creation-payload-codec.js +2 -2
  77. package/dist/runtime/protocol/service_wire_codec.generated.d.ts +4 -0
  78. package/dist/runtime/protocol/service_wire_codec.generated.js +17 -7
  79. package/dist/runtime/spots/index.d.ts +20 -5
  80. package/dist/runtime/spots/index.js +14 -0
  81. package/dist/runtime/spots/spot-actor-admission-coordinator.d.ts +3 -3
  82. package/dist/runtime/spots/spot-actor-packet-dispatch.d.ts +1 -0
  83. package/dist/runtime/spots/spot-actor-packet-dispatch.js +13 -7
  84. package/dist/runtime/spots/spot-actor-packet-drain.d.ts +1 -0
  85. package/dist/runtime/spots/spot-actor-packet-drain.js +12 -5
  86. package/dist/runtime/spots/spot-entry-activation.d.ts +2 -1
  87. package/dist/runtime/spots/spot-node-runtime-manager.d.ts +8 -6
  88. package/dist/runtime/spots/spot-node-runtime-manager.js +2 -0
  89. package/dist/runtime/spots/spot-route-packet-dispatch.js +7 -4
  90. package/dist/runtime/spots/spot-routed-spot-packet-dispatch.js +3 -1
  91. package/dist/runtime/spots/spot-serial-executor.d.ts +2 -1
  92. package/dist/runtime/spots/spot-subscription-dispatch.js +3 -1
  93. package/dist/runtime/spots/spot-timer.d.ts +1 -1
  94. package/dist/runtime/streams/actor-session-binding-registry.d.ts +9 -3
  95. package/dist/runtime/streams/actor-session-binding-registry.js +17 -0
  96. package/dist/runtime/streams/index.d.ts +3 -3
  97. package/dist/runtime/streams/index.js +22 -2
  98. package/dist/runtime/streams/managed-stream.js +2 -1
  99. package/dist/runtime/streams/session-calls.d.ts +1 -1
  100. package/dist/runtime/streams/session-calls.js +3 -9
  101. package/dist/runtime/streams/session-context.d.ts +2 -2
  102. package/dist/runtime/streams/session-context.js +2 -2
  103. package/dist/runtime/streams/stream-session-runtime.d.ts +1 -0
  104. package/dist/runtime/streams/stream-session-runtime.js +37 -12
  105. package/package.json +3 -3
  106. package/src/contracts/Actors/ZLinkActor.ts +6 -0
  107. package/src/contracts/Actors/ZLinkSessionBindingIdentity.ts +7 -0
  108. package/src/contracts/Actors/index.ts +1 -0
  109. package/src/contracts/Common/payload-slab.ts +29 -0
  110. package/src/contracts/Handlers/Contexts.ts +1 -0
  111. package/src/internal.ts +17 -0
  112. package/src/runtime/actors/actor-client.ts +48 -23
  113. package/src/runtime/actors/actor-mailbox.ts +1 -1
  114. package/src/runtime/actors/actor-runtime-state.ts +1 -1
  115. package/src/runtime/actors/spot-actor-dispatch.ts +4 -3
  116. package/src/runtime/application-jobs/application-ingress-record-owner.ts +4 -0
  117. package/src/runtime/backend/contracts/index.ts +2 -0
  118. package/src/runtime/backend/mesh-completion-table.ts +2 -0
  119. package/src/runtime/backend/node/node-mesh-backend-adapter.ts +5 -1
  120. package/src/runtime/backend/node/node-raw-binding-port.ts +20 -7
  121. package/src/runtime/backend/node/node-raw-mesh-backend.ts +18 -10
  122. package/src/runtime/channels/channel-dispatch-pipeline.ts +6 -5
  123. package/src/runtime/channels/channel-dispatchers.ts +39 -19
  124. package/src/runtime/channels/channel-envelope.ts +547 -20
  125. package/src/runtime/channels/channel-receive-loops.ts +93 -36
  126. package/src/runtime/channels/channel-runtime-lifecycle.ts +2 -2
  127. package/src/runtime/channels/channel-socket-registry.ts +3 -3
  128. package/src/runtime/diagnostics/runtime-metrics.ts +3 -1
  129. package/src/runtime/execution/serial-execution-queue.ts +13 -13
  130. package/src/runtime/foundation/raw-service-mesh-runtime.ts +40 -39
  131. package/src/runtime/foundation/service-liveness-registry.ts +1 -1
  132. package/src/runtime/foundation/service-mailbox.ts +6 -1
  133. package/src/runtime/foundation/service-runtime-contracts.ts +7 -1
  134. package/src/runtime/foundation/service-stateful-registry.ts +39 -14
  135. package/src/runtime/foundation/service-stateful-runtime.ts +87 -21
  136. package/src/runtime/foundation/service-stateful-wire-codec.ts +18 -0
  137. package/src/runtime/foundation/service-wire-constants.generated.ts +10 -0
  138. package/src/runtime/foundation/service-wire-m6a-codec.ts +29 -7
  139. package/src/runtime/host/actor-placement-coordinator.ts +1 -1
  140. package/src/runtime/host/actor-transfer-runtime.ts +3 -3
  141. package/src/runtime/host/application-job-queue.ts +1 -1
  142. package/src/runtime/host/index.ts +183 -44
  143. package/src/runtime/host/location-runtime-owner.ts +2 -2
  144. package/src/runtime/host/service-relocation-host-runtime.ts +101 -65
  145. package/src/runtime/host/spot-address-transport.ts +67 -61
  146. package/src/runtime/locations/auto-connect-loop.ts +24 -16
  147. package/src/runtime/locations/auto-connect-planner.ts +2 -5
  148. package/src/runtime/locations/auto-connect-reconciler.ts +37 -24
  149. package/src/runtime/locations/in-memory-location-store.ts +1 -74
  150. package/src/runtime/locations/location-store-repository.ts +32 -153
  151. package/src/runtime/locations/runtime.ts +16 -28
  152. package/src/runtime/messaging/creation-payload-codec.ts +3 -3
  153. package/src/runtime/protocol/service_wire_codec.generated.ts +18 -8
  154. package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
  155. package/src/runtime/spots/index.ts +28 -4
  156. package/src/runtime/spots/spot-actor-admission-coordinator.ts +1 -1
  157. package/src/runtime/spots/spot-actor-packet-dispatch.ts +16 -6
  158. package/src/runtime/spots/spot-actor-packet-drain.ts +21 -6
  159. package/src/runtime/spots/spot-entry-activation.ts +1 -1
  160. package/src/runtime/spots/spot-node-runtime-manager.ts +8 -6
  161. package/src/runtime/spots/spot-route-packet-dispatch.ts +8 -4
  162. package/src/runtime/spots/spot-routed-spot-packet-dispatch.ts +3 -1
  163. package/src/runtime/spots/spot-serial-executor.ts +1 -1
  164. package/src/runtime/spots/spot-subscription-dispatch.ts +3 -1
  165. package/src/runtime/spots/spot-timer.ts +1 -1
  166. package/src/runtime/streams/actor-session-binding-registry.ts +30 -3
  167. package/src/runtime/streams/index.ts +30 -4
  168. package/src/runtime/streams/managed-stream.ts +2 -1
  169. package/src/runtime/streams/session-calls.ts +9 -14
  170. package/src/runtime/streams/session-context.ts +6 -11
  171. package/src/runtime/streams/stream-session-runtime.ts +49 -20
@@ -6,7 +6,6 @@ import type { ZLinkDomainLocationStore as ZLinkLocationStore } from './domain-st
6
6
  import type { ZLinkOwnerLeaseTracker } from './lease-tracker';
7
7
  import type { ZLinkAutoConnectLocal } from './auto-connect-types';
8
8
  import type { ZLinkAutoConnectReconciler } from './auto-connect-reconciler';
9
- import { ZLinkStateLane } from '../execution/state-lane';
10
9
 
11
10
  export interface ZLinkAutoConnectLoopOptions {
12
11
  readonly reconciler: ZLinkAutoConnectReconciler;
@@ -19,7 +18,6 @@ export interface ZLinkAutoConnectLoopOptions {
19
18
  }
20
19
 
21
20
  export class ZLinkAutoConnectLoop {
22
- private readonly lane = new ZLinkStateLane();
23
21
  private readonly reconciler: ZLinkAutoConnectReconciler;
24
22
  private readonly options: Required<ZLinkLocationOptionOverrides>;
25
23
  private readonly meshName: string;
@@ -46,15 +44,19 @@ export class ZLinkAutoConnectLoop {
46
44
  }
47
45
 
48
46
  async start(signal?: AbortSignal): Promise<void> {
49
- if (await this.lane.run(() => this.controller !== undefined)) return;
50
- await this.tick(signal);
51
- const started = await this.lane.run(() => {
52
- if (this.controller !== undefined) return false;
53
- this.controller = new AbortController();
54
- return true;
55
- });
56
- if (!started) return;
57
- this.scheduleNext();
47
+ if (this.controller !== undefined) return;
48
+ // Reserve the lifecycle before yielding so stop also owns the first publication.
49
+ const controller = new AbortController();
50
+ this.controller = controller;
51
+ try {
52
+ await this.tick(signal);
53
+ } catch (error) {
54
+ if (this.controller === controller) {
55
+ await this.prepareTransportShutdown();
56
+ }
57
+ throw error;
58
+ }
59
+ this.scheduleNext(controller);
58
60
  }
59
61
 
60
62
  async requestPeerReconcile(meshName: string, _peerRid: string): Promise<void> {
@@ -91,8 +93,14 @@ export class ZLinkAutoConnectLoop {
91
93
 
92
94
  async tick(signal?: AbortSignal): Promise<void> {
93
95
  const controller = this.controller;
96
+ const tickSignal =
97
+ controller === undefined
98
+ ? signal
99
+ : signal === undefined
100
+ ? controller.signal
101
+ : AbortSignal.any([signal, controller.signal]);
94
102
  await this.enqueueTick(async () => {
95
- if (controller?.signal.aborted !== true) await this.tickCore(signal ?? controller?.signal);
103
+ if (tickSignal?.aborted !== true) await this.tickCore(tickSignal);
96
104
  });
97
105
  }
98
106
 
@@ -144,15 +152,15 @@ export class ZLinkAutoConnectLoop {
144
152
  return this.lastTickFailed;
145
153
  }
146
154
 
147
- private scheduleNext(): void {
148
- if (this.controller === undefined || this.controller.signal.aborted) {
155
+ private scheduleNext(controller: AbortController): void {
156
+ if (this.controller !== controller || controller.signal.aborted) {
149
157
  return;
150
158
  }
151
159
  this.timer = this.setTimer(() => {
152
160
  this.timer = undefined;
153
- void this.tick(this.controller?.signal)
161
+ void this.tick(controller.signal)
154
162
  .catch(() => undefined)
155
- .finally(() => this.scheduleNext());
163
+ .finally(() => this.scheduleNext(controller));
156
164
  }, this.options.pollingIntervalMs);
157
165
  }
158
166
  }
@@ -130,14 +130,11 @@ function targetOf(peer: ZLinkPeerLocation): ZLinkAutoConnectTarget {
130
130
  }
131
131
 
132
132
  function isAutoConnectSelf(local: ZLinkAutoConnectLocal, peer: ZLinkPeerLocation): boolean {
133
- if (
133
+ return (
134
134
  local.nodeRid !== undefined &&
135
135
  peer.nodeRid !== undefined &&
136
136
  routingIdsEqual(local.nodeRid, peer.nodeRid)
137
- ) {
138
- return true;
139
- }
140
- return peer.endpoint === local.endpoint;
137
+ );
141
138
  }
142
139
 
143
140
  function shouldDialAutoConnectPeer(local: ZLinkAutoConnectLocal, peer: ZLinkPeerLocation): boolean {
@@ -101,12 +101,7 @@ export class ZLinkAutoConnectReconciler {
101
101
  }
102
102
 
103
103
  async tick(signal?: AbortSignal): Promise<void> {
104
- try {
105
- await this.publishLocal(signal);
106
- } catch {
107
- await this.recordStoreFailure();
108
- return;
109
- }
104
+ if (!(await this.publishLocalForTick(signal))) return;
110
105
 
111
106
  if (!this.reconcilePeers) {
112
107
  await this.lane.run(() => {
@@ -125,8 +120,9 @@ export class ZLinkAutoConnectReconciler {
125
120
  },
126
121
  signal
127
122
  );
123
+ signal?.throwIfAborted();
128
124
  } catch {
129
- await this.recordStoreFailure();
125
+ await this.recordStoreFailure(signal);
130
126
  return;
131
127
  }
132
128
 
@@ -144,16 +140,7 @@ export class ZLinkAutoConnectReconciler {
144
140
  await this.lane.run(() => {
145
141
  this.localPublished = false;
146
142
  });
147
- try {
148
- const republished = await this.publishLocal(signal);
149
- if (!republished) {
150
- await this.recordStoreFailure();
151
- return;
152
- }
153
- } catch {
154
- await this.recordStoreFailure();
155
- return;
156
- }
143
+ if (!(await this.publishLocalForTick(signal))) return;
157
144
  }
158
145
 
159
146
  if (await this.lane.run(() => this.storeFailedValue)) {
@@ -323,21 +310,44 @@ export class ZLinkAutoConnectReconciler {
323
310
  this.executor.disconnect(target);
324
311
  }
325
312
 
313
+ private async publishLocalForTick(signal?: AbortSignal): Promise<boolean> {
314
+ let published: boolean;
315
+ try {
316
+ published = await this.publishLocal(signal);
317
+ signal?.throwIfAborted();
318
+ } catch {
319
+ await this.recordStoreFailure(signal);
320
+ return false;
321
+ }
322
+ if (!published) {
323
+ await this.recordStoreFailure(signal);
324
+ return false;
325
+ }
326
+ return true;
327
+ }
328
+
326
329
  private async publishLocal(signal?: AbortSignal): Promise<boolean> {
330
+ signal?.throwIfAborted();
327
331
  const prepared = await this.lane.run(() => this.preparePublishLocalCore());
328
- if (prepared === undefined) return await this.lane.run(() => this.localPublished);
332
+ if (prepared === undefined) {
333
+ return this.localRow === undefined || (await this.lane.run(() => this.localPublished));
334
+ }
329
335
  const claimed = await this.runtime.writePeer(
330
336
  prepared.row,
331
337
  ZLinkLocationWriteIntent.NewClaim,
332
338
  signal
333
339
  );
334
340
  if (claimed.status === ZLinkLocationWriteStatus.Stored) {
335
- return await this.lane.run(() => this.completePublishClaimCore(claimed.generation));
341
+ return await this.lane.run(() => {
342
+ signal?.throwIfAborted();
343
+ return this.completePublishClaimCore(claimed.generation);
344
+ });
336
345
  }
337
346
 
338
- const renewal = await this.lane.run(() =>
339
- this.preparePublishRenewalCore(prepared.row, claimed.status)
340
- );
347
+ const renewal = await this.lane.run(() => {
348
+ signal?.throwIfAborted();
349
+ return this.preparePublishRenewalCore(prepared.row, claimed.status);
350
+ });
341
351
  if (renewal !== undefined) {
342
352
  const renewed = await this.runtime.writePeer(
343
353
  {
@@ -348,6 +358,7 @@ export class ZLinkAutoConnectReconciler {
348
358
  signal
349
359
  );
350
360
  return await this.lane.run(() => {
361
+ signal?.throwIfAborted();
351
362
  this.localPublished = renewed.status === ZLinkLocationWriteStatus.Stored;
352
363
  return this.localPublished;
353
364
  });
@@ -397,8 +408,10 @@ export class ZLinkAutoConnectReconciler {
397
408
  : undefined;
398
409
  }
399
410
 
400
- private async recordStoreFailure(): Promise<void> {
401
- const retryTargets = await this.lane.run(() => this.beginStoreFailureCore());
411
+ private async recordStoreFailure(signal?: AbortSignal): Promise<void> {
412
+ const retryTargets = await this.lane.run(() =>
413
+ signal?.aborted === true ? undefined : this.beginStoreFailureCore()
414
+ );
402
415
  if (retryTargets === undefined) return;
403
416
  for (const [key, target] of retryTargets) {
404
417
  await this.connectTarget(target, async () => {
@@ -1054,52 +1054,7 @@ export class ZLinkInMemoryLocationStore
1054
1054
  ) {
1055
1055
  return 0n;
1056
1056
  }
1057
- const ownerId = owner.ownerId;
1058
- let removed = Number(await this.authority.removeAllByOwner(owner, signal));
1059
- for (const [key, row] of [...this.meshNodes.rows]) {
1060
- if (row.ownerId !== owner.ownerId) continue;
1061
- this.meshNodes.rows.delete(key);
1062
- this.releaseEntrySpotIdentity(row);
1063
- this.bump(ZLinkLocationKind.Peer, row.meshName);
1064
- removed++;
1065
- }
1066
- removed += this.removeByOwner(
1067
- this.peers,
1068
- ownerId,
1069
- (row) => row.ownerId,
1070
- ZLinkLocationKind.Peer,
1071
- (row) => row.meshName
1072
- );
1073
- removed += this.removeByOwner(
1074
- this.spots,
1075
- ownerId,
1076
- (row) => row.ownerId,
1077
- ZLinkLocationKind.Spot,
1078
- (row) => row.meshName
1079
- );
1080
- removed += this.removeByOwner(
1081
- this.actors,
1082
- ownerId,
1083
- (row) => row.ownerId,
1084
- ZLinkLocationKind.Actor,
1085
- () => undefined
1086
- );
1087
- removed += this.removeByOwner(
1088
- this.routes,
1089
- ownerId,
1090
- (row) => row.ownerId,
1091
- ZLinkLocationKind.Route,
1092
- () => undefined
1093
- );
1094
- removed += this.removeByOwner(
1095
- this.clientServers,
1096
- ownerId,
1097
- (row) => row.ownerId,
1098
- ZLinkLocationKind.ClientServer,
1099
- (row) => row.channelName
1100
- );
1101
- removed += this.removeFanoutByOwner(owner);
1102
- return BigInt(removed);
1057
+ return this.authority.removeAllByOwner(owner, signal);
1103
1058
  }
1104
1059
 
1105
1060
  private claimEntrySpotIdentity(
@@ -1247,24 +1202,6 @@ export class ZLinkInMemoryLocationStore
1247
1202
  return stored(owner.leaseGeneration, this.now());
1248
1203
  }
1249
1204
 
1250
- private removeByOwner<TRow>(
1251
- table: RowTable<TRow>,
1252
- ownerId: string,
1253
- ownerOf: (row: TRow) => string,
1254
- kind: ZLinkLocationKind,
1255
- meshOf: (row: TRow) => string | undefined
1256
- ): number {
1257
- let removed = 0;
1258
- for (const [key, row] of [...table.rows.entries()]) {
1259
- if (ownerOf(row) === ownerId) {
1260
- table.rows.delete(key);
1261
- this.bump(kind, meshOf(row));
1262
- removed++;
1263
- }
1264
- }
1265
- return removed;
1266
- }
1267
-
1268
1205
  private liveOwnerLease(ownerId: string, now: Date): InMemoryOwnerLease | undefined {
1269
1206
  const lease = this.leases.get(ownerId);
1270
1207
  return lease !== undefined && lease.leaseExpiresAt.getTime() > now.getTime()
@@ -1279,16 +1216,6 @@ export class ZLinkInMemoryLocationStore
1279
1216
  }
1280
1217
  }
1281
1218
 
1282
- private removeFanoutByOwner(owner: ZLinkLocationOwnerToken): number {
1283
- let removed = 0;
1284
- for (const [key, row] of this.fanoutPublishers.rows) {
1285
- if (row.ownerId !== owner.ownerId) continue;
1286
- this.fanoutPublishers.rows.delete(key);
1287
- removed++;
1288
- }
1289
- return removed;
1290
- }
1291
-
1292
1219
  private bumpScope(scope: ZLinkLocationChangeStampScope): void {
1293
1220
  const key = stampKey(scope);
1294
1221
  this.stamps.set(key, (this.stamps.get(key) ?? 0n) + 1n);
@@ -206,12 +206,6 @@ interface OwnerCleanupCandidate {
206
206
  readonly scanVersion: ZLinkStoreVersion;
207
207
  }
208
208
 
209
- interface OwnerCleanupRecord {
210
- readonly descriptor: OwnedStoreRecord;
211
- }
212
-
213
- const MAX_OWNER_CLEANUP_BATCH_ROWS = 2_047;
214
-
215
209
  /**
216
210
  * Maps framework domain records to the minimal provider SPI.
217
211
  *
@@ -1335,11 +1329,18 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1335
1329
  key: ZLinkAuthorityKey,
1336
1330
  expectedStoreVersion: string,
1337
1331
  signal?: AbortSignal,
1338
- request?: ZLinkEndedOwnerReleaseIntent
1332
+ request?: ZLinkEndedOwnerReleaseIntent,
1333
+ requiredOwner?: ZLinkLocationOwnerToken
1339
1334
  ): Promise<boolean> {
1340
1335
  return (
1341
- (await this.tryReleaseEndedReservation(key, expectedStoreVersion, signal, request)) ===
1342
- 'reclaimed'
1336
+ (await this.tryReleaseEndedReservation(
1337
+ key,
1338
+ expectedStoreVersion,
1339
+ signal,
1340
+ request,
1341
+ undefined,
1342
+ requiredOwner
1343
+ )) === 'reclaimed'
1343
1344
  );
1344
1345
  }
1345
1346
 
@@ -1348,7 +1349,8 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1348
1349
  expectedStoreVersion: string,
1349
1350
  signal?: AbortSignal,
1350
1351
  request?: ZLinkEndedOwnerReleaseIntent,
1351
- deadlineUnixMs?: bigint
1352
+ deadlineUnixMs?: bigint,
1353
+ requiredOwner?: ZLinkLocationOwnerToken
1352
1354
  ): Promise<'notReclaimable' | 'conflict' | 'reclaimed'> {
1353
1355
  for (;;) {
1354
1356
  throwIfOperationExpired(signal, deadlineUnixMs);
@@ -1383,6 +1385,13 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
1383
1385
  this.provider.read(capacityRowKey, signal)
1384
1386
  ]);
1385
1387
  const ownerLive = sameLiveOwner(lease, snapshot);
1388
+ if (
1389
+ requiredOwner !== undefined &&
1390
+ (!ownerLive ||
1391
+ requiredOwner.ownerId !== snapshot.ownerId ||
1392
+ requiredOwner.leaseGeneration !== snapshot.ownerLeaseGeneration)
1393
+ )
1394
+ return 'notReclaimable';
1386
1395
  let members: EndedSpotMember[] = [];
1387
1396
  if (snapshot.allocation.state === 'active') {
1388
1397
  if (ownerLive) return 'notReclaimable';
@@ -2852,50 +2861,7 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2852
2861
  const leaseKey = ownerKey(owner.ownerId);
2853
2862
  const lease = await this.provider.read(leaseKey, signal);
2854
2863
  if (liveOwnerLeaseGeneration(lease) !== owner.leaseGeneration) return 0n;
2855
- let removed = await this.removeOwnedAuthorities(owner, signal);
2856
- const candidates: OwnerCleanupCandidate[] = [];
2857
- for (const prefix of [
2858
- 'mesh-node\0',
2859
- 'client-server\0',
2860
- 'fanout-publisher\0',
2861
- `${PREFIX}spot:`,
2862
- `${PREFIX}actor:`,
2863
- `${PREFIX}route:`
2864
- ]) {
2865
- for (;;) {
2866
- const prefixCandidates: OwnerCleanupCandidate[] = [];
2867
- let cursor: ZLinkStoreScanCursor | undefined;
2868
- let expired = false;
2869
- do {
2870
- const result = await this.provider.scan({ prefix, cursor, limit: 1_000 }, signal);
2871
- if (result.kind === 'expired') {
2872
- expired = true;
2873
- break;
2874
- }
2875
- for (const item of result.value.items) {
2876
- prefixCandidates.push({ key: item.key, scanVersion: item.value.version });
2877
- }
2878
- cursor = result.value.nextCursor;
2879
- } while (cursor !== undefined);
2880
- if (expired) {
2881
- // Snapshot scan expiry invalidates every page from this prefix.
2882
- // Restart from its first page instead of silently leaving rows
2883
- // behind after a partial scan.
2884
- continue;
2885
- }
2886
- candidates.push(...prefixCandidates);
2887
- break;
2888
- }
2889
- }
2890
- for (let offset = 0; offset < candidates.length; offset += MAX_OWNER_CLEANUP_BATCH_ROWS) {
2891
- removed += await this.removeOwnerCleanupBatch(
2892
- owner,
2893
- leaseKey,
2894
- candidates.slice(offset, offset + MAX_OWNER_CLEANUP_BATCH_ROWS),
2895
- signal
2896
- );
2897
- }
2898
- return removed + (await super.removeAllByOwner(owner));
2864
+ return this.removeOwnedAuthorities(owner, signal);
2899
2865
  }
2900
2866
 
2901
2867
  private async removeOwnedAuthorities(
@@ -2978,91 +2944,20 @@ export class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationStore {
2978
2944
  signal
2979
2945
  );
2980
2946
  if (aborted.kind === 'aborted') removed += 1n;
2981
- }
2982
- return removed;
2983
- }
2984
-
2985
- private async removeOwnerCleanupBatch(
2986
- owner: ZLinkLocationOwnerToken,
2987
- leaseKey: ZLinkStoreKey,
2988
- candidates: readonly OwnerCleanupCandidate[],
2989
- signal?: AbortSignal
2990
- ): Promise<bigint> {
2991
- if (candidates.length === 0) return 0n;
2992
-
2993
- const currentRows = await Promise.all(
2994
- candidates.map((candidate) => this.provider.read(candidate.key, signal))
2995
- );
2996
- const unchangedOwnedRows: OwnerCleanupCandidate[] = [];
2997
- for (let index = 0; index < candidates.length; index += 1) {
2998
- const candidate = candidates[index]!;
2999
- const current = currentRows[index]!;
3000
- if (
3001
- current.kind === 'missing' ||
3002
- current.value.version.value !== candidate.scanVersion.value
3003
- ) {
3004
- continue;
3005
- }
3006
- const record = decodeJson<OwnerCleanupRecord>(current.value.bytes);
3007
- const descriptor = record.descriptor;
3008
- if (
3009
- descriptor.ownerId !== owner.ownerId ||
3010
- descriptorOwnerLeaseGeneration(descriptor) !== owner.leaseGeneration
3011
- ) {
3012
- continue;
2947
+ else if (aborted.kind === 'stale') {
2948
+ if (
2949
+ await this.releaseEndedReservationRequest(
2950
+ { value: encodedAuthority } as ZLinkAuthorityKey,
2951
+ current.value.version.value,
2952
+ signal,
2953
+ undefined,
2954
+ owner
2955
+ )
2956
+ )
2957
+ removed += 1n;
3013
2958
  }
3014
- unchangedOwnedRows.push({
3015
- key: candidate.key,
3016
- scanVersion: current.value.version
3017
- });
3018
- }
3019
- if (unchangedOwnedRows.length === 0) return 0n;
3020
-
3021
- const currentLease = await this.provider.read(leaseKey, signal);
3022
- if (
3023
- currentLease.kind === 'missing' ||
3024
- liveOwnerLeaseGeneration(currentLease) !== owner.leaseGeneration
3025
- ) {
3026
- return 0n;
3027
2959
  }
3028
- const result = await this.provider.write(
3029
- {
3030
- conditions: [
3031
- { kind: 'version', key: leaseKey, expected: currentLease.value.version },
3032
- ...unchangedOwnedRows.map((row) => ({
3033
- kind: 'version' as const,
3034
- key: row.key,
3035
- expected: row.scanVersion
3036
- }))
3037
- ],
3038
- mutations: unchangedOwnedRows.map((row) => ({
3039
- kind: 'delete' as const,
3040
- key: row.key
3041
- }))
3042
- },
3043
- signal
3044
- );
3045
- if (result.kind === 'applied') return BigInt(unchangedOwnedRows.length);
3046
-
3047
- // A row may have changed while the page was being prepared. Split the
3048
- // bounded batch so unaffected rows can still be removed atomically, while
3049
- // the changed row is discarded by the next version re-read. A changed
3050
- // owner lease causes every recursive branch to stop at the fence above.
3051
- if (unchangedOwnedRows.length === 1) return 0n;
3052
- const midpoint = Math.ceil(unchangedOwnedRows.length / 2);
3053
- const left = await this.removeOwnerCleanupBatch(
3054
- owner,
3055
- leaseKey,
3056
- unchangedOwnedRows.slice(0, midpoint),
3057
- signal
3058
- );
3059
- const right = await this.removeOwnerCleanupBatch(
3060
- owner,
3061
- leaseKey,
3062
- unchangedOwnedRows.slice(midpoint),
3063
- signal
3064
- );
3065
- return left + right;
2960
+ return removed;
3066
2961
  }
3067
2962
 
3068
2963
  private async updateDescriptor<T extends OwnedDescriptor>(
@@ -3769,7 +3664,6 @@ type OwnedDescriptor =
3769
3664
 
3770
3665
  type LeaseOwnedLocation = ZLinkSpotLocation | ZLinkActorLocation;
3771
3666
  type StoredLocation = LeaseOwnedLocation | ZLinkRouteLocation;
3772
- type OwnedStoreRecord = OwnedDescriptor | StoredLocation;
3773
3667
 
3774
3668
  function validateProviderAggregateRequest(request: ZLinkAggregatePrepareRequest): void {
3775
3669
  requireText(request.aggregateId.value, 'aggregate ID');
@@ -5060,21 +4954,6 @@ function locationOwnerGeneration(value: LeaseOwnedLocation): bigint {
5060
4954
  return value.leaseGeneration;
5061
4955
  }
5062
4956
 
5063
- function descriptorOwnerLeaseGeneration(value: OwnedStoreRecord): bigint | undefined {
5064
- if ('leaseGeneration' in value) {
5065
- return typeof value.leaseGeneration === 'bigint'
5066
- ? value.leaseGeneration
5067
- : BigInt(value.leaseGeneration);
5068
- }
5069
- if ('generation' in value) {
5070
- return typeof value.generation === 'bigint' ? value.generation : BigInt(value.generation);
5071
- }
5072
- return undefined;
5073
- }
5074
-
5075
- // Location runtime §10: a write whose result was lost is confirmed by reading
5076
- // the same key. The stored row already is this write when it differs only in
5077
- // the time the Store stamped, as for every other descriptor kind.
5078
4957
  function sameMeshDescriptor(
5079
4958
  left: ZLinkMeshNodeDescriptor,
5080
4959
  right: ZLinkMeshNodeDescriptor
@@ -220,7 +220,7 @@ export class ZLinkLocationRuntime implements ZLinkLocationRuntimeQuery {
220
220
  return this.ownerToken;
221
221
  }
222
222
 
223
- /** Location runtime §5: a startup Store request ends within the renew timeout. */
223
+ /** Location runtime §5: a descriptor publication ends within the renew timeout. */
224
224
  async writeDescriptor<T>(
225
225
  action: (signal?: AbortSignal) => Promise<T>,
226
226
  signal?: AbortSignal
@@ -386,7 +386,7 @@ export class ZLinkLocationRuntime implements ZLinkLocationRuntimeQuery {
386
386
  async cleanupOwner(signal?: AbortSignal): Promise<void> {
387
387
  const pending = this.heartbeatRenewal;
388
388
  try {
389
- await withTimeout(() => pending ?? Promise.resolve(), undefined, signal);
389
+ await (pending ?? Promise.resolve());
390
390
  } catch (error) {
391
391
  if (!isAbortError(error)) throw error;
392
392
  } finally {
@@ -396,25 +396,14 @@ export class ZLinkLocationRuntime implements ZLinkLocationRuntimeQuery {
396
396
  return;
397
397
  }
398
398
  try {
399
- const owner = this.ownerToken ??
400
- this.lastOwnerToken ?? {
401
- ownerId: this.ownerId,
402
- leaseGeneration: 0n
403
- };
404
- await withTimeout(
405
- async (cleanupSignal) => {
406
- await this.stores.locationStore.removeAllByOwner(owner, cleanupSignal);
407
- const currentLease = await this.stores.ownerLeaseStore.readOwnerLease(
408
- this.ownerId,
409
- cleanupSignal
410
- );
411
- if (currentLease.kind === 'found') {
412
- await this.stores.ownerLeaseStore.releaseOwnerLease(currentLease.token, cleanupSignal);
413
- }
414
- },
415
- undefined,
416
- signal
417
- );
399
+ const owner = this.ownerToken ?? this.lastOwnerToken;
400
+ signal?.throwIfAborted();
401
+ if (owner !== undefined) {
402
+ await this.stores.locationStore.removeAllByOwner(owner, signal);
403
+ signal?.throwIfAborted();
404
+ await this.stores.ownerLeaseStore.releaseOwnerLease(owner, signal);
405
+ signal?.throwIfAborted();
406
+ }
418
407
  this.ownerToken = undefined;
419
408
  this.lastOwnerToken = undefined;
420
409
  this.ownerCleanupComplete = true;
@@ -516,7 +505,7 @@ export class ZLinkLocationRuntime implements ZLinkLocationRuntimeQuery {
516
505
  }
517
506
  try {
518
507
  if (!this.started) {
519
- // stop owns confirmation and release under the host shutdown signal.
508
+ this.lastOwnerToken = claim.token;
520
509
  return false;
521
510
  }
522
511
  this.lastOwnerToken = this.ownerToken;
@@ -765,7 +754,10 @@ export class ZLinkLocationRuntime implements ZLinkLocationRuntimeQuery {
765
754
  const normalizedEndpoint = normalizeEndpoint(peer.endpoint);
766
755
  const stamped = { ...peer, endpoint: normalizedEndpoint, ownerId: this.ownerId };
767
756
  const result = await this.guardWrite(() =>
768
- this.stores.peerStore.updatePeer(stamped, intent, signal)
757
+ this.writeDescriptor(
758
+ (writeSignal) => this.stores.peerStore.updatePeer(stamped, intent, writeSignal),
759
+ signal
760
+ )
769
761
  );
770
762
  const key: ZLinkPeerLocationKey = {
771
763
  autoConnectType: peer.autoConnectType,
@@ -1353,11 +1345,7 @@ export class ZLinkLocationRuntime implements ZLinkLocationRuntimeQuery {
1353
1345
  }
1354
1346
  }
1355
1347
 
1356
- private notifyIfStale(
1357
- result: ZLinkLocationWriteResult,
1358
- kind: ZLinkLocationKind,
1359
- key: string
1360
- ): void {
1348
+ notifyIfStale(result: ZLinkLocationWriteResult, kind: ZLinkLocationKind, key: string): void {
1361
1349
  if (result.status !== ZLinkLocationWriteStatus.IgnoredStale) {
1362
1350
  return;
1363
1351
  }
@@ -6,7 +6,7 @@ import { ZLinkBufferMessage as RuntimeMessage } from '../backend/runtime-message
6
6
  import type { ZLinkMessageSerializer } from '../../contracts';
7
7
  import {
8
8
  decodeApplicationPayloadView,
9
- encodeApplicationPayload
9
+ encodeOwnedApplicationPayload
10
10
  } from '../foundation/service-wire-m6a-codec';
11
11
  import {
12
12
  decodeFrameworkPayloadMessage,
@@ -23,7 +23,7 @@ export function encodeFrameworkCreationPayload(
23
23
  ): Buffer {
24
24
  const message = encodeFrameworkPayloadMessage(payload ?? null, registry);
25
25
  try {
26
- return encodeApplicationPayload({
26
+ return encodeOwnedApplicationPayload({
27
27
  packetName: CREATION_PACKET_NAME,
28
28
  contentType: frameworkPayloadContentType(message),
29
29
  payload: message.data()
@@ -42,7 +42,7 @@ export function decodeFrameworkCreationPayload<T>(
42
42
  if (application.packetName !== CREATION_PACKET_NAME) {
43
43
  throw new RangeError('Creation payload has an unexpected packet name.');
44
44
  }
45
- // The M6A view decoder returns a Buffer over the retained input storage.
45
+ // Creation frames use owned storage and stay alive through synchronous decode.
46
46
  const message = RuntimeMessage.fromOwned(application.payload as Buffer);
47
47
  try {
48
48
  return decodeFrameworkPayloadMessage<T>(