@absolutejs/sync 1.18.1 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/index.js +41 -2
- package/dist/engine/index.js.map +3 -3
- package/dist/engine/syncEngine.d.ts +75 -0
- package/dist/index.js +41 -2
- package/dist/index.js.map +3 -3
- package/dist/testing.js +41 -2
- package/dist/testing.js.map +3 -3
- package/package.json +1 -1
|
@@ -266,6 +266,36 @@ export type SyncEngine = {
|
|
|
266
266
|
* Added in 1.13.0.
|
|
267
267
|
*/
|
|
268
268
|
metrics: () => EngineMetrics;
|
|
269
|
+
/**
|
|
270
|
+
* Capture the engine's change log + version as a serializable
|
|
271
|
+
* {@link ChangeLogSnapshot} the host can persist (disk, S3, the cluster
|
|
272
|
+
* bus) and restore on the next boot via
|
|
273
|
+
* {@link SyncEngineOptions.initialChangeLog} or
|
|
274
|
+
* {@link SyncEngine.importChangeLog}. The receiving engine MUST share this
|
|
275
|
+
* engine's `instanceId` — otherwise the resume contract silently breaks.
|
|
276
|
+
*
|
|
277
|
+
* Cheap: the snapshot's `entries` is a shallow copy of the bounded log
|
|
278
|
+
* (capped by `changeLogSize` / `changeLogRetainMs`). Call on a timer or on
|
|
279
|
+
* graceful shutdown — both are fine; the snapshot is monotonic in commit
|
|
280
|
+
* order, so a partial roll-forward (apply entries newer than the snapshot
|
|
281
|
+
* from another source) is safe.
|
|
282
|
+
*
|
|
283
|
+
* Added in 1.19.0.
|
|
284
|
+
*/
|
|
285
|
+
exportChangeLog: () => ChangeLogSnapshot;
|
|
286
|
+
/**
|
|
287
|
+
* Adopt a {@link ChangeLogSnapshot} into a running engine that has not yet
|
|
288
|
+
* committed any local changes (its `version` is 0). The snapshot's
|
|
289
|
+
* `instanceId` MUST match this engine's `instanceId`. Throws otherwise.
|
|
290
|
+
*
|
|
291
|
+
* Convenience for hosts that want to set up the engine, register surfaces,
|
|
292
|
+
* AND THEN restore. Equivalent to passing the snapshot via
|
|
293
|
+
* `createSyncEngine({ initialChangeLog })` if you have it at construction
|
|
294
|
+
* time. Returns the number of entries imported.
|
|
295
|
+
*
|
|
296
|
+
* Added in 1.19.0.
|
|
297
|
+
*/
|
|
298
|
+
importChangeLog: (snapshot: ChangeLogSnapshot) => number;
|
|
269
299
|
/**
|
|
270
300
|
* Subscribe to the live engine activity stream (changes, mutation outcomes,
|
|
271
301
|
* subscribe/unsubscribe). Returns an unsubscribe. Powers the devtools feed.
|
|
@@ -384,6 +414,35 @@ export declare class CdcConsumerSlowError extends Error {
|
|
|
384
414
|
readonly lastDeliveredVersion: number;
|
|
385
415
|
constructor(maxBuffer: number, lastDeliveredVersion: number);
|
|
386
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* Serializable snapshot of an engine's change log + monotonic version, returned
|
|
419
|
+
* by {@link SyncEngine.exportChangeLog} and consumed by
|
|
420
|
+
* {@link SyncEngineOptions.initialChangeLog} or
|
|
421
|
+
* {@link SyncEngine.importChangeLog}.
|
|
422
|
+
*
|
|
423
|
+
* The PaaS host persists this on shard rotation (every N seconds or on graceful
|
|
424
|
+
* shutdown) and hands it back to the replacement engine so resume cursors
|
|
425
|
+
* referencing this `instanceId` keep working past the restart. Bounded by the
|
|
426
|
+
* receiving engine's `changeLogSize` + `changeLogRetainMs` policies — entries
|
|
427
|
+
* that exceed either cap on import are trimmed exactly as if they had been
|
|
428
|
+
* logged live.
|
|
429
|
+
*
|
|
430
|
+
* Added in 1.19.0.
|
|
431
|
+
*/
|
|
432
|
+
export type ChangeLogSnapshot = {
|
|
433
|
+
/** The exporting engine's `instanceId`. Receiver MUST match. */
|
|
434
|
+
instanceId: string;
|
|
435
|
+
/** The exporting engine's monotonic version at snapshot time. */
|
|
436
|
+
version: number;
|
|
437
|
+
/** Every retained log entry, in commit order (oldest first). */
|
|
438
|
+
entries: ReadonlyArray<LoggedChange>;
|
|
439
|
+
/**
|
|
440
|
+
* Optional version-stamp the host may use to compare snapshots without
|
|
441
|
+
* deserializing the entries (e.g. for incremental persistence). Set to
|
|
442
|
+
* `Date.now()` at export time. Receivers ignore this field.
|
|
443
|
+
*/
|
|
444
|
+
exportedAt?: number;
|
|
445
|
+
};
|
|
387
446
|
export type SyncEngineOptions = {
|
|
388
447
|
/**
|
|
389
448
|
* Stable identifier for this engine instance. Defaults to a per-process
|
|
@@ -482,6 +541,22 @@ export type SyncEngineOptions = {
|
|
|
482
541
|
* @see {@link BridgeFetchConfig}
|
|
483
542
|
*/
|
|
484
543
|
bridgeFetch?: BridgeFetchConfig;
|
|
544
|
+
/**
|
|
545
|
+
* Seed the engine's change log on boot from a prior snapshot — produced by
|
|
546
|
+
* {@link SyncEngine.exportChangeLog} on the previous instance, persisted by
|
|
547
|
+
* the host across a shard reboot, then handed back here. Cursors that
|
|
548
|
+
* referenced this engine's `instanceId` stay resumable past the restart
|
|
549
|
+
* (provided their last-seen point still lives in the retained log).
|
|
550
|
+
*
|
|
551
|
+
* The snapshot's `instanceId` MUST match `options.instanceId` (otherwise
|
|
552
|
+
* `createSyncEngine` throws — a wrong-id restore would silently break the
|
|
553
|
+
* resume contract). Snapshot `version` becomes this engine's local
|
|
554
|
+
* monotonic version; entries are inserted in version order. Subscribers,
|
|
555
|
+
* permissions, schemas, schedules, packs, mutations, and the reactive
|
|
556
|
+
* cache are NOT in the snapshot — re-register them as normal after
|
|
557
|
+
* `createSyncEngine` returns. Added in 1.19.0.
|
|
558
|
+
*/
|
|
559
|
+
initialChangeLog?: ChangeLogSnapshot;
|
|
485
560
|
};
|
|
486
561
|
/**
|
|
487
562
|
* The Tier 3 sync engine: a registry of collections plus the view syncer. It is
|
package/dist/index.js
CHANGED
|
@@ -1309,6 +1309,31 @@ var createSyncEngine = (options = {}) => {
|
|
|
1309
1309
|
const runInTransaction = options.transaction;
|
|
1310
1310
|
const instanceId = options.instanceId ?? globalThis.crypto?.randomUUID?.() ?? `i${Math.random()}`;
|
|
1311
1311
|
let clusterBus;
|
|
1312
|
+
const importChangeLog = (snapshot) => {
|
|
1313
|
+
if (version !== 0) {
|
|
1314
|
+
throw new Error(`[sync] importChangeLog: engine already has version ${version}; ` + `restore must happen before any local writes commit.`);
|
|
1315
|
+
}
|
|
1316
|
+
if (snapshot.instanceId !== instanceId) {
|
|
1317
|
+
throw new Error(`[sync] importChangeLog: snapshot instanceId "${snapshot.instanceId}" ` + `does not match this engine's instanceId "${instanceId}". ` + `Pass options.instanceId = "${snapshot.instanceId}" to createSyncEngine.`);
|
|
1318
|
+
}
|
|
1319
|
+
version = snapshot.version;
|
|
1320
|
+
for (const entry of snapshot.entries) {
|
|
1321
|
+
changeLog.push(entry);
|
|
1322
|
+
}
|
|
1323
|
+
while (changeLog.length > changeLogSize) {
|
|
1324
|
+
changeLog.shift();
|
|
1325
|
+
}
|
|
1326
|
+
if (changeLogRetainMs !== null && changeLogRetainMs > 0) {
|
|
1327
|
+
const cutoff = Date.now() - changeLogRetainMs;
|
|
1328
|
+
while (changeLog.length > 0 && changeLog[0].at < cutoff) {
|
|
1329
|
+
changeLog.shift();
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
return snapshot.entries.length;
|
|
1333
|
+
};
|
|
1334
|
+
if (options.initialChangeLog !== undefined) {
|
|
1335
|
+
importChangeLog(options.initialChangeLog);
|
|
1336
|
+
}
|
|
1312
1337
|
const broadcast = (changes, originVersion) => {
|
|
1313
1338
|
if (clusterBus !== undefined && changes.length > 0) {
|
|
1314
1339
|
clusterBus.publish({ changes, origin: instanceId, originVersion });
|
|
@@ -1832,13 +1857,20 @@ var createSyncEngine = (options = {}) => {
|
|
|
1832
1857
|
oldestPerOrigin.set(entry.origin, entry.originVersion);
|
|
1833
1858
|
}
|
|
1834
1859
|
}
|
|
1860
|
+
const oldestLogVersion = changeLog[0]?.version;
|
|
1835
1861
|
for (const [origin, lastSeen] of Object.entries(sinceVec)) {
|
|
1836
1862
|
if (origin === instanceId) {
|
|
1837
1863
|
if (lastSeen >= version)
|
|
1838
1864
|
continue;
|
|
1839
1865
|
const oldestLocal = oldestPerOrigin.get(instanceId);
|
|
1840
|
-
if (oldestLocal
|
|
1866
|
+
if (oldestLocal !== undefined) {
|
|
1867
|
+
if (oldestLocal > lastSeen + 1)
|
|
1868
|
+
return false;
|
|
1869
|
+
continue;
|
|
1870
|
+
}
|
|
1871
|
+
if (oldestLogVersion !== undefined && oldestLogVersion > lastSeen + 1) {
|
|
1841
1872
|
return false;
|
|
1873
|
+
}
|
|
1842
1874
|
} else {
|
|
1843
1875
|
const oldestPeer = oldestPerOrigin.get(origin);
|
|
1844
1876
|
if (oldestPeer === undefined) {
|
|
@@ -2571,6 +2603,13 @@ var createSyncEngine = (options = {}) => {
|
|
|
2571
2603
|
}))
|
|
2572
2604
|
};
|
|
2573
2605
|
},
|
|
2606
|
+
exportChangeLog: () => ({
|
|
2607
|
+
entries: changeLog.slice(),
|
|
2608
|
+
exportedAt: Date.now(),
|
|
2609
|
+
instanceId,
|
|
2610
|
+
version
|
|
2611
|
+
}),
|
|
2612
|
+
importChangeLog,
|
|
2574
2613
|
metrics: () => {
|
|
2575
2614
|
const now = Date.now();
|
|
2576
2615
|
const byCollection = {};
|
|
@@ -2986,5 +3025,5 @@ export {
|
|
|
2986
3025
|
createPresenceHub
|
|
2987
3026
|
};
|
|
2988
3027
|
|
|
2989
|
-
//# debugId=
|
|
3028
|
+
//# debugId=EB8C381967D0311A64756E2164756E21
|
|
2990
3029
|
//# sourceMappingURL=index.js.map
|