@zlink-systems/framework 0.10.0 → 0.11.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/runtime/backend/runtime-values.d.ts +1 -0
- package/dist/runtime/backend/runtime-values.js +6 -0
- package/dist/runtime/channels/channel-socket-registry.js +11 -0
- package/dist/runtime/channels/client-server-location-runtime.js +15 -3
- package/dist/runtime/locations/location-store-repository.d.ts +1 -0
- package/dist/runtime/locations/location-store-repository.js +64 -75
- package/package.json +9 -3
- package/src/runtime/backend/runtime-values.ts +6 -0
- package/src/runtime/channels/channel-socket-registry.ts +13 -0
- package/src/runtime/channels/client-server-location-runtime.ts +16 -2
- package/src/runtime/locations/location-store-repository.ts +118 -80
|
@@ -45,6 +45,7 @@ export declare class ZLinkBackendResultError extends Error {
|
|
|
45
45
|
}
|
|
46
46
|
export declare function isZLinkBackendResultError(error: unknown): error is ZLinkBackendResultError;
|
|
47
47
|
export declare function isBackendNotConnectedError(error: unknown): boolean;
|
|
48
|
+
export declare function isBackendRequestTimeoutError(error: unknown): boolean;
|
|
48
49
|
export type ZLinkBackendMessageLike = Message | Buffer | Uint8Array | string;
|
|
49
50
|
export interface ZLinkBackendSendSubmitBuilder {
|
|
50
51
|
message(message: ZLinkBackendMessageLike): ZLinkBackendSendSubmitBuilder;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ZLinkBackendResultError = exports.ZLINK_BACKEND_RECV_DONT_WAIT = exports.ZLINK_BACKEND_SEND_NONE = exports.SubmitResult = exports.RequestResult = void 0;
|
|
4
4
|
exports.isZLinkBackendResultError = isZLinkBackendResultError;
|
|
5
5
|
exports.isBackendNotConnectedError = isBackendNotConnectedError;
|
|
6
|
+
exports.isBackendRequestTimeoutError = isBackendRequestTimeoutError;
|
|
6
7
|
exports.RequestResult = Object.freeze({
|
|
7
8
|
Ok: 0,
|
|
8
9
|
TimedOut: 101,
|
|
@@ -64,3 +65,8 @@ function isBackendNotConnectedError(error) {
|
|
|
64
65
|
const result = error.result;
|
|
65
66
|
return result === exports.SubmitResult.NotConnected || result === exports.RequestResult.NotConnected;
|
|
66
67
|
}
|
|
68
|
+
function isBackendRequestTimeoutError(error) {
|
|
69
|
+
return isZLinkBackendResultError(error)
|
|
70
|
+
&& error.operation === 'request'
|
|
71
|
+
&& error.result === exports.RequestResult.TimedOut;
|
|
72
|
+
}
|
|
@@ -15,6 +15,7 @@ const runtime_state_projections_1 = require("../foundation/runtime-state-project
|
|
|
15
15
|
const service_wire_m6a_codec_1 = require("../foundation/service-wire-m6a-codec");
|
|
16
16
|
const client_server_service_wire_1 = require("./client-server-service-wire");
|
|
17
17
|
const fanout_service_wire_1 = require("./fanout-service-wire");
|
|
18
|
+
const runtime_values_1 = require("../backend/runtime-values");
|
|
18
19
|
const MAX_LIFECYCLE_GENERATION = 0x7fffffffffffffffn;
|
|
19
20
|
const CLIENT_SERVER_PROBE_INTERVAL_MS = 5_000;
|
|
20
21
|
const CLIENT_SERVER_PEER_DEADLINE_MS = 15_000;
|
|
@@ -952,6 +953,7 @@ class ZLinkChannelSocketRegistry {
|
|
|
952
953
|
return;
|
|
953
954
|
const admissionAttempt = Symbol(connectionId);
|
|
954
955
|
connection.admissionAttempt = admissionAttempt;
|
|
956
|
+
let retryAdmission = false;
|
|
955
957
|
try {
|
|
956
958
|
const admission = await requestClientServerAdmission(connection.dealer, channelName, 'default', 15_000);
|
|
957
959
|
if (this.clientServerConnections.get(connectionId) !== connection
|
|
@@ -972,12 +974,21 @@ class ZLinkChannelSocketRegistry {
|
|
|
972
974
|
|| connection.admissionAttempt !== admissionAttempt)
|
|
973
975
|
return;
|
|
974
976
|
this.removeReadyConnection(connectionId);
|
|
977
|
+
if ((0, runtime_values_1.isBackendRequestTimeoutError)(error)) {
|
|
978
|
+
retryAdmission = true;
|
|
979
|
+
return;
|
|
980
|
+
}
|
|
975
981
|
throw error;
|
|
976
982
|
}
|
|
977
983
|
finally {
|
|
978
984
|
if (this.clientServerConnections.get(connectionId) === connection
|
|
985
|
+
&& connection.physicalConnectionId === physicalConnectionId
|
|
979
986
|
&& connection.admissionAttempt === admissionAttempt) {
|
|
980
987
|
connection.admissionAttempt = undefined;
|
|
988
|
+
if (retryAdmission) {
|
|
989
|
+
void this.admitConfiguredClientServerConnection(channelName, connectionId)
|
|
990
|
+
.catch(error => this.oneWayFailureSink?.(error));
|
|
991
|
+
}
|
|
981
992
|
}
|
|
982
993
|
}
|
|
983
994
|
}
|
|
@@ -8,6 +8,7 @@ const runtime_message_1 = require("../backend/runtime-message");
|
|
|
8
8
|
const client_server_service_wire_1 = require("./client-server-service-wire");
|
|
9
9
|
const runtime_state_projections_1 = require("../foundation/runtime-state-projections");
|
|
10
10
|
const state_lane_1 = require("../execution/state-lane");
|
|
11
|
+
const runtime_values_1 = require("../backend/runtime-values");
|
|
11
12
|
/**
|
|
12
13
|
* Owns the dedicated ClientServer discovery domain. It never converts these
|
|
13
14
|
* descriptors into RouteMesh peer rows.
|
|
@@ -260,6 +261,7 @@ class ZLinkClientServerLocationRuntime {
|
|
|
260
261
|
if (current === undefined) {
|
|
261
262
|
return;
|
|
262
263
|
}
|
|
264
|
+
let retryAdmission = false;
|
|
263
265
|
try {
|
|
264
266
|
const admission = await requestAdmission(current.dealer, current.target.descriptor, this.options.ownerLeaseRenewTimeoutMs);
|
|
265
267
|
const admittedDescriptor = await this.lane.run(() => {
|
|
@@ -294,13 +296,23 @@ class ZLinkClientServerLocationRuntime {
|
|
|
294
296
|
current.target.dealer = undefined;
|
|
295
297
|
await this.sockets.closeClientServerConnection(connectionId);
|
|
296
298
|
}
|
|
299
|
+
if ((0, runtime_values_1.isBackendRequestTimeoutError)(error)) {
|
|
300
|
+
retryAdmission = true;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
297
303
|
throw error;
|
|
298
304
|
}
|
|
299
305
|
finally {
|
|
300
|
-
await this.lane.run(() => {
|
|
301
|
-
if (this.connections.get(connectionId)
|
|
302
|
-
|
|
306
|
+
const shouldRetry = await this.lane.run(() => {
|
|
307
|
+
if (this.connections.get(connectionId) !== current.target)
|
|
308
|
+
return false;
|
|
309
|
+
current.target.handshakeInFlight = false;
|
|
310
|
+
return retryAdmission;
|
|
303
311
|
});
|
|
312
|
+
if (shouldRetry) {
|
|
313
|
+
void this.handleTransportReady(connectionId, _routingId, _endpoint)
|
|
314
|
+
.catch(error => this.locationRuntime.reportDiscoveryFailure(error));
|
|
315
|
+
}
|
|
304
316
|
}
|
|
305
317
|
}
|
|
306
318
|
async removeSupersededConnections(admitted) {
|
|
@@ -56,6 +56,7 @@ export declare class ZLinkLocationStoreRepository extends ZLinkInMemoryLocationS
|
|
|
56
56
|
private removeOwnedAuthorities;
|
|
57
57
|
private removeOwnerCleanupBatch;
|
|
58
58
|
private updateDescriptor;
|
|
59
|
+
private writeDescriptorWithReconciliation;
|
|
59
60
|
private removeDescriptor;
|
|
60
61
|
private listDescriptors;
|
|
61
62
|
private updateOwnedLocation;
|
|
@@ -23,6 +23,7 @@ const MAX_CREATION_TERMINAL_BYTES = 1024 * 1024;
|
|
|
23
23
|
const CREATION_TERMINAL_RETENTION_MS = 5 * 60 * 1000;
|
|
24
24
|
const AGGREGATE_COMMIT_RETRY_WINDOW_MS = 5_000;
|
|
25
25
|
const MAX_AGGREGATE_COMMIT_CONFLICT_RETRIES = 64;
|
|
26
|
+
const MAX_DESCRIPTOR_WRITE_RETRIES = 3;
|
|
26
27
|
const MAX_OWNER_CLEANUP_BATCH_ROWS = 2_047;
|
|
27
28
|
/**
|
|
28
29
|
* Maps framework domain records to the minimal provider SPI.
|
|
@@ -1225,62 +1226,7 @@ class ZLinkLocationStoreRepository extends in_memory_location_store_1.ZLinkInMem
|
|
|
1225
1226
|
return result.kind === 'applied' ? 'released' : 'stale';
|
|
1226
1227
|
}
|
|
1227
1228
|
async updateMeshNode(descriptor, intent, signal) {
|
|
1228
|
-
|
|
1229
|
-
const rowKey = meshKey(descriptor.meshName, descriptor.rid);
|
|
1230
|
-
const [lease, current] = await Promise.all([
|
|
1231
|
-
this.provider.read(leaseKey, signal),
|
|
1232
|
-
this.provider.read(rowKey, signal)
|
|
1233
|
-
]);
|
|
1234
|
-
if (lease.kind === 'missing')
|
|
1235
|
-
return rejected(lease.storeNow);
|
|
1236
|
-
if (liveOwnerLeaseGeneration(lease) !== descriptor.leaseGeneration) {
|
|
1237
|
-
return rejected(lease.value.storeNow);
|
|
1238
|
-
}
|
|
1239
|
-
let generation = 1n;
|
|
1240
|
-
let rowCondition = { kind: 'missing', key: rowKey };
|
|
1241
|
-
if (current.kind === 'found') {
|
|
1242
|
-
const record = decodeCanonicalDescriptorRecord(current.value.bytes);
|
|
1243
|
-
const stored = reviveMeshDescriptor(record.descriptor);
|
|
1244
|
-
generation = descriptorStoreGeneration(record, current.value.version.value);
|
|
1245
|
-
if (sameMeshDescriptor(stored, descriptor)) {
|
|
1246
|
-
return { status: Locations_1.ZLinkLocationWriteStatus.Stored, generation, updatedAt: current.value.storeNow };
|
|
1247
|
-
}
|
|
1248
|
-
const currentLease = await this.provider.read(ownerKey(stored.ownerId), signal);
|
|
1249
|
-
const currentLeaseGeneration = liveOwnerLeaseGeneration(currentLease);
|
|
1250
|
-
const takeover = canTakeOverStoredLocation(intent, stored.ownerId, stored.leaseGeneration, descriptor.ownerId, descriptor.leaseGeneration, currentLeaseGeneration);
|
|
1251
|
-
const renew = intent === 2
|
|
1252
|
-
&& stored.ownerId === descriptor.ownerId
|
|
1253
|
-
&& stored.leaseGeneration === descriptor.leaseGeneration
|
|
1254
|
-
&& stored.lifecycleGeneration === descriptor.lifecycleGeneration
|
|
1255
|
-
&& descriptor.descriptorRevision > stored.descriptorRevision;
|
|
1256
|
-
if (!takeover && !renew) {
|
|
1257
|
-
return {
|
|
1258
|
-
status: Locations_1.ZLinkLocationWriteStatus.IgnoredStale,
|
|
1259
|
-
generation,
|
|
1260
|
-
updatedAt: current.value.storeNow
|
|
1261
|
-
};
|
|
1262
|
-
}
|
|
1263
|
-
if (takeover)
|
|
1264
|
-
generation += 1n;
|
|
1265
|
-
rowCondition = { kind: 'version', key: rowKey, expected: current.value.version };
|
|
1266
|
-
}
|
|
1267
|
-
else if (intent === 2) {
|
|
1268
|
-
return rejected(lease.value.storeNow);
|
|
1269
|
-
}
|
|
1270
|
-
const result = await this.provider.write({
|
|
1271
|
-
conditions: [
|
|
1272
|
-
{ kind: 'version', key: leaseKey, expected: lease.value.version },
|
|
1273
|
-
rowCondition
|
|
1274
|
-
],
|
|
1275
|
-
mutations: [{
|
|
1276
|
-
kind: 'put',
|
|
1277
|
-
key: rowKey,
|
|
1278
|
-
bytes: encodeCanonicalDescriptorRecord(generation, persistMeshDescriptor(descriptor))
|
|
1279
|
-
}]
|
|
1280
|
-
}, signal);
|
|
1281
|
-
if (result.kind === 'conflict')
|
|
1282
|
-
return rejected(result.storeNow);
|
|
1283
|
-
return { status: Locations_1.ZLinkLocationWriteStatus.Stored, generation, updatedAt: result.storeNow };
|
|
1229
|
+
return this.updateDescriptor(meshKey(descriptor.meshName, descriptor.rid), descriptor, intent, reviveMeshDescriptor, sameMeshDescriptor, canRenewMesh, signal);
|
|
1284
1230
|
}
|
|
1285
1231
|
async removeMeshNode(key, owner, signal) {
|
|
1286
1232
|
const rowKey = meshKey(key.meshName, key.rid);
|
|
@@ -1656,11 +1602,10 @@ class ZLinkLocationStoreRepository extends in_memory_location_store_1.ZLinkInMem
|
|
|
1656
1602
|
]);
|
|
1657
1603
|
if (lease.kind === 'missing')
|
|
1658
1604
|
return rejected(lease.storeNow);
|
|
1659
|
-
if (
|
|
1605
|
+
if (!matchesLiveOwnerLease(lease, descriptor.ownerId, descriptor.leaseGeneration)) {
|
|
1660
1606
|
return rejected(lease.value.storeNow);
|
|
1661
1607
|
}
|
|
1662
1608
|
let generation = 1n;
|
|
1663
|
-
let rowCondition = { kind: 'missing', key: rowKey };
|
|
1664
1609
|
if (current.kind === 'found') {
|
|
1665
1610
|
const record = decodeCanonicalDescriptorRecord(current.value.bytes);
|
|
1666
1611
|
const stored = revive(record.descriptor);
|
|
@@ -1681,25 +1626,44 @@ class ZLinkLocationStoreRepository extends in_memory_location_store_1.ZLinkInMem
|
|
|
1681
1626
|
}
|
|
1682
1627
|
if (takeover)
|
|
1683
1628
|
generation += 1n;
|
|
1684
|
-
rowCondition = { kind: 'version', key: rowKey, expected: current.value.version };
|
|
1685
1629
|
}
|
|
1686
1630
|
else if (intent === 2) {
|
|
1687
1631
|
return rejected(lease.value.storeNow);
|
|
1688
1632
|
}
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1633
|
+
return this.writeDescriptorWithReconciliation(leaseKey, descriptor.ownerId, descriptor.leaseGeneration, lease, rowKey, current, encodeCanonicalDescriptorRecord(generation, descriptor), generation, signal);
|
|
1634
|
+
}
|
|
1635
|
+
async writeDescriptorWithReconciliation(leaseKey, ownerId, leaseGeneration, liveLease, rowKey, predecessor, encodedDescriptor, generation, signal) {
|
|
1636
|
+
let currentLease = liveLease;
|
|
1637
|
+
let currentRow = predecessor;
|
|
1638
|
+
for (let attempt = 0; attempt <= MAX_DESCRIPTOR_WRITE_RETRIES; attempt += 1) {
|
|
1639
|
+
const rowCondition = currentRow.kind === 'found'
|
|
1640
|
+
? { kind: 'version', key: rowKey, expected: currentRow.value.version }
|
|
1641
|
+
: { kind: 'missing', key: rowKey };
|
|
1642
|
+
const result = await this.provider.write({
|
|
1643
|
+
conditions: [
|
|
1644
|
+
{ kind: 'version', key: leaseKey, expected: currentLease.value.version },
|
|
1645
|
+
rowCondition
|
|
1646
|
+
],
|
|
1647
|
+
mutations: [{ kind: 'put', key: rowKey, bytes: encodedDescriptor }]
|
|
1648
|
+
}, signal);
|
|
1649
|
+
if (result.kind === 'applied') {
|
|
1650
|
+
return { status: Locations_1.ZLinkLocationWriteStatus.Stored, generation, updatedAt: result.storeNow };
|
|
1651
|
+
}
|
|
1652
|
+
if (attempt === MAX_DESCRIPTOR_WRITE_RETRIES)
|
|
1653
|
+
return ignoredStale(result.storeNow);
|
|
1654
|
+
const [refreshedLease, refreshedRow] = await Promise.all([
|
|
1655
|
+
this.provider.read(leaseKey, signal),
|
|
1656
|
+
this.provider.read(rowKey, signal)
|
|
1657
|
+
]);
|
|
1658
|
+
if (refreshedLease.kind === 'missing'
|
|
1659
|
+
|| !matchesLiveOwnerLease(refreshedLease, ownerId, leaseGeneration)
|
|
1660
|
+
|| !sameDescriptorPredecessor(predecessor, refreshedRow)) {
|
|
1661
|
+
return ignoredStale(result.storeNow);
|
|
1662
|
+
}
|
|
1663
|
+
currentLease = refreshedLease;
|
|
1664
|
+
currentRow = refreshedRow;
|
|
1665
|
+
}
|
|
1666
|
+
throw new Error('Descriptor write retry loop exhausted unexpectedly.');
|
|
1703
1667
|
}
|
|
1704
1668
|
async removeDescriptor(rowKey, owner, revive, signal) {
|
|
1705
1669
|
const current = await this.provider.read(rowKey, signal);
|
|
@@ -2933,10 +2897,24 @@ function sameCreationTarget(snapshot, target) {
|
|
|
2933
2897
|
&& snapshot.ownerLeaseGeneration === target.owner.leaseGeneration;
|
|
2934
2898
|
}
|
|
2935
2899
|
function sameLiveOwner(lease, snapshot) {
|
|
2936
|
-
if (
|
|
2900
|
+
if (lease.kind === 'missing')
|
|
2901
|
+
return false;
|
|
2902
|
+
return matchesLiveOwnerLease(lease, snapshot.ownerId, snapshot.ownerLeaseGeneration);
|
|
2903
|
+
}
|
|
2904
|
+
function matchesLiveOwnerLease(lease, ownerId, leaseGeneration) {
|
|
2905
|
+
if (lease.value.expiresAt === undefined
|
|
2906
|
+
|| lease.value.expiresAt.getTime() <= lease.value.storeNow.getTime()) {
|
|
2937
2907
|
return false;
|
|
2908
|
+
}
|
|
2938
2909
|
const owner = decodeOwnerRecord(lease.value.bytes);
|
|
2939
|
-
return owner.ownerId ===
|
|
2910
|
+
return owner.ownerId === ownerId
|
|
2911
|
+
&& BigInt(owner.leaseGeneration) === leaseGeneration;
|
|
2912
|
+
}
|
|
2913
|
+
function sameDescriptorPredecessor(expected, current) {
|
|
2914
|
+
if (expected.kind === 'missing' || current.kind === 'missing') {
|
|
2915
|
+
return expected.kind === current.kind;
|
|
2916
|
+
}
|
|
2917
|
+
return Buffer.from(expected.value.bytes).equals(Buffer.from(current.value.bytes));
|
|
2940
2918
|
}
|
|
2941
2919
|
function liveOwnerLeaseGeneration(lease) {
|
|
2942
2920
|
if (lease.kind === 'missing'
|
|
@@ -3118,6 +3096,10 @@ function sameClientServerDescriptor(left, right) {
|
|
|
3118
3096
|
function sameFanoutDescriptor(left, right) {
|
|
3119
3097
|
return descriptorFingerprint(left, ['updatedAt']) === descriptorFingerprint(right, ['updatedAt']);
|
|
3120
3098
|
}
|
|
3099
|
+
function canRenewMesh(current, next) {
|
|
3100
|
+
return sameOwnerGeneration(current, next)
|
|
3101
|
+
&& next.descriptorRevision > current.descriptorRevision;
|
|
3102
|
+
}
|
|
3121
3103
|
function canRenewClientServer(current, next) {
|
|
3122
3104
|
return sameOwnerGeneration(current, next)
|
|
3123
3105
|
&& next.descriptorRevision > current.descriptorRevision
|
|
@@ -3188,6 +3170,13 @@ function rejected(updatedAt) {
|
|
|
3188
3170
|
updatedAt
|
|
3189
3171
|
};
|
|
3190
3172
|
}
|
|
3173
|
+
function ignoredStale(updatedAt) {
|
|
3174
|
+
return {
|
|
3175
|
+
status: Locations_1.ZLinkLocationWriteStatus.IgnoredStale,
|
|
3176
|
+
generation: 0n,
|
|
3177
|
+
updatedAt
|
|
3178
|
+
};
|
|
3179
|
+
}
|
|
3191
3180
|
function requireOwnerInput(ownerId, leaseTtlMs) {
|
|
3192
3181
|
if (Buffer.byteLength(ownerId, 'utf8') < 1 || ownerId.includes('\0')) {
|
|
3193
3182
|
throw new TypeError('Owner ID must be non-empty UTF-8 without NUL.');
|
package/package.json
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zlink-systems/framework",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/zlink-systems/zlink.git",
|
|
8
|
+
"directory": "framework/languages/node/packages/framework"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://zlink.systems/",
|
|
5
11
|
"main": "dist/index.js",
|
|
6
12
|
"types": "dist/index.d.ts",
|
|
7
13
|
"exports": {
|
|
@@ -13,7 +19,7 @@
|
|
|
13
19
|
"dependencies": {
|
|
14
20
|
"@opentelemetry/api": "^1.9.1",
|
|
15
21
|
"@opentelemetry/api-logs": "^0.221.0",
|
|
16
|
-
"@zlink-systems/stream-wire": "0.
|
|
17
|
-
"@zlink-systems/zlink": "0.17.
|
|
22
|
+
"@zlink-systems/stream-wire": "0.11.0",
|
|
23
|
+
"@zlink-systems/zlink": "0.17.6"
|
|
18
24
|
}
|
|
19
25
|
}
|
|
@@ -68,6 +68,12 @@ export function isBackendNotConnectedError(error: unknown): boolean {
|
|
|
68
68
|
const result = error.result;
|
|
69
69
|
return result === SubmitResult.NotConnected || result === RequestResult.NotConnected;
|
|
70
70
|
}
|
|
71
|
+
|
|
72
|
+
export function isBackendRequestTimeoutError(error: unknown): boolean {
|
|
73
|
+
return isZLinkBackendResultError(error)
|
|
74
|
+
&& error.operation === 'request'
|
|
75
|
+
&& error.result === RequestResult.TimedOut;
|
|
76
|
+
}
|
|
71
77
|
export type ZLinkBackendMessageLike = Message | Buffer | Uint8Array | string;
|
|
72
78
|
export interface ZLinkBackendSendSubmitBuilder {
|
|
73
79
|
message(message: ZLinkBackendMessageLike): ZLinkBackendSendSubmitBuilder;
|
|
@@ -53,6 +53,9 @@ import {
|
|
|
53
53
|
} from './fanout-service-wire';
|
|
54
54
|
import type { ZLinkChannelEnvelopeHeader } from './channel-envelope';
|
|
55
55
|
import type { ApplicationJobQueue } from '../host/application-job-queue';
|
|
56
|
+
import {
|
|
57
|
+
isBackendRequestTimeoutError
|
|
58
|
+
} from '../backend/runtime-values';
|
|
56
59
|
|
|
57
60
|
const MAX_LIFECYCLE_GENERATION = 0x7fff_ffff_ffff_ffffn;
|
|
58
61
|
const CLIENT_SERVER_PROBE_INTERVAL_MS = 5_000;
|
|
@@ -1213,6 +1216,7 @@ export class ZLinkChannelSocketRegistry {
|
|
|
1213
1216
|
if (connection.admissionAttempt !== undefined) return;
|
|
1214
1217
|
const admissionAttempt = Symbol(connectionId);
|
|
1215
1218
|
connection.admissionAttempt = admissionAttempt;
|
|
1219
|
+
let retryAdmission = false;
|
|
1216
1220
|
try {
|
|
1217
1221
|
const admission = await requestClientServerAdmission(
|
|
1218
1222
|
connection.dealer,
|
|
@@ -1242,11 +1246,20 @@ export class ZLinkChannelSocketRegistry {
|
|
|
1242
1246
|
|| connection.physicalConnectionId !== physicalConnectionId
|
|
1243
1247
|
|| connection.admissionAttempt !== admissionAttempt) return;
|
|
1244
1248
|
this.removeReadyConnection(connectionId);
|
|
1249
|
+
if (isBackendRequestTimeoutError(error)) {
|
|
1250
|
+
retryAdmission = true;
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1245
1253
|
throw error;
|
|
1246
1254
|
} finally {
|
|
1247
1255
|
if (this.clientServerConnections.get(connectionId) === connection
|
|
1256
|
+
&& connection.physicalConnectionId === physicalConnectionId
|
|
1248
1257
|
&& connection.admissionAttempt === admissionAttempt) {
|
|
1249
1258
|
connection.admissionAttempt = undefined;
|
|
1259
|
+
if (retryAdmission) {
|
|
1260
|
+
void this.admitConfiguredClientServerConnection(channelName, connectionId)
|
|
1261
|
+
.catch(error => this.oneWayFailureSink?.(error));
|
|
1262
|
+
}
|
|
1250
1263
|
}
|
|
1251
1264
|
}
|
|
1252
1265
|
}
|
|
@@ -24,6 +24,9 @@ import {
|
|
|
24
24
|
} from './client-server-service-wire';
|
|
25
25
|
import { discoveryAvailabilityForRuntimeState } from '../foundation/runtime-state-projections';
|
|
26
26
|
import { ZLinkStateLane } from '../execution/state-lane';
|
|
27
|
+
import {
|
|
28
|
+
isBackendRequestTimeoutError
|
|
29
|
+
} from '../backend/runtime-values';
|
|
27
30
|
|
|
28
31
|
interface ActiveClientServerTarget {
|
|
29
32
|
descriptor: ZLinkClientServerServerDescriptor;
|
|
@@ -331,6 +334,7 @@ export class ZLinkClientServerLocationRuntime {
|
|
|
331
334
|
if (current === undefined) {
|
|
332
335
|
return;
|
|
333
336
|
}
|
|
337
|
+
let retryAdmission = false;
|
|
334
338
|
try {
|
|
335
339
|
const admission = await requestAdmission(
|
|
336
340
|
current.dealer,
|
|
@@ -370,11 +374,21 @@ export class ZLinkClientServerLocationRuntime {
|
|
|
370
374
|
current.target.dealer = undefined;
|
|
371
375
|
await this.sockets.closeClientServerConnection(connectionId);
|
|
372
376
|
}
|
|
377
|
+
if (isBackendRequestTimeoutError(error)) {
|
|
378
|
+
retryAdmission = true;
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
373
381
|
throw error;
|
|
374
382
|
} finally {
|
|
375
|
-
await this.lane.run(() => {
|
|
376
|
-
if (this.connections.get(connectionId)
|
|
383
|
+
const shouldRetry = await this.lane.run(() => {
|
|
384
|
+
if (this.connections.get(connectionId) !== current.target) return false;
|
|
385
|
+
current.target.handshakeInFlight = false;
|
|
386
|
+
return retryAdmission;
|
|
377
387
|
});
|
|
388
|
+
if (shouldRetry) {
|
|
389
|
+
void this.handleTransportReady(connectionId, _routingId, _endpoint)
|
|
390
|
+
.catch(error => this.locationRuntime.reportDiscoveryFailure(error));
|
|
391
|
+
}
|
|
378
392
|
}
|
|
379
393
|
}
|
|
380
394
|
|