@zlink-systems/framework 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/contracts/Configuration/Builders.d.ts +2 -0
- package/dist/contracts/Configuration/InternalDefaults.d.ts +3 -0
- package/dist/contracts/Configuration/InternalDefaults.js +4 -1
- package/dist/contracts/Configuration/RegistrationBuilders.js +9 -0
- package/dist/contracts/Configuration/RegistrationNormalizers.d.ts +1 -0
- package/dist/contracts/Configuration/RegistrationNormalizers.js +16 -0
- package/dist/contracts/Configuration/RegistrationTypes.d.ts +3 -0
- package/dist/runtime/actors/actor-mailbox.d.ts +2 -2
- package/dist/runtime/actors/actor-mailbox.js +2 -2
- package/dist/runtime/backend/node/node-raw-mesh-backend.js +1 -3
- package/dist/runtime/execution/serial-execution-queue.d.ts +12 -3
- package/dist/runtime/execution/serial-execution-queue.js +116 -28
- package/dist/runtime/foundation/operation-registry.d.ts +3 -2
- package/dist/runtime/foundation/operation-registry.js +5 -1
- package/dist/runtime/foundation/service-authority-payload-codec.d.ts +0 -2
- package/dist/runtime/foundation/service-authority-payload-codec.js +0 -43
- package/dist/runtime/foundation/service-stateful-registry.d.ts +4 -3
- package/dist/runtime/foundation/service-stateful-registry.js +5 -2
- package/dist/runtime/foundation/service-stateful-runtime.d.ts +1 -8
- package/dist/runtime/foundation/service-stateful-runtime.js +78 -126
- package/dist/runtime/foundation/service-stateful-wire-codec.d.ts +0 -11
- package/dist/runtime/foundation/service-stateful-wire-codec.js +1 -23
- package/dist/runtime/foundation/service-topology-registry.d.ts +5 -0
- package/dist/runtime/foundation/service-topology-registry.js +16 -17
- package/dist/runtime/host/instance-activation-authority.d.ts +0 -1
- package/dist/runtime/host/instance-activation-authority.js +46 -97
- package/dist/runtime/host/spot-address-transport.js +1 -1
- package/dist/runtime/host/stateful-authority-route-runtime.d.ts +1 -0
- package/dist/runtime/host/stateful-authority-route-runtime.js +17 -9
- package/dist/runtime/locations/domain-store-contract.d.ts +1 -0
- package/dist/runtime/locations/in-memory-authority-store.d.ts +2 -0
- package/dist/runtime/locations/in-memory-authority-store.js +22 -8
- package/dist/runtime/locations/in-memory-location-store.d.ts +1 -0
- package/dist/runtime/locations/in-memory-location-store.js +3 -0
- package/dist/runtime/locations/internal-store-contracts.d.ts +1 -1
- package/dist/runtime/locations/location-store-repository.d.ts +1 -0
- package/dist/runtime/locations/location-store-repository.js +92 -49
- package/dist/runtime/spots/spot-serial-executor.js +4 -1
- package/dist/runtime/spots/spot-serial-turn-executor.d.ts +3 -0
- package/dist/runtime/spots/spot-serial-turn-executor.js +5 -0
- package/dist/runtime/streams/index.d.ts +0 -1
- package/dist/runtime/streams/index.js +4 -3
- package/dist/runtime/streams/protocol.js +0 -9
- package/dist/runtime/streams/session-context.d.ts +0 -4
- package/dist/runtime/streams/session-context.js +0 -26
- package/dist/runtime/streams/session-serial-executor.d.ts +0 -1
- package/dist/runtime/streams/session-serial-executor.js +0 -3
- package/dist/runtime/streams/stream-message-utils.d.ts +0 -1
- package/dist/runtime/streams/stream-message-utils.js +0 -20
- package/dist/runtime/streams/stream-session-runtime.d.ts +6 -3
- package/dist/runtime/streams/stream-session-runtime.js +40 -85
- package/package.json +3 -3
- package/src/contracts/Configuration/Builders.ts +2 -0
- package/src/contracts/Configuration/InternalDefaults.ts +4 -0
- package/src/contracts/Configuration/RegistrationBuilders.ts +13 -0
- package/src/contracts/Configuration/RegistrationNormalizers.ts +25 -0
- package/src/contracts/Configuration/RegistrationTypes.ts +3 -0
- package/src/runtime/actors/actor-mailbox.ts +7 -2
- package/src/runtime/backend/node/node-raw-mesh-backend.ts +1 -7
- package/src/runtime/execution/serial-execution-queue.ts +117 -29
- package/src/runtime/foundation/operation-registry.ts +16 -9
- package/src/runtime/foundation/service-authority-payload-codec.ts +0 -46
- package/src/runtime/foundation/service-stateful-registry.ts +10 -5
- package/src/runtime/foundation/service-stateful-runtime.ts +103 -181
- package/src/runtime/foundation/service-stateful-wire-codec.ts +0 -25
- package/src/runtime/foundation/service-topology-registry.ts +30 -20
- package/src/runtime/host/instance-activation-authority.ts +58 -125
- package/src/runtime/host/spot-address-transport.ts +1 -1
- package/src/runtime/host/stateful-authority-route-runtime.ts +22 -9
- package/src/runtime/locations/domain-store-contract.ts +5 -0
- package/src/runtime/locations/in-memory-authority-store.ts +42 -8
- package/src/runtime/locations/in-memory-location-store.ts +8 -0
- package/src/runtime/locations/internal-store-contracts.ts +1 -0
- package/src/runtime/locations/location-store-repository.ts +123 -64
- package/src/runtime/protocol/service_wire_pilot_codec.generated.ts +1 -1
- package/src/runtime/spots/spot-serial-executor.ts +4 -1
- package/src/runtime/spots/spot-serial-turn-executor.ts +10 -0
- package/src/runtime/streams/index.ts +3 -1
- package/src/runtime/streams/protocol.ts +0 -13
- package/src/runtime/streams/session-context.ts +0 -32
- package/src/runtime/streams/session-serial-executor.ts +0 -8
- package/src/runtime/streams/stream-message-utils.ts +1 -27
- package/src/runtime/streams/stream-session-runtime.ts +54 -97
- package/dist/runtime/streams/session-requests.d.ts +0 -25
- package/dist/runtime/streams/session-requests.js +0 -101
- package/src/runtime/streams/session-requests.ts +0 -110
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ServiceStatefulRuntime =
|
|
3
|
+
exports.ServiceStatefulRuntime = void 0;
|
|
4
4
|
exports.statefulMailboxData = statefulMailboxData;
|
|
5
5
|
const stream_wire_1 = require("@zlink-systems/stream-wire");
|
|
6
6
|
const node_async_hooks_1 = require("node:async_hooks");
|
|
@@ -31,15 +31,6 @@ const SPOT_MOVING = 34;
|
|
|
31
31
|
const USER_SPOT_OPERATION_CAPACITY = 65_536;
|
|
32
32
|
const USER_SPOT_OPERATION_REPLAY_RETENTION_MS = 5 * 60_000;
|
|
33
33
|
const ACTOR_ROUTE_NOT_FOUND = 1;
|
|
34
|
-
class ServiceInstanceActivationRedirectError extends Error {
|
|
35
|
-
route;
|
|
36
|
-
constructor(route) {
|
|
37
|
-
super(`Instance Spot '${route.targetSpotId}' is owned by '${route.targetNodeRid}'.`);
|
|
38
|
-
this.route = route;
|
|
39
|
-
this.name = 'ServiceInstanceActivationRedirectError';
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
exports.ServiceInstanceActivationRedirectError = ServiceInstanceActivationRedirectError;
|
|
43
34
|
/**
|
|
44
35
|
* Owns M6B routing, identity fences and terminal operations. Application turns
|
|
45
36
|
* remain in the common mailbox, where a claimed Spot or Actor owner is serial.
|
|
@@ -410,7 +401,7 @@ class ServiceStatefulRuntime {
|
|
|
410
401
|
const committed = await authority.commit(target, reservation, spot);
|
|
411
402
|
if (committed.kind !== 'committed') {
|
|
412
403
|
this.registry.closeSpot(spot.ref);
|
|
413
|
-
throw
|
|
404
|
+
throw activationRefused();
|
|
414
405
|
}
|
|
415
406
|
return this.finishRecoveredInstanceActivation(envelope, committed.route, expectedCurrentRoute);
|
|
416
407
|
}
|
|
@@ -1116,6 +1107,10 @@ class ServiceStatefulRuntime {
|
|
|
1116
1107
|
const metadata = (record.flags & service_stateful_wire_codec_1.M6bServiceWireFlag.metadata) !== 0;
|
|
1117
1108
|
decoded = (0, service_stateful_wire_codec_1.decodeStatefulHeader)(record.parts[0], record.parts[metadata ? 2 : 1], metadata ? record.parts[1] : undefined);
|
|
1118
1109
|
}
|
|
1110
|
+
if (decoded.kind === 'instanceSpot' &&
|
|
1111
|
+
(decoded.operationKind === 'request') !== (record.requestSequence !== undefined)) {
|
|
1112
|
+
return 'protocolError';
|
|
1113
|
+
}
|
|
1119
1114
|
const hasPayload = [
|
|
1120
1115
|
'spotSend',
|
|
1121
1116
|
'spotRequest',
|
|
@@ -1164,6 +1159,12 @@ class ServiceStatefulRuntime {
|
|
|
1164
1159
|
return await this.handleIngress(record, decoded, payloadFrame, metadataFrame, canonicalActorJoin, canonicalPayload);
|
|
1165
1160
|
}
|
|
1166
1161
|
catch (error) {
|
|
1162
|
+
if (decoded.kind === 'instanceSpot' &&
|
|
1163
|
+
decoded.activation === 'missing' &&
|
|
1164
|
+
(error instanceof contracts_1.ZLinkFrameworkException || error instanceof service_stateful_registry_1.ServiceStaleGenerationError)) {
|
|
1165
|
+
this.finishMissingInstanceActivationFailure(record, decoded, error);
|
|
1166
|
+
return 'infrastructure';
|
|
1167
|
+
}
|
|
1167
1168
|
const correlation = statefulCorrelation(decoded);
|
|
1168
1169
|
if (correlation !== undefined) {
|
|
1169
1170
|
const result = failure(error);
|
|
@@ -1254,7 +1255,7 @@ class ServiceStatefulRuntime {
|
|
|
1254
1255
|
sourceSpotId: record.sourceSpotId,
|
|
1255
1256
|
targetSpot: record.target.spot,
|
|
1256
1257
|
...(record.kind === 'spotRequest'
|
|
1257
|
-
? { reply: this.replyPort(ingress, record.correlation
|
|
1258
|
+
? { reply: this.replyPort(ingress, record.correlation) }
|
|
1258
1259
|
: {})
|
|
1259
1260
|
});
|
|
1260
1261
|
}
|
|
@@ -1290,7 +1291,7 @@ class ServiceStatefulRuntime {
|
|
|
1290
1291
|
originalReplyRouteId: ingress.requestSequence ?? 0n
|
|
1291
1292
|
},
|
|
1292
1293
|
...(record.kind === 'actorRequest'
|
|
1293
|
-
? { reply: this.replyPort(ingress, record.correlation
|
|
1294
|
+
? { reply: this.replyPort(ingress, record.correlation) }
|
|
1294
1295
|
: {})
|
|
1295
1296
|
});
|
|
1296
1297
|
}
|
|
@@ -1324,10 +1325,10 @@ class ServiceStatefulRuntime {
|
|
|
1324
1325
|
return this.handleUserSpotOperation(ingress, record);
|
|
1325
1326
|
}
|
|
1326
1327
|
}
|
|
1327
|
-
enqueueInstanceSpot(ingress, record, payloadFrame, metadataFrame) {
|
|
1328
|
+
enqueueInstanceSpot(ingress, record, payloadFrame, metadataFrame, localReply) {
|
|
1328
1329
|
if (record.activation === 'missing' && this.asyncInstanceAuthority !== undefined) {
|
|
1329
1330
|
const ownedIngress = shareIngressOwnership(ingress);
|
|
1330
|
-
void this.continueMissingInstanceActivation(ownedIngress, record, payloadFrame,
|
|
1331
|
+
void this.continueMissingInstanceActivation(ownedIngress, record, payloadFrame, localReply, metadataFrame).finally(() => ownedIngress.applicationJobOwner?.close());
|
|
1331
1332
|
return 'infrastructure';
|
|
1332
1333
|
}
|
|
1333
1334
|
if (record.activation === 'ready' &&
|
|
@@ -1344,7 +1345,7 @@ class ServiceStatefulRuntime {
|
|
|
1344
1345
|
objectGeneration: spot.ref.generation
|
|
1345
1346
|
}
|
|
1346
1347
|
: this.instanceApplicationTarget(record);
|
|
1347
|
-
return this.enqueueActivatedInstanceSpot(ingress, record, payloadFrame, spot,
|
|
1348
|
+
return this.enqueueActivatedInstanceSpot(ingress, record, payloadFrame, spot, localReply, this.instanceApplicationTerminalCompletion(applicationTarget), metadataFrame);
|
|
1348
1349
|
}
|
|
1349
1350
|
needsInstanceApplicationMaterialization(ingress, record) {
|
|
1350
1351
|
const lifecycle = this.instanceApplicationLifecycle;
|
|
@@ -1376,7 +1377,10 @@ class ServiceStatefulRuntime {
|
|
|
1376
1377
|
return 'application';
|
|
1377
1378
|
}
|
|
1378
1379
|
const directReply = record.operationKind === 'request'
|
|
1379
|
-
? (localReply ??
|
|
1380
|
+
? (localReply ??
|
|
1381
|
+
(ingress.requestSequence === undefined
|
|
1382
|
+
? undefined
|
|
1383
|
+
: this.replyPort(ingress, record.replyRouteId)))
|
|
1380
1384
|
: undefined;
|
|
1381
1385
|
const applicationTarget = applicationTargetOverride ??
|
|
1382
1386
|
(record.activation === 'missing'
|
|
@@ -1410,7 +1414,7 @@ class ServiceStatefulRuntime {
|
|
|
1410
1414
|
...(metadataFrame === undefined ? {} : { applicationMetadata: metadataFrame }),
|
|
1411
1415
|
...(terminalCompletion === undefined ? {} : { onTerminalCompletion: terminalCompletion }),
|
|
1412
1416
|
...(onHandlerTurnStarted === undefined ? {} : { onHandlerTurnStarted }),
|
|
1413
|
-
...(record.operationKind === 'request'
|
|
1417
|
+
...(record.operationKind === 'request' && directReply !== undefined
|
|
1414
1418
|
? {
|
|
1415
1419
|
reply: onTerminalCompletion === undefined
|
|
1416
1420
|
? directReply
|
|
@@ -1442,27 +1446,7 @@ class ServiceStatefulRuntime {
|
|
|
1442
1446
|
}
|
|
1443
1447
|
}
|
|
1444
1448
|
catch (error) {
|
|
1445
|
-
|
|
1446
|
-
if (error instanceof ServiceInstanceActivationRedirectError && !this.closed) {
|
|
1447
|
-
try {
|
|
1448
|
-
await this.relayMissingInstanceActivation(record, payloadFrame, error.route, ingress, localReply, metadataFrame);
|
|
1449
|
-
return;
|
|
1450
|
-
}
|
|
1451
|
-
catch (relayError) {
|
|
1452
|
-
terminalError = relayError;
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
if (record.operationKind !== 'request')
|
|
1456
|
-
return;
|
|
1457
|
-
const result = failure(terminalError);
|
|
1458
|
-
if (localReply !== undefined) {
|
|
1459
|
-
localReply(result.terminalResult, result.failureCode);
|
|
1460
|
-
}
|
|
1461
|
-
else {
|
|
1462
|
-
if (record.replyRouteId === undefined)
|
|
1463
|
-
return;
|
|
1464
|
-
this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
|
|
1465
|
-
}
|
|
1449
|
+
this.finishMissingInstanceActivationFailure(ingress, record, error, localReply);
|
|
1466
1450
|
}
|
|
1467
1451
|
}
|
|
1468
1452
|
async continueReadyInstanceMaterialization(ingress, record, payloadFrame, metadataFrame) {
|
|
@@ -1492,7 +1476,9 @@ class ServiceStatefulRuntime {
|
|
|
1492
1476
|
}
|
|
1493
1477
|
}
|
|
1494
1478
|
catch (error) {
|
|
1495
|
-
if (record.operationKind !== 'request' ||
|
|
1479
|
+
if (record.operationKind !== 'request' ||
|
|
1480
|
+
record.replyRouteId === undefined ||
|
|
1481
|
+
ingress.requestSequence === undefined)
|
|
1496
1482
|
return;
|
|
1497
1483
|
const result = failure(error);
|
|
1498
1484
|
this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
|
|
@@ -1545,8 +1531,11 @@ class ServiceStatefulRuntime {
|
|
|
1545
1531
|
return { onTerminalCompletion, onHandlerTurnStarted };
|
|
1546
1532
|
}
|
|
1547
1533
|
instanceApplicationTerminalCompletion(target) {
|
|
1534
|
+
const lifecycle = this.instanceApplicationLifecycle;
|
|
1535
|
+
if (lifecycle === undefined)
|
|
1536
|
+
return undefined;
|
|
1548
1537
|
return async () => {
|
|
1549
|
-
const authorityReleased =
|
|
1538
|
+
const authorityReleased = await lifecycle.completeTerminal(target);
|
|
1550
1539
|
if (!authorityReleased)
|
|
1551
1540
|
return;
|
|
1552
1541
|
const current = this.instanceIntents.get(target.targetSpotId);
|
|
@@ -1601,39 +1590,33 @@ class ServiceStatefulRuntime {
|
|
|
1601
1590
|
for (const waiter of waiters)
|
|
1602
1591
|
waiter();
|
|
1603
1592
|
}
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
};
|
|
1615
|
-
if (record.operationKind === 'send') {
|
|
1616
|
-
const submitted = await this.submitOneWay(route.targetNodeRid, instanceOperationParts([
|
|
1617
|
-
(0, service_stateful_wire_codec_1.encodeInstanceSpotActivationHeader)(redirectedTarget, this.nodeGeneration, this.nodeRid, record.sourceSpotId, 'send', record.operation, record.deadlineUnixMs, undefined, metadataFrame !== undefined),
|
|
1618
|
-
payloadFrame
|
|
1619
|
-
], metadataFrame));
|
|
1620
|
-
if (submitted !== runtime_values_1.SubmitResult.Ok) {
|
|
1621
|
-
throw new Error(`Instance activation redirect was not admitted: ${submitted}.`);
|
|
1593
|
+
finishMissingInstanceActivationFailure(ingress, record, error, localReply) {
|
|
1594
|
+
if (record.operationKind === 'request') {
|
|
1595
|
+
if (record.deadlineUnixMs <= BigInt(Date.now()))
|
|
1596
|
+
return;
|
|
1597
|
+
const result = failure(error);
|
|
1598
|
+
if (localReply !== undefined) {
|
|
1599
|
+
localReply(result.terminalResult, result.failureCode);
|
|
1600
|
+
}
|
|
1601
|
+
else if (ingress.requestSequence !== undefined && record.replyRouteId !== undefined) {
|
|
1602
|
+
this.replyWire(ingress, record.replyRouteId, result.terminalResult, result.failureCode);
|
|
1622
1603
|
}
|
|
1623
1604
|
return;
|
|
1624
1605
|
}
|
|
1625
|
-
const
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1606
|
+
const reporter = this.dispatchErrors;
|
|
1607
|
+
if (reporter?.captureEnabled() === true) {
|
|
1608
|
+
reporter.report({
|
|
1609
|
+
surface: ZLinkDispatchOptions_1.ZLinkDispatchErrorSurface.InstanceSpot,
|
|
1610
|
+
messageKind: ZLinkDispatchOptions_1.ZLinkDispatchMessageKind.Send,
|
|
1611
|
+
reason: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorReason.StaleTarget,
|
|
1612
|
+
action: ZLinkDispatchOptions_1.ZLinkRuntimeDispatchErrorAction.Drop,
|
|
1613
|
+
meshName: this.dispatchErrorMeshName,
|
|
1614
|
+
sourceRid: record.sourceNodeRid,
|
|
1615
|
+
spotId: record.target.targetSpotId,
|
|
1616
|
+
instanceSpotType: record.target.stableType,
|
|
1617
|
+
activationState: 'activating',
|
|
1618
|
+
error
|
|
1619
|
+
});
|
|
1637
1620
|
}
|
|
1638
1621
|
}
|
|
1639
1622
|
requireInstanceActivation(ingress, record) {
|
|
@@ -1731,12 +1714,9 @@ class ServiceStatefulRuntime {
|
|
|
1731
1714
|
return local;
|
|
1732
1715
|
}
|
|
1733
1716
|
if (current.kind === 'ready') {
|
|
1734
|
-
throw
|
|
1717
|
+
throw activationRefused();
|
|
1735
1718
|
}
|
|
1736
1719
|
const reserved = authority.reserve(target, record.operation, record.deadlineUnixMs);
|
|
1737
|
-
if (reserved.kind === 'ready') {
|
|
1738
|
-
throw new ServiceInstanceActivationRedirectError(reserved.route);
|
|
1739
|
-
}
|
|
1740
1720
|
let activation;
|
|
1741
1721
|
try {
|
|
1742
1722
|
activation = this.activateInstanceSpot(target.targetSpotId, target.stableType, reserved.reservation.attempt, reserved.reservation.authorityOwnerGeneration);
|
|
@@ -1749,7 +1729,7 @@ class ServiceStatefulRuntime {
|
|
|
1749
1729
|
if (committed.kind === 'lost') {
|
|
1750
1730
|
if (activation.created)
|
|
1751
1731
|
this.registry.closeSpot(activation.spot.ref);
|
|
1752
|
-
throw
|
|
1732
|
+
throw activationRefused();
|
|
1753
1733
|
}
|
|
1754
1734
|
if (!routeMatchesLocal(committed.route, activation.spot, this.nodeRid, this.nodeGeneration)) {
|
|
1755
1735
|
if (activation.created)
|
|
@@ -1803,12 +1783,12 @@ class ServiceStatefulRuntime {
|
|
|
1803
1783
|
}
|
|
1804
1784
|
};
|
|
1805
1785
|
}
|
|
1786
|
+
if (current.kind === 'ready' && current.route.targetNodeRid !== this.nodeRid) {
|
|
1787
|
+
throw activationRefused();
|
|
1788
|
+
}
|
|
1806
1789
|
if (current.kind === 'ready' &&
|
|
1807
1790
|
current.authority !== undefined &&
|
|
1808
1791
|
(0, service_authority_payload_codec_1.decodeServiceClosingSpotAuthority)(current.authority.payload) !== undefined) {
|
|
1809
|
-
if (current.route.targetNodeRid !== this.nodeRid) {
|
|
1810
|
-
throw new ServiceInstanceActivationRedirectError(current.route);
|
|
1811
|
-
}
|
|
1812
1792
|
if (current.route.targetNodeGeneration !== this.nodeGeneration ||
|
|
1813
1793
|
(local !== undefined &&
|
|
1814
1794
|
!routeMatchesLocal(current.route, local, this.nodeRid, this.nodeGeneration)) ||
|
|
@@ -1821,17 +1801,11 @@ class ServiceStatefulRuntime {
|
|
|
1821
1801
|
};
|
|
1822
1802
|
}
|
|
1823
1803
|
if (!this.acceptsLocalMissingInstancePlacement()) {
|
|
1824
|
-
if (current.kind === 'ready' && current.route.targetNodeRid !== this.nodeRid) {
|
|
1825
|
-
throw new ServiceInstanceActivationRedirectError(current.route);
|
|
1826
|
-
}
|
|
1827
1804
|
// A draining or retiring node must not create a successor generation
|
|
1828
1805
|
// from a stale client route while relocation is publishing its target.
|
|
1829
1806
|
throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
1830
1807
|
}
|
|
1831
1808
|
if (current.kind === 'ready') {
|
|
1832
|
-
if (current.route.targetNodeRid !== this.nodeRid) {
|
|
1833
|
-
throw new ServiceInstanceActivationRedirectError(current.route);
|
|
1834
|
-
}
|
|
1835
1809
|
if (current.route.targetNodeGeneration !== this.nodeGeneration) {
|
|
1836
1810
|
throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
1837
1811
|
}
|
|
@@ -1867,16 +1841,9 @@ class ServiceStatefulRuntime {
|
|
|
1867
1841
|
await lifecycle.discard(target);
|
|
1868
1842
|
materialized = lifecycle.isMaterialized(target);
|
|
1869
1843
|
}
|
|
1870
|
-
// The
|
|
1871
|
-
//
|
|
1872
|
-
//
|
|
1873
|
-
// A newer reservation can also become visible after an explicit close
|
|
1874
|
-
// releases authority but before local disposal removes the old
|
|
1875
|
-
// projection. The close gate serializes that replacement; rejecting it
|
|
1876
|
-
// here turns a valid next-generation Instance intent into a stale-route
|
|
1877
|
-
// failure.
|
|
1878
|
-
// If disposal did not remove an orphaned materialization, keep fencing
|
|
1879
|
-
// it instead of admitting work into two application generations.
|
|
1844
|
+
// The local projection does not decide reservation ownership. Preserve
|
|
1845
|
+
// the Creating fence for Reserve, which refuses a competing attempt.
|
|
1846
|
+
// Keep fencing an orphan that disposal did not remove.
|
|
1880
1847
|
if ((!joinsCreating && current.kind !== 'missing') ||
|
|
1881
1848
|
(!joinsCreating && materialized !== false && !materializing)) {
|
|
1882
1849
|
throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
@@ -1901,9 +1868,6 @@ class ServiceStatefulRuntime {
|
|
|
1901
1868
|
finally {
|
|
1902
1869
|
deadline.close();
|
|
1903
1870
|
}
|
|
1904
|
-
if (reserved.kind === 'ready') {
|
|
1905
|
-
throw new ServiceInstanceActivationRedirectError(reserved.route);
|
|
1906
|
-
}
|
|
1907
1871
|
if (this.closed || record.deadlineUnixMs < BigInt(Date.now())) {
|
|
1908
1872
|
await authority.abort(target, reserved.reservation);
|
|
1909
1873
|
throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
@@ -1932,7 +1896,7 @@ class ServiceStatefulRuntime {
|
|
|
1932
1896
|
if (activation.created)
|
|
1933
1897
|
this.registry.closeSpot(activation.spot.ref);
|
|
1934
1898
|
await this.instanceApplicationLifecycle?.discard(target);
|
|
1935
|
-
throw
|
|
1899
|
+
throw activationRefused();
|
|
1936
1900
|
}
|
|
1937
1901
|
if (!routeMatchesLocal(committed.route, activation.spot, this.nodeRid, this.nodeGeneration)) {
|
|
1938
1902
|
if (activation.created)
|
|
@@ -1960,7 +1924,7 @@ class ServiceStatefulRuntime {
|
|
|
1960
1924
|
const confirmed = await authority.read(target);
|
|
1961
1925
|
if (confirmed.kind !== 'ready' || !sameInstanceRoute(confirmed.route, route)) {
|
|
1962
1926
|
if (confirmed.kind === 'ready' && confirmed.route.targetNodeRid !== this.nodeRid) {
|
|
1963
|
-
throw
|
|
1927
|
+
throw activationRefused();
|
|
1964
1928
|
}
|
|
1965
1929
|
throw new service_stateful_registry_1.ServiceStaleGenerationError('spot', target.targetSpotId);
|
|
1966
1930
|
}
|
|
@@ -2056,7 +2020,7 @@ class ServiceStatefulRuntime {
|
|
|
2056
2020
|
kindData: control,
|
|
2057
2021
|
...(canonicalApplicationPayload === undefined ? {} : { canonicalApplicationPayload }),
|
|
2058
2022
|
reply: (terminalResult, failureCode, replyPayload, tail) => {
|
|
2059
|
-
return this.replyPort(ingress, record.correlation
|
|
2023
|
+
return this.replyPort(ingress, record.correlation)(terminalResult, failureCode, replyPayload, tail);
|
|
2060
2024
|
}
|
|
2061
2025
|
});
|
|
2062
2026
|
}
|
|
@@ -2538,20 +2502,20 @@ class ServiceStatefulRuntime {
|
|
|
2538
2502
|
}
|
|
2539
2503
|
return accepted;
|
|
2540
2504
|
}
|
|
2541
|
-
replyPort(ingress, correlation
|
|
2505
|
+
replyPort(ingress, correlation) {
|
|
2542
2506
|
if (correlation === undefined)
|
|
2543
2507
|
throw new service_wire_m6a_codec_1.ServiceWireProtocolError('Request correlation is missing.');
|
|
2544
2508
|
return (terminalResult, failureCode, payload, tail) => {
|
|
2545
2509
|
this.replyWire(ingress, correlation, terminalResult, failureCode, payload, tail);
|
|
2546
|
-
void operationKind;
|
|
2547
2510
|
return true;
|
|
2548
2511
|
};
|
|
2549
2512
|
}
|
|
2550
2513
|
replyWire(ingress, correlation, terminalResult, failureCode, payload, tail) {
|
|
2551
|
-
|
|
2514
|
+
const parts = [
|
|
2552
2515
|
(0, service_stateful_wire_codec_1.encodeStatefulReply)(correlation, terminalResult, failureCode, tail),
|
|
2553
2516
|
...(payload === undefined ? [] : [(0, service_wire_m6a_codec_1.encodeApplicationPayload)(payload)])
|
|
2554
|
-
]
|
|
2517
|
+
];
|
|
2518
|
+
this.raw.replyService(ingress, parts);
|
|
2555
2519
|
}
|
|
2556
2520
|
handleUserSpotOperation(ingress, record) {
|
|
2557
2521
|
if (ingress.requestSequence === undefined ||
|
|
@@ -3094,24 +3058,11 @@ class ServiceStatefulRuntime {
|
|
|
3094
3058
|
return;
|
|
3095
3059
|
}
|
|
3096
3060
|
if (decoded.kind === 'instanceSpot') {
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
const activation = this.requireInstanceActivation(ingress, decoded);
|
|
3103
|
-
this.enqueueApplicationFrame(ingress, `spot:${activation.ref.spotId}`, payloadFrame, {
|
|
3104
|
-
receiveKind: service_runtime_contracts_1.ReceiveKind.InstanceSpotActivation,
|
|
3105
|
-
operationKind: service_runtime_contracts_1.OperationKind.InstanceSpotRequest,
|
|
3106
|
-
correlation: pending.id,
|
|
3107
|
-
...(decoded.sourceSpotId === undefined ? {} : { sourceSpotId: decoded.sourceSpotId }),
|
|
3108
|
-
targetSpot: activation.ref,
|
|
3109
|
-
reply: localReply
|
|
3110
|
-
});
|
|
3111
|
-
}
|
|
3112
|
-
catch (error) {
|
|
3113
|
-
this.operations.reply(pending.id, failure(error));
|
|
3114
|
-
}
|
|
3061
|
+
const hasMetadata = (ingress.flags & service_stateful_wire_codec_1.M6bServiceWireFlag.metadata) !== 0;
|
|
3062
|
+
const metadataFrame = hasMetadata
|
|
3063
|
+
? (0, service_metadata_codec_1.validateServiceMetadataFrame)(ingress.parts[1])
|
|
3064
|
+
: undefined;
|
|
3065
|
+
this.enqueueInstanceSpot(ingress, decoded, ingress.parts[hasMetadata ? 2 : 1], metadataFrame, localReply);
|
|
3115
3066
|
return;
|
|
3116
3067
|
}
|
|
3117
3068
|
this.operations.reply(pending.id, {
|
|
@@ -3292,9 +3243,7 @@ class ServiceStatefulRuntime {
|
|
|
3292
3243
|
...(wire.correlation === undefined ? {} : { correlation: wire.correlation }),
|
|
3293
3244
|
sourceSpotId: wire.sourceSpotId,
|
|
3294
3245
|
targetSpot: wire.target.spot,
|
|
3295
|
-
...(wire.kind === 'spotRequest'
|
|
3296
|
-
? { reply: this.replyPort(ingress, wire.correlation, 'spotRequest') }
|
|
3297
|
-
: {})
|
|
3246
|
+
...(wire.kind === 'spotRequest' ? { reply: this.replyPort(ingress, wire.correlation) } : {})
|
|
3298
3247
|
});
|
|
3299
3248
|
}
|
|
3300
3249
|
async drainSpotMessageFollow(state) {
|
|
@@ -3671,6 +3620,9 @@ function actorLocation(actor) {
|
|
|
3671
3620
|
membershipEpoch: actor.membershipEpoch
|
|
3672
3621
|
};
|
|
3673
3622
|
}
|
|
3623
|
+
function activationRefused() {
|
|
3624
|
+
return new contracts_1.ZLinkFrameworkException(contracts_1.ZLinkFrameworkErrorKind.Unavailable, 'Instance cold activation authority was refused.');
|
|
3625
|
+
}
|
|
3674
3626
|
function failure(error) {
|
|
3675
3627
|
if (error instanceof service_stateful_registry_1.ServiceStaleGenerationError) {
|
|
3676
3628
|
return (0, framework_errors_internal_1.internalFrameworkWireReply)(framework_errors_internal_1.ZLinkFrameworkInternalErrorKind.ActorLocationStale);
|
|
@@ -105,8 +105,6 @@ export type ServiceWireRelocationObject = {
|
|
|
105
105
|
readonly spotId: string;
|
|
106
106
|
readonly objectGeneration: bigint;
|
|
107
107
|
};
|
|
108
|
-
/** Shared CRC-32C wire bounds for the direct relocation payload transfer. */
|
|
109
|
-
export declare const RELOCATION_PAYLOAD_TOTAL_LENGTH_MAX = 274877906944n;
|
|
110
108
|
export declare const RELOCATION_PAYLOAD_CHUNK_COUNT_MAX = 4096;
|
|
111
109
|
export { RELOCATION_STATE_CHUNK_DATA_MAX_BYTES } from '../../contracts/Configuration/InternalDefaults';
|
|
112
110
|
interface ServiceWireRelocationBase {
|
|
@@ -576,15 +574,6 @@ export declare function decodeMaintenanceReplyRelayAck(frame: Uint8Array): Servi
|
|
|
576
574
|
export declare function decodeStatefulReply(frame: Uint8Array, expectedCorrelation: bigint, operationKind: 'spotRequest' | 'actorRequest' | 'actorLookup' | 'actorDestroy' | 'actorJoin' | 'streamBind' | 'streamUnbind' | 'instanceSpotRequest' | 'userSpotCreate' | 'userSpotClose' | 'actorCreate', hasPayload?: boolean): ServiceStatefulReply;
|
|
577
575
|
export declare function sessionBindingFromWire(actor: ServiceActorRef, sessionRid: string, sessionOwnerNodeRid: string, generation: bigint, membershipEpoch: bigint, sessionOwnerNodeGeneration?: bigint, sessionOwnerId?: string, sessionOwnerLeaseGeneration?: bigint): ServiceSessionBinding;
|
|
578
576
|
export declare function decodeServiceWireFrozenRecord(bytes: Uint8Array): ServiceWireFrozenRecord;
|
|
579
|
-
/**
|
|
580
|
-
* Consumes exactly one canonical frozen record at `offset`. Relocation
|
|
581
|
-
* saved-work is a vector of back-to-back records, so its boundary is the
|
|
582
|
-
* record grammar itself rather than an enclosing length field.
|
|
583
|
-
*/
|
|
584
|
-
export declare function decodeServiceWireFrozenRecordPrefix(bytes: Uint8Array, offset?: number): {
|
|
585
|
-
readonly record: ServiceWireFrozenRecord;
|
|
586
|
-
readonly length: number;
|
|
587
|
-
};
|
|
588
577
|
export declare function encodeServiceWireFrozenRecord(record: ServiceWireFrozenRecord): Buffer;
|
|
589
578
|
export declare function encodeServiceWireFrozenActorApplicationRecord(input: {
|
|
590
579
|
readonly source: ServiceWireRequestSourceFence;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.M6bServiceWireFlag = exports.RELOCATION_STATE_CHUNK_DATA_MAX_BYTES = exports.RELOCATION_PAYLOAD_CHUNK_COUNT_MAX = exports.
|
|
3
|
+
exports.M6bServiceWireFlag = exports.RELOCATION_STATE_CHUNK_DATA_MAX_BYTES = exports.RELOCATION_PAYLOAD_CHUNK_COUNT_MAX = exports.M6bServiceWireCommand = void 0;
|
|
4
4
|
exports.serviceSessionRelocationIdentityKey = serviceSessionRelocationIdentityKey;
|
|
5
5
|
exports.encodeSpotHeader = encodeSpotHeader;
|
|
6
6
|
exports.encodeActorHeader = encodeActorHeader;
|
|
@@ -33,7 +33,6 @@ exports.decodeMaintenanceReplyRelayAck = decodeMaintenanceReplyRelayAck;
|
|
|
33
33
|
exports.decodeStatefulReply = decodeStatefulReply;
|
|
34
34
|
exports.sessionBindingFromWire = sessionBindingFromWire;
|
|
35
35
|
exports.decodeServiceWireFrozenRecord = decodeServiceWireFrozenRecord;
|
|
36
|
-
exports.decodeServiceWireFrozenRecordPrefix = decodeServiceWireFrozenRecordPrefix;
|
|
37
36
|
exports.encodeServiceWireFrozenRecord = encodeServiceWireFrozenRecord;
|
|
38
37
|
exports.encodeServiceWireFrozenActorApplicationRecord = encodeServiceWireFrozenActorApplicationRecord;
|
|
39
38
|
const stream_wire_1 = require("@zlink-systems/stream-wire");
|
|
@@ -55,8 +54,6 @@ const MAGIC_0 = service_wire_constants_generated_1.SERVICE_WIRE_MAGIC[0];
|
|
|
55
54
|
const MAGIC_1 = service_wire_constants_generated_1.SERVICE_WIRE_MAGIC[1];
|
|
56
55
|
const MAJOR = service_wire_constants_generated_1.SERVICE_WIRE_MAJOR;
|
|
57
56
|
exports.M6bServiceWireCommand = service_wire_constants_generated_1.ServiceWireCommand;
|
|
58
|
-
/** Shared CRC-32C wire bounds for the direct relocation payload transfer. */
|
|
59
|
-
exports.RELOCATION_PAYLOAD_TOTAL_LENGTH_MAX = 274877906944n;
|
|
60
57
|
exports.RELOCATION_PAYLOAD_CHUNK_COUNT_MAX = 4096;
|
|
61
58
|
var InternalDefaults_2 = require("../../contracts/Configuration/InternalDefaults");
|
|
62
59
|
Object.defineProperty(exports, "RELOCATION_STATE_CHUNK_DATA_MAX_BYTES", { enumerable: true, get: function () { return InternalDefaults_2.RELOCATION_STATE_CHUNK_DATA_MAX_BYTES; } });
|
|
@@ -1623,25 +1620,6 @@ function decodeServiceWireFrozenRecord(bytes) {
|
|
|
1623
1620
|
reader.end('frozen record');
|
|
1624
1621
|
return { ...record, canonicalBytes: Buffer.from(bytes) };
|
|
1625
1622
|
}
|
|
1626
|
-
/**
|
|
1627
|
-
* Consumes exactly one canonical frozen record at `offset`. Relocation
|
|
1628
|
-
* saved-work is a vector of back-to-back records, so its boundary is the
|
|
1629
|
-
* record grammar itself rather than an enclosing length field.
|
|
1630
|
-
*/
|
|
1631
|
-
function decodeServiceWireFrozenRecordPrefix(bytes, offset = 0) {
|
|
1632
|
-
if (!Number.isSafeInteger(offset) || offset < 0 || offset >= bytes.byteLength) {
|
|
1633
|
-
fail('Frozen record offset is outside the encoded bytes.');
|
|
1634
|
-
}
|
|
1635
|
-
const reader = new FrozenReader(bytes.subarray(offset));
|
|
1636
|
-
const record = decodeServiceWireFrozenRecordFrom(reader);
|
|
1637
|
-
return {
|
|
1638
|
-
record: {
|
|
1639
|
-
...record,
|
|
1640
|
-
canonicalBytes: Buffer.from(bytes.subarray(offset, offset + reader.position))
|
|
1641
|
-
},
|
|
1642
|
-
length: reader.position
|
|
1643
|
-
};
|
|
1644
|
-
}
|
|
1645
1623
|
function decodeServiceWireFrozenRecordFrom(reader) {
|
|
1646
1624
|
const recordKind = reader.u8('recordKind');
|
|
1647
1625
|
if (recordKind < 1 || recordKind > 14)
|
|
@@ -58,7 +58,12 @@ export declare class ServiceTopologyRegistry {
|
|
|
58
58
|
selectChannel(channelName: string, isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
|
|
59
59
|
selectPlacement(isReady?: (peer: AdmittedServicePeer) => boolean): AdmittedServicePeer | undefined;
|
|
60
60
|
selectObjectPlacement(stableType: string, isReady?: (descriptor: ServiceNodeDescriptor) => boolean): ServiceNodeDescriptor | undefined;
|
|
61
|
+
selectObjectPlacementWithStatus(stableType: string, isReady?: (descriptor: ServiceNodeDescriptor) => boolean): {
|
|
62
|
+
readonly status: ServiceObjectPlacementStatus;
|
|
63
|
+
readonly descriptor?: ServiceNodeDescriptor;
|
|
64
|
+
};
|
|
61
65
|
objectPlacementStatus(stableType: string, isReady?: (descriptor: ServiceNodeDescriptor) => boolean): ServiceObjectPlacementStatus;
|
|
66
|
+
private objectPlacementCandidates;
|
|
62
67
|
instanceSpotPlacementTypes(): readonly string[];
|
|
63
68
|
private selectWeightedCycle;
|
|
64
69
|
private rebuildSelections;
|
|
@@ -181,21 +181,22 @@ class ServiceTopologyRegistry {
|
|
|
181
181
|
.map(clonePeer), (peer) => peer.descriptor.placementWeight, (peer) => peer.descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.descriptor.nodeRoutingId, right.descriptor.nodeRoutingId), isReady);
|
|
182
182
|
}
|
|
183
183
|
selectObjectPlacement(stableType, isReady = () => true) {
|
|
184
|
+
return this.selectObjectPlacementWithStatus(stableType, isReady).descriptor;
|
|
185
|
+
}
|
|
186
|
+
selectObjectPlacementWithStatus(stableType, isReady = () => true) {
|
|
184
187
|
requireText(stableType, 'stableType');
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
descriptor.activeCapacityUsed < descriptor.activeCapacityLimit &&
|
|
193
|
-
descriptor.pendingCapacityUsed < descriptor.pendingCapacityLimit &&
|
|
194
|
-
descriptor.protocolCapabilities.includes(capability))
|
|
195
|
-
.map(cloneDescriptor);
|
|
196
|
-
}, (descriptor) => descriptor.placementWeight, (descriptor) => descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.nodeRoutingId, right.nodeRoutingId), isReady);
|
|
188
|
+
const candidates = this.objectPlacementCandidates(stableType, isReady);
|
|
189
|
+
if (candidates.status !== 'available')
|
|
190
|
+
return { status: candidates.status };
|
|
191
|
+
const descriptor = this.selectWeightedCycle(`object:${stableType}`, () => this.objectPlacementCandidates(stableType).eligible.map(cloneDescriptor), (descriptor) => descriptor.placementWeight, (descriptor) => descriptor.nodeRoutingId, (left, right) => compareOrdinal(left.nodeRoutingId, right.nodeRoutingId), isReady);
|
|
192
|
+
return descriptor === undefined
|
|
193
|
+
? { status: 'unavailable' }
|
|
194
|
+
: { status: 'available', descriptor };
|
|
197
195
|
}
|
|
198
196
|
objectPlacementStatus(stableType, isReady = () => true) {
|
|
197
|
+
return this.objectPlacementCandidates(stableType, isReady).status;
|
|
198
|
+
}
|
|
199
|
+
objectPlacementCandidates(stableType, isReady = () => true) {
|
|
199
200
|
requireText(stableType, 'stableType');
|
|
200
201
|
const capability = `object-type:${stableType}`;
|
|
201
202
|
const supported = [
|
|
@@ -205,15 +206,13 @@ class ServiceTopologyRegistry {
|
|
|
205
206
|
descriptor.objectRole === 'server' &&
|
|
206
207
|
descriptor.protocolCapabilities.includes(capability));
|
|
207
208
|
if (supported.length === 0)
|
|
208
|
-
return 'unsupported';
|
|
209
|
+
return { status: 'unsupported', eligible: [] };
|
|
209
210
|
const withCapacity = supported.filter((descriptor) => descriptor.activeCapacityUsed < descriptor.activeCapacityLimit &&
|
|
210
211
|
descriptor.pendingCapacityUsed < descriptor.pendingCapacityLimit);
|
|
211
212
|
if (withCapacity.length === 0)
|
|
212
|
-
return 'capacity';
|
|
213
|
+
return { status: 'capacity', eligible: [] };
|
|
213
214
|
const weighted = withCapacity.filter((descriptor) => descriptor.placementWeight > 0);
|
|
214
|
-
|
|
215
|
-
return 'unsupported';
|
|
216
|
-
return weighted.some(isReady) ? 'available' : 'unavailable';
|
|
215
|
+
return { status: weighted.some(isReady) ? 'available' : 'unavailable', eligible: weighted };
|
|
217
216
|
}
|
|
218
217
|
instanceSpotPlacementTypes() {
|
|
219
218
|
const prefix = 'instance-spot-type:';
|
|
@@ -27,6 +27,5 @@ export declare class ZLinkInstanceActivationAuthority implements ServiceAsyncIns
|
|
|
27
27
|
abort(target: ServiceInstanceActivationTarget, reservation: ServiceInstanceActivationReservation): Promise<void>;
|
|
28
28
|
private requireOwner;
|
|
29
29
|
private deleteOrphan;
|
|
30
|
-
private awaitConcurrentActivation;
|
|
31
30
|
private requirePending;
|
|
32
31
|
}
|