@peerbit/shared-log 13.2.24 → 13.2.26

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.
Files changed (81) hide show
  1. package/dist/src/checked-prune.d.ts +12 -0
  2. package/dist/src/checked-prune.d.ts.map +1 -1
  3. package/dist/src/checked-prune.js +33 -0
  4. package/dist/src/checked-prune.js.map +1 -1
  5. package/dist/src/coordinate-persistence.d.ts +211 -0
  6. package/dist/src/coordinate-persistence.d.ts.map +1 -0
  7. package/dist/src/coordinate-persistence.js +1133 -0
  8. package/dist/src/coordinate-persistence.js.map +1 -0
  9. package/dist/src/errors.d.ts +1 -0
  10. package/dist/src/errors.d.ts.map +1 -1
  11. package/dist/src/errors.js +19 -0
  12. package/dist/src/errors.js.map +1 -1
  13. package/dist/src/index.d.ts +127 -133
  14. package/dist/src/index.d.ts.map +1 -1
  15. package/dist/src/index.js +1400 -4269
  16. package/dist/src/index.js.map +1 -1
  17. package/dist/src/instance-lifecycle.d.ts +117 -0
  18. package/dist/src/instance-lifecycle.d.ts.map +1 -0
  19. package/dist/src/instance-lifecycle.js +254 -0
  20. package/dist/src/instance-lifecycle.js.map +1 -0
  21. package/dist/src/join-warmup.d.ts +86 -0
  22. package/dist/src/join-warmup.d.ts.map +1 -0
  23. package/dist/src/join-warmup.js +351 -0
  24. package/dist/src/join-warmup.js.map +1 -0
  25. package/dist/src/native-write-through-block-store.d.ts +123 -0
  26. package/dist/src/native-write-through-block-store.d.ts.map +1 -0
  27. package/dist/src/native-write-through-block-store.js +989 -0
  28. package/dist/src/native-write-through-block-store.js.map +1 -0
  29. package/dist/src/peer-session.d.ts +117 -0
  30. package/dist/src/peer-session.d.ts.map +1 -0
  31. package/dist/src/peer-session.js +259 -0
  32. package/dist/src/peer-session.js.map +1 -0
  33. package/dist/src/replication-announcement.d.ts +116 -0
  34. package/dist/src/replication-announcement.d.ts.map +1 -0
  35. package/dist/src/replication-announcement.js +515 -0
  36. package/dist/src/replication-announcement.js.map +1 -0
  37. package/dist/src/replicator-liveness.d.ts +59 -0
  38. package/dist/src/replicator-liveness.d.ts.map +1 -0
  39. package/dist/src/replicator-liveness.js +304 -0
  40. package/dist/src/replicator-liveness.js.map +1 -0
  41. package/dist/src/sync/dispatch-lifecycle.d.ts +48 -0
  42. package/dist/src/sync/dispatch-lifecycle.d.ts.map +1 -0
  43. package/dist/src/sync/dispatch-lifecycle.js +141 -0
  44. package/dist/src/sync/dispatch-lifecycle.js.map +1 -0
  45. package/dist/src/sync/factory.d.ts +29 -0
  46. package/dist/src/sync/factory.d.ts.map +1 -0
  47. package/dist/src/sync/factory.js +87 -0
  48. package/dist/src/sync/factory.js.map +1 -0
  49. package/dist/src/sync/pending-sync-store.d.ts +105 -0
  50. package/dist/src/sync/pending-sync-store.d.ts.map +1 -0
  51. package/dist/src/sync/pending-sync-store.js +877 -0
  52. package/dist/src/sync/pending-sync-store.js.map +1 -0
  53. package/dist/src/sync/rateless-iblt.d.ts +4 -1
  54. package/dist/src/sync/rateless-iblt.d.ts.map +1 -1
  55. package/dist/src/sync/rateless-iblt.js +102 -106
  56. package/dist/src/sync/rateless-iblt.js.map +1 -1
  57. package/dist/src/sync/simple.d.ts +10 -41
  58. package/dist/src/sync/simple.d.ts.map +1 -1
  59. package/dist/src/sync/simple.js +259 -952
  60. package/dist/src/sync/simple.js.map +1 -1
  61. package/dist/src/sync/sync-peer-state.d.ts +16 -0
  62. package/dist/src/sync/sync-peer-state.d.ts.map +1 -0
  63. package/dist/src/sync/sync-peer-state.js +82 -0
  64. package/dist/src/sync/sync-peer-state.js.map +1 -0
  65. package/package.json +12 -12
  66. package/src/checked-prune.ts +35 -0
  67. package/src/coordinate-persistence.ts +1796 -0
  68. package/src/errors.ts +21 -0
  69. package/src/index.ts +4014 -7858
  70. package/src/instance-lifecycle.ts +351 -0
  71. package/src/join-warmup.ts +507 -0
  72. package/src/native-write-through-block-store.ts +1170 -0
  73. package/src/peer-session.ts +336 -0
  74. package/src/replication-announcement.ts +744 -0
  75. package/src/replicator-liveness.ts +439 -0
  76. package/src/sync/dispatch-lifecycle.ts +230 -0
  77. package/src/sync/factory.ts +154 -0
  78. package/src/sync/pending-sync-store.ts +1085 -0
  79. package/src/sync/rateless-iblt.ts +122 -132
  80. package/src/sync/simple.ts +334 -1144
  81. package/src/sync/sync-peer-state.ts +107 -0
