@peerbit/shared-log 16.0.2 → 16.0.3
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/coordinate-persistence.d.ts +18 -1
- package/dist/src/coordinate-persistence.d.ts.map +1 -1
- package/dist/src/coordinate-persistence.js +61 -4
- package/dist/src/coordinate-persistence.js.map +1 -1
- package/dist/src/index.d.ts +7 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +261 -200
- package/dist/src/index.js.map +1 -1
- package/package.json +8 -8
- package/src/coordinate-persistence.ts +66 -5
- package/src/index.ts +345 -257
package/src/index.ts
CHANGED
|
@@ -637,6 +637,13 @@ export type NativeBackboneCoordinateRollback<R extends "u32" | "u64"> = {
|
|
|
637
637
|
hashes: Set<string>;
|
|
638
638
|
entries: Map<string, ResidentCoordinateEntry<R>>;
|
|
639
639
|
generations: Map<string, number>;
|
|
640
|
+
/**
|
|
641
|
+
* Set by `settleResidentCoordinateSnapshot` once the token's holds on the
|
|
642
|
+
* mutation-generation map have been released. Load-bearing: it makes the
|
|
643
|
+
* settle idempotent, so a second settle of this token cannot consume a
|
|
644
|
+
* different token's hold on a shared hash.
|
|
645
|
+
*/
|
|
646
|
+
settled?: boolean;
|
|
640
647
|
};
|
|
641
648
|
|
|
642
649
|
export type RepairDispatchEntry<R extends "u32" | "u64"> =
|
|
@@ -3114,9 +3121,23 @@ export class SharedLog<
|
|
|
3114
3121
|
};
|
|
3115
3122
|
for (const coordinate of intent.coordinates) {
|
|
3116
3123
|
rollback.hashes.add(coordinate.hash);
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3124
|
+
// Same hold-counted row shape the coordinator's own
|
|
3125
|
+
// snapshot writes: one hold per hash, released by the
|
|
3126
|
+
// settle after the replay consumes the token below.
|
|
3127
|
+
// RELIES ON `intent.coordinates` HOLDING UNIQUE HASHES —
|
|
3128
|
+
// it is built from the token's `hashes` Set (see
|
|
3129
|
+
// setNativeStrictDurableTransactionOperation). Holds are
|
|
3130
|
+
// taken per element here but released per unique hash by
|
|
3131
|
+
// the settle, so a duplicate would take two and release
|
|
3132
|
+
// one and retain that row forever. That is the safe
|
|
3133
|
+
// direction (a retained row, never a fail-open rollback),
|
|
3134
|
+
// but keep the source a Set.
|
|
3135
|
+
const row = mutationGenerations.get(coordinate.hash);
|
|
3136
|
+
const generation = (row?.generation ?? 0) + 1;
|
|
3137
|
+
mutationGenerations.set(coordinate.hash, {
|
|
3138
|
+
generation,
|
|
3139
|
+
holds: (row?.holds ?? 0) + 1,
|
|
3140
|
+
});
|
|
3120
3141
|
rollback.generations.set(coordinate.hash, generation);
|
|
3121
3142
|
if (coordinate.value) {
|
|
3122
3143
|
const number = (value: string) =>
|
|
@@ -3142,6 +3163,9 @@ export class SharedLog<
|
|
|
3142
3163
|
"",
|
|
3143
3164
|
rollback,
|
|
3144
3165
|
);
|
|
3166
|
+
// The replay fabricated these generations itself one turn
|
|
3167
|
+
// earlier and has now consumed them, so the rows are dead.
|
|
3168
|
+
this._coordinates.settleResidentCoordinateSnapshot(rollback);
|
|
3145
3169
|
}
|
|
3146
3170
|
for (const document of intent.documents) {
|
|
3147
3171
|
this.restoreNativeBackboneDocument({
|
|
@@ -10979,12 +11003,21 @@ export class SharedLog<
|
|
|
10979
11003
|
return rollbackLowerPublication(error);
|
|
10980
11004
|
}
|
|
10981
11005
|
if (!result) {
|
|
11006
|
+
// Abandon arm: the token was minted but nothing downstream can
|
|
11007
|
+
// roll it back from here.
|
|
11008
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
11009
|
+
lowerPublicationRollback?.coordinateEntries,
|
|
11010
|
+
);
|
|
10982
11011
|
return this.completeNativeStrictDurableTransaction(
|
|
10983
11012
|
nativeStrictTransaction,
|
|
10984
11013
|
).then(() => undefined);
|
|
10985
11014
|
}
|
|
10986
11015
|
return mapMaybePromise(result, async (prepared) => {
|
|
10987
11016
|
if (!prepared) {
|
|
11017
|
+
// Abandon arm: same shape as the `!result` arm above.
|
|
11018
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
11019
|
+
lowerPublicationRollback?.coordinateEntries,
|
|
11020
|
+
);
|
|
10988
11021
|
await this.completeNativeStrictDurableTransaction(
|
|
10989
11022
|
nativeStrictTransaction,
|
|
10990
11023
|
);
|
|
@@ -11018,6 +11051,11 @@ export class SharedLog<
|
|
|
11018
11051
|
prepared.appendFacts.hash,
|
|
11019
11052
|
lowerPublicationRollback?.coordinateEntries,
|
|
11020
11053
|
);
|
|
11054
|
+
// Terminal: this is the last rollback consumer for the
|
|
11055
|
+
// token. A throw above leaves the row, which is safe.
|
|
11056
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
11057
|
+
lowerPublicationRollback?.coordinateEntries,
|
|
11058
|
+
);
|
|
11021
11059
|
for (const document of lowerPublicationRollback?.documents ?? []) {
|
|
11022
11060
|
this.restoreNativeBackboneDocument(document);
|
|
11023
11061
|
}
|
|
@@ -11088,6 +11126,14 @@ export class SharedLog<
|
|
|
11088
11126
|
} catch (error) {
|
|
11089
11127
|
return rollback(error);
|
|
11090
11128
|
}
|
|
11129
|
+
// Success seam. The last await inside the protected try is the
|
|
11130
|
+
// finalizer acknowledge; `finish()` is synchronous, so no async
|
|
11131
|
+
// boundary separates the catch above from this statement and
|
|
11132
|
+
// `rollback` can no longer fire. Nothing downstream rolls back
|
|
11133
|
+
// (the retire below only warns), so the token is terminal here.
|
|
11134
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
11135
|
+
lowerPublicationRollback?.coordinateEntries,
|
|
11136
|
+
);
|
|
11091
11137
|
this.applyPreparedAppendFactsWithDeferredCoordinateDeletes(
|
|
11092
11138
|
prepared.appendFacts,
|
|
11093
11139
|
prepared.removed,
|
|
@@ -11544,6 +11590,12 @@ export class SharedLog<
|
|
|
11544
11590
|
}
|
|
11545
11591
|
return mapMaybePromise(result, async (prepared) => {
|
|
11546
11592
|
if (!prepared || !backboneAppend) {
|
|
11593
|
+
// Abandon arm: the token was minted inside
|
|
11594
|
+
// `prepareBackboneAppend` and nothing downstream of
|
|
11595
|
+
// this return can roll it back.
|
|
11596
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
11597
|
+
nativeCoordinateRollback,
|
|
11598
|
+
);
|
|
11547
11599
|
await this.completeNativeStrictDurableTransaction(
|
|
11548
11600
|
nativeStrictTransaction,
|
|
11549
11601
|
);
|
|
@@ -11635,6 +11687,10 @@ export class SharedLog<
|
|
|
11635
11687
|
prepared.appendFacts.hash,
|
|
11636
11688
|
rollbackCoordinateEntries,
|
|
11637
11689
|
);
|
|
11690
|
+
// Terminal: last rollback consumer for the token.
|
|
11691
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
11692
|
+
rollbackCoordinateEntries,
|
|
11693
|
+
);
|
|
11638
11694
|
} catch (rollbackError) {
|
|
11639
11695
|
rollbackFailures.push(rollbackError);
|
|
11640
11696
|
}
|
|
@@ -11711,6 +11767,12 @@ export class SharedLog<
|
|
|
11711
11767
|
} catch (error) {
|
|
11712
11768
|
return rollback(error);
|
|
11713
11769
|
}
|
|
11770
|
+
// Success seam: the finalizer acknowledge above is the last
|
|
11771
|
+
// await inside the protected try, so `rollback` can no
|
|
11772
|
+
// longer fire and the retire below only warns.
|
|
11773
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
11774
|
+
rollbackCoordinateEntries,
|
|
11775
|
+
);
|
|
11714
11776
|
this.applyPreparedAppendFactsWithDeferredCoordinateDeletes(
|
|
11715
11777
|
prepared.appendFacts,
|
|
11716
11778
|
prepared.removed,
|
|
@@ -12630,6 +12692,10 @@ export class SharedLog<
|
|
|
12630
12692
|
throw error;
|
|
12631
12693
|
}
|
|
12632
12694
|
if (!appended || !backboneAppends) {
|
|
12695
|
+
// Abandon arm: no consumer downstream of this return.
|
|
12696
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
12697
|
+
batchCoordinateRollback,
|
|
12698
|
+
);
|
|
12633
12699
|
await this.completeNativeStrictDurableTransaction(
|
|
12634
12700
|
nativeStrictTransaction,
|
|
12635
12701
|
);
|
|
@@ -12667,6 +12733,10 @@ export class SharedLog<
|
|
|
12667
12733
|
appended.appendFacts[0]?.hash ?? "",
|
|
12668
12734
|
batchCoordinateRollback,
|
|
12669
12735
|
);
|
|
12736
|
+
// Terminal: last rollback consumer for the batch token.
|
|
12737
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
12738
|
+
batchCoordinateRollback,
|
|
12739
|
+
);
|
|
12670
12740
|
} catch (rollbackError) {
|
|
12671
12741
|
rollbackFailures.push(rollbackError);
|
|
12672
12742
|
}
|
|
@@ -12798,6 +12868,11 @@ export class SharedLog<
|
|
|
12798
12868
|
} catch (error) {
|
|
12799
12869
|
return rollbackBatch(error);
|
|
12800
12870
|
}
|
|
12871
|
+
// Success seam: `rollbackBatch` has exactly one call site (the catch
|
|
12872
|
+
// above), and everything from here on escapes without any rollback.
|
|
12873
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
12874
|
+
batchCoordinateRollback,
|
|
12875
|
+
);
|
|
12801
12876
|
|
|
12802
12877
|
this.throwIfReplicationOwnershipLifecycleInactive(
|
|
12803
12878
|
ownershipLifecycleController,
|
|
@@ -18907,292 +18982,305 @@ export class SharedLog<
|
|
|
18907
18982
|
reusableCoordinatePersistItems,
|
|
18908
18983
|
)
|
|
18909
18984
|
: undefined;
|
|
18910
|
-
|
|
18911
|
-
|
|
18912
|
-
|
|
18913
|
-
|
|
18914
|
-
|
|
18915
|
-
|
|
18916
|
-
|
|
18917
|
-
|
|
18918
|
-
|
|
18919
|
-
(committedHashes) => {
|
|
18920
|
-
nativePreparedCommittedHashes = new Set(committedHashes);
|
|
18921
|
-
},
|
|
18922
|
-
)
|
|
18923
|
-
: undefined;
|
|
18924
|
-
const finishNativePreparedCoordinates = async (properties: {
|
|
18925
|
-
nativePreparedCommitted: boolean;
|
|
18926
|
-
}) => {
|
|
18927
|
-
if (
|
|
18928
|
-
!properties.nativePreparedCommitted ||
|
|
18929
|
-
!nativePreparedCoordinateBatch
|
|
18930
|
-
) {
|
|
18931
|
-
return;
|
|
18932
|
-
}
|
|
18933
|
-
try {
|
|
18934
|
-
nativeBackboneOnlyPersistedHashes =
|
|
18935
|
-
await this._coordinates.finishBackboneOnlyReceiveCoordinateBatch(
|
|
18936
|
-
nativePreparedCoordinateBatch,
|
|
18985
|
+
try {
|
|
18986
|
+
const nativePreparedJoinCommit = canUsePreparedAppendFacts
|
|
18987
|
+
? this._coordinates.createNativeBackbonePreparedJoinCommit(
|
|
18988
|
+
nativeReceiveCoordinateBatch,
|
|
18989
|
+
(batch) => {
|
|
18990
|
+
nativePreparedCoordinateBatch = batch;
|
|
18991
|
+
},
|
|
18992
|
+
nativeCommitVerifyHashes,
|
|
18993
|
+
nativeCommitVerifyAllHashes,
|
|
18937
18994
|
syncProfile,
|
|
18995
|
+
(committedHashes) => {
|
|
18996
|
+
nativePreparedCommittedHashes = new Set(committedHashes);
|
|
18997
|
+
},
|
|
18998
|
+
)
|
|
18999
|
+
: undefined;
|
|
19000
|
+
const finishNativePreparedCoordinates = async (properties: {
|
|
19001
|
+
nativePreparedCommitted: boolean;
|
|
19002
|
+
}) => {
|
|
19003
|
+
if (
|
|
19004
|
+
!properties.nativePreparedCommitted ||
|
|
19005
|
+
!nativePreparedCoordinateBatch
|
|
19006
|
+
) {
|
|
19007
|
+
return;
|
|
19008
|
+
}
|
|
19009
|
+
try {
|
|
19010
|
+
nativeBackboneOnlyPersistedHashes =
|
|
19011
|
+
await this._coordinates.finishBackboneOnlyReceiveCoordinateBatch(
|
|
19012
|
+
nativePreparedCoordinateBatch,
|
|
19013
|
+
syncProfile,
|
|
19014
|
+
);
|
|
19015
|
+
nativePreparedCoordinatesFinished = true;
|
|
19016
|
+
} catch (error) {
|
|
19017
|
+
this._coordinates.rollbackBackboneOnlyReceiveCoordinateBatch(
|
|
19018
|
+
nativePreparedCoordinateBatch,
|
|
18938
19019
|
);
|
|
18939
|
-
|
|
18940
|
-
|
|
18941
|
-
|
|
18942
|
-
|
|
18943
|
-
|
|
18944
|
-
|
|
19020
|
+
throw error;
|
|
19021
|
+
}
|
|
19022
|
+
};
|
|
19023
|
+
const preparedAppendCanValidateAppend =
|
|
19024
|
+
canAppendAlreadyValidated ||
|
|
19025
|
+
(nativeCommitCanValidateAppend && !!nativePreparedJoinCommit);
|
|
19026
|
+
if (!preparedAppendCanValidateAppend) {
|
|
19027
|
+
canUsePreparedAppendFacts = false;
|
|
18945
19028
|
}
|
|
18946
|
-
|
|
18947
|
-
|
|
18948
|
-
|
|
18949
|
-
|
|
18950
|
-
|
|
18951
|
-
|
|
18952
|
-
|
|
18953
|
-
|
|
18954
|
-
|
|
18955
|
-
|
|
18956
|
-
? nativeCommitVerifyAllHashes
|
|
18957
|
-
? !!this._nativeBackbone?.graph
|
|
18958
|
-
.commitVerifiedAllPreparedRawReceiveJoinBatch ||
|
|
18959
|
-
!!this._nativeBackbone?.graph
|
|
18960
|
-
.commitVerifiedPreparedRawReceiveJoinBatch
|
|
19029
|
+
const nativePreparedJoinCommitValidatesPlan =
|
|
19030
|
+
!!nativePreparedJoinCommit &&
|
|
19031
|
+
(nativeCommitVerifyHashes && nativeCommitVerifyHashes.length > 0
|
|
19032
|
+
? nativeCommitVerifyAllHashes
|
|
19033
|
+
? !!this._nativeBackbone?.graph
|
|
19034
|
+
.commitVerifiedAllPreparedRawReceiveJoinBatch ||
|
|
19035
|
+
!!this._nativeBackbone?.graph
|
|
19036
|
+
.commitVerifiedPreparedRawReceiveJoinBatch
|
|
19037
|
+
: !!this._nativeBackbone?.graph
|
|
19038
|
+
.commitVerifiedPreparedRawReceiveJoinBatch
|
|
18961
19039
|
: !!this._nativeBackbone?.graph
|
|
18962
|
-
.
|
|
18963
|
-
|
|
18964
|
-
|
|
18965
|
-
|
|
18966
|
-
|
|
18967
|
-
|
|
18968
|
-
|
|
18969
|
-
|
|
18970
|
-
|
|
18971
|
-
|
|
18972
|
-
|
|
18973
|
-
|
|
18974
|
-
|
|
18975
|
-
|
|
18976
|
-
|
|
18977
|
-
|
|
19040
|
+
.commitPreparedRawReceiveJoinBatch);
|
|
19041
|
+
const trustedLowerLog = this.log as unknown as TrustedLowerLog<T>;
|
|
19042
|
+
// With a program-level onChange consumer the hash-only
|
|
19043
|
+
// sink is not used: the lower-log join dispatches the
|
|
19044
|
+
// change event (lazy entry views over the prepared raw
|
|
19045
|
+
// facts) so per-entry consumers observe every commit.
|
|
19046
|
+
const joinOnAppendHashes = programOnChange
|
|
19047
|
+
? undefined
|
|
19048
|
+
: onAppendHashes;
|
|
19049
|
+
const joinedPreparedFacts =
|
|
19050
|
+
canUsePreparedAppendFacts &&
|
|
19051
|
+
(await trustedLowerLog.joinPreparedAppendFactsBatch(
|
|
19052
|
+
preparedAppendFacts,
|
|
19053
|
+
{
|
|
19054
|
+
__peerbitEntriesAlreadyMissing: true,
|
|
19055
|
+
__peerbitCanAppendAlreadyValidated: true,
|
|
19056
|
+
__peerbitDeferIndexWrite: true,
|
|
19057
|
+
__peerbitOnAppendHashes: joinOnAppendHashes,
|
|
19058
|
+
__peerbitProfile: syncProfile,
|
|
19059
|
+
__peerbitNativePreparedJoinCommit: nativePreparedJoinCommit,
|
|
19060
|
+
__peerbitNativePreparedJoinCommitValidatesPlan:
|
|
19061
|
+
nativePreparedJoinCommitValidatesPlan,
|
|
19062
|
+
__peerbitOnPreparedJoinCommitted: nativePreparedJoinCommit
|
|
19063
|
+
? finishNativePreparedCoordinates
|
|
19064
|
+
: undefined,
|
|
19065
|
+
},
|
|
19066
|
+
));
|
|
19067
|
+
if (!joinedPreparedFacts) {
|
|
19068
|
+
await trustedLowerLog.join(materializeAllToMergeEntries(), {
|
|
19069
|
+
__peerbitBatchIndependent: true,
|
|
18978
19070
|
__peerbitEntriesAlreadyMissing: true,
|
|
18979
|
-
__peerbitCanAppendAlreadyValidated:
|
|
19071
|
+
__peerbitCanAppendAlreadyValidated:
|
|
19072
|
+
fallbackCanAppendAlreadyValidated,
|
|
18980
19073
|
__peerbitDeferIndexWrite: true,
|
|
18981
19074
|
__peerbitOnAppendHashes: joinOnAppendHashes,
|
|
18982
19075
|
__peerbitProfile: syncProfile,
|
|
18983
|
-
|
|
18984
|
-
|
|
18985
|
-
|
|
18986
|
-
|
|
18987
|
-
|
|
18988
|
-
|
|
18989
|
-
|
|
18990
|
-
|
|
18991
|
-
|
|
18992
|
-
|
|
18993
|
-
|
|
18994
|
-
|
|
18995
|
-
|
|
18996
|
-
|
|
18997
|
-
|
|
18998
|
-
|
|
18999
|
-
|
|
19000
|
-
|
|
19001
|
-
|
|
19002
|
-
|
|
19003
|
-
|
|
19004
|
-
|
|
19005
|
-
|
|
19006
|
-
|
|
19007
|
-
|
|
19008
|
-
const admittedHashes = joinedPreparedFacts
|
|
19009
|
-
? new Set(allToMergeHashes)
|
|
19010
|
-
: await this.log.hasMany(allToMergeHashes);
|
|
19011
|
-
admittedMergeHashes = admittedHashes;
|
|
19012
|
-
const admittedShallowEntries =
|
|
19013
|
-
admittedHashes.size === allToMergeShallowEntries.length
|
|
19014
|
-
? allToMergeShallowEntries
|
|
19015
|
-
: allToMergeShallowEntries.filter((entry) =>
|
|
19076
|
+
});
|
|
19077
|
+
}
|
|
19078
|
+
// A recursive lower-log join can resolve successfully while declining
|
|
19079
|
+
// an individual top-level entry (for example, when one of its parents
|
|
19080
|
+
// is temporarily unavailable). The public Log.join() API intentionally
|
|
19081
|
+
// does not expose that per-entry result, so make local index presence the
|
|
19082
|
+
// authority before publishing any SharedLog-side effects. A successful
|
|
19083
|
+
// prepared-facts batch is atomic and already proves every input hash.
|
|
19084
|
+
const admittedHashes = joinedPreparedFacts
|
|
19085
|
+
? new Set(allToMergeHashes)
|
|
19086
|
+
: await this.log.hasMany(allToMergeHashes);
|
|
19087
|
+
admittedMergeHashes = admittedHashes;
|
|
19088
|
+
const admittedShallowEntries =
|
|
19089
|
+
admittedHashes.size === allToMergeShallowEntries.length
|
|
19090
|
+
? allToMergeShallowEntries
|
|
19091
|
+
: allToMergeShallowEntries.filter((entry) =>
|
|
19092
|
+
admittedHashes.has(entry.hash),
|
|
19093
|
+
);
|
|
19094
|
+
if (!joinedPreparedFacts) {
|
|
19095
|
+
reusableCoordinatePersistItems =
|
|
19096
|
+
reusableCoordinatePersistItems.filter((item) =>
|
|
19097
|
+
admittedHashes.has(item.entry.hash),
|
|
19098
|
+
);
|
|
19099
|
+
coordinatePersistFallbackEntries =
|
|
19100
|
+
coordinatePersistFallbackEntries.filter((entry) =>
|
|
19016
19101
|
admittedHashes.has(entry.hash),
|
|
19017
19102
|
);
|
|
19018
|
-
|
|
19019
|
-
|
|
19020
|
-
reusableCoordinatePersistItems.
|
|
19021
|
-
|
|
19022
|
-
|
|
19023
|
-
|
|
19024
|
-
|
|
19025
|
-
|
|
19026
|
-
|
|
19027
|
-
|
|
19028
|
-
|
|
19029
|
-
|
|
19030
|
-
|
|
19031
|
-
|
|
19032
|
-
|
|
19033
|
-
|
|
19034
|
-
|
|
19035
|
-
|
|
19036
|
-
|
|
19037
|
-
|
|
19038
|
-
|
|
19039
|
-
|
|
19040
|
-
|
|
19041
|
-
|
|
19042
|
-
|
|
19043
|
-
|
|
19044
|
-
|
|
19045
|
-
|
|
19046
|
-
|
|
19047
|
-
|
|
19048
|
-
|
|
19049
|
-
|
|
19050
|
-
} else if (nativePreparedCoordinateBatch) {
|
|
19051
|
-
try {
|
|
19052
|
-
nativeBackboneOnlyPersistedHashes =
|
|
19053
|
-
await this._coordinates.finishBackboneOnlyReceiveCoordinateBatch(
|
|
19103
|
+
}
|
|
19104
|
+
const reusableCoordinatePersistItemCount =
|
|
19105
|
+
reusableCoordinatePersistItems.length;
|
|
19106
|
+
if (syncProfile) {
|
|
19107
|
+
emitSyncProfileDuration(syncProfile, lowerLogJoinStartedAt, {
|
|
19108
|
+
name: "sharedLog.receive.lowerLogJoin",
|
|
19109
|
+
component: "shared-log",
|
|
19110
|
+
entries: allToMerge.length,
|
|
19111
|
+
messages: 1,
|
|
19112
|
+
details: {
|
|
19113
|
+
hashOnlyEntryAdded,
|
|
19114
|
+
batchHashOnlyEntryAdded,
|
|
19115
|
+
programOnChange,
|
|
19116
|
+
joinedPreparedFacts,
|
|
19117
|
+
admittedEntries: admittedHashes.size,
|
|
19118
|
+
nativePreparedCoordinatesFinished,
|
|
19119
|
+
},
|
|
19120
|
+
});
|
|
19121
|
+
}
|
|
19122
|
+
const coordinatePersistStartedAt = syncProfileStart(syncProfile);
|
|
19123
|
+
if (nativePreparedCoordinatesFinished) {
|
|
19124
|
+
// The lower-log prepared receive transaction already finished
|
|
19125
|
+
// the native coordinate mirror/journal after entry-index commit.
|
|
19126
|
+
} else if (nativePreparedCoordinateBatch) {
|
|
19127
|
+
try {
|
|
19128
|
+
nativeBackboneOnlyPersistedHashes =
|
|
19129
|
+
await this._coordinates.finishBackboneOnlyReceiveCoordinateBatch(
|
|
19130
|
+
nativePreparedCoordinateBatch,
|
|
19131
|
+
syncProfile,
|
|
19132
|
+
);
|
|
19133
|
+
} catch (error) {
|
|
19134
|
+
this._coordinates.rollbackBackboneOnlyReceiveCoordinateBatch(
|
|
19054
19135
|
nativePreparedCoordinateBatch,
|
|
19055
|
-
syncProfile,
|
|
19056
19136
|
);
|
|
19057
|
-
|
|
19058
|
-
|
|
19059
|
-
|
|
19060
|
-
|
|
19061
|
-
|
|
19137
|
+
throw error;
|
|
19138
|
+
}
|
|
19139
|
+
} else {
|
|
19140
|
+
nativeBackboneOnlyPersistedHashes =
|
|
19141
|
+
await this._coordinates.persistBackboneOnlyReceiveCoordinateBatch(
|
|
19142
|
+
reusableCoordinatePersistItems,
|
|
19143
|
+
);
|
|
19062
19144
|
}
|
|
19063
|
-
|
|
19064
|
-
|
|
19065
|
-
|
|
19066
|
-
reusableCoordinatePersistItems,
|
|
19067
|
-
);
|
|
19068
|
-
}
|
|
19069
|
-
if (
|
|
19070
|
-
nativeBackboneOnlyPersistedHashes &&
|
|
19071
|
-
nativeBackboneOnlyPersistedHashes.size > 0
|
|
19072
|
-
) {
|
|
19073
|
-
for (
|
|
19074
|
-
let i = reusableCoordinatePersistItems.length - 1;
|
|
19075
|
-
i >= 0;
|
|
19076
|
-
i--
|
|
19145
|
+
if (
|
|
19146
|
+
nativeBackboneOnlyPersistedHashes &&
|
|
19147
|
+
nativeBackboneOnlyPersistedHashes.size > 0
|
|
19077
19148
|
) {
|
|
19078
|
-
|
|
19079
|
-
|
|
19080
|
-
|
|
19081
|
-
|
|
19149
|
+
for (
|
|
19150
|
+
let i = reusableCoordinatePersistItems.length - 1;
|
|
19151
|
+
i >= 0;
|
|
19152
|
+
i--
|
|
19082
19153
|
) {
|
|
19083
|
-
|
|
19154
|
+
if (
|
|
19155
|
+
nativeBackboneOnlyPersistedHashes.has(
|
|
19156
|
+
reusableCoordinatePersistItems[i]!.entry.hash,
|
|
19157
|
+
)
|
|
19158
|
+
) {
|
|
19159
|
+
reusableCoordinatePersistItems.splice(i, 1);
|
|
19160
|
+
}
|
|
19084
19161
|
}
|
|
19085
19162
|
}
|
|
19086
|
-
|
|
19087
|
-
|
|
19088
|
-
|
|
19089
|
-
|
|
19090
|
-
|
|
19091
|
-
|
|
19092
|
-
|
|
19093
|
-
|
|
19094
|
-
|
|
19095
|
-
|
|
19096
|
-
|
|
19097
|
-
|
|
19098
|
-
|
|
19099
|
-
|
|
19100
|
-
|
|
19101
|
-
|
|
19102
|
-
|
|
19103
|
-
|
|
19104
|
-
|
|
19105
|
-
|
|
19106
|
-
|
|
19107
|
-
|
|
19108
|
-
|
|
19109
|
-
|
|
19110
|
-
|
|
19111
|
-
|
|
19112
|
-
|
|
19113
|
-
}
|
|
19114
|
-
}
|
|
19115
|
-
|
|
19116
|
-
|
|
19117
|
-
|
|
19118
|
-
|
|
19119
|
-
|
|
19120
|
-
|
|
19121
|
-
|
|
19122
|
-
|
|
19123
|
-
|
|
19124
|
-
|
|
19125
|
-
|
|
19126
|
-
|
|
19127
|
-
|
|
19128
|
-
|
|
19163
|
+
if (reusableCoordinatePersistItems.length > 0) {
|
|
19164
|
+
await this._coordinates.persistCoordinatesBatch(
|
|
19165
|
+
reusableCoordinatePersistItems,
|
|
19166
|
+
);
|
|
19167
|
+
}
|
|
19168
|
+
if (coordinatePersistFallbackEntries.length > 0) {
|
|
19169
|
+
await this.planEntryLeaderBatch(
|
|
19170
|
+
coordinatePersistFallbackEntries.map((entry) => ({
|
|
19171
|
+
entry,
|
|
19172
|
+
replicas:
|
|
19173
|
+
receiveReplicaCounts.get(entry.hash) ??
|
|
19174
|
+
decodeReplicas(entry).getValue(this),
|
|
19175
|
+
options: { roleAge: 0, persist: {} },
|
|
19176
|
+
})),
|
|
19177
|
+
);
|
|
19178
|
+
}
|
|
19179
|
+
if (syncProfile) {
|
|
19180
|
+
emitSyncProfileDuration(syncProfile, coordinatePersistStartedAt, {
|
|
19181
|
+
name: "sharedLog.receive.coordinatePersist",
|
|
19182
|
+
component: "shared-log",
|
|
19183
|
+
entries: entriesToPersist.length,
|
|
19184
|
+
messages: 1,
|
|
19185
|
+
details: {
|
|
19186
|
+
reusedLeaderPlans: reusableCoordinatePersistItemCount,
|
|
19187
|
+
nativeBackboneOnly:
|
|
19188
|
+
nativeBackboneOnlyPersistedHashes?.size ?? 0,
|
|
19189
|
+
},
|
|
19190
|
+
});
|
|
19191
|
+
}
|
|
19192
|
+
for (const hash of admittedHashes) {
|
|
19193
|
+
confirmedHashes.add(hash);
|
|
19194
|
+
}
|
|
19195
|
+
const checkedPruneStartedAt = syncProfileStart(syncProfile);
|
|
19196
|
+
const ownershipChangedDuringReceive =
|
|
19197
|
+
!this.isReceiveOwnershipSnapshotStable(receiveOwnershipRevision);
|
|
19198
|
+
if (ownershipChangedDuringReceive) {
|
|
19199
|
+
const freshAuditRevision =
|
|
19200
|
+
this._instanceLifecycle?._receiveOwnershipRevision ?? 0;
|
|
19201
|
+
const armFreshAuditRetry = () => {
|
|
19202
|
+
for (const entry of admittedShallowEntries) {
|
|
19203
|
+
this.scheduleCheckedPruneRetry(
|
|
19204
|
+
{ entry, leaders: new Map() },
|
|
19205
|
+
receiveOwnershipLifecycleController,
|
|
19206
|
+
);
|
|
19207
|
+
}
|
|
19208
|
+
};
|
|
19209
|
+
try {
|
|
19210
|
+
await this.pruneJoinedEntriesNoLongerLed(
|
|
19211
|
+
admittedShallowEntries,
|
|
19212
|
+
{
|
|
19213
|
+
decodedReplicaCounts: receiveReplicaCounts,
|
|
19214
|
+
freshReceiveOwnerAudit: true,
|
|
19215
|
+
preserveExistingPruneOnLocalResult: true,
|
|
19216
|
+
profile: syncProfile,
|
|
19217
|
+
},
|
|
19129
19218
|
receiveOwnershipLifecycleController,
|
|
19130
19219
|
);
|
|
19220
|
+
this.throwIfReplicationOwnershipLifecycleInactive(
|
|
19221
|
+
receiveOwnershipLifecycleController,
|
|
19222
|
+
);
|
|
19223
|
+
if (
|
|
19224
|
+
!this.isReceiveOwnershipSnapshotStable(freshAuditRevision)
|
|
19225
|
+
) {
|
|
19226
|
+
armFreshAuditRetry();
|
|
19227
|
+
}
|
|
19228
|
+
} catch {
|
|
19229
|
+
// The lower-log and coordinate commits are already durable. A
|
|
19230
|
+
// sender retry will filter these hashes as present, so retain a
|
|
19231
|
+
// bounded local obligation instead of failing the admitted receive.
|
|
19232
|
+
this.throwIfReplicationOwnershipLifecycleInactive(
|
|
19233
|
+
receiveOwnershipLifecycleController,
|
|
19234
|
+
);
|
|
19235
|
+
armFreshAuditRetry();
|
|
19131
19236
|
}
|
|
19132
|
-
}
|
|
19133
|
-
try {
|
|
19237
|
+
} else {
|
|
19134
19238
|
await this.pruneJoinedEntriesNoLongerLed(
|
|
19135
19239
|
admittedShallowEntries,
|
|
19136
19240
|
{
|
|
19137
19241
|
decodedReplicaCounts: receiveReplicaCounts,
|
|
19138
|
-
freshReceiveOwnerAudit: true,
|
|
19139
19242
|
preserveExistingPruneOnLocalResult: true,
|
|
19243
|
+
reusableLeaderPlans: reusableCoordinatePlans,
|
|
19140
19244
|
profile: syncProfile,
|
|
19141
19245
|
},
|
|
19142
19246
|
receiveOwnershipLifecycleController,
|
|
19143
19247
|
);
|
|
19144
|
-
this.throwIfReplicationOwnershipLifecycleInactive(
|
|
19145
|
-
receiveOwnershipLifecycleController,
|
|
19146
|
-
);
|
|
19147
|
-
if (
|
|
19148
|
-
!this.isReceiveOwnershipSnapshotStable(freshAuditRevision)
|
|
19149
|
-
) {
|
|
19150
|
-
armFreshAuditRetry();
|
|
19151
|
-
}
|
|
19152
|
-
} catch {
|
|
19153
|
-
// The lower-log and coordinate commits are already durable. A
|
|
19154
|
-
// sender retry will filter these hashes as present, so retain a
|
|
19155
|
-
// bounded local obligation instead of failing the admitted receive.
|
|
19156
|
-
this.throwIfReplicationOwnershipLifecycleInactive(
|
|
19157
|
-
receiveOwnershipLifecycleController,
|
|
19158
|
-
);
|
|
19159
|
-
armFreshAuditRetry();
|
|
19160
19248
|
}
|
|
19161
|
-
|
|
19162
|
-
|
|
19163
|
-
|
|
19164
|
-
|
|
19165
|
-
|
|
19166
|
-
|
|
19167
|
-
|
|
19168
|
-
|
|
19169
|
-
},
|
|
19170
|
-
receiveOwnershipLifecycleController,
|
|
19171
|
-
);
|
|
19172
|
-
}
|
|
19173
|
-
if (syncProfile) {
|
|
19174
|
-
emitSyncProfileDuration(syncProfile, checkedPruneStartedAt, {
|
|
19175
|
-
name: "sharedLog.receive.checkedPrune",
|
|
19176
|
-
component: "shared-log",
|
|
19177
|
-
entries: allToMerge.length,
|
|
19178
|
-
messages: 1,
|
|
19179
|
-
});
|
|
19180
|
-
}
|
|
19249
|
+
if (syncProfile) {
|
|
19250
|
+
emitSyncProfileDuration(syncProfile, checkedPruneStartedAt, {
|
|
19251
|
+
name: "sharedLog.receive.checkedPrune",
|
|
19252
|
+
component: "shared-log",
|
|
19253
|
+
entries: allToMerge.length,
|
|
19254
|
+
messages: 1,
|
|
19255
|
+
});
|
|
19256
|
+
}
|
|
19181
19257
|
|
|
19182
|
-
|
|
19183
|
-
|
|
19184
|
-
|
|
19185
|
-
|
|
19186
|
-
|
|
19187
|
-
|
|
19188
|
-
|
|
19189
|
-
|
|
19190
|
-
|
|
19191
|
-
|
|
19192
|
-
|
|
19193
|
-
|
|
19258
|
+
for (const plan of joinPlans) {
|
|
19259
|
+
plan.toDelete
|
|
19260
|
+
?.filter((entry) => admittedMergeHashes.has(entry.hash))
|
|
19261
|
+
.map((entry) =>
|
|
19262
|
+
this.pruneDebouncedFnAddIfNotKeeping({
|
|
19263
|
+
key: entry.hash,
|
|
19264
|
+
value: {
|
|
19265
|
+
entry,
|
|
19266
|
+
leaders: plan.leaders as Map<string, any>,
|
|
19267
|
+
},
|
|
19268
|
+
}),
|
|
19269
|
+
);
|
|
19270
|
+
}
|
|
19271
|
+
this.rebalanceParticipationDebounced?.call();
|
|
19272
|
+
} finally {
|
|
19273
|
+
// Settle seam for the receive token. Every consumer that can
|
|
19274
|
+
// roll it back runs inline before control leaves this block: the
|
|
19275
|
+
// prepared-join callback resolves during the join await, and the
|
|
19276
|
+
// late finish/rollback arm runs above. This `finally` is what
|
|
19277
|
+
// closes the abandon arms (no prepared-join commit, a declined
|
|
19278
|
+
// native commit, a downgrade to the plain join) without having
|
|
19279
|
+
// to enumerate them.
|
|
19280
|
+
this._coordinates.settleResidentCoordinateSnapshot(
|
|
19281
|
+
nativeReceiveCoordinateBatch?.rollbackCoordinateEntries,
|
|
19282
|
+
);
|
|
19194
19283
|
}
|
|
19195
|
-
this.rebalanceParticipationDebounced?.call();
|
|
19196
19284
|
}
|
|
19197
19285
|
|
|
19198
19286
|
for (const plan of joinPlans) {
|