@absolutejs/sync 1.18.2 → 1.20.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/devtools.d.ts +5 -0
- package/dist/engine/index.d.ts +2 -2
- package/dist/engine/index.js +83 -3
- package/dist/engine/index.js.map +3 -3
- package/dist/engine/syncEngine.d.ts +115 -0
- package/dist/index.js +82 -3
- package/dist/index.js.map +3 -3
- package/dist/testing.js +82 -3
- package/dist/testing.js.map +3 -3
- package/package.json +1 -1
|
@@ -96,6 +96,11 @@ export type EngineMetrics = {
|
|
|
96
96
|
retried: number;
|
|
97
97
|
/** Currently running, not yet committed or failed. */
|
|
98
98
|
inFlight: number;
|
|
99
|
+
/**
|
|
100
|
+
* Waiting in the `mutationConcurrency` queue. Always `0` when
|
|
101
|
+
* `mutationConcurrency` is unset (no semaphore). Added in 1.20.0.
|
|
102
|
+
*/
|
|
103
|
+
queued: number;
|
|
99
104
|
};
|
|
100
105
|
schedules: {
|
|
101
106
|
registered: number;
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -47,8 +47,8 @@ export type { MutationActions, MutationDefinition, MutationHandler, TableWriter,
|
|
|
47
47
|
export type { BridgeFetchConfig, BridgeFetchEndpoint, BridgeFetchResponse, HandlerMetricsHook, HandlerMetricsRecord, SandboxConfig } from './sandbox';
|
|
48
48
|
export { exponentialBackoff, isSerializationFailure, RetriesExhaustedError } from './retry';
|
|
49
49
|
export type { ExponentialBackoffOptions, RetryPolicy } from './retry';
|
|
50
|
-
export { CdcConsumerSlowError, createSyncEngine, MissedChangesError, SchemaError, UnauthorizedError } from './syncEngine';
|
|
51
|
-
export type { CrdtFields, LoggedChange, StreamChangesOptions, SubscribeArgs, Subscription, SyncEngine } from './syncEngine';
|
|
50
|
+
export { CdcConsumerSlowError, createSyncEngine, MissedChangesError, MutationQueueOverflowError, SchemaError, UnauthorizedError } from './syncEngine';
|
|
51
|
+
export type { ChangeLogSnapshot, CrdtFields, LoggedChange, StreamChangesOptions, SubscribeArgs, Subscription, SyncEngine, SyncEngineOptions } from './syncEngine';
|
|
52
52
|
export { syncCdc } from './cdc';
|
|
53
53
|
export type { SyncCdcOptions } from './cdc';
|
|
54
54
|
export type { CrdtMergeable } from '../crdt';
|
package/dist/engine/index.js
CHANGED
|
@@ -1410,6 +1410,15 @@ class CdcConsumerSlowError extends Error {
|
|
|
1410
1410
|
this.lastDeliveredVersion = lastDeliveredVersion;
|
|
1411
1411
|
}
|
|
1412
1412
|
}
|
|
1413
|
+
|
|
1414
|
+
class MutationQueueOverflowError extends Error {
|
|
1415
|
+
queueLimit;
|
|
1416
|
+
constructor(queueLimit) {
|
|
1417
|
+
super(`Mutation queue overflowed (limit ${queueLimit}); the engine is at ` + `its mutationConcurrency cap and the waiting queue is full. ` + `Retry later or shed load at the gateway.`);
|
|
1418
|
+
this.name = "MutationQueueOverflowError";
|
|
1419
|
+
this.queueLimit = queueLimit;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1413
1422
|
var defaultKey = (row) => row.id;
|
|
1414
1423
|
var shallowEqual4 = (a, b) => {
|
|
1415
1424
|
if (a === b) {
|
|
@@ -1526,6 +1535,40 @@ var createSyncEngine = (options = {}) => {
|
|
|
1526
1535
|
let mutationsFailed = 0;
|
|
1527
1536
|
let mutationsRetried = 0;
|
|
1528
1537
|
let mutationsInFlight = 0;
|
|
1538
|
+
const mutationWaiters = [];
|
|
1539
|
+
let mutationsQueued = 0;
|
|
1540
|
+
const acquireMutationSlot = async () => {
|
|
1541
|
+
const limit = options.mutationConcurrency;
|
|
1542
|
+
if (limit === undefined) {
|
|
1543
|
+
mutationsInFlight += 1;
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
if (mutationsInFlight < limit && mutationWaiters.length === 0) {
|
|
1547
|
+
mutationsInFlight += 1;
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
const queueLimit = options.mutationQueueLimit;
|
|
1551
|
+
if (queueLimit !== undefined && mutationsQueued >= queueLimit) {
|
|
1552
|
+
throw new MutationQueueOverflowError(queueLimit);
|
|
1553
|
+
}
|
|
1554
|
+
mutationsQueued += 1;
|
|
1555
|
+
try {
|
|
1556
|
+
await new Promise((resolve) => {
|
|
1557
|
+
mutationWaiters.push(resolve);
|
|
1558
|
+
});
|
|
1559
|
+
} finally {
|
|
1560
|
+
mutationsQueued -= 1;
|
|
1561
|
+
}
|
|
1562
|
+
mutationsInFlight += 1;
|
|
1563
|
+
};
|
|
1564
|
+
const releaseMutationSlot = () => {
|
|
1565
|
+
mutationsInFlight -= 1;
|
|
1566
|
+
if (options.mutationConcurrency === undefined)
|
|
1567
|
+
return;
|
|
1568
|
+
const next = mutationWaiters.shift();
|
|
1569
|
+
if (next !== undefined)
|
|
1570
|
+
next();
|
|
1571
|
+
};
|
|
1529
1572
|
const reactiveCacheMax = options.reactiveCache?.max ?? 256;
|
|
1530
1573
|
const reactiveCacheTtlMs = options.reactiveCache?.ttlMs ?? 60000;
|
|
1531
1574
|
const cachedReruns = new Map;
|
|
@@ -1567,6 +1610,31 @@ var createSyncEngine = (options = {}) => {
|
|
|
1567
1610
|
const runInTransaction = options.transaction;
|
|
1568
1611
|
const instanceId = options.instanceId ?? globalThis.crypto?.randomUUID?.() ?? `i${Math.random()}`;
|
|
1569
1612
|
let clusterBus;
|
|
1613
|
+
const importChangeLog = (snapshot) => {
|
|
1614
|
+
if (version !== 0) {
|
|
1615
|
+
throw new Error(`[sync] importChangeLog: engine already has version ${version}; ` + `restore must happen before any local writes commit.`);
|
|
1616
|
+
}
|
|
1617
|
+
if (snapshot.instanceId !== instanceId) {
|
|
1618
|
+
throw new Error(`[sync] importChangeLog: snapshot instanceId "${snapshot.instanceId}" ` + `does not match this engine's instanceId "${instanceId}". ` + `Pass options.instanceId = "${snapshot.instanceId}" to createSyncEngine.`);
|
|
1619
|
+
}
|
|
1620
|
+
version = snapshot.version;
|
|
1621
|
+
for (const entry of snapshot.entries) {
|
|
1622
|
+
changeLog.push(entry);
|
|
1623
|
+
}
|
|
1624
|
+
while (changeLog.length > changeLogSize) {
|
|
1625
|
+
changeLog.shift();
|
|
1626
|
+
}
|
|
1627
|
+
if (changeLogRetainMs !== null && changeLogRetainMs > 0) {
|
|
1628
|
+
const cutoff = Date.now() - changeLogRetainMs;
|
|
1629
|
+
while (changeLog.length > 0 && changeLog[0].at < cutoff) {
|
|
1630
|
+
changeLog.shift();
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
return snapshot.entries.length;
|
|
1634
|
+
};
|
|
1635
|
+
if (options.initialChangeLog !== undefined) {
|
|
1636
|
+
importChangeLog(options.initialChangeLog);
|
|
1637
|
+
}
|
|
1570
1638
|
const broadcast = (changes, originVersion) => {
|
|
1571
1639
|
if (clusterBus !== undefined && changes.length > 0) {
|
|
1572
1640
|
clusterBus.publish({ changes, origin: instanceId, originVersion });
|
|
@@ -2528,6 +2596,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
2528
2596
|
throw new UnauthorizedError(`run mutation "${name}"`);
|
|
2529
2597
|
}
|
|
2530
2598
|
}
|
|
2599
|
+
await acquireMutationSlot();
|
|
2531
2600
|
const sandboxRunner = sandboxRunners.get(name);
|
|
2532
2601
|
const invokeHandler = sandboxRunner !== undefined ? sandboxRunner : (a, c, actions) => Promise.resolve(mutation.handler(a, c, actions));
|
|
2533
2602
|
const runHandler = async (tx) => {
|
|
@@ -2543,7 +2612,6 @@ var createSyncEngine = (options = {}) => {
|
|
|
2543
2612
|
const startedAt = Date.now();
|
|
2544
2613
|
let lastError;
|
|
2545
2614
|
let attemptsMade = 0;
|
|
2546
|
-
mutationsInFlight += 1;
|
|
2547
2615
|
try {
|
|
2548
2616
|
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
2549
2617
|
attemptsMade = attempt;
|
|
@@ -2596,7 +2664,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
2596
2664
|
}
|
|
2597
2665
|
throw lastError;
|
|
2598
2666
|
} finally {
|
|
2599
|
-
|
|
2667
|
+
releaseMutationSlot();
|
|
2600
2668
|
}
|
|
2601
2669
|
},
|
|
2602
2670
|
runMutations: async (specs, ctx) => {
|
|
@@ -2609,6 +2677,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
2609
2677
|
}
|
|
2610
2678
|
return { args: spec.args, mutation, name: spec.name };
|
|
2611
2679
|
});
|
|
2680
|
+
await acquireMutationSlot();
|
|
2612
2681
|
const runBatch = async (tx) => {
|
|
2613
2682
|
const results = [];
|
|
2614
2683
|
const accumulated = [];
|
|
@@ -2646,6 +2715,8 @@ var createSyncEngine = (options = {}) => {
|
|
|
2646
2715
|
status: "error"
|
|
2647
2716
|
});
|
|
2648
2717
|
throw error;
|
|
2718
|
+
} finally {
|
|
2719
|
+
releaseMutationSlot();
|
|
2649
2720
|
}
|
|
2650
2721
|
},
|
|
2651
2722
|
registerSchedule: (schedule) => {
|
|
@@ -2836,6 +2907,13 @@ var createSyncEngine = (options = {}) => {
|
|
|
2836
2907
|
}))
|
|
2837
2908
|
};
|
|
2838
2909
|
},
|
|
2910
|
+
exportChangeLog: () => ({
|
|
2911
|
+
entries: changeLog.slice(),
|
|
2912
|
+
exportedAt: Date.now(),
|
|
2913
|
+
instanceId,
|
|
2914
|
+
version
|
|
2915
|
+
}),
|
|
2916
|
+
importChangeLog,
|
|
2839
2917
|
metrics: () => {
|
|
2840
2918
|
const now = Date.now();
|
|
2841
2919
|
const byCollection = {};
|
|
@@ -2858,6 +2936,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
2858
2936
|
completed: mutationsCompleted,
|
|
2859
2937
|
failed: mutationsFailed,
|
|
2860
2938
|
inFlight: mutationsInFlight,
|
|
2939
|
+
queued: mutationsQueued,
|
|
2861
2940
|
retried: mutationsRetried
|
|
2862
2941
|
},
|
|
2863
2942
|
reactiveCache: {
|
|
@@ -3402,9 +3481,10 @@ export {
|
|
|
3402
3481
|
RetriesExhaustedError,
|
|
3403
3482
|
PackTableConflictError,
|
|
3404
3483
|
PackMissingDependencyError,
|
|
3484
|
+
MutationQueueOverflowError,
|
|
3405
3485
|
MissedChangesError,
|
|
3406
3486
|
CdcConsumerSlowError
|
|
3407
3487
|
};
|
|
3408
3488
|
|
|
3409
|
-
//# debugId=
|
|
3489
|
+
//# debugId=E104F5FFDBD631C764756E2164756E21
|
|
3410
3490
|
//# sourceMappingURL=index.js.map
|