@peerbit/shared-log 13.2.31 → 13.2.32
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/exchange-heads.d.ts +13 -2
- package/dist/src/exchange-heads.d.ts.map +1 -1
- package/dist/src/exchange-heads.js +13 -2
- package/dist/src/exchange-heads.js.map +1 -1
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +181 -21
- package/dist/src/index.js.map +1 -1
- package/dist/src/replication-announcement.d.ts +3 -1
- package/dist/src/replication-announcement.d.ts.map +1 -1
- package/dist/src/replication-announcement.js +1 -0
- package/dist/src/replication-announcement.js.map +1 -1
- package/dist/src/replication-info-v2-send.d.ts +107 -0
- package/dist/src/replication-info-v2-send.d.ts.map +1 -0
- package/dist/src/replication-info-v2-send.js +415 -0
- package/dist/src/replication-info-v2-send.js.map +1 -0
- package/dist/src/replication.d.ts +26 -4
- package/dist/src/replication.d.ts.map +1 -1
- package/dist/src/replication.js +60 -5
- package/dist/src/replication.js.map +1 -1
- package/package.json +8 -8
- package/src/exchange-heads.ts +15 -2
- package/src/index.ts +225 -16
- package/src/replication-announcement.ts +10 -4
- package/src/replication-info-v2-send.ts +568 -0
- package/src/replication.ts +39 -5
package/dist/src/index.js
CHANGED
|
@@ -54,7 +54,7 @@ import { CheckedPruneCoordinator, } from "./checked-prune.js";
|
|
|
54
54
|
import { CPUUsageIntervalLag } from "./cpu.js";
|
|
55
55
|
import { debouncedAccumulatorMap, } from "./debounce.js";
|
|
56
56
|
import { NativeDurableCommitError, NoPeersError, isNotStartedError, } from "./errors.js";
|
|
57
|
-
import { EXCHANGE_HEADS_REPAIR_HINT, EntryWithRefs, ExchangeHeadsMessage, MAX_RAW_EXCHANGE_MESSAGE_SIZE, RawEntryWithRefs, RawExchangeHeadsMessage, RequestIPrune, RequestIPruneV2, ResponseIPrune, ResponseIPruneV2, SYNC_CAPABILITY_RAW_EXCHANGE_HEADS, SYNC_CAPABILITY_REPLICATION_INFO_V2_DECODE, StashBackedRawExchangeHeadsMessage, SyncCapabilitiesMessage, collectRawExchangeHeadSendPlan, createExchangeHeadsMessages, createRawExchangeHeadsMessages, getExchangeHeadHash, getPreparedRawExchangeGid, getPreparedRawExchangeHashNumber, getPreparedRawExchangeHeadAppendFacts, getPreparedRawExchangeHeadGid, getPreparedRawExchangeHeadRequestedReplicas, getPreparedRawExchangeHeadShallowEntry, getPreparedRawExchangeHeadSignatureVerified, getPreparedRawExchangeNext, getPreparedRawExchangeRequestedReplicas, getPreparedRawExchangeTimestamp, getRawExchangeHeadByteLength, getRawExchangeHeadStashIndexes, initExchangeHeadEntry, isPreparedRawEntryWithRefs, isStashBackedRawExchangeHeadsMessage, materializeVerifiedRawExchangeHeadsMessage, } from "./exchange-heads.js";
|
|
57
|
+
import { EXCHANGE_HEADS_REPAIR_HINT, EntryWithRefs, ExchangeHeadsMessage, MAX_RAW_EXCHANGE_MESSAGE_SIZE, RawEntryWithRefs, RawExchangeHeadsMessage, RequestIPrune, RequestIPruneV2, ResponseIPrune, ResponseIPruneV2, SYNC_CAPABILITY_RAW_EXCHANGE_HEADS, SYNC_CAPABILITY_REPLICATION_INFO_V2_APPLY, SYNC_CAPABILITY_REPLICATION_INFO_V2_DECODE, SYNC_CAPABILITY_REPLICATION_INFO_V2_SEND, StashBackedRawExchangeHeadsMessage, SyncCapabilitiesMessage, collectRawExchangeHeadSendPlan, createExchangeHeadsMessages, createRawExchangeHeadsMessages, getExchangeHeadHash, getPreparedRawExchangeGid, getPreparedRawExchangeHashNumber, getPreparedRawExchangeHeadAppendFacts, getPreparedRawExchangeHeadGid, getPreparedRawExchangeHeadRequestedReplicas, getPreparedRawExchangeHeadShallowEntry, getPreparedRawExchangeHeadSignatureVerified, getPreparedRawExchangeNext, getPreparedRawExchangeRequestedReplicas, getPreparedRawExchangeTimestamp, getRawExchangeHeadByteLength, getRawExchangeHeadStashIndexes, initExchangeHeadEntry, isPreparedRawEntryWithRefs, isStashBackedRawExchangeHeadsMessage, materializeVerifiedRawExchangeHeadsMessage, } from "./exchange-heads.js";
|
|
58
58
|
import { FanoutEnvelope } from "./fanout-envelope.js";
|
|
59
59
|
import { CoordinatePersistenceCoordinator, combineCoordinateDeleteHashes, isPromiseLike, mapMaybePromise, normalizedHashValues, } from "./coordinate-persistence.js";
|
|
60
60
|
import { InstanceLifecycle } from "./instance-lifecycle.js";
|
|
@@ -67,10 +67,11 @@ import { PeerSessionRegistry, } from "./peer-session.js";
|
|
|
67
67
|
import { PIDReplicationController } from "./pid.js";
|
|
68
68
|
import { EntryReplicatedU32, EntryReplicatedU64, ReplicationIntent, ReplicationRangeIndexableU32, ReplicationRangeIndexableU64, ReplicationRangeMessage, appromixateCoverage, calculateCoverage, countCoveringRangesSameOwner, createAssignedRangesQuery, debounceAggregationChanges, getAllMergeCandiates, getCoverSet, getSamples, isEntryReplicated, isMatured, isReplicationRangeMessage, mergeRanges, minimumWidthToCover, shouldAssigneToRangeBoundary as shouldAssignToRangeBoundary, toRebalance, } from "./ranges.js";
|
|
69
69
|
import { ReplicationAnnouncementCoordinator, isTransientReplicationAnnouncementError, } from "./replication-announcement.js";
|
|
70
|
+
import { ReplicationInfoV2SendCoordinator } from "./replication-info-v2-send.js";
|
|
70
71
|
import { createReplicationDomainHash, } from "./replication-domain-hash.js";
|
|
71
72
|
import { createReplicationDomainTime, } from "./replication-domain-time.js";
|
|
72
73
|
import {} from "./replication-domain.js";
|
|
73
|
-
import { AbsoluteReplicas, AddedReplicationSegmentMessage, AllReplicatingSegmentsMessage, MinReplicas, ReplicationError, ReplicationPingMessage, RequestReplicationInfoMessage, ResponseRoleMessage, StoppedReplicating, decodeReplicas, encodeReplicas, isReplicationInfoV2Message, maxReplicas, } from "./replication.js";
|
|
74
|
+
import { AbsoluteReplicas, AddedReplicationSegmentMessage, AllReplicatingSegmentsMessage, MinReplicas, ReplicationError, ReplicationPingMessage, RequestReplicationInfoMessage, RequestReplicationInfoV2Message, ResponseRoleMessage, StoppedReplicating, decodeReplicas, encodeReplicas, isReplicationInfoV2Message, maxReplicas, } from "./replication.js";
|
|
74
75
|
import { ReplicatorLivenessMonitor } from "./replicator-liveness.js";
|
|
75
76
|
import { Observer, Replicator } from "./role.js";
|
|
76
77
|
import { createSyncronizer } from "./sync/factory.js";
|
|
@@ -1608,6 +1609,7 @@ let SharedLog = (() => {
|
|
|
1608
1609
|
// A fn that we can call many times that recalculates the participation role
|
|
1609
1610
|
rebalanceParticipationDebounced;
|
|
1610
1611
|
_announcements;
|
|
1612
|
+
_v2Send;
|
|
1611
1613
|
// A fn for debouncing the calls for pruning
|
|
1612
1614
|
pruneDebouncedFn;
|
|
1613
1615
|
_checkedPruneAuditTimer;
|
|
@@ -1637,6 +1639,10 @@ let SharedLog = (() => {
|
|
|
1637
1639
|
// Sync capability bits advertised by peers (SyncCapabilitiesMessage), keyed
|
|
1638
1640
|
// by public key hash. Entries are dropped on unsubscribe/disconnect.
|
|
1639
1641
|
_peerSyncCapabilities;
|
|
1642
|
+
// Signed transport session carried by the capability envelope. Kept in a
|
|
1643
|
+
// parallel map so existing capability-number consumers remain unchanged.
|
|
1644
|
+
_peerSyncCapabilitySessions;
|
|
1645
|
+
_peerSyncCapabilityTimestamps;
|
|
1640
1646
|
// Pending live raw exchange-head gossip, coalesced per recipient set and
|
|
1641
1647
|
// flushed at the end of the current event-loop turn (or when a batch cap
|
|
1642
1648
|
// is hit). Only used when every recipient advertised raw capability.
|
|
@@ -1690,6 +1696,7 @@ let SharedLog = (() => {
|
|
|
1690
1696
|
// this).
|
|
1691
1697
|
queueCurrentReplicationStateAnnouncementRepair: () => this._announcements.queueCurrentReplicationStateAnnouncementRepair(),
|
|
1692
1698
|
queueCurrentReplicationStateAnnouncementRetry: (error) => this._announcements.queueCurrentReplicationStateAnnouncementRetry(error),
|
|
1699
|
+
enqueueReplicationInfoV2: (message) => this._v2Send.enqueue(message),
|
|
1693
1700
|
isClosed: () => this.closed,
|
|
1694
1701
|
getCloseSignal: () => this._closeController.signal,
|
|
1695
1702
|
getMyReplicationSegments: () => this.getMyReplicationSegments(),
|
|
@@ -1704,6 +1711,22 @@ let SharedLog = (() => {
|
|
|
1704
1711
|
callRebalanceParticipationDebounced: () => this.rebalanceParticipationDebounced?.call(),
|
|
1705
1712
|
});
|
|
1706
1713
|
}
|
|
1714
|
+
createReplicationInfoV2SendCoordinator() {
|
|
1715
|
+
return new ReplicationInfoV2SendCoordinator({
|
|
1716
|
+
getRpc: () => this.rpc,
|
|
1717
|
+
getSelfKey: () => this.node.identity.publicKey,
|
|
1718
|
+
getSenderTransportSession: () => BigInt(this.node.services.pubsub.session),
|
|
1719
|
+
getMyReplicationSegments: () => this.getMyReplicationSegments(),
|
|
1720
|
+
validatePersistedReplicationRangeSnapshot: (ranges) => this.validatePersistedReplicationRangeSnapshot(ranges),
|
|
1721
|
+
isClosed: () => this.closed,
|
|
1722
|
+
isPeerSessionOpen: (peerHash, peerSession) => this._peerSessions.isCurrent(peerHash, peerSession) &&
|
|
1723
|
+
peerSession.phase === "open" &&
|
|
1724
|
+
!this._peerSessions.isReplicationInfoBlocked(peerHash) &&
|
|
1725
|
+
this._peerSessions.isReceiveCleanupGateOpen(peerHash),
|
|
1726
|
+
captureReplicationOwnershipLifecycle: () => this.captureReplicationOwnershipLifecycle(),
|
|
1727
|
+
isReplicationOwnershipLifecycleActive: (controller) => this.isRepairLifecycleActive(controller),
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1707
1730
|
createReplicatorLivenessMonitor() {
|
|
1708
1731
|
return new ReplicatorLivenessMonitor({
|
|
1709
1732
|
// Sweep-driven probes and activity marks dispatch through the owner so
|
|
@@ -1798,6 +1821,8 @@ let SharedLog = (() => {
|
|
|
1798
1821
|
this._appendBackfillPendingByTarget = new Map();
|
|
1799
1822
|
this._topicSubscribersCache = new Map();
|
|
1800
1823
|
this._peerSyncCapabilities = new Map();
|
|
1824
|
+
this._peerSyncCapabilitySessions = new Map();
|
|
1825
|
+
this._peerSyncCapabilityTimestamps = new Map();
|
|
1801
1826
|
this._liveRawGossipBatches = new Map();
|
|
1802
1827
|
this._liveRawGossipFlushScheduled = false;
|
|
1803
1828
|
this.coordinateToHash = new Cache({ max: 1e6, ttl: 1e4 });
|
|
@@ -1806,6 +1831,7 @@ let SharedLog = (() => {
|
|
|
1806
1831
|
this._replicatorJoinEmitted = new Set();
|
|
1807
1832
|
this._replicatorsReconciled = false;
|
|
1808
1833
|
this._liveness = this.createReplicatorLivenessMonitor();
|
|
1834
|
+
this._v2Send = this.createReplicationInfoV2SendCoordinator();
|
|
1809
1835
|
this._announcements = this.createReplicationAnnouncementCoordinator();
|
|
1810
1836
|
this.pendingMaturity = new Map();
|
|
1811
1837
|
this._closeController = new AbortController();
|
|
@@ -2196,6 +2222,72 @@ let SharedLog = (() => {
|
|
|
2196
2222
|
SYNC_CAPABILITY_RAW_EXCHANGE_HEADS) !==
|
|
2197
2223
|
0);
|
|
2198
2224
|
}
|
|
2225
|
+
observePeerSyncCapabilities(properties) {
|
|
2226
|
+
const { peerHash, capabilities, transportSession, timestamp, openingSession, } = properties;
|
|
2227
|
+
if (openingSession) {
|
|
2228
|
+
const previous = this._openingSyncCapabilitiesByPeer.get(peerHash);
|
|
2229
|
+
if (transportSession !== undefined &&
|
|
2230
|
+
timestamp !== undefined &&
|
|
2231
|
+
previous?.epoch === openingSession &&
|
|
2232
|
+
previous.transportSession === transportSession) {
|
|
2233
|
+
if (previous.timestamp !== undefined && timestamp < previous.timestamp) {
|
|
2234
|
+
return false;
|
|
2235
|
+
}
|
|
2236
|
+
this._openingSyncCapabilitiesByPeer.set(peerHash, {
|
|
2237
|
+
epoch: openingSession,
|
|
2238
|
+
capabilities: previous.capabilities | capabilities,
|
|
2239
|
+
transportSession,
|
|
2240
|
+
timestamp: previous.timestamp === undefined || timestamp > previous.timestamp
|
|
2241
|
+
? timestamp
|
|
2242
|
+
: previous.timestamp,
|
|
2243
|
+
});
|
|
2244
|
+
return true;
|
|
2245
|
+
}
|
|
2246
|
+
this._openingSyncCapabilitiesByPeer.set(peerHash, {
|
|
2247
|
+
epoch: openingSession,
|
|
2248
|
+
capabilities,
|
|
2249
|
+
transportSession,
|
|
2250
|
+
timestamp,
|
|
2251
|
+
});
|
|
2252
|
+
return true;
|
|
2253
|
+
}
|
|
2254
|
+
if (transportSession === undefined || timestamp === undefined) {
|
|
2255
|
+
// Test/in-process synthetic contexts predate signed envelope captures.
|
|
2256
|
+
// They may exercise capability-number behavior, but can never authorize V2.
|
|
2257
|
+
this._peerSyncCapabilities.set(peerHash, capabilities);
|
|
2258
|
+
this._peerSyncCapabilitySessions.delete(peerHash);
|
|
2259
|
+
this._peerSyncCapabilityTimestamps.delete(peerHash);
|
|
2260
|
+
this._v2Send.advancePeerCapability(peerHash);
|
|
2261
|
+
return true;
|
|
2262
|
+
}
|
|
2263
|
+
const previousSession = this._peerSyncCapabilitySessions.get(peerHash);
|
|
2264
|
+
const previousTimestamp = this._peerSyncCapabilityTimestamps.get(peerHash);
|
|
2265
|
+
const sameTransportSession = previousSession === transportSession;
|
|
2266
|
+
if (sameTransportSession &&
|
|
2267
|
+
previousTimestamp !== undefined &&
|
|
2268
|
+
timestamp < previousTimestamp) {
|
|
2269
|
+
return false;
|
|
2270
|
+
}
|
|
2271
|
+
const previousCapabilities = this._peerSyncCapabilities.get(peerHash) ?? 0;
|
|
2272
|
+
const nextCapabilities = sameTransportSession
|
|
2273
|
+
? previousCapabilities | capabilities
|
|
2274
|
+
: capabilities;
|
|
2275
|
+
const generationAdvanced = !sameTransportSession ||
|
|
2276
|
+
previousTimestamp === undefined ||
|
|
2277
|
+
timestamp > previousTimestamp ||
|
|
2278
|
+
nextCapabilities !== previousCapabilities;
|
|
2279
|
+
this._peerSyncCapabilities.set(peerHash, nextCapabilities);
|
|
2280
|
+
this._peerSyncCapabilitySessions.set(peerHash, transportSession);
|
|
2281
|
+
this._peerSyncCapabilityTimestamps.set(peerHash, previousTimestamp === undefined ||
|
|
2282
|
+
!sameTransportSession ||
|
|
2283
|
+
timestamp > previousTimestamp
|
|
2284
|
+
? timestamp
|
|
2285
|
+
: previousTimestamp);
|
|
2286
|
+
if (generationAdvanced) {
|
|
2287
|
+
this._v2Send.advancePeerCapability(peerHash);
|
|
2288
|
+
}
|
|
2289
|
+
return true;
|
|
2290
|
+
}
|
|
2199
2291
|
/**
|
|
2200
2292
|
* Live append gossip may use the raw exchange-heads path only when we
|
|
2201
2293
|
* opted into raw sync and every remote recipient advertised raw capability
|
|
@@ -8895,6 +8987,8 @@ let SharedLog = (() => {
|
|
|
8895
8987
|
ttl: LEADER_PLAN_CACHE_TTL_MS,
|
|
8896
8988
|
});
|
|
8897
8989
|
this._peerSyncCapabilities = new Map();
|
|
8990
|
+
this._peerSyncCapabilitySessions = new Map();
|
|
8991
|
+
this._peerSyncCapabilityTimestamps = new Map();
|
|
8898
8992
|
this._liveRawGossipBatches = new Map();
|
|
8899
8993
|
this._liveRawGossipFlushScheduled = false;
|
|
8900
8994
|
this.coordinateToHash = new Cache({ max: 1e6, ttl: 1e4 });
|
|
@@ -8910,6 +9004,8 @@ let SharedLog = (() => {
|
|
|
8910
9004
|
this._lastLocalAppendAt = 0;
|
|
8911
9005
|
this._announcements ??= this.createReplicationAnnouncementCoordinator();
|
|
8912
9006
|
this._announcements.resetForOpen();
|
|
9007
|
+
this._v2Send ??= this.createReplicationInfoV2SendCoordinator();
|
|
9008
|
+
this._v2Send.resetForOpen();
|
|
8913
9009
|
const adaptiveReplicateOptions = options?.replicate && isAdaptiveReplicatorOption(options.replicate)
|
|
8914
9010
|
? options.replicate
|
|
8915
9011
|
: undefined;
|
|
@@ -10029,6 +10125,9 @@ let SharedLog = (() => {
|
|
|
10029
10125
|
this._liveness._replicatorLivenessFailures.delete(peerHash);
|
|
10030
10126
|
this._liveness._replicatorLastActivityAt.delete(peerHash);
|
|
10031
10127
|
this._peerSyncCapabilities.delete(peerHash);
|
|
10128
|
+
this._peerSyncCapabilitySessions.delete(peerHash);
|
|
10129
|
+
this._peerSyncCapabilityTimestamps.delete(peerHash);
|
|
10130
|
+
this._v2Send.clearPeer(peerHash);
|
|
10032
10131
|
this.cleanupPendingIHavePeer(peerHash);
|
|
10033
10132
|
this.cleanupCheckedPrunePeer(peerHash, ownershipLifecycleController, this._checkedPrune);
|
|
10034
10133
|
}
|
|
@@ -10845,6 +10944,9 @@ let SharedLog = (() => {
|
|
|
10845
10944
|
this._openingSyncCapabilitiesByPeer?.clear();
|
|
10846
10945
|
this._gidPeersHistory?.clear();
|
|
10847
10946
|
this._peerSyncCapabilities?.clear();
|
|
10947
|
+
this._peerSyncCapabilitySessions?.clear();
|
|
10948
|
+
this._peerSyncCapabilityTimestamps?.clear();
|
|
10949
|
+
this._v2Send?.clearForClose();
|
|
10848
10950
|
this._liveRawGossipBatches?.clear();
|
|
10849
10951
|
this._nativeSharedLogState?.clearGidPeers();
|
|
10850
10952
|
this._nativeBackbone?.clearGidPeers();
|
|
@@ -10965,12 +11067,14 @@ let SharedLog = (() => {
|
|
|
10965
11067
|
}
|
|
10966
11068
|
}, 2_000);
|
|
10967
11069
|
try {
|
|
10968
|
-
|
|
10969
|
-
|
|
10970
|
-
|
|
10971
|
-
|
|
10972
|
-
|
|
10973
|
-
.catch(() => { })
|
|
11070
|
+
const reset = new AllReplicatingSegmentsMessage({ segments: [] });
|
|
11071
|
+
await Promise.all([
|
|
11072
|
+
this.rpc.send(reset, {
|
|
11073
|
+
priority: CONVERGENCE_MESSAGE_PRIORITY,
|
|
11074
|
+
signal: abort.signal,
|
|
11075
|
+
}).catch(() => { }),
|
|
11076
|
+
this._v2Send.sendTerminalReset(abort.signal),
|
|
11077
|
+
]);
|
|
10974
11078
|
}
|
|
10975
11079
|
finally {
|
|
10976
11080
|
clearTimeout(abortTimer);
|
|
@@ -11088,12 +11192,14 @@ let SharedLog = (() => {
|
|
|
11088
11192
|
}
|
|
11089
11193
|
}, 2_000);
|
|
11090
11194
|
try {
|
|
11091
|
-
|
|
11092
|
-
|
|
11093
|
-
|
|
11094
|
-
|
|
11095
|
-
|
|
11096
|
-
.catch(() => { })
|
|
11195
|
+
const reset = new AllReplicatingSegmentsMessage({ segments: [] });
|
|
11196
|
+
await Promise.all([
|
|
11197
|
+
this.rpc.send(reset, {
|
|
11198
|
+
priority: CONVERGENCE_MESSAGE_PRIORITY,
|
|
11199
|
+
signal: abort.signal,
|
|
11200
|
+
}).catch(() => { }),
|
|
11201
|
+
this._v2Send.sendTerminalReset(abort.signal),
|
|
11202
|
+
]);
|
|
11097
11203
|
}
|
|
11098
11204
|
finally {
|
|
11099
11205
|
clearTimeout(abortTimer);
|
|
@@ -11565,7 +11671,8 @@ let SharedLog = (() => {
|
|
|
11565
11671
|
// allocation cost, and before liveness or apply-queue side effects.
|
|
11566
11672
|
this.validateStoppedReplicationAnnouncement(msg.segmentIds);
|
|
11567
11673
|
}
|
|
11568
|
-
if (!context.from.equals(this.node.identity.publicKey)
|
|
11674
|
+
if (!context.from.equals(this.node.identity.publicKey) &&
|
|
11675
|
+
!(msg instanceof RequestReplicationInfoV2Message)) {
|
|
11569
11676
|
this._liveness.markReplicatorActivity(receiveFromHash);
|
|
11570
11677
|
}
|
|
11571
11678
|
const syncProfile = this._logProperties?.sync?.profile;
|
|
@@ -12908,6 +13015,8 @@ let SharedLog = (() => {
|
|
|
12908
13015
|
}
|
|
12909
13016
|
else if (msg instanceof SyncCapabilitiesMessage) {
|
|
12910
13017
|
if (!context.from.equals(this.node.identity.publicKey)) {
|
|
13018
|
+
const capabilityTransportSession = context.message?.header?.session;
|
|
13019
|
+
const capabilityTimestamp = context.message?.header?.timestamp;
|
|
12911
13020
|
// No await separates this from the capture above, so the captured
|
|
12912
13021
|
// window state is exact: the legacy re-read of the opening map here
|
|
12913
13022
|
// could never observe a different value.
|
|
@@ -12916,17 +13025,47 @@ let SharedLog = (() => {
|
|
|
12916
13025
|
// A prior unsubscribe cleanup may still be ahead of this reconnect
|
|
12917
13026
|
// barrier. Stage the new generation's one-shot advertisement so that
|
|
12918
13027
|
// cleanup cannot erase it before the opening transition commits.
|
|
12919
|
-
this.
|
|
12920
|
-
|
|
13028
|
+
this.observePeerSyncCapabilities({
|
|
13029
|
+
peerHash: receiveFromHash,
|
|
12921
13030
|
capabilities: msg.capabilities,
|
|
13031
|
+
transportSession: capabilityTransportSession,
|
|
13032
|
+
timestamp: capabilityTimestamp,
|
|
13033
|
+
openingSession: receiveSession,
|
|
12922
13034
|
});
|
|
12923
13035
|
}
|
|
12924
13036
|
else {
|
|
12925
|
-
this.
|
|
13037
|
+
this.observePeerSyncCapabilities({
|
|
13038
|
+
peerHash: receiveFromHash,
|
|
13039
|
+
capabilities: msg.capabilities,
|
|
13040
|
+
transportSession: capabilityTransportSession,
|
|
13041
|
+
timestamp: capabilityTimestamp,
|
|
13042
|
+
});
|
|
12926
13043
|
}
|
|
12927
13044
|
}
|
|
12928
13045
|
return;
|
|
12929
13046
|
}
|
|
13047
|
+
else if (msg instanceof RequestReplicationInfoV2Message) {
|
|
13048
|
+
const requiredCapabilities = SYNC_CAPABILITY_REPLICATION_INFO_V2_DECODE |
|
|
13049
|
+
SYNC_CAPABILITY_REPLICATION_INFO_V2_APPLY;
|
|
13050
|
+
if (receiveSession &&
|
|
13051
|
+
((this._peerSyncCapabilities.get(receiveFromHash) ?? 0) &
|
|
13052
|
+
requiredCapabilities) ===
|
|
13053
|
+
requiredCapabilities &&
|
|
13054
|
+
this._peerSyncCapabilitySessions.get(receiveFromHash) ===
|
|
13055
|
+
context.message.header.session &&
|
|
13056
|
+
this._peerSyncCapabilityTimestamps.has(receiveFromHash) &&
|
|
13057
|
+
context.message.header.timestamp >
|
|
13058
|
+
this._peerSyncCapabilityTimestamps.get(receiveFromHash) &&
|
|
13059
|
+
this._v2Send.acceptRequest(msg, {
|
|
13060
|
+
from: context.from,
|
|
13061
|
+
peerSession: receiveSession,
|
|
13062
|
+
receiverTransportSession: context.message.header.session,
|
|
13063
|
+
requestTimestamp: context.message.header.timestamp,
|
|
13064
|
+
})) {
|
|
13065
|
+
this._liveness.markReplicatorActivity(receiveFromHash);
|
|
13066
|
+
}
|
|
13067
|
+
return;
|
|
13068
|
+
}
|
|
12930
13069
|
else if (await this.syncronizer.onMessage(msg, context)) {
|
|
12931
13070
|
return; // the syncronizer has handled the message
|
|
12932
13071
|
}
|
|
@@ -13162,6 +13301,7 @@ let SharedLog = (() => {
|
|
|
13162
13301
|
}
|
|
13163
13302
|
const segments = replicationSegments.map((x) => x.toReplicationRange());
|
|
13164
13303
|
this.validatePersistedReplicationRangeSnapshot(segments);
|
|
13304
|
+
this._v2Send.enqueueSnapshotForPeer(receiveFromHash);
|
|
13165
13305
|
await this.rpc
|
|
13166
13306
|
.send(new AllReplicatingSegmentsMessage({ segments }), {
|
|
13167
13307
|
mode: new AcknowledgeDelivery({
|
|
@@ -15463,6 +15603,12 @@ let SharedLog = (() => {
|
|
|
15463
15603
|
if (!ownsSubscriptionEpoch()) {
|
|
15464
15604
|
return;
|
|
15465
15605
|
}
|
|
15606
|
+
// A destination stream is scoped to exactly one topic-subscription
|
|
15607
|
+
// session. Abort the predecessor synchronously before either barrier can
|
|
15608
|
+
// yield; a late queue completion must never enter the new session.
|
|
15609
|
+
this._v2Send.clearPeer(peerHash);
|
|
15610
|
+
this._peerSyncCapabilitySessions.delete(peerHash);
|
|
15611
|
+
this._peerSyncCapabilityTimestamps.delete(peerHash);
|
|
15466
15612
|
if (subscribed) {
|
|
15467
15613
|
const pendingOpeningCapabilities = this._openingSyncCapabilitiesByPeer.get(peerHash);
|
|
15468
15614
|
if (pendingOpeningCapabilities &&
|
|
@@ -15493,6 +15639,18 @@ let SharedLog = (() => {
|
|
|
15493
15639
|
const openingCapabilities = this._openingSyncCapabilitiesByPeer.get(peerHash);
|
|
15494
15640
|
if (openingCapabilities?.epoch === expectedSubscriptionEpoch) {
|
|
15495
15641
|
this._peerSyncCapabilities.set(peerHash, openingCapabilities.capabilities);
|
|
15642
|
+
if (openingCapabilities.transportSession === undefined) {
|
|
15643
|
+
this._peerSyncCapabilitySessions.delete(peerHash);
|
|
15644
|
+
}
|
|
15645
|
+
else {
|
|
15646
|
+
this._peerSyncCapabilitySessions.set(peerHash, openingCapabilities.transportSession);
|
|
15647
|
+
}
|
|
15648
|
+
if (openingCapabilities.timestamp === undefined) {
|
|
15649
|
+
this._peerSyncCapabilityTimestamps.delete(peerHash);
|
|
15650
|
+
}
|
|
15651
|
+
else {
|
|
15652
|
+
this._peerSyncCapabilityTimestamps.set(peerHash, openingCapabilities.timestamp);
|
|
15653
|
+
}
|
|
15496
15654
|
this._openingSyncCapabilitiesByPeer.delete(peerHash);
|
|
15497
15655
|
}
|
|
15498
15656
|
this._peerSessions.unblockReplicationInfo(peerHash);
|
|
@@ -15562,10 +15720,12 @@ let SharedLog = (() => {
|
|
|
15562
15720
|
this._liveness._replicatorLivenessFailures.delete(peerHash);
|
|
15563
15721
|
this._liveness.markReplicatorActivity(peerHash);
|
|
15564
15722
|
this._peerSessions.markOpen(peerHash, expectedSubscriptionEpoch);
|
|
15565
|
-
// Decode support
|
|
15566
|
-
//
|
|
15567
|
-
//
|
|
15723
|
+
// Decode support and receiver-led negotiation readiness are separate bits.
|
|
15724
|
+
// This node never initiates a request in this rollout; normal replication
|
|
15725
|
+
// announcements remain legacy unless a peer explicitly grants one signed,
|
|
15726
|
+
// session-bound V2 destination stream.
|
|
15568
15727
|
const receiveCapabilities = SYNC_CAPABILITY_REPLICATION_INFO_V2_DECODE |
|
|
15728
|
+
SYNC_CAPABILITY_REPLICATION_INFO_V2_SEND |
|
|
15569
15729
|
(this._logProperties?.sync?.rawExchangeHeads === true
|
|
15570
15730
|
? SYNC_CAPABILITY_RAW_EXCHANGE_HEADS
|
|
15571
15731
|
: 0);
|