@peerbit/shared-log 13.2.33 → 13.2.34
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/src/index.d.ts.map +1 -1
- package/dist/src/index.js +61 -53
- package/dist/src/index.js.map +1 -1
- package/dist/src/replication-info-v2-receive.d.ts +73 -4
- package/dist/src/replication-info-v2-receive.d.ts.map +1 -1
- package/dist/src/replication-info-v2-receive.js +366 -2
- package/dist/src/replication-info-v2-receive.js.map +1 -1
- package/dist/src/replication-info-v2-send.d.ts +20 -0
- package/dist/src/replication-info-v2-send.d.ts.map +1 -1
- package/dist/src/replication-info-v2-send.js +155 -54
- package/dist/src/replication-info-v2-send.js.map +1 -1
- package/package.json +8 -8
- package/src/index.ts +82 -75
- package/src/replication-info-v2-receive.ts +538 -6
- package/src/replication-info-v2-send.ts +206 -64
|
@@ -4,6 +4,7 @@ type LegacyReplicationInfoMessage = AllReplicatingSegmentsMessage | AddedReplica
|
|
|
4
4
|
export type ReplicationInfoV2ReceivePhase = "awaiting-full" | "active" | "resync";
|
|
5
5
|
type LocalCapabilityReady = {
|
|
6
6
|
peerHash: string;
|
|
7
|
+
receiveEpoch: object | null;
|
|
7
8
|
receiverTransportSession: bigint;
|
|
8
9
|
/**
|
|
9
10
|
* The local capability envelope and the later RequestV2 envelope use the
|
|
@@ -11,6 +12,41 @@ type LocalCapabilityReady = {
|
|
|
11
12
|
* be strictly newer, so never construct it in this captured millisecond.
|
|
12
13
|
*/
|
|
13
14
|
requestNotBeforeMs: number;
|
|
15
|
+
advertisement?: ReplicationInfoV2LocalCapabilityAdvertisement;
|
|
16
|
+
};
|
|
17
|
+
type LocalCapabilityReadyProperties = {
|
|
18
|
+
peerHash: string;
|
|
19
|
+
peerSession: object;
|
|
20
|
+
receiveEpoch: object | null;
|
|
21
|
+
receiverTransportSession: bigint;
|
|
22
|
+
requestNotBeforeMs: number;
|
|
23
|
+
};
|
|
24
|
+
export type ReplicationInfoV2LocalCapabilityAdvertisementHandle = {
|
|
25
|
+
firstAttempt: Promise<void>;
|
|
26
|
+
releaseLegacyBarrier(): void;
|
|
27
|
+
};
|
|
28
|
+
export type ReplicationInfoV2LocalCapabilityContext = {
|
|
29
|
+
peerHash: string;
|
|
30
|
+
target: PublicSignKey;
|
|
31
|
+
lifecycleSignal: AbortSignal;
|
|
32
|
+
legacyBarrierReleased: boolean;
|
|
33
|
+
};
|
|
34
|
+
export type ReplicationInfoV2LocalCapabilityAdvertisement = {
|
|
35
|
+
peerHash: string;
|
|
36
|
+
target: PublicSignKey;
|
|
37
|
+
peerSession: object;
|
|
38
|
+
receiveEpoch: object | null;
|
|
39
|
+
lifecycleSignal: AbortSignal;
|
|
40
|
+
onLifecycleAbort: () => void;
|
|
41
|
+
controller: AbortController;
|
|
42
|
+
context: ReplicationInfoV2LocalCapabilityContext;
|
|
43
|
+
attempts: number;
|
|
44
|
+
ready: boolean;
|
|
45
|
+
acknowledgedReady?: LocalCapabilityReady;
|
|
46
|
+
receiverTransportSession?: bigint;
|
|
47
|
+
timer?: ReturnType<typeof setTimeout>;
|
|
48
|
+
inFlight?: Promise<void>;
|
|
49
|
+
firstAttempt?: Promise<void>;
|
|
14
50
|
};
|
|
15
51
|
export type ReplicationInfoV2LocalCapabilityRefresh = {
|
|
16
52
|
receiverTransportSession: bigint;
|
|
@@ -67,6 +103,8 @@ export type ReplicationInfoV2ReceiveDeps = {
|
|
|
67
103
|
getSelfKey: () => PublicSignKey;
|
|
68
104
|
getReceiverTransportSession: () => bigint;
|
|
69
105
|
isClosed: () => boolean;
|
|
106
|
+
isPeerSessionCurrent: (peerHash: string, peerSession: object) => boolean;
|
|
107
|
+
isReceiveEpochCurrent: (peerHash: string, receiveEpoch: object | null) => boolean;
|
|
70
108
|
isPeerStateCurrent: (peerHash: string, peerSession: object, receiveEpoch: object | null) => boolean;
|
|
71
109
|
isSenderTransportSessionCurrent: (peerHash: string, senderTransportSession: bigint) => boolean;
|
|
72
110
|
sendRequest: (request: RequestReplicationInfoV2Message, target: PublicSignKey, signal: AbortSignal) => Promise<void>;
|
|
@@ -78,6 +116,7 @@ export type ReplicationInfoV2ReceiveDeps = {
|
|
|
78
116
|
signal: AbortSignal;
|
|
79
117
|
}) => Promise<ReplicationInfoV2LocalCapabilityRefresh | undefined>;
|
|
80
118
|
onRequestError?: (error: unknown) => void;
|
|
119
|
+
onLocalCapabilityError?: (error: unknown) => void;
|
|
81
120
|
now?: () => number;
|
|
82
121
|
requestRetryMs?: number;
|
|
83
122
|
maxRequestRetryMs?: number;
|
|
@@ -94,6 +133,8 @@ export declare class ReplicationInfoV2ReceiveCoordinator {
|
|
|
94
133
|
_receiveStates: Map<string, ReplicationInfoV2ReceiveState>;
|
|
95
134
|
_cutoverPeerSessions: WeakSet<object>;
|
|
96
135
|
_localCapabilityReadyBySession: WeakMap<object, LocalCapabilityReady>;
|
|
136
|
+
_localCapabilityContextBySession: WeakMap<object, ReplicationInfoV2LocalCapabilityContext>;
|
|
137
|
+
_localCapabilityAdvertisementsByPeer: Map<string, ReplicationInfoV2LocalCapabilityAdvertisement>;
|
|
97
138
|
_reservedAdmissionsByPeer: Map<string, ReplicationInfoV2ReceiveAdmission>;
|
|
98
139
|
private readonly now;
|
|
99
140
|
private readonly requestRetryMs;
|
|
@@ -107,14 +148,42 @@ export declare class ReplicationInfoV2ReceiveCoordinator {
|
|
|
107
148
|
/** Revoke an unauthenticated or downgraded capability generation. */
|
|
108
149
|
revokePeerCapability(peerHash: string, reopenLegacy?: boolean): void;
|
|
109
150
|
private clearState;
|
|
110
|
-
|
|
111
|
-
|
|
151
|
+
private clearLocalCapabilityAdvertisement;
|
|
152
|
+
private isLocalCapabilityAdvertisementOwnerCurrent;
|
|
153
|
+
private isLocalCapabilityAdvertisementGenerationCurrent;
|
|
154
|
+
private isLocalCapabilityAdvertisementReadyOpen;
|
|
155
|
+
private localCapabilityRetryDelay;
|
|
156
|
+
private armLocalCapabilityAdvertisement;
|
|
157
|
+
private runLocalCapabilityAdvertisement;
|
|
158
|
+
/**
|
|
159
|
+
* Start local authenticated-apply advertisement independently of the legacy
|
|
160
|
+
* join path. ACK and legacy publication are a two-phase barrier: the first
|
|
161
|
+
* attempt always settles independently, while readiness is promoted only
|
|
162
|
+
* after the host releases the legacy barrier. Failed attempts leave one
|
|
163
|
+
* exact-session worker retrying with capped exponential backoff.
|
|
164
|
+
*/
|
|
165
|
+
advertiseLocalCapability(properties: {
|
|
166
|
+
target: PublicSignKey;
|
|
167
|
+
peerSession: object;
|
|
168
|
+
receiveEpoch: object | null;
|
|
169
|
+
signal: AbortSignal;
|
|
170
|
+
}): ReplicationInfoV2LocalCapabilityAdvertisementHandle;
|
|
171
|
+
private releaseLocalCapabilityLegacyBarrier;
|
|
172
|
+
private promoteLocalCapabilityAdvertisement;
|
|
173
|
+
/**
|
|
174
|
+
* Re-advertise one exact current recovery epoch from the stable membership
|
|
175
|
+
* context captured during opening. The opening handle owns barrier release;
|
|
176
|
+
* recovery never bypasses a legacy snapshot or role publication still in
|
|
177
|
+
* progress.
|
|
178
|
+
*/
|
|
179
|
+
reAdvertiseLocalCapabilityForRecovery(properties: {
|
|
112
180
|
peerHash: string;
|
|
113
181
|
peerSession: object;
|
|
114
182
|
receiveEpoch: object | null;
|
|
115
|
-
receiverTransportSession: bigint;
|
|
116
|
-
requestNotBeforeMs: number;
|
|
117
183
|
}): boolean;
|
|
184
|
+
/** Record success of this session's ACKed local APPLY advertisement. */
|
|
185
|
+
markLocalCapabilityReady(properties: LocalCapabilityReadyProperties): boolean;
|
|
186
|
+
private recordLocalCapabilityReady;
|
|
118
187
|
/**
|
|
119
188
|
* Promote one signed capability generation after the opening barrier has
|
|
120
189
|
* committed. Repeated same-session advertisements refresh freshness only;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"replication-info-v2-receive.d.ts","sourceRoot":"","sources":["../../src/replication-info-v2-receive.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,aAAa,EAA2B,MAAM,iBAAiB,CAAC;AAM9E,OAAO,EAEN,8BAA8B,EAC9B,6BAA6B,EAE7B,KAAK,wBAAwB,EAC7B,+BAA+B,EAC/B,kBAAkB,EAElB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"replication-info-v2-receive.d.ts","sourceRoot":"","sources":["../../src/replication-info-v2-receive.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,7 @@ const DEFAULT_MAX_REQUEST_RETRY_MS = 30_000;
|
|
|
10
10
|
const DEFAULT_REQUEST_MAX_ATTEMPTS = 7;
|
|
11
11
|
const DEFAULT_LEGACY_FALLBACK_DELAY_MS = 5_000;
|
|
12
12
|
const MAX_U64 = (1n << 64n) - 1n;
|
|
13
|
+
const MAX_BACKOFF_EXPONENT = 20;
|
|
13
14
|
const bytesEqual = (left, right) => {
|
|
14
15
|
if (left.byteLength !== right.byteLength) {
|
|
15
16
|
return false;
|
|
@@ -41,6 +42,8 @@ export class ReplicationInfoV2ReceiveCoordinator {
|
|
|
41
42
|
_receiveStates;
|
|
42
43
|
_cutoverPeerSessions;
|
|
43
44
|
_localCapabilityReadyBySession;
|
|
45
|
+
_localCapabilityContextBySession;
|
|
46
|
+
_localCapabilityAdvertisementsByPeer;
|
|
44
47
|
_reservedAdmissionsByPeer;
|
|
45
48
|
now;
|
|
46
49
|
requestRetryMs;
|
|
@@ -57,6 +60,8 @@ export class ReplicationInfoV2ReceiveCoordinator {
|
|
|
57
60
|
this._receiveStates = new Map();
|
|
58
61
|
this._cutoverPeerSessions = new WeakSet();
|
|
59
62
|
this._localCapabilityReadyBySession = new WeakMap();
|
|
63
|
+
this._localCapabilityContextBySession = new WeakMap();
|
|
64
|
+
this._localCapabilityAdvertisementsByPeer = new Map();
|
|
60
65
|
this._reservedAdmissionsByPeer = new Map();
|
|
61
66
|
}
|
|
62
67
|
resetForOpen() {
|
|
@@ -64,25 +69,41 @@ export class ReplicationInfoV2ReceiveCoordinator {
|
|
|
64
69
|
this._receiveStates = new Map();
|
|
65
70
|
this._cutoverPeerSessions = new WeakSet();
|
|
66
71
|
this._localCapabilityReadyBySession = new WeakMap();
|
|
72
|
+
this._localCapabilityContextBySession = new WeakMap();
|
|
73
|
+
this._localCapabilityAdvertisementsByPeer = new Map();
|
|
67
74
|
this._reservedAdmissionsByPeer = new Map();
|
|
68
75
|
}
|
|
69
76
|
clearForClose() {
|
|
77
|
+
for (const advertisement of [
|
|
78
|
+
...(this._localCapabilityAdvertisementsByPeer?.values() ?? []),
|
|
79
|
+
]) {
|
|
80
|
+
this.clearLocalCapabilityAdvertisement(advertisement);
|
|
81
|
+
}
|
|
82
|
+
this._localCapabilityAdvertisementsByPeer?.clear();
|
|
70
83
|
for (const state of this._receiveStates?.values() ?? []) {
|
|
71
84
|
this.clearState(state);
|
|
72
85
|
}
|
|
73
86
|
this._receiveStates?.clear();
|
|
74
87
|
this._cutoverPeerSessions = new WeakSet();
|
|
75
88
|
this._localCapabilityReadyBySession = new WeakMap();
|
|
89
|
+
this._localCapabilityContextBySession = new WeakMap();
|
|
76
90
|
}
|
|
77
91
|
clearPeer(peerHash, expectedSession) {
|
|
92
|
+
const advertisement = this._localCapabilityAdvertisementsByPeer.get(peerHash);
|
|
93
|
+
if (advertisement &&
|
|
94
|
+
(!expectedSession || advertisement.peerSession === expectedSession)) {
|
|
95
|
+
this.clearLocalCapabilityAdvertisement(advertisement);
|
|
96
|
+
}
|
|
78
97
|
const state = this._receiveStates.get(peerHash);
|
|
79
98
|
if (state && (!expectedSession || state.peerSession === expectedSession)) {
|
|
80
99
|
this.clearState(state);
|
|
81
100
|
this._localCapabilityReadyBySession.delete(state.peerSession);
|
|
101
|
+
this._localCapabilityContextBySession.delete(state.peerSession);
|
|
82
102
|
this._cutoverPeerSessions.delete(state.peerSession);
|
|
83
103
|
}
|
|
84
104
|
if (expectedSession) {
|
|
85
105
|
this._localCapabilityReadyBySession.delete(expectedSession);
|
|
106
|
+
this._localCapabilityContextBySession.delete(expectedSession);
|
|
86
107
|
this._cutoverPeerSessions.delete(expectedSession);
|
|
87
108
|
}
|
|
88
109
|
}
|
|
@@ -112,8 +133,345 @@ export class ReplicationInfoV2ReceiveCoordinator {
|
|
|
112
133
|
this._receiveStates.delete(state.peerHash);
|
|
113
134
|
}
|
|
114
135
|
}
|
|
136
|
+
clearLocalCapabilityAdvertisement(state, options) {
|
|
137
|
+
if (state.timer) {
|
|
138
|
+
clearTimeout(state.timer);
|
|
139
|
+
state.timer = undefined;
|
|
140
|
+
}
|
|
141
|
+
state.lifecycleSignal.removeEventListener("abort", state.onLifecycleAbort);
|
|
142
|
+
state.controller.abort();
|
|
143
|
+
const wasMapped = this._localCapabilityAdvertisementsByPeer.get(state.peerHash) === state;
|
|
144
|
+
if (wasMapped) {
|
|
145
|
+
this._localCapabilityAdvertisementsByPeer.delete(state.peerHash);
|
|
146
|
+
}
|
|
147
|
+
const ready = this._localCapabilityReadyBySession.get(state.peerSession);
|
|
148
|
+
if (ready?.advertisement === state) {
|
|
149
|
+
this._localCapabilityReadyBySession.delete(state.peerSession);
|
|
150
|
+
}
|
|
151
|
+
if (wasMapped &&
|
|
152
|
+
!options?.preserveContext &&
|
|
153
|
+
this._localCapabilityContextBySession.get(state.peerSession) ===
|
|
154
|
+
state.context) {
|
|
155
|
+
this._localCapabilityContextBySession.delete(state.peerSession);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
isLocalCapabilityAdvertisementOwnerCurrent(state) {
|
|
159
|
+
return (this._localCapabilityAdvertisementsByPeer.get(state.peerHash) === state &&
|
|
160
|
+
this._localCapabilityContextBySession.get(state.peerSession) ===
|
|
161
|
+
state.context &&
|
|
162
|
+
state.context.peerHash === state.peerHash &&
|
|
163
|
+
state.context.lifecycleSignal === state.lifecycleSignal &&
|
|
164
|
+
state.context.target.equals(state.target) &&
|
|
165
|
+
!state.controller.signal.aborted &&
|
|
166
|
+
!state.lifecycleSignal.aborted &&
|
|
167
|
+
!this.deps.isClosed() &&
|
|
168
|
+
this.deps.isPeerSessionCurrent(state.peerHash, state.peerSession) &&
|
|
169
|
+
(state.receiverTransportSession === undefined ||
|
|
170
|
+
this.deps.getReceiverTransportSession() ===
|
|
171
|
+
state.receiverTransportSession));
|
|
172
|
+
}
|
|
173
|
+
isLocalCapabilityAdvertisementGenerationCurrent(state) {
|
|
174
|
+
return (this.isLocalCapabilityAdvertisementOwnerCurrent(state) &&
|
|
175
|
+
this.deps.isReceiveEpochCurrent(state.peerHash, state.receiveEpoch));
|
|
176
|
+
}
|
|
177
|
+
isLocalCapabilityAdvertisementReadyOpen(state) {
|
|
178
|
+
return (this.isLocalCapabilityAdvertisementGenerationCurrent(state) &&
|
|
179
|
+
this.deps.isPeerStateCurrent(state.peerHash, state.peerSession, state.receiveEpoch));
|
|
180
|
+
}
|
|
181
|
+
localCapabilityRetryDelay(state) {
|
|
182
|
+
const exponent = Math.max(0, state.attempts - 1);
|
|
183
|
+
return Math.min(this.maxRequestRetryMs, this.requestRetryMs * 2 ** Math.min(exponent, MAX_BACKOFF_EXPONENT));
|
|
184
|
+
}
|
|
185
|
+
armLocalCapabilityAdvertisement(state) {
|
|
186
|
+
if (state.timer || state.inFlight || state.ready) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (state.acknowledgedReady && !state.context.legacyBarrierReleased) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (!this.isLocalCapabilityAdvertisementOwnerCurrent(state)) {
|
|
193
|
+
this.clearLocalCapabilityAdvertisement(state);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!this.isLocalCapabilityAdvertisementGenerationCurrent(state)) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
state.timer = setTimeout(() => {
|
|
200
|
+
state.timer = undefined;
|
|
201
|
+
if (!this.isLocalCapabilityAdvertisementOwnerCurrent(state)) {
|
|
202
|
+
this.clearLocalCapabilityAdvertisement(state);
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (!this.isLocalCapabilityAdvertisementGenerationCurrent(state)) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (!this.isLocalCapabilityAdvertisementReadyOpen(state)) {
|
|
209
|
+
this.armLocalCapabilityAdvertisement(state);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
void this.runLocalCapabilityAdvertisement(state);
|
|
213
|
+
}, this.localCapabilityRetryDelay(state));
|
|
214
|
+
state.timer.unref?.();
|
|
215
|
+
}
|
|
216
|
+
async runLocalCapabilityAdvertisement(state) {
|
|
217
|
+
if (state.inFlight) {
|
|
218
|
+
await state.inFlight;
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (state.ready) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
if (!this.isLocalCapabilityAdvertisementOwnerCurrent(state)) {
|
|
225
|
+
this.clearLocalCapabilityAdvertisement(state);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (!this.isLocalCapabilityAdvertisementGenerationCurrent(state)) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (state.acknowledgedReady) {
|
|
232
|
+
if (state.context.legacyBarrierReleased) {
|
|
233
|
+
this.promoteLocalCapabilityAdvertisement(state);
|
|
234
|
+
}
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (!this.isLocalCapabilityAdvertisementReadyOpen(state)) {
|
|
238
|
+
this.armLocalCapabilityAdvertisement(state);
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
state.attempts = Math.min(state.attempts + 1, MAX_BACKOFF_EXPONENT + 1);
|
|
242
|
+
let operation;
|
|
243
|
+
operation = (async () => {
|
|
244
|
+
const refreshed = await this.deps.refreshLocalCapability({
|
|
245
|
+
peerHash: state.peerHash,
|
|
246
|
+
target: state.target,
|
|
247
|
+
peerSession: state.peerSession,
|
|
248
|
+
receiveEpoch: state.receiveEpoch,
|
|
249
|
+
signal: AbortSignal.any([
|
|
250
|
+
state.controller.signal,
|
|
251
|
+
state.lifecycleSignal,
|
|
252
|
+
]),
|
|
253
|
+
});
|
|
254
|
+
if (!refreshed ||
|
|
255
|
+
!this.isLocalCapabilityAdvertisementGenerationCurrent(state) ||
|
|
256
|
+
this.deps.getReceiverTransportSession() !==
|
|
257
|
+
refreshed.receiverTransportSession) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
state.receiverTransportSession = refreshed.receiverTransportSession;
|
|
261
|
+
state.acknowledgedReady = {
|
|
262
|
+
peerHash: state.peerHash,
|
|
263
|
+
receiveEpoch: state.receiveEpoch,
|
|
264
|
+
receiverTransportSession: refreshed.receiverTransportSession,
|
|
265
|
+
requestNotBeforeMs: refreshed.requestNotBeforeMs,
|
|
266
|
+
advertisement: state,
|
|
267
|
+
};
|
|
268
|
+
state.attempts = 0;
|
|
269
|
+
this.promoteLocalCapabilityAdvertisement(state);
|
|
270
|
+
})()
|
|
271
|
+
.catch((error) => {
|
|
272
|
+
if (!state.controller.signal.aborted &&
|
|
273
|
+
!state.lifecycleSignal.aborted &&
|
|
274
|
+
!this.deps.isClosed()) {
|
|
275
|
+
this.deps.onLocalCapabilityError?.(error);
|
|
276
|
+
}
|
|
277
|
+
})
|
|
278
|
+
.finally(() => {
|
|
279
|
+
if (state.inFlight === operation) {
|
|
280
|
+
state.inFlight = undefined;
|
|
281
|
+
}
|
|
282
|
+
if (!this.isLocalCapabilityAdvertisementOwnerCurrent(state)) {
|
|
283
|
+
this.clearLocalCapabilityAdvertisement(state);
|
|
284
|
+
}
|
|
285
|
+
else if (this.isLocalCapabilityAdvertisementGenerationCurrent(state) &&
|
|
286
|
+
!state.ready) {
|
|
287
|
+
if (!state.acknowledgedReady || state.context.legacyBarrierReleased) {
|
|
288
|
+
this.armLocalCapabilityAdvertisement(state);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
state.inFlight = operation;
|
|
293
|
+
await operation;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Start local authenticated-apply advertisement independently of the legacy
|
|
297
|
+
* join path. ACK and legacy publication are a two-phase barrier: the first
|
|
298
|
+
* attempt always settles independently, while readiness is promoted only
|
|
299
|
+
* after the host releases the legacy barrier. Failed attempts leave one
|
|
300
|
+
* exact-session worker retrying with capped exponential backoff.
|
|
301
|
+
*/
|
|
302
|
+
advertiseLocalCapability(properties) {
|
|
303
|
+
const peerHash = properties.target.hashcode();
|
|
304
|
+
if (properties.signal.aborted ||
|
|
305
|
+
this.deps.isClosed() ||
|
|
306
|
+
!this.deps.isPeerSessionCurrent(peerHash, properties.peerSession) ||
|
|
307
|
+
!this.deps.isReceiveEpochCurrent(peerHash, properties.receiveEpoch)) {
|
|
308
|
+
return {
|
|
309
|
+
firstAttempt: Promise.resolve(),
|
|
310
|
+
releaseLegacyBarrier: () => { },
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
let context = this._localCapabilityContextBySession.get(properties.peerSession);
|
|
314
|
+
if (context &&
|
|
315
|
+
(context.peerHash !== peerHash ||
|
|
316
|
+
!context.target.equals(properties.target) ||
|
|
317
|
+
context.lifecycleSignal !== properties.signal)) {
|
|
318
|
+
return {
|
|
319
|
+
firstAttempt: Promise.resolve(),
|
|
320
|
+
releaseLegacyBarrier: () => { },
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (!context) {
|
|
324
|
+
context = {
|
|
325
|
+
peerHash,
|
|
326
|
+
target: properties.target,
|
|
327
|
+
lifecycleSignal: properties.signal,
|
|
328
|
+
legacyBarrierReleased: false,
|
|
329
|
+
};
|
|
330
|
+
this._localCapabilityContextBySession.set(properties.peerSession, context);
|
|
331
|
+
}
|
|
332
|
+
let state = this._localCapabilityAdvertisementsByPeer.get(peerHash);
|
|
333
|
+
if (state &&
|
|
334
|
+
(state.peerSession !== properties.peerSession ||
|
|
335
|
+
state.context !== context ||
|
|
336
|
+
state.lifecycleSignal !== properties.signal ||
|
|
337
|
+
!state.target.equals(properties.target))) {
|
|
338
|
+
this.clearLocalCapabilityAdvertisement(state);
|
|
339
|
+
state = undefined;
|
|
340
|
+
}
|
|
341
|
+
if (state && state.receiveEpoch !== properties.receiveEpoch) {
|
|
342
|
+
this.clearLocalCapabilityAdvertisement(state, { preserveContext: true });
|
|
343
|
+
state = undefined;
|
|
344
|
+
}
|
|
345
|
+
if (state && !this.isLocalCapabilityAdvertisementOwnerCurrent(state)) {
|
|
346
|
+
this.clearLocalCapabilityAdvertisement(state);
|
|
347
|
+
state = undefined;
|
|
348
|
+
}
|
|
349
|
+
if (this._localCapabilityContextBySession.get(properties.peerSession) !==
|
|
350
|
+
context) {
|
|
351
|
+
return {
|
|
352
|
+
firstAttempt: Promise.resolve(),
|
|
353
|
+
releaseLegacyBarrier: () => { },
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
if (!state) {
|
|
357
|
+
const controller = new AbortController();
|
|
358
|
+
const advertisement = {
|
|
359
|
+
peerHash,
|
|
360
|
+
target: properties.target,
|
|
361
|
+
peerSession: properties.peerSession,
|
|
362
|
+
receiveEpoch: properties.receiveEpoch,
|
|
363
|
+
lifecycleSignal: properties.signal,
|
|
364
|
+
onLifecycleAbort: () => { },
|
|
365
|
+
controller,
|
|
366
|
+
context,
|
|
367
|
+
attempts: 0,
|
|
368
|
+
ready: false,
|
|
369
|
+
receiverTransportSession: this.deps.getReceiverTransportSession(),
|
|
370
|
+
};
|
|
371
|
+
advertisement.onLifecycleAbort = () => this.clearLocalCapabilityAdvertisement(advertisement);
|
|
372
|
+
properties.signal.addEventListener("abort", advertisement.onLifecycleAbort, {
|
|
373
|
+
once: true,
|
|
374
|
+
});
|
|
375
|
+
this._localCapabilityAdvertisementsByPeer.set(peerHash, advertisement);
|
|
376
|
+
state = advertisement;
|
|
377
|
+
}
|
|
378
|
+
const firstAttempt = state.firstAttempt ??
|
|
379
|
+
(state.firstAttempt = this.runLocalCapabilityAdvertisement(state));
|
|
380
|
+
const advertisement = state;
|
|
381
|
+
return {
|
|
382
|
+
firstAttempt,
|
|
383
|
+
releaseLegacyBarrier: () => this.releaseLocalCapabilityLegacyBarrier(advertisement.peerSession, context),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
releaseLocalCapabilityLegacyBarrier(peerSession, context) {
|
|
387
|
+
if (context.legacyBarrierReleased) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (this._localCapabilityContextBySession.get(peerSession) !== context ||
|
|
391
|
+
context.lifecycleSignal.aborted ||
|
|
392
|
+
this.deps.isClosed() ||
|
|
393
|
+
!this.deps.isPeerSessionCurrent(context.peerHash, peerSession)) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
context.legacyBarrierReleased = true;
|
|
397
|
+
const state = this._localCapabilityAdvertisementsByPeer.get(context.peerHash);
|
|
398
|
+
if (state?.peerSession === peerSession && state.context === context) {
|
|
399
|
+
if (!this.isLocalCapabilityAdvertisementOwnerCurrent(state)) {
|
|
400
|
+
this.clearLocalCapabilityAdvertisement(state);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
this.promoteLocalCapabilityAdvertisement(state);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
promoteLocalCapabilityAdvertisement(state) {
|
|
407
|
+
const ready = state.acknowledgedReady;
|
|
408
|
+
if (state.ready ||
|
|
409
|
+
!state.context.legacyBarrierReleased ||
|
|
410
|
+
!ready ||
|
|
411
|
+
ready.receiveEpoch !== state.receiveEpoch ||
|
|
412
|
+
ready.advertisement !== state ||
|
|
413
|
+
!this.isLocalCapabilityAdvertisementOwnerCurrent(state) ||
|
|
414
|
+
!this.isLocalCapabilityAdvertisementGenerationCurrent(state) ||
|
|
415
|
+
this.deps.getReceiverTransportSession() !== ready.receiverTransportSession) {
|
|
416
|
+
return state.ready;
|
|
417
|
+
}
|
|
418
|
+
if (!this.isLocalCapabilityAdvertisementReadyOpen(state)) {
|
|
419
|
+
this.armLocalCapabilityAdvertisement(state);
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
if (!this.recordLocalCapabilityReady({
|
|
423
|
+
peerHash: state.peerHash,
|
|
424
|
+
peerSession: state.peerSession,
|
|
425
|
+
receiveEpoch: state.receiveEpoch,
|
|
426
|
+
receiverTransportSession: ready.receiverTransportSession,
|
|
427
|
+
requestNotBeforeMs: ready.requestNotBeforeMs,
|
|
428
|
+
}, state)) {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
state.ready = true;
|
|
432
|
+
return true;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Re-advertise one exact current recovery epoch from the stable membership
|
|
436
|
+
* context captured during opening. The opening handle owns barrier release;
|
|
437
|
+
* recovery never bypasses a legacy snapshot or role publication still in
|
|
438
|
+
* progress.
|
|
439
|
+
*/
|
|
440
|
+
reAdvertiseLocalCapabilityForRecovery(properties) {
|
|
441
|
+
const context = this._localCapabilityContextBySession.get(properties.peerSession);
|
|
442
|
+
if (!context ||
|
|
443
|
+
context.peerHash !== properties.peerHash ||
|
|
444
|
+
context.lifecycleSignal.aborted ||
|
|
445
|
+
this.deps.isClosed() ||
|
|
446
|
+
!this.deps.isPeerSessionCurrent(properties.peerHash, properties.peerSession) ||
|
|
447
|
+
!this.deps.isReceiveEpochCurrent(properties.peerHash, properties.receiveEpoch)) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
const state = this._receiveStates.get(properties.peerHash);
|
|
451
|
+
if (state?.peerSession === properties.peerSession &&
|
|
452
|
+
state.receiveEpoch === properties.receiveEpoch &&
|
|
453
|
+
state.receiverBinding !== undefined) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
const ready = this._localCapabilityReadyBySession.get(properties.peerSession);
|
|
457
|
+
if (ready?.peerHash === properties.peerHash &&
|
|
458
|
+
ready.receiveEpoch === properties.receiveEpoch &&
|
|
459
|
+
ready.receiverTransportSession === this.deps.getReceiverTransportSession()) {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
this.advertiseLocalCapability({
|
|
463
|
+
target: context.target,
|
|
464
|
+
peerSession: properties.peerSession,
|
|
465
|
+
receiveEpoch: properties.receiveEpoch,
|
|
466
|
+
signal: context.lifecycleSignal,
|
|
467
|
+
});
|
|
468
|
+
return true;
|
|
469
|
+
}
|
|
115
470
|
/** Record success of this session's ACKed local APPLY advertisement. */
|
|
116
471
|
markLocalCapabilityReady(properties) {
|
|
472
|
+
return this.recordLocalCapabilityReady(properties);
|
|
473
|
+
}
|
|
474
|
+
recordLocalCapabilityReady(properties, advertisement) {
|
|
117
475
|
const { peerHash, peerSession, receiverTransportSession } = properties;
|
|
118
476
|
const state = this._receiveStates.get(peerHash);
|
|
119
477
|
if (this.deps.isClosed() ||
|
|
@@ -123,8 +481,10 @@ export class ReplicationInfoV2ReceiveCoordinator {
|
|
|
123
481
|
}
|
|
124
482
|
const ready = {
|
|
125
483
|
peerHash,
|
|
484
|
+
receiveEpoch: properties.receiveEpoch,
|
|
126
485
|
receiverTransportSession,
|
|
127
486
|
requestNotBeforeMs: properties.requestNotBeforeMs,
|
|
487
|
+
advertisement,
|
|
128
488
|
};
|
|
129
489
|
this._localCapabilityReadyBySession.set(peerSession, ready);
|
|
130
490
|
if (state?.peerSession === peerSession &&
|
|
@@ -198,7 +558,9 @@ export class ReplicationInfoV2ReceiveCoordinator {
|
|
|
198
558
|
});
|
|
199
559
|
}
|
|
200
560
|
const ready = this._localCapabilityReadyBySession.get(peerSession);
|
|
201
|
-
if (ready?.peerHash === peerHash &&
|
|
561
|
+
if (ready?.peerHash === peerHash &&
|
|
562
|
+
ready.receiveEpoch === receiveEpoch &&
|
|
563
|
+
state.receiverBinding === undefined) {
|
|
202
564
|
this.bindLocalCapability(state, ready);
|
|
203
565
|
}
|
|
204
566
|
if (state.phase !== "active") {
|
|
@@ -231,7 +593,7 @@ export class ReplicationInfoV2ReceiveCoordinator {
|
|
|
231
593
|
};
|
|
232
594
|
this._receiveStates.set(peerHash, state);
|
|
233
595
|
const ready = this._localCapabilityReadyBySession.get(peerSession);
|
|
234
|
-
if (ready?.peerHash === peerHash) {
|
|
596
|
+
if (ready?.peerHash === peerHash && ready.receiveEpoch === receiveEpoch) {
|
|
235
597
|
this.bindLocalCapability(state, ready);
|
|
236
598
|
this.armRequest(state, 0);
|
|
237
599
|
}
|
|
@@ -629,6 +991,7 @@ export class ReplicationInfoV2ReceiveCoordinator {
|
|
|
629
991
|
}
|
|
630
992
|
const ready = {
|
|
631
993
|
peerHash: state.peerHash,
|
|
994
|
+
receiveEpoch: state.receiveEpoch,
|
|
632
995
|
receiverTransportSession: refreshed.receiverTransportSession,
|
|
633
996
|
requestNotBeforeMs: refreshed.requestNotBeforeMs,
|
|
634
997
|
};
|
|
@@ -673,6 +1036,7 @@ export class ReplicationInfoV2ReceiveCoordinator {
|
|
|
673
1036
|
const ready = this._localCapabilityReadyBySession.get(state.peerSession);
|
|
674
1037
|
if (!ready ||
|
|
675
1038
|
ready.peerHash !== state.peerHash ||
|
|
1039
|
+
ready.receiveEpoch !== state.receiveEpoch ||
|
|
676
1040
|
ready.receiverTransportSession !== state.receiverTransportSession) {
|
|
677
1041
|
return;
|
|
678
1042
|
}
|