@@ -0,0 +1,1133 @@
1
+ import { Or, StringMatch, toId, } from "@peerbit/indexer-interface";
2
+ import { Entry, EntryType, } from "@peerbit/log";
3
+ import { getPreparedRawExchangeTimestamp } from "./exchange-heads.js";
4
+ import { isEntryReplicated, shouldAssigneToRangeBoundary as shouldAssignToRangeBoundary, } from "./ranges.js";
5
+ import { decodeReplicas } from "./replication.js";
6
+ import { emitSyncProfileDuration, emitSyncProfileEvent, syncProfileStart, } from "./sync/profile.js";
7
+ export const isPromiseLike = (value) => !!value && typeof value.then === "function";
8
+ export const mapMaybePromise = (value, fn) => (isPromiseLike(value) ? value.then(fn) : fn(value));
9
+ const EMPTY_HASHES = [];
10
+ export const normalizedHashValues = (hashes) => {
11
+ if (Array.isArray(hashes)) {
12
+ if (hashes.length === 0) {
13
+ return EMPTY_HASHES;
14
+ }
15
+ if (hashes.length === 1) {
16
+ return hashes[0] ? hashes : EMPTY_HASHES;
17
+ }
18
+ }
19
+ const values = [];
20
+ const seen = new Set();
21
+ for (const hash of hashes) {
22
+ if (!hash || seen.has(hash)) {
23
+ continue;
24
+ }
25
+ seen.add(hash);
26
+ values.push(hash);
27
+ }
28
+ return values;
29
+ };
30
+ export const combineCoordinateDeleteHashes = (nextHashes, deleteHashes) => {
31
+ if (!deleteHashes || deleteHashes.length === 0) {
32
+ return nextHashes;
33
+ }
34
+ if (nextHashes.length === 0) {
35
+ return deleteHashes;
36
+ }
37
+ const combined = [];
38
+ const seen = new Set();
39
+ for (const hash of nextHashes) {
40
+ if (!seen.has(hash)) {
41
+ seen.add(hash);
42
+ combined.push(hash);
43
+ }
44
+ }
45
+ for (const hash of deleteHashes) {
46
+ if (!seen.has(hash)) {
47
+ seen.add(hash);
48
+ combined.push(hash);
49
+ }
50
+ }
51
+ return combined;
52
+ };
53
+ /**
54
+ * Stage 4.5 (PR-1): the coordinate-persistence module.
55
+ *
56
+ * The four coordinate state fields moved here in the state-ownership commit;
57
+ * this commit moves the 39 persistence methods (34 main-cluster + 5 early
58
+ * helpers) with byte-identical bodies — the only edits are the mechanical
59
+ * glue `this._coordinates.<field>` -> `this.<field>` for the owned state and
60
+ * `this.<hostDep>` -> `this.deps.<hostDep>()` for host state (plus the single
61
+ * `_nativeDurableRecoveryReadyForReopen = true` write, which becomes
62
+ * `deps.setDurableRecoveryReadyForReopen(true)`). SharedLog callers now use
63
+ * the coordinator directly; only the three compatibility state accessors
64
+ * remain on the host.
65
+ *
66
+ * Reset discipline is unchanged from the legacy host fields: each open/close
67
+ * site resets exactly the subset of fields it always reset (the resident
68
+ * mirror survives openNativeBackbone, the persistence adapter does not, and
69
+ * the mutation-generation ratchet deliberately survives everything but a
70
+ * fresh instance).
71
+ */
72
+ export class CoordinatePersistenceCoordinator {
73
+ deps;
74
+ /**
75
+ * Resident mirror of the coordinate rows (hash -> entry or native
76
+ * fields). Established by the native hydration paths; `undefined` means
77
+ * the resident fast path is unavailable.
78
+ */
79
+ _residentEntryCoordinatesByHash;
80
+ /** The (optional) durable native-backbone coordinate journal adapter. */
81
+ _nativeBackboneCoordinatePersistence;
82
+ // Moved from SharedLog src/index.ts (same name — the sanctioned
83
+ // file-to-file ratchet move; see scripts/ci/check-fence-ratchet.mjs
84
+ // TARGETS). Per-hash mutation-generation ratchet baseline: rollback
85
+ // snapshots capture the generation current at snapshot time and later
86
+ // roll back only while that generation is still current, so a rollback
87
+ // superseded by a newer mutation is a strict no-op. The map deliberately
88
+ // survives open/close cycles of the same instance.
89
+ _nativeCoordinateMutationGenerations;
90
+ /**
91
+ * Wall-clock watermark of the last settled native coordinate journal
92
+ * flush; drives the `flushIntervalMs` on-append flush threshold.
93
+ */
94
+ _nativeBackboneCoordinateJournalLastFlushMs = 0;
95
+ constructor(deps) {
96
+ this.deps = deps;
97
+ }
98
+ deleteCoordinatesForHashes(hashes, ownershipLifecycleController) {
99
+ if (ownershipLifecycleController) {
100
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
101
+ }
102
+ const values = normalizedHashValues(hashes);
103
+ if (values.length === 0) {
104
+ return;
105
+ }
106
+ this.forgetCoordinateStateForHashValues(values);
107
+ const coordinateIndex = this.deps.entryCoordinatesIndex();
108
+ if (coordinateIndex.delIdsNoReturn) {
109
+ return mapMaybePromise(coordinateIndex.delIdsNoReturn(values), () => {
110
+ if (ownershipLifecycleController) {
111
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
112
+ }
113
+ });
114
+ }
115
+ if (coordinateIndex.delIds) {
116
+ return mapMaybePromise(coordinateIndex.delIds(values), () => {
117
+ if (ownershipLifecycleController) {
118
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
119
+ }
120
+ });
121
+ }
122
+ return mapMaybePromise(this.deps.entryCoordinatesIndex().del({
123
+ query: values.length === 1
124
+ ? { hash: values[0] }
125
+ : new Or(values.map((hash) => new StringMatch({ key: "hash", value: hash }))),
126
+ }), () => {
127
+ if (ownershipLifecycleController) {
128
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
129
+ }
130
+ });
131
+ }
132
+ forgetCoordinateStateForHashes(hashes) {
133
+ const values = normalizedHashValues(hashes);
134
+ if (values.length === 0) {
135
+ return;
136
+ }
137
+ this.forgetCoordinateStateForHashValues(values);
138
+ }
139
+ forgetCoordinateStateForHashValues(values) {
140
+ this.deps.nativeSharedLogState()?.deleteEntryCoordinatesBatch(values);
141
+ this.deps.nativeBackbone()?.deleteEntryCoordinatesBatch(values);
142
+ this.forgetResidentCoordinateStateForHashValues(values);
143
+ }
144
+ forgetResidentCoordinateStateForHashes(hashes) {
145
+ const values = normalizedHashValues(hashes);
146
+ if (values.length === 0) {
147
+ return;
148
+ }
149
+ this.forgetResidentCoordinateStateForHashValues(values);
150
+ }
151
+ forgetResidentCoordinateStateForHashValues(values) {
152
+ if (this._residentEntryCoordinatesByHash) {
153
+ for (const hash of values) {
154
+ this._residentEntryCoordinatesByHash.delete(hash);
155
+ }
156
+ }
157
+ }
158
+ async createCoordinates(entry, minReplicas) {
159
+ if (typeof entry !== "number" &&
160
+ typeof entry !== "bigint" &&
161
+ this.deps.canPlanNativeHashGid(entry)) {
162
+ const nativeCoordinates = (this.deps.nativeBackbone() ?? this.deps.nativeRangePlanner())?.getGidCoordinates(entry.meta.gid, minReplicas);
163
+ if (nativeCoordinates) {
164
+ return nativeCoordinates;
165
+ }
166
+ }
167
+ const cursor = typeof entry === "number" || typeof entry === "bigint"
168
+ ? entry
169
+ : await this.deps.domain().fromEntry(entry);
170
+ const nativeGrid = (this.deps.nativeBackbone() ?? this.deps.nativeRangePlanner())?.getGrid(cursor, minReplicas);
171
+ return (nativeGrid ?? this.deps.indexableDomain().numbers.getGrid(cursor, minReplicas));
172
+ }
173
+ async getCoordinates(entry) {
174
+ const nativeCoordinates = (this.deps.nativeBackbone() ?? this.deps.nativeSharedLogState())?.getEntryCoordinates(entry.hash);
175
+ if (nativeCoordinates) {
176
+ return nativeCoordinates;
177
+ }
178
+ const result = await this.deps.entryCoordinatesIndex()
179
+ .iterate({ query: { hash: entry.hash } })
180
+ .all();
181
+ return result[0].value.coordinates;
182
+ }
183
+ getNativeLogEntryMetadataBatch(hashes) {
184
+ const normalized = [...hashes];
185
+ if (normalized.length === 0) {
186
+ return [];
187
+ }
188
+ const backboneMetadata = this.deps.nativeBackbone()?.graph.entryMetadataHintsBatch(normalized) ??
189
+ this.deps.nativeBackbone()?.graph.entryMetadataBatch(normalized);
190
+ if (backboneMetadata?.every((entry) => entry != null)) {
191
+ return backboneMetadata;
192
+ }
193
+ const indexMetadata = this.deps.log().entryIndex.getNativeEntryMetadataHintsBatch(normalized) ??
194
+ this.deps.log().entryIndex.getNativeEntryMetadataBatch(normalized);
195
+ if (!backboneMetadata) {
196
+ return indexMetadata;
197
+ }
198
+ if (!indexMetadata) {
199
+ return backboneMetadata;
200
+ }
201
+ return backboneMetadata.map((entry, index) => entry ?? indexMetadata[index]);
202
+ }
203
+ createReusableReceiveCoordinatePlans(receiveGroups, options) {
204
+ const reusablePlans = new Map();
205
+ if (this.deps.timeUntilRoleMaturity() > 0 && !options?.allowRoleAgeZeroPlans) {
206
+ return reusablePlans;
207
+ }
208
+ for (const group of receiveGroups) {
209
+ const plan = group.leaderPlan;
210
+ if (!plan) {
211
+ continue;
212
+ }
213
+ const replicas = options?.decodedReplicaCounts?.get(group.latestEntry.hash) ??
214
+ decodeReplicas(group.latestEntry).getValue(this.deps.host());
215
+ if (replicas !== group.maxMaxReplicas) {
216
+ continue;
217
+ }
218
+ const prepared = this.createCoordinatePersistenceEntryFromLeaderPlan({
219
+ entry: group.latestEntry,
220
+ plan,
221
+ replicas,
222
+ });
223
+ if (!prepared) {
224
+ continue;
225
+ }
226
+ reusablePlans.set(group.latestEntry.hash, {
227
+ plan,
228
+ replicas,
229
+ prepared,
230
+ });
231
+ }
232
+ return reusablePlans;
233
+ }
234
+ createBackboneOnlyReceiveCoordinateBatch(items) {
235
+ if (!this.deps.nativeBackbone() ||
236
+ items.length === 0 ||
237
+ !this.canUseBackboneOnlyCoordinatePersistence()) {
238
+ return undefined;
239
+ }
240
+ const rows = items
241
+ .filter((item) => item.prepared)
242
+ .map((item) => {
243
+ const prepared = item.prepared;
244
+ const deleteHashes = this.deps.getEntryNext(item.entry);
245
+ return {
246
+ item,
247
+ prepared,
248
+ fields: prepared.fields,
249
+ deleteHashes,
250
+ };
251
+ });
252
+ if (rows.length === 0) {
253
+ return undefined;
254
+ }
255
+ return {
256
+ rows,
257
+ rollbackCoordinateEntries: this.snapshotResidentCoordinateEntries(rows.flatMap((row) => [row.item.entry.hash, ...row.deleteHashes])),
258
+ };
259
+ }
260
+ nativeBackboneReceiveCoordinateRowsToColumns(rows) {
261
+ const hashes = new Array(rows.length);
262
+ const gids = new Array(rows.length);
263
+ const hashNumberValues = new BigUint64Array(rows.length);
264
+ const coordinateCounts = new Uint32Array(rows.length);
265
+ const coordinateValues = new BigUint64Array(rows.reduce((sum, row) => sum + row.fields.coordinates.length, 0));
266
+ const nextHashBatches = new Array(rows.length);
267
+ const assignedToRangeBoundaries = new Uint8Array(rows.length);
268
+ const requestedReplicaValues = new Uint32Array(rows.length);
269
+ let coordinateOffset = 0;
270
+ for (let i = 0; i < rows.length; i++) {
271
+ const { item, prepared, fields, deleteHashes } = rows[i];
272
+ hashes[i] = item.entry.hash;
273
+ gids[i] = fields.gid;
274
+ hashNumberValues[i] =
275
+ typeof fields.hashNumber === "bigint"
276
+ ? fields.hashNumber
277
+ : BigInt(fields.hashNumberString ?? fields.hashNumber);
278
+ coordinateCounts[i] = fields.coordinates.length;
279
+ for (const coordinate of fields.coordinates) {
280
+ coordinateValues[coordinateOffset++] =
281
+ typeof coordinate === "bigint" ? coordinate : BigInt(coordinate);
282
+ }
283
+ nextHashBatches[i] = deleteHashes;
284
+ assignedToRangeBoundaries[i] =
285
+ prepared.assignedToRangeBoundary === true ? 1 : 0;
286
+ requestedReplicaValues[i] = item.replicas;
287
+ }
288
+ return {
289
+ hashes,
290
+ gids,
291
+ hashNumberValues,
292
+ coordinateCounts,
293
+ coordinateValues,
294
+ nextHashBatches,
295
+ assignedToRangeBoundaries,
296
+ requestedReplicaValues,
297
+ };
298
+ }
299
+ async finishBackboneOnlyReceiveCoordinateBatch(batch, profile) {
300
+ const mirrorStartedAt = syncProfileStart(profile);
301
+ const persistedHashes = new Set();
302
+ const coordinateToHashRows = [];
303
+ let deleteCount = 0;
304
+ for (const { item, prepared, fields, deleteHashes } of batch.rows) {
305
+ persistedHashes.add(item.entry.hash);
306
+ this._residentEntryCoordinatesByHash?.set(item.entry.hash, prepared.coordinateEntry ?? fields);
307
+ for (const deletedHash of deleteHashes) {
308
+ this._residentEntryCoordinatesByHash?.delete(deletedHash);
309
+ deleteCount++;
310
+ }
311
+ for (const coordinate of item.coordinates) {
312
+ coordinateToHashRows.push([coordinate, item.entry.hash]);
313
+ }
314
+ }
315
+ this.deps.coordinateToHash().addMany(coordinateToHashRows);
316
+ emitSyncProfileDuration(profile, mirrorStartedAt, {
317
+ name: "sharedLog.receive.coordinateResidentMirror",
318
+ component: "shared-log",
319
+ entries: batch.rows.length,
320
+ count: coordinateToHashRows.length,
321
+ messages: 1,
322
+ details: { deletes: deleteCount },
323
+ });
324
+ const flushStartedAt = syncProfileStart(profile);
325
+ const flushed = this.flushNativeBackboneCoordinateJournalOnAppend();
326
+ if (isPromiseLike(flushed)) {
327
+ await flushed;
328
+ }
329
+ emitSyncProfileDuration(profile, flushStartedAt, {
330
+ name: "sharedLog.receive.coordinateJournalFlush",
331
+ component: "shared-log",
332
+ entries: batch.rows.length,
333
+ messages: 1,
334
+ });
335
+ return persistedHashes;
336
+ }
337
+ rollbackBackboneOnlyReceiveCoordinateBatch(batch) {
338
+ for (const { item } of batch.rows) {
339
+ this.rollbackNativeBackboneCoordinateAppend(item.entry.hash, batch.rollbackCoordinateEntries);
340
+ }
341
+ }
342
+ async persistBackboneOnlyReceiveCoordinateBatch(items) {
343
+ const backbone = this.deps.nativeBackbone();
344
+ const batch = this.createBackboneOnlyReceiveCoordinateBatch(items);
345
+ if (!backbone || !batch) {
346
+ return undefined;
347
+ }
348
+ try {
349
+ backbone.commitEntryCoordinatesColumnsBatch(this.nativeBackboneReceiveCoordinateRowsToColumns(batch.rows));
350
+ return await this.finishBackboneOnlyReceiveCoordinateBatch(batch);
351
+ }
352
+ catch (error) {
353
+ this.rollbackBackboneOnlyReceiveCoordinateBatch(batch);
354
+ throw error;
355
+ }
356
+ }
357
+ emitNativeBackboneRawCommitProfile(profile, nativeProfile, entries, verifyCount) {
358
+ if (!profile || !nativeProfile) {
359
+ return;
360
+ }
361
+ const events = [
362
+ [
363
+ "sharedLog.receive.nativeRawCommit.pendingCheck",
364
+ nativeProfile.nativeBackboneRawReceivePendingCheckMs,
365
+ ],
366
+ [
367
+ "sharedLog.receive.nativeRawCommit.verify",
368
+ nativeProfile.nativeBackboneRawReceiveVerifyMs,
369
+ verifyCount,
370
+ ],
371
+ [
372
+ "sharedLog.receive.nativeRawCommit.verifyStatus",
373
+ nativeProfile.nativeBackboneRawReceiveVerifyStatusMs,
374
+ ],
375
+ [
376
+ "sharedLog.receive.nativeRawCommit.joinPlan",
377
+ nativeProfile.nativeBackboneRawReceiveJoinPlanMs,
378
+ ],
379
+ [
380
+ "sharedLog.receive.nativeRawCommit.removePending",
381
+ nativeProfile.nativeBackboneRawReceiveRemoveMs,
382
+ ],
383
+ [
384
+ "sharedLog.receive.nativeRawCommit.blockPut",
385
+ nativeProfile.nativeBackboneRawReceiveBlockPutMs,
386
+ ],
387
+ [
388
+ "sharedLog.receive.nativeRawCommit.graphPut",
389
+ nativeProfile.nativeBackboneRawReceiveGraphPutMs,
390
+ ],
391
+ [
392
+ "sharedLog.receive.nativeRawCommit.coordinateCommit",
393
+ nativeProfile.nativeBackboneRawReceiveCoordinateCommitMs,
394
+ ],
395
+ ];
396
+ for (const [name, durationMs, count] of events) {
397
+ if (durationMs > 0) {
398
+ emitSyncProfileEvent(profile, {
399
+ name,
400
+ component: "shared-log",
401
+ durationMs,
402
+ entries,
403
+ count,
404
+ messages: 1,
405
+ });
406
+ }
407
+ }
408
+ }
409
+ createNativeBackbonePreparedJoinCommit(coordinateBatch, onCoordinatesCommitted, verifyHashes, verifyAllHashes = false, profile, onPreparedEntriesCommitted) {
410
+ const backbone = this.deps.nativeBackbone();
411
+ if (!backbone ||
412
+ this.deps.remoteBlocks()?.localStore !== backbone.blocks ||
413
+ (verifyHashes &&
414
+ verifyHashes.length > 0 &&
415
+ !backbone.graph.commitVerifiedPreparedRawReceiveJoinBatch)) {
416
+ return undefined;
417
+ }
418
+ return ({ entries, hashes, headFlags, headFlagsBytes, trustedMissing, validatePlan, }) => {
419
+ this.deps.throwIfReplicationOwnershipPoisoned();
420
+ if (!trustedMissing || entries.length === 0) {
421
+ return false;
422
+ }
423
+ const coordinateColumns = coordinateBatch && coordinateBatch.rows.length > 0
424
+ ? this.nativeBackboneReceiveCoordinateRowsToColumns(coordinateBatch.rows)
425
+ : undefined;
426
+ if (validatePlan) {
427
+ const verifiedCommitStartedAt = syncProfileStart(profile);
428
+ const profileNativeBackbone = !!profile &&
429
+ !!backbone.resetAppendProfile &&
430
+ !!backbone.setAppendProfileEnabled &&
431
+ !!backbone.appendProfile;
432
+ if (profileNativeBackbone) {
433
+ backbone.resetAppendProfile();
434
+ backbone.setAppendProfileEnabled(true);
435
+ }
436
+ let committed;
437
+ try {
438
+ if (verifyHashes && verifyHashes.length > 0) {
439
+ if (verifyAllHashes) {
440
+ committed =
441
+ backbone.graph.commitVerifiedAllPreparedRawReceiveJoinBatch?.(hashes, headFlagsBytes, coordinateColumns);
442
+ }
443
+ committed ??=
444
+ backbone.graph.commitVerifiedPreparedRawReceiveJoinBatch?.(hashes, headFlagsBytes, verifyHashes, coordinateColumns);
445
+ }
446
+ else {
447
+ committed = backbone.graph.commitPreparedRawReceiveJoinBatch?.(hashes, headFlagsBytes, coordinateColumns);
448
+ }
449
+ }
450
+ finally {
451
+ if (profileNativeBackbone) {
452
+ backbone.setAppendProfileEnabled(false);
453
+ this.emitNativeBackboneRawCommitProfile(profile, backbone.appendProfile(), entries.length, verifyHashes?.length ?? 0);
454
+ }
455
+ }
456
+ if (verifyHashes && verifyHashes.length > 0 && profile) {
457
+ emitSyncProfileDuration(profile, verifiedCommitStartedAt, {
458
+ name: "sharedLog.receive.nativeVerifiedCommit",
459
+ component: "shared-log",
460
+ entries: entries.length,
461
+ count: verifyHashes.length,
462
+ messages: 1,
463
+ });
464
+ }
465
+ if (committed === true) {
466
+ onPreparedEntriesCommitted?.(hashes);
467
+ if (coordinateBatch) {
468
+ onCoordinatesCommitted?.(coordinateBatch);
469
+ }
470
+ return true;
471
+ }
472
+ if (committed === false) {
473
+ backbone.graph.clearPreparedRawReceiveEntries?.(hashes);
474
+ return false;
475
+ }
476
+ }
477
+ if (backbone.graph.commitPreparedRawReceiveBatch(hashes, headFlagsBytes, coordinateColumns)) {
478
+ onPreparedEntriesCommitted?.(hashes);
479
+ if (coordinateBatch) {
480
+ onCoordinatesCommitted?.(coordinateBatch);
481
+ }
482
+ return true;
483
+ }
484
+ const commitEntries = new Array(entries.length);
485
+ for (let i = 0; i < entries.length; i++) {
486
+ const entry = entries[i];
487
+ if (!entry.bytes ||
488
+ !entry.nativeEntry ||
489
+ entry.meta.type !== EntryType.APPEND) {
490
+ return false;
491
+ }
492
+ commitEntries[i] = {
493
+ ...entry.nativeEntry,
494
+ head: headFlags[i] ?? true,
495
+ bytes: entry.bytes,
496
+ };
497
+ }
498
+ if (coordinateBatch && coordinateBatch.rows.length > 0) {
499
+ backbone.graph.commitBlocksGraphAndCoordinatesBatch(commitEntries, coordinateColumns);
500
+ onCoordinatesCommitted?.(coordinateBatch);
501
+ }
502
+ else {
503
+ backbone.graph.commitBlocksAndGraphBatch(commitEntries);
504
+ }
505
+ onPreparedEntriesCommitted?.(hashes);
506
+ return true;
507
+ };
508
+ }
509
+ createCoordinatePersistenceEntryFromLeaderPlan(properties) {
510
+ const assignedToRangeBoundary = properties.plan.assignedToRangeBoundary ??
511
+ shouldAssignToRangeBoundary(properties.plan.leaders, properties.replicas);
512
+ const hashNumber = this.deps.getEntryHashNumber(properties.entry);
513
+ const metaBytes = properties.entry.getMetaBytes?.();
514
+ if (metaBytes) {
515
+ const rawTimestamp = properties.entry instanceof Entry
516
+ ? getPreparedRawExchangeTimestamp(properties.entry)
517
+ : undefined;
518
+ const wallTime = rawTimestamp?.wallTime ??
519
+ properties.entry.meta.clock.timestamp.wallTime;
520
+ return {
521
+ assignedToRangeBoundary,
522
+ fields: {
523
+ hash: properties.entry.hash,
524
+ hashNumber,
525
+ hashNumberString: hashNumber.toString(),
526
+ gid: this.deps.getEntryGid(properties.entry),
527
+ coordinates: properties.plan.coordinates,
528
+ coordinateStrings: properties.plan.coordinateStrings ??
529
+ properties.plan.coordinates.map((coordinate) => coordinate.toString()),
530
+ wallTime,
531
+ wallTimeString: wallTime.toString(),
532
+ assignedToRangeBoundary,
533
+ metaBytes,
534
+ },
535
+ };
536
+ }
537
+ return this.createCoordinatePersistenceEntry({
538
+ coordinates: properties.plan.coordinates,
539
+ entry: properties.entry,
540
+ leaders: properties.plan.leaders,
541
+ replicas: properties.replicas,
542
+ assignedToRangeBoundary,
543
+ hashNumber,
544
+ });
545
+ }
546
+ createCoordinatePersistenceEntry(properties) {
547
+ const assignedToRangeBoundary = properties.assignedToRangeBoundary ??
548
+ shouldAssignToRangeBoundary(properties.leaders, properties.replicas);
549
+ if (properties.prev &&
550
+ properties.prev.assignedToRangeBoundary === assignedToRangeBoundary) {
551
+ return false;
552
+ }
553
+ const metaBytes = properties.entry.getMetaBytes?.();
554
+ const coordinateEntry = new (this.deps.indexableDomain().constructorEntry)({
555
+ assignedToRangeBoundary,
556
+ coordinates: properties.coordinates,
557
+ meta: properties.entry.meta,
558
+ metaBytes,
559
+ hash: properties.entry.hash,
560
+ hashNumber: properties.hashNumber ?? this.deps.getEntryHashNumber(properties.entry),
561
+ });
562
+ return {
563
+ coordinateEntry,
564
+ assignedToRangeBoundary,
565
+ fields: {
566
+ hash: coordinateEntry.hash,
567
+ hashNumber: coordinateEntry.hashNumber,
568
+ gid: coordinateEntry.gid,
569
+ coordinates: coordinateEntry.coordinates,
570
+ wallTime: coordinateEntry.wallTime,
571
+ assignedToRangeBoundary: coordinateEntry.assignedToRangeBoundary,
572
+ metaBytes: coordinateEntry.getMetaBytes(),
573
+ },
574
+ };
575
+ }
576
+ createCoordinatePersistenceEntryFromNativePlan(properties) {
577
+ if (properties.plan.hash !== properties.entry.hash ||
578
+ properties.plan.gid !== this.deps.getEntryGid(properties.entry)) {
579
+ return false;
580
+ }
581
+ const assignedToRangeBoundary = properties.plan.assignedToRangeBoundary;
582
+ if (properties.prev &&
583
+ properties.prev.assignedToRangeBoundary === assignedToRangeBoundary) {
584
+ return false;
585
+ }
586
+ const coordinates = properties.plan.coordinates;
587
+ const hashNumber = properties.plan.hashNumber;
588
+ const metaBytes = properties.entry.getMetaBytes?.();
589
+ if (metaBytes) {
590
+ const rawTimestamp = properties.entry instanceof Entry
591
+ ? getPreparedRawExchangeTimestamp(properties.entry)
592
+ : undefined;
593
+ const wallTime = rawTimestamp?.wallTime ??
594
+ properties.entry.meta.clock.timestamp.wallTime;
595
+ return {
596
+ assignedToRangeBoundary,
597
+ fields: {
598
+ hash: properties.plan.hash,
599
+ hashNumber,
600
+ hashNumberString: properties.plan.hashNumberString,
601
+ gid: properties.plan.gid,
602
+ coordinates,
603
+ coordinateStrings: properties.plan.coordinateStrings,
604
+ wallTime,
605
+ wallTimeString: wallTime.toString(),
606
+ assignedToRangeBoundary,
607
+ metaBytes,
608
+ },
609
+ };
610
+ }
611
+ const entryMeta = properties.entry.meta;
612
+ const coordinateEntry = new (this.deps.indexableDomain().constructorEntry)({
613
+ assignedToRangeBoundary,
614
+ coordinates,
615
+ meta: entryMeta,
616
+ hash: properties.plan.hash,
617
+ hashNumber,
618
+ });
619
+ return {
620
+ coordinateEntry,
621
+ assignedToRangeBoundary,
622
+ fields: {
623
+ hash: properties.plan.hash,
624
+ hashNumber,
625
+ hashNumberString: properties.plan.hashNumberString,
626
+ gid: properties.plan.gid,
627
+ coordinates,
628
+ coordinateStrings: properties.plan.coordinateStrings,
629
+ wallTime: coordinateEntry.wallTime,
630
+ wallTimeString: coordinateEntry.wallTime.toString(),
631
+ assignedToRangeBoundary,
632
+ metaBytes: coordinateEntry.getMetaBytes(),
633
+ },
634
+ };
635
+ }
636
+ createCoordinateEntryFromNativeFields(fields) {
637
+ return new (this.deps.indexableDomain().constructorEntry)({
638
+ assignedToRangeBoundary: fields.assignedToRangeBoundary,
639
+ coordinates: fields.coordinates,
640
+ metaBytes: fields.metaBytes,
641
+ gid: fields.gid,
642
+ wallTime: fields.wallTime,
643
+ hash: fields.hash,
644
+ hashNumber: fields.hashNumber,
645
+ });
646
+ }
647
+ materializePreparedCoordinateEntry(prepared) {
648
+ return (prepared.coordinateEntry ??=
649
+ this.createCoordinateEntryFromNativeFields(prepared.fields));
650
+ }
651
+ materializeResidentCoordinateEntry(entry) {
652
+ return isEntryReplicated(entry)
653
+ ? entry
654
+ : this.createCoordinateEntryFromNativeFields(entry);
655
+ }
656
+ materializeRepairDispatchEntries(entries) {
657
+ const materialized = new Map();
658
+ for (const [hash, entry] of entries) {
659
+ materialized.set(hash, this.materializeResidentCoordinateEntry(entry));
660
+ }
661
+ return materialized;
662
+ }
663
+ snapshotResidentCoordinateEntries(hashes) {
664
+ const uniqueHashes = new Set([...hashes].filter(Boolean));
665
+ if (uniqueHashes.size === 0) {
666
+ return undefined;
667
+ }
668
+ const entries = new Map();
669
+ const generations = new Map();
670
+ const mutationGenerations = (this._nativeCoordinateMutationGenerations ??=
671
+ new Map());
672
+ for (const hash of uniqueHashes) {
673
+ const generation = (mutationGenerations.get(hash) ?? 0) + 1;
674
+ mutationGenerations.set(hash, generation);
675
+ generations.set(hash, generation);
676
+ const entry = this._residentEntryCoordinatesByHash?.get(hash);
677
+ if (entry) {
678
+ entries.set(hash, entry);
679
+ }
680
+ }
681
+ return { hashes: uniqueHashes, entries, generations };
682
+ }
683
+ rollbackNativeBackboneCoordinateAppend(appendHash, rollback) {
684
+ const backbone = this.deps.nativeBackbone();
685
+ if (!backbone) {
686
+ return;
687
+ }
688
+ const hashes = rollback?.hashes ?? new Set([appendHash]);
689
+ const mutationGenerations = (this._nativeCoordinateMutationGenerations ??=
690
+ new Map());
691
+ for (const hash of hashes) {
692
+ const expectedGeneration = rollback?.generations.get(hash);
693
+ if (expectedGeneration !== undefined &&
694
+ mutationGenerations.get(hash) !== expectedGeneration) {
695
+ continue;
696
+ }
697
+ backbone.deleteEntryCoordinates(hash);
698
+ this.deps.nativeSharedLogState()?.deleteEntryCoordinates(hash);
699
+ this._residentEntryCoordinatesByHash?.delete(hash);
700
+ const entry = rollback?.entries.get(hash);
701
+ if (!entry) {
702
+ continue;
703
+ }
704
+ const fields = isEntryReplicated(entry)
705
+ ? {
706
+ hash: entry.hash,
707
+ gid: entry.gid,
708
+ coordinates: entry.coordinates,
709
+ assignedToRangeBoundary: entry.assignedToRangeBoundary,
710
+ hashNumber: entry.hashNumber,
711
+ }
712
+ : entry;
713
+ const requestedReplicas = isEntryReplicated(entry)
714
+ ? decodeReplicas(entry).getValue(this.deps.host())
715
+ : fields.coordinates.length;
716
+ backbone.putEntryCoordinates(fields.hash, fields.gid, fields.coordinates, fields.assignedToRangeBoundary, requestedReplicas, fields.hashNumber);
717
+ this.deps.nativeSharedLogState()?.putEntryCoordinates(fields.hash, fields.gid, fields.coordinates, fields.assignedToRangeBoundary, requestedReplicas, fields.hashNumber);
718
+ this._residentEntryCoordinatesByHash?.set(hash, entry);
719
+ }
720
+ }
721
+ async rollbackNativeBackboneCoordinateAppendDurably(appendHash, rollback) {
722
+ this.rollbackNativeBackboneCoordinateAppend(appendHash, rollback);
723
+ const coordinateIndex = this.deps.entryCoordinatesIndex();
724
+ const hashes = rollback?.hashes ?? new Set([appendHash]);
725
+ const mutationGenerations = (this._nativeCoordinateMutationGenerations ??=
726
+ new Map());
727
+ for (const hash of hashes) {
728
+ const expectedGeneration = rollback?.generations.get(hash);
729
+ if (expectedGeneration !== undefined &&
730
+ mutationGenerations.get(hash) !== expectedGeneration) {
731
+ continue;
732
+ }
733
+ const previous = rollback?.entries.get(hash);
734
+ if (previous) {
735
+ await coordinateIndex.put(this.materializeResidentCoordinateEntry(previous));
736
+ }
737
+ else if (coordinateIndex.delIds) {
738
+ await coordinateIndex.delIds([hash]);
739
+ }
740
+ else if (coordinateIndex.delIdsNoReturn) {
741
+ await coordinateIndex.delIdsNoReturn([hash]);
742
+ }
743
+ else {
744
+ await coordinateIndex.del({ query: { hash } });
745
+ }
746
+ }
747
+ const flushed = this.flushNativeBackboneCoordinateJournal();
748
+ if (isPromiseLike(flushed)) {
749
+ await flushed;
750
+ }
751
+ }
752
+ persistPreparedCoordinate(properties, ownershipLifecycleController = this.deps.captureReplicationOwnershipLifecycle()) {
753
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
754
+ const { assignedToRangeBoundary, fields } = properties.prepared;
755
+ const deleteHashes = combineCoordinateDeleteHashes(properties.nextHashes, properties.deleteHashes);
756
+ const coordinateIndex = this.deps.entryCoordinatesIndex();
757
+ let deleteNextOptions;
758
+ let putResult;
759
+ if (coordinateIndex.putSharedLogCoordinateFieldsAndDeleteHashesNoReturn) {
760
+ putResult =
761
+ coordinateIndex.putSharedLogCoordinateFieldsAndDeleteHashesNoReturn(fields, deleteHashes);
762
+ }
763
+ else if (coordinateIndex.putSharedLogCoordinateFieldsAndDeleteHashes) {
764
+ putResult = coordinateIndex.putSharedLogCoordinateFieldsAndDeleteHashes(fields, deleteHashes);
765
+ }
766
+ else if (coordinateIndex.putSharedLogCoordinateFieldsAndDeleteIds) {
767
+ putResult = coordinateIndex.putSharedLogCoordinateFieldsAndDeleteIds(fields, deleteHashes, toId(fields.hash));
768
+ }
769
+ else if (coordinateIndex.putSharedLogCoordinateAndDeleteIds) {
770
+ const coordinateEntry = this.materializePreparedCoordinateEntry(properties.prepared);
771
+ putResult = coordinateIndex.putSharedLogCoordinateAndDeleteIds(coordinateEntry, fields, deleteHashes, toId(fields.hash));
772
+ }
773
+ else if (deleteHashes.length > 0 && coordinateIndex.putAndDeleteIds) {
774
+ const coordinateEntry = this.materializePreparedCoordinateEntry(properties.prepared);
775
+ putResult = coordinateIndex.putAndDeleteIds(coordinateEntry, deleteHashes);
776
+ }
777
+ else {
778
+ const coordinateEntry = this.materializePreparedCoordinateEntry(properties.prepared);
779
+ deleteNextOptions =
780
+ deleteHashes.length === 0
781
+ ? undefined
782
+ : deleteHashes.length === 1
783
+ ? { query: { hash: deleteHashes[0] } }
784
+ : {
785
+ query: new Or(deleteHashes.map((x) => new StringMatch({ key: "hash", value: x }))),
786
+ };
787
+ if (deleteNextOptions && coordinateIndex.putAndDelete) {
788
+ putResult = coordinateIndex.putAndDelete(coordinateEntry, deleteNextOptions);
789
+ }
790
+ else {
791
+ putResult = this.deps.entryCoordinatesIndex().put(coordinateEntry);
792
+ }
793
+ }
794
+ const finish = () => {
795
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
796
+ const nativeDeleteHashes = combineCoordinateDeleteHashes(properties.nextHashes, properties.deleteHashes);
797
+ if (properties.commitNative !== false) {
798
+ this.deps.nativeSharedLogState()?.commitEntryCoordinates(properties.hash, fields.gid, properties.coordinates, nativeDeleteHashes, assignedToRangeBoundary, properties.replicas, fields.hashNumber);
799
+ }
800
+ if (properties.commitNativeBackbone !== false) {
801
+ this.deps.nativeBackbone()?.commitEntryCoordinates(properties.hash, fields.gid, properties.coordinates, nativeDeleteHashes, assignedToRangeBoundary, properties.replicas, fields.hashNumber);
802
+ }
803
+ if (this._residentEntryCoordinatesByHash) {
804
+ this._residentEntryCoordinatesByHash.set(properties.hash, properties.prepared.coordinateEntry ?? fields);
805
+ for (const nextHash of nativeDeleteHashes) {
806
+ this._residentEntryCoordinatesByHash.delete(nextHash);
807
+ }
808
+ }
809
+ for (const coordinate of properties.coordinates) {
810
+ this.deps.coordinateToHash().add(coordinate, properties.hash);
811
+ }
812
+ if (deleteNextOptions && !coordinateIndex.putAndDelete) {
813
+ return mapMaybePromise(this.deps.entryCoordinatesIndex().del(deleteNextOptions), () => true);
814
+ }
815
+ return true;
816
+ };
817
+ return mapMaybePromise(putResult, finish);
818
+ }
819
+ persistPreparedCoordinateNativeTransaction(properties, ownershipLifecycleController = this.deps.captureReplicationOwnershipLifecycle()) {
820
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
821
+ const { fields } = properties.prepared;
822
+ const putNative = properties.coordinateIndex
823
+ .putSharedLogCoordinateFieldsEncodedAndDeleteHashesNoReturn ??
824
+ properties.coordinateIndex
825
+ .putSharedLogCoordinateFieldsAndDeleteHashesNoReturn;
826
+ if (!putNative) {
827
+ return false;
828
+ }
829
+ const putResult = putNative.call(properties.coordinateIndex, fields, combineCoordinateDeleteHashes(properties.nextHashes, properties.deleteHashes));
830
+ const finish = () => {
831
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
832
+ const nativeDeleteHashes = combineCoordinateDeleteHashes(properties.nextHashes, properties.deleteHashes);
833
+ if (properties.commitNative !== false) {
834
+ this.deps.nativeSharedLogState()?.commitEntryCoordinates(properties.hash, fields.gid, properties.coordinates, nativeDeleteHashes, properties.prepared.assignedToRangeBoundary, properties.coordinates.length, fields.hashNumber);
835
+ }
836
+ if (properties.commitNativeBackbone !== false) {
837
+ this.deps.nativeBackbone()?.commitEntryCoordinates(properties.hash, fields.gid, properties.coordinates, nativeDeleteHashes, properties.prepared.assignedToRangeBoundary, properties.coordinates.length, fields.hashNumber);
838
+ }
839
+ if (this._residentEntryCoordinatesByHash) {
840
+ this._residentEntryCoordinatesByHash.set(properties.hash, properties.prepared.coordinateEntry ?? fields);
841
+ for (const nextHash of nativeDeleteHashes) {
842
+ this._residentEntryCoordinatesByHash.delete(nextHash);
843
+ }
844
+ }
845
+ for (const coordinate of properties.coordinates) {
846
+ this.deps.coordinateToHash().add(coordinate, properties.hash);
847
+ }
848
+ return true;
849
+ };
850
+ return mapMaybePromise(putResult, finish);
851
+ }
852
+ persistBackboneCoordinateFieldsNativeTransaction(properties, ownershipLifecycleController = this.deps.captureReplicationOwnershipLifecycle()) {
853
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
854
+ const { fields } = properties;
855
+ const useBackboneOnlyCoordinatePersistence = this.canUseBackboneOnlyCoordinatePersistence();
856
+ const finish = () => {
857
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
858
+ this.deps.nativeSharedLogState()?.commitEntryCoordinates(properties.hash, fields.gid, properties.coordinates, properties.deleteHashes, fields.assignedToRangeBoundary, properties.coordinates.length, fields.hashNumber);
859
+ if (this._residentEntryCoordinatesByHash) {
860
+ this._residentEntryCoordinatesByHash.set(properties.hash, fields);
861
+ for (const deletedHash of properties.deleteHashes) {
862
+ this._residentEntryCoordinatesByHash.delete(deletedHash);
863
+ }
864
+ }
865
+ for (const coordinate of properties.coordinates) {
866
+ this.deps.coordinateToHash().add(coordinate, properties.hash);
867
+ }
868
+ if (this._nativeBackboneCoordinatePersistence) {
869
+ const flushed = this.flushNativeBackboneCoordinateJournalOnAppend();
870
+ if (isPromiseLike(flushed)) {
871
+ return mapMaybePromise(flushed, () => true);
872
+ }
873
+ }
874
+ return true;
875
+ };
876
+ if ((properties.skipGenericTransientCoordinateIndex &&
877
+ this.canUseRuntimeOnlyNativeBackboneCoordinates(properties.coordinateIndex)) ||
878
+ useBackboneOnlyCoordinatePersistence) {
879
+ return finish();
880
+ }
881
+ const putNative = properties.coordinateIndex
882
+ .putSharedLogCoordinateFieldsEncodedAndDeleteHashesNoReturn ??
883
+ properties.coordinateIndex
884
+ .putSharedLogCoordinateFieldsAndDeleteHashesNoReturn;
885
+ if (!putNative) {
886
+ return false;
887
+ }
888
+ const putResult = putNative.call(properties.coordinateIndex, fields, properties.deleteHashes);
889
+ return mapMaybePromise(putResult, finish);
890
+ }
891
+ flushNativeBackboneCoordinateJournal() {
892
+ const backbone = this.deps.nativeBackbone();
893
+ const persistence = this._nativeBackboneCoordinatePersistence;
894
+ if (!backbone || !persistence || this.deps.isDropStarted()) {
895
+ return undefined;
896
+ }
897
+ if (backbone.coordinatePendingJournalLength === 0 &&
898
+ backbone.documentPendingJournalLength === 0 &&
899
+ backbone.documentSignerPendingJournalLength === 0) {
900
+ return undefined;
901
+ }
902
+ return mapMaybePromise(persistence.flushJournal(backbone), () => {
903
+ this._nativeBackboneCoordinateJournalLastFlushMs = Date.now();
904
+ return undefined;
905
+ });
906
+ }
907
+ flushNativeBackboneCoordinateJournalOnAppend() {
908
+ const backbone = this.deps.nativeBackbone();
909
+ const persistence = this._nativeBackboneCoordinatePersistence;
910
+ if (!backbone || !persistence || this.deps.isDropStarted()) {
911
+ return undefined;
912
+ }
913
+ if (persistence.flushJournalOnAppend) {
914
+ const flushed = persistence.flushJournalOnAppend(backbone);
915
+ if (!isPromiseLike(flushed)) {
916
+ return undefined;
917
+ }
918
+ return mapMaybePromise(flushed, () => {
919
+ return undefined;
920
+ });
921
+ }
922
+ if (!this.shouldFlushNativeBackboneCoordinateJournalOnAppend()) {
923
+ return undefined;
924
+ }
925
+ return this.flushNativeBackboneCoordinateJournal();
926
+ }
927
+ shouldFlushNativeBackboneCoordinateJournalOnAppend() {
928
+ const persistence = this._nativeBackboneCoordinatePersistence;
929
+ if (!persistence || persistence.flushOnAppend !== false) {
930
+ return true;
931
+ }
932
+ const backbone = this.deps.nativeBackbone();
933
+ if (!backbone || backbone.coordinatePendingJournalLength === 0) {
934
+ return false;
935
+ }
936
+ if (persistence.flushMaxPendingBytes != null &&
937
+ backbone.coordinatePendingJournalByteLength >=
938
+ persistence.flushMaxPendingBytes) {
939
+ return true;
940
+ }
941
+ return (persistence.flushIntervalMs != null &&
942
+ Date.now() - this._nativeBackboneCoordinateJournalLastFlushMs >=
943
+ persistence.flushIntervalMs);
944
+ }
945
+ async closeNativeBackboneCoordinatePersistence() {
946
+ const persistence = this._nativeBackboneCoordinatePersistence;
947
+ if (!persistence) {
948
+ return;
949
+ }
950
+ if (this.deps.isDropStarted()) {
951
+ // `drop()` owns the durable namespace lifecycle. Never flush the live wasm
952
+ // journals or invoke an ordinary custom close after its tombstone/erase has
953
+ // started: a close implementation that rewrites cached state could resurrect
954
+ // files after a successful terminal drop.
955
+ return;
956
+ }
957
+ if (this.deps.getDurableCommitFailure() &&
958
+ !this.deps.isDurableRecoveryReadyForReopen()) {
959
+ // The failed native transaction was never published by the lower log.
960
+ // Its coordinate/document/signer records are still only in the wasm
961
+ // pending journals. Closing without flushing discards that generation;
962
+ // the next backbone hydrates the last acknowledged checkpoint.
963
+ await persistence.close?.();
964
+ this.deps.setDurableRecoveryReadyForReopen(true);
965
+ return;
966
+ }
967
+ await this.flushNativeBackboneCoordinateJournal();
968
+ await persistence.close?.();
969
+ }
970
+ canUseBackboneOnlyCoordinatePersistence() {
971
+ return (!!this._nativeBackboneCoordinatePersistence &&
972
+ this.deps.canUseNativeBackboneResidentCoordinateState());
973
+ }
974
+ canUseNativeBackboneResidentCoordinateState() {
975
+ return (!!this.deps.nativeBackbone() &&
976
+ !!this._residentEntryCoordinatesByHash &&
977
+ !this.deps.hasCustomFindLeaders());
978
+ }
979
+ canUseRuntimeOnlyNativeBackboneCoordinates(coordinateIndex) {
980
+ if (!this.deps.canUseNativeBackboneResidentCoordinateState() ||
981
+ Object.prototype.hasOwnProperty.call(coordinateIndex, "putSharedLogCoordinateFieldsEncodedAndDeleteHashesNoReturn") ||
982
+ Object.prototype.hasOwnProperty.call(coordinateIndex, "putSharedLogCoordinateFieldsAndDeleteHashesNoReturn")) {
983
+ return false;
984
+ }
985
+ const persisted = coordinateIndex.persisted?.();
986
+ return persisted === false;
987
+ }
988
+ async persistCoordinate(properties, ownershipLifecycleController = this.deps.captureReplicationOwnershipLifecycle()) {
989
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
990
+ const prepared = properties.prepared ?? this.createCoordinatePersistenceEntry(properties);
991
+ if (!prepared) {
992
+ return false;
993
+ }
994
+ return this.persistPreparedCoordinate({
995
+ prepared,
996
+ hash: properties.entry.hash,
997
+ nextHashes: properties.nextHashes ?? properties.entry.meta.next,
998
+ coordinates: properties.coordinates,
999
+ replicas: properties.replicas,
1000
+ commitNative: properties.commitNative,
1001
+ commitNativeBackbone: properties.commitNativeBackbone,
1002
+ deleteHashes: properties.deleteHashes,
1003
+ }, ownershipLifecycleController);
1004
+ }
1005
+ async persistCoordinatesBatch(items, ownershipLifecycleController = this.deps.captureReplicationOwnershipLifecycle()) {
1006
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
1007
+ if (items.length === 0) {
1008
+ return [];
1009
+ }
1010
+ const prepared = items.map((item) => ({
1011
+ item,
1012
+ prepared: item.prepared ?? this.createCoordinatePersistenceEntry(item),
1013
+ }));
1014
+ const changed = prepared.filter((entry) => entry.prepared !== false);
1015
+ if (changed.length === 0) {
1016
+ return items.map(() => false);
1017
+ }
1018
+ const coordinateIndex = this.deps.entryCoordinatesIndex();
1019
+ const canUseGenericPutBatch = typeof coordinateIndex.putBatch === "function" &&
1020
+ changed.every(({ item }) => item.entry.meta.next.length === 0);
1021
+ if (coordinateIndex.putSharedLogCoordinateFieldsAndDeleteHashesBatchNoReturn) {
1022
+ await coordinateIndex.putSharedLogCoordinateFieldsAndDeleteHashesBatchNoReturn(changed.map(({ item, prepared }) => ({
1023
+ fields: prepared.fields,
1024
+ deleteHashes: item.entry.meta.next,
1025
+ })));
1026
+ }
1027
+ else if (coordinateIndex.putSharedLogCoordinateFieldsAndDeleteHashesBatch) {
1028
+ await coordinateIndex.putSharedLogCoordinateFieldsAndDeleteHashesBatch(changed.map(({ item, prepared }) => ({
1029
+ fields: prepared.fields,
1030
+ deleteHashes: item.entry.meta.next,
1031
+ })));
1032
+ }
1033
+ else if (coordinateIndex.putSharedLogCoordinateFieldsAndDeleteIdsBatch) {
1034
+ await coordinateIndex.putSharedLogCoordinateFieldsAndDeleteIdsBatch(changed.map(({ item, prepared }) => ({
1035
+ fields: prepared.fields,
1036
+ deleteIds: item.entry.meta.next,
1037
+ id: toId(prepared.fields.hash),
1038
+ })));
1039
+ }
1040
+ else if (coordinateIndex.putSharedLogCoordinatesAndDeleteIdsBatch) {
1041
+ await coordinateIndex.putSharedLogCoordinatesAndDeleteIdsBatch(changed.map(({ item, prepared }) => ({
1042
+ value: this.materializePreparedCoordinateEntry(prepared),
1043
+ fields: prepared.fields,
1044
+ deleteIds: item.entry.meta.next,
1045
+ id: toId(prepared.fields.hash),
1046
+ })));
1047
+ }
1048
+ else if (canUseGenericPutBatch) {
1049
+ await coordinateIndex.putBatch(changed.map(({ prepared }) => this.materializePreparedCoordinateEntry(prepared)));
1050
+ }
1051
+ else {
1052
+ const results = [];
1053
+ for (const item of items) {
1054
+ results.push(await this.persistCoordinate(item, ownershipLifecycleController));
1055
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
1056
+ }
1057
+ return results;
1058
+ }
1059
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
1060
+ const nativeCoordinateCommits = changed.filter(({ item }) => item.commitNative !== false);
1061
+ const nativeSharedLogState = this.deps.nativeSharedLogState();
1062
+ if (nativeCoordinateCommits.length > 0 && nativeSharedLogState) {
1063
+ if (nativeSharedLogState.commitEntryCoordinatesBatch) {
1064
+ nativeSharedLogState.commitEntryCoordinatesBatch(nativeCoordinateCommits.map(({ item, prepared }) => ({
1065
+ hash: item.entry.hash,
1066
+ gid: prepared.fields.gid,
1067
+ coordinates: item.coordinates,
1068
+ nextHashes: item.entry.meta.next,
1069
+ assignedToRangeBoundary: prepared.assignedToRangeBoundary,
1070
+ requestedReplicas: item.replicas,
1071
+ hashNumber: prepared.fields.hashNumber,
1072
+ })));
1073
+ }
1074
+ else {
1075
+ for (const { item, prepared } of nativeCoordinateCommits) {
1076
+ nativeSharedLogState.commitEntryCoordinates(item.entry.hash, prepared.fields.gid, item.coordinates, item.entry.meta.next, prepared.assignedToRangeBoundary, item.replicas, prepared.fields.hashNumber);
1077
+ }
1078
+ }
1079
+ }
1080
+ const nativeBackboneCoordinateCommits = changed.filter(({ item }) => item.commitNativeBackbone !== false);
1081
+ const nativeBackboneForBatch = this.deps.nativeBackbone();
1082
+ if (nativeBackboneCoordinateCommits.length > 0 && nativeBackboneForBatch) {
1083
+ if (nativeBackboneForBatch.commitEntryCoordinatesBatch) {
1084
+ nativeBackboneForBatch.commitEntryCoordinatesBatch(nativeBackboneCoordinateCommits.map(({ item, prepared }) => ({
1085
+ hash: item.entry.hash,
1086
+ gid: prepared.fields.gid,
1087
+ coordinates: item.coordinates,
1088
+ nextHashes: item.entry.meta.next,
1089
+ assignedToRangeBoundary: prepared.assignedToRangeBoundary,
1090
+ requestedReplicas: item.replicas,
1091
+ hashNumber: prepared.fields.hashNumber,
1092
+ })));
1093
+ }
1094
+ else {
1095
+ for (const { item, prepared } of nativeBackboneCoordinateCommits) {
1096
+ nativeBackboneForBatch.commitEntryCoordinates(item.entry.hash, prepared.fields.gid, item.coordinates, item.entry.meta.next, prepared.assignedToRangeBoundary, item.replicas, prepared.fields.hashNumber);
1097
+ }
1098
+ }
1099
+ }
1100
+ for (const { item, prepared } of changed) {
1101
+ if (this._residentEntryCoordinatesByHash) {
1102
+ this._residentEntryCoordinatesByHash.set(item.entry.hash, prepared.coordinateEntry ?? prepared.fields);
1103
+ for (const nextHash of item.entry.meta.next) {
1104
+ this._residentEntryCoordinatesByHash.delete(nextHash);
1105
+ }
1106
+ }
1107
+ for (const coordinate of item.coordinates) {
1108
+ this.deps.coordinateToHash().add(coordinate, item.entry.hash);
1109
+ }
1110
+ }
1111
+ const changedHashes = new Set(changed.map(({ prepared }) => prepared.fields.hash));
1112
+ return items.map((item) => changedHashes.has(item.entry.hash));
1113
+ }
1114
+ async deleteCoordinates(properties, ownershipLifecycleController) {
1115
+ if (ownershipLifecycleController) {
1116
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
1117
+ }
1118
+ this.deps.nativeSharedLogState()?.deleteEntryCoordinates(properties.hash);
1119
+ this.deps.nativeBackbone()?.deleteEntryCoordinates(properties.hash);
1120
+ this._residentEntryCoordinatesByHash?.delete(properties.hash);
1121
+ const coordinateIndex = this.deps.entryCoordinatesIndex();
1122
+ if (coordinateIndex.delIds) {
1123
+ await coordinateIndex.delIds([properties.hash]);
1124
+ }
1125
+ else {
1126
+ await this.deps.entryCoordinatesIndex().del({ query: properties });
1127
+ }
1128
+ if (ownershipLifecycleController) {
1129
+ this.deps.throwIfReplicationOwnershipLifecycleInactive(ownershipLifecycleController);
1130
+ }
1131
+ }
1132
+ }
1133
+ //# sourceMappingURL=coordinate-persistence.js.map