@absolutejs/sync 1.19.0 → 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 +51 -3
- package/dist/engine/index.js.map +3 -3
- package/dist/engine/syncEngine.d.ts +40 -0
- package/dist/index.js +50 -3
- package/dist/index.js.map +3 -3
- package/dist/testing.js +50 -3
- package/dist/testing.js.map +3 -3
- package/package.json +1 -1
|
@@ -414,6 +414,17 @@ export declare class CdcConsumerSlowError extends Error {
|
|
|
414
414
|
readonly lastDeliveredVersion: number;
|
|
415
415
|
constructor(maxBuffer: number, lastDeliveredVersion: number);
|
|
416
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* Thrown by `runMutation` / `runMutations` when `mutationConcurrency` is
|
|
419
|
+
* saturated AND the waiting queue is already at `mutationQueueLimit`. The
|
|
420
|
+
* caller sees this immediately (no queue time) so the host can shed load
|
|
421
|
+
* with a clean 429 instead of letting the queue grow unboundedly. Added
|
|
422
|
+
* in 1.20.0.
|
|
423
|
+
*/
|
|
424
|
+
export declare class MutationQueueOverflowError extends Error {
|
|
425
|
+
readonly queueLimit: number;
|
|
426
|
+
constructor(queueLimit: number);
|
|
427
|
+
}
|
|
417
428
|
/**
|
|
418
429
|
* Serializable snapshot of an engine's change log + monotonic version, returned
|
|
419
430
|
* by {@link SyncEngine.exportChangeLog} and consumed by
|
|
@@ -557,6 +568,35 @@ export type SyncEngineOptions = {
|
|
|
557
568
|
* `createSyncEngine` returns. Added in 1.19.0.
|
|
558
569
|
*/
|
|
559
570
|
initialChangeLog?: ChangeLogSnapshot;
|
|
571
|
+
/**
|
|
572
|
+
* Maximum concurrent in-flight mutations (`runMutation` + `runMutations`).
|
|
573
|
+
* Calls beyond the limit wait in a FIFO queue and run as slots free up;
|
|
574
|
+
* `engine.metrics().mutations.queued` surfaces the queue depth.
|
|
575
|
+
*
|
|
576
|
+
* A single tenant flooding `runMutation` can otherwise drive unbounded
|
|
577
|
+
* memory growth (per-mutation `actions` buffers, retry timers, sandbox
|
|
578
|
+
* invocations queued against the isolate pool). Set this to a value
|
|
579
|
+
* appropriate for the host's tenant tier — e.g. `32` for a free tier,
|
|
580
|
+
* `256` for paid. Without this option the engine is unbounded
|
|
581
|
+
* (matching pre-1.20 behavior).
|
|
582
|
+
*
|
|
583
|
+
* Sandboxed mutations are gated by the same semaphore. If you need
|
|
584
|
+
* finer-grained control (sandbox-only throttling), see
|
|
585
|
+
* `@absolutejs/isolated-jsc`'s pool size — that's the lower layer.
|
|
586
|
+
*
|
|
587
|
+
* Added in 1.20.0.
|
|
588
|
+
*/
|
|
589
|
+
mutationConcurrency?: number;
|
|
590
|
+
/**
|
|
591
|
+
* Cap on the queue of waiting mutations once `mutationConcurrency` is
|
|
592
|
+
* saturated. Calls beyond this cap throw {@link MutationQueueOverflowError}
|
|
593
|
+
* immediately instead of queueing — the host can surface a clean 429 or
|
|
594
|
+
* apply a tenant-specific shed policy. Defaults to unbounded (queue
|
|
595
|
+
* never rejects). Only meaningful when `mutationConcurrency` is set.
|
|
596
|
+
*
|
|
597
|
+
* Added in 1.20.0.
|
|
598
|
+
*/
|
|
599
|
+
mutationQueueLimit?: number;
|
|
560
600
|
};
|
|
561
601
|
/**
|
|
562
602
|
* The Tier 3 sync engine: a registry of collections plus the view syncer. It is
|
package/dist/index.js
CHANGED
|
@@ -1152,6 +1152,15 @@ class CdcConsumerSlowError extends Error {
|
|
|
1152
1152
|
this.lastDeliveredVersion = lastDeliveredVersion;
|
|
1153
1153
|
}
|
|
1154
1154
|
}
|
|
1155
|
+
|
|
1156
|
+
class MutationQueueOverflowError extends Error {
|
|
1157
|
+
queueLimit;
|
|
1158
|
+
constructor(queueLimit) {
|
|
1159
|
+
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.`);
|
|
1160
|
+
this.name = "MutationQueueOverflowError";
|
|
1161
|
+
this.queueLimit = queueLimit;
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1155
1164
|
var defaultKey = (row) => row.id;
|
|
1156
1165
|
var shallowEqual3 = (a, b) => {
|
|
1157
1166
|
if (a === b) {
|
|
@@ -1268,6 +1277,40 @@ var createSyncEngine = (options = {}) => {
|
|
|
1268
1277
|
let mutationsFailed = 0;
|
|
1269
1278
|
let mutationsRetried = 0;
|
|
1270
1279
|
let mutationsInFlight = 0;
|
|
1280
|
+
const mutationWaiters = [];
|
|
1281
|
+
let mutationsQueued = 0;
|
|
1282
|
+
const acquireMutationSlot = async () => {
|
|
1283
|
+
const limit = options.mutationConcurrency;
|
|
1284
|
+
if (limit === undefined) {
|
|
1285
|
+
mutationsInFlight += 1;
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
if (mutationsInFlight < limit && mutationWaiters.length === 0) {
|
|
1289
|
+
mutationsInFlight += 1;
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
const queueLimit = options.mutationQueueLimit;
|
|
1293
|
+
if (queueLimit !== undefined && mutationsQueued >= queueLimit) {
|
|
1294
|
+
throw new MutationQueueOverflowError(queueLimit);
|
|
1295
|
+
}
|
|
1296
|
+
mutationsQueued += 1;
|
|
1297
|
+
try {
|
|
1298
|
+
await new Promise((resolve) => {
|
|
1299
|
+
mutationWaiters.push(resolve);
|
|
1300
|
+
});
|
|
1301
|
+
} finally {
|
|
1302
|
+
mutationsQueued -= 1;
|
|
1303
|
+
}
|
|
1304
|
+
mutationsInFlight += 1;
|
|
1305
|
+
};
|
|
1306
|
+
const releaseMutationSlot = () => {
|
|
1307
|
+
mutationsInFlight -= 1;
|
|
1308
|
+
if (options.mutationConcurrency === undefined)
|
|
1309
|
+
return;
|
|
1310
|
+
const next = mutationWaiters.shift();
|
|
1311
|
+
if (next !== undefined)
|
|
1312
|
+
next();
|
|
1313
|
+
};
|
|
1271
1314
|
const reactiveCacheMax = options.reactiveCache?.max ?? 256;
|
|
1272
1315
|
const reactiveCacheTtlMs = options.reactiveCache?.ttlMs ?? 60000;
|
|
1273
1316
|
const cachedReruns = new Map;
|
|
@@ -2295,6 +2338,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
2295
2338
|
throw new UnauthorizedError(`run mutation "${name}"`);
|
|
2296
2339
|
}
|
|
2297
2340
|
}
|
|
2341
|
+
await acquireMutationSlot();
|
|
2298
2342
|
const sandboxRunner = sandboxRunners.get(name);
|
|
2299
2343
|
const invokeHandler = sandboxRunner !== undefined ? sandboxRunner : (a, c, actions) => Promise.resolve(mutation.handler(a, c, actions));
|
|
2300
2344
|
const runHandler = async (tx) => {
|
|
@@ -2310,7 +2354,6 @@ var createSyncEngine = (options = {}) => {
|
|
|
2310
2354
|
const startedAt = Date.now();
|
|
2311
2355
|
let lastError;
|
|
2312
2356
|
let attemptsMade = 0;
|
|
2313
|
-
mutationsInFlight += 1;
|
|
2314
2357
|
try {
|
|
2315
2358
|
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
2316
2359
|
attemptsMade = attempt;
|
|
@@ -2363,7 +2406,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
2363
2406
|
}
|
|
2364
2407
|
throw lastError;
|
|
2365
2408
|
} finally {
|
|
2366
|
-
|
|
2409
|
+
releaseMutationSlot();
|
|
2367
2410
|
}
|
|
2368
2411
|
},
|
|
2369
2412
|
runMutations: async (specs, ctx) => {
|
|
@@ -2376,6 +2419,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
2376
2419
|
}
|
|
2377
2420
|
return { args: spec.args, mutation, name: spec.name };
|
|
2378
2421
|
});
|
|
2422
|
+
await acquireMutationSlot();
|
|
2379
2423
|
const runBatch = async (tx) => {
|
|
2380
2424
|
const results = [];
|
|
2381
2425
|
const accumulated = [];
|
|
@@ -2413,6 +2457,8 @@ var createSyncEngine = (options = {}) => {
|
|
|
2413
2457
|
status: "error"
|
|
2414
2458
|
});
|
|
2415
2459
|
throw error;
|
|
2460
|
+
} finally {
|
|
2461
|
+
releaseMutationSlot();
|
|
2416
2462
|
}
|
|
2417
2463
|
},
|
|
2418
2464
|
registerSchedule: (schedule) => {
|
|
@@ -2632,6 +2678,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
2632
2678
|
completed: mutationsCompleted,
|
|
2633
2679
|
failed: mutationsFailed,
|
|
2634
2680
|
inFlight: mutationsInFlight,
|
|
2681
|
+
queued: mutationsQueued,
|
|
2635
2682
|
retried: mutationsRetried
|
|
2636
2683
|
},
|
|
2637
2684
|
reactiveCache: {
|
|
@@ -3025,5 +3072,5 @@ export {
|
|
|
3025
3072
|
createPresenceHub
|
|
3026
3073
|
};
|
|
3027
3074
|
|
|
3028
|
-
//# debugId=
|
|
3075
|
+
//# debugId=9DD57DF563EB9CB564756E2164756E21
|
|
3029
3076
|
//# sourceMappingURL=index.js.map
|