@absolutejs/sync 1.12.3 → 1.13.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 +53 -0
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/index.js +110 -49
- package/dist/engine/index.js.map +3 -3
- package/dist/engine/syncEngine.d.ts +28 -1
- package/dist/index.js +110 -49
- package/dist/index.js.map +3 -3
- package/dist/testing.js +110 -49
- package/dist/testing.js.map +3 -3
- package/package.json +1 -1
|
@@ -48,6 +48,59 @@ export type EngineInspection = {
|
|
|
48
48
|
readsTables: string[];
|
|
49
49
|
}[];
|
|
50
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* Operator-shaped point-in-time engine state (see {@link SyncEngine.metrics}) —
|
|
53
|
+
* numeric counters + memory estimates + throughput totals since engine start.
|
|
54
|
+
*
|
|
55
|
+
* Distinct from {@link EngineInspection}, which is devtools-shaped (named
|
|
56
|
+
* collections, recent-change tail, registered packs). `metrics()` is what a
|
|
57
|
+
* PaaS host scrapes on an interval to answer "is this engine healthy" and
|
|
58
|
+
* "what's its resource footprint" — feed it to `@absolutejs/metering` to
|
|
59
|
+
* attribute cost per engine.
|
|
60
|
+
*/
|
|
61
|
+
export type EngineMetrics = {
|
|
62
|
+
/** `Date.now()` when this snapshot was taken. */
|
|
63
|
+
at: number;
|
|
64
|
+
/** How long this engine has been running, in milliseconds. */
|
|
65
|
+
uptimeMs: number;
|
|
66
|
+
/** Current change-feed version (monotonic). */
|
|
67
|
+
version: number;
|
|
68
|
+
changeLog: {
|
|
69
|
+
/** Number of entries currently retained. */
|
|
70
|
+
entries: number;
|
|
71
|
+
/** Hard cap on entries (from `SyncEngineOptions.changeLogSize`). */
|
|
72
|
+
capacity: number;
|
|
73
|
+
/** Time-based retention window, when set (`SyncEngineOptions.changeLogRetainMs`). */
|
|
74
|
+
retainMs: number | null;
|
|
75
|
+
/** Version of the oldest retained entry, or `null` when empty. */
|
|
76
|
+
oldestVersion: number | null;
|
|
77
|
+
/** Wall-clock age of the oldest retained entry in ms, or `null` when empty. */
|
|
78
|
+
oldestAgeMs: number | null;
|
|
79
|
+
};
|
|
80
|
+
subscriptions: {
|
|
81
|
+
/** Active subscriptions across every collection. */
|
|
82
|
+
total: number;
|
|
83
|
+
/** Per-collection breakdown — the values sum to `total`. */
|
|
84
|
+
byCollection: Record<string, number>;
|
|
85
|
+
};
|
|
86
|
+
reactiveCache: {
|
|
87
|
+
entries: number;
|
|
88
|
+
capacity: number;
|
|
89
|
+
};
|
|
90
|
+
mutations: {
|
|
91
|
+
/** Mutations completed successfully since engine start. */
|
|
92
|
+
completed: number;
|
|
93
|
+
/** Mutations that exhausted their retry budget and failed. */
|
|
94
|
+
failed: number;
|
|
95
|
+
/** Per-attempt retries fired since start (a single mutation may bump this multiple times). */
|
|
96
|
+
retried: number;
|
|
97
|
+
/** Currently running, not yet committed or failed. */
|
|
98
|
+
inFlight: number;
|
|
99
|
+
};
|
|
100
|
+
schedules: {
|
|
101
|
+
registered: number;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
51
104
|
/**
|
|
52
105
|
* A live engine event (see {@link SyncEngine.onActivity}): a committed change or
|
|
53
106
|
* a mutation outcome. `at` is `Date.now()`. (Live subscription counts come from
|
package/dist/engine/index.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export type { SyncCdcOptions } from './cdc';
|
|
|
54
54
|
export type { CrdtMergeable } from '../crdt';
|
|
55
55
|
export { defineSchema, field } from './schema';
|
|
56
56
|
export type { FieldValidator, SchemaDefinition, TableSchema } from './schema';
|
|
57
|
-
export type { CollectionInspection, CollectionKind, EngineActivity, EngineInspection } from './devtools';
|
|
57
|
+
export type { CollectionInspection, CollectionKind, EngineActivity, EngineInspection, EngineMetrics } from './devtools';
|
|
58
58
|
export { hydrateRoute, mutateRoute } from './routes';
|
|
59
59
|
export type { SyncRouteContext } from './routes';
|
|
60
60
|
export { createSyncConnection } from './connection';
|
package/dist/engine/index.js
CHANGED
|
@@ -1492,8 +1492,14 @@ var createSyncEngine = (options = {}) => {
|
|
|
1492
1492
|
const active = new Map;
|
|
1493
1493
|
const tableIndex = new Map;
|
|
1494
1494
|
const changeLogSize = options.changeLogSize ?? 1024;
|
|
1495
|
+
const changeLogRetainMs = options.changeLogRetainMs ?? null;
|
|
1495
1496
|
const changeLog = [];
|
|
1496
1497
|
let version = 0;
|
|
1498
|
+
const engineStartedAt = Date.now();
|
|
1499
|
+
let mutationsCompleted = 0;
|
|
1500
|
+
let mutationsFailed = 0;
|
|
1501
|
+
let mutationsRetried = 0;
|
|
1502
|
+
let mutationsInFlight = 0;
|
|
1497
1503
|
const reactiveCacheMax = options.reactiveCache?.max ?? 256;
|
|
1498
1504
|
const reactiveCacheTtlMs = options.reactiveCache?.ttlMs ?? 60000;
|
|
1499
1505
|
const cachedReruns = new Map;
|
|
@@ -1904,6 +1910,12 @@ var createSyncEngine = (options = {}) => {
|
|
|
1904
1910
|
if (changeLog.length > changeLogSize) {
|
|
1905
1911
|
changeLog.shift();
|
|
1906
1912
|
}
|
|
1913
|
+
if (changeLogRetainMs !== null && changeLogRetainMs > 0) {
|
|
1914
|
+
const cutoff = entry.at - changeLogRetainMs;
|
|
1915
|
+
while (changeLog.length > 0 && changeLog[0].at < cutoff) {
|
|
1916
|
+
changeLog.shift();
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1907
1919
|
for (const subscriber of streamSubscribers) {
|
|
1908
1920
|
subscriber(entry);
|
|
1909
1921
|
}
|
|
@@ -1911,10 +1923,11 @@ var createSyncEngine = (options = {}) => {
|
|
|
1911
1923
|
const applyChange = async (table, change, shouldBroadcast = true) => {
|
|
1912
1924
|
version += 1;
|
|
1913
1925
|
const changeVersion = version;
|
|
1914
|
-
|
|
1926
|
+
const at = Date.now();
|
|
1927
|
+
logChange(changeVersion, { version: changeVersion, table, change, at });
|
|
1915
1928
|
emitActivity({
|
|
1916
1929
|
type: "change",
|
|
1917
|
-
at
|
|
1930
|
+
at,
|
|
1918
1931
|
table,
|
|
1919
1932
|
op: change.op,
|
|
1920
1933
|
version: changeVersion
|
|
@@ -1945,11 +1958,12 @@ var createSyncEngine = (options = {}) => {
|
|
|
1945
1958
|
const batchVersion = version;
|
|
1946
1959
|
const perSubscription = new Map;
|
|
1947
1960
|
const reactiveChanges = [];
|
|
1961
|
+
const batchAt = Date.now();
|
|
1948
1962
|
for (const { table, change } of changes) {
|
|
1949
|
-
logChange(batchVersion, { version: batchVersion, table, change });
|
|
1963
|
+
logChange(batchVersion, { version: batchVersion, table, change, at: batchAt });
|
|
1950
1964
|
emitActivity({
|
|
1951
1965
|
type: "change",
|
|
1952
|
-
at:
|
|
1966
|
+
at: batchAt,
|
|
1953
1967
|
table,
|
|
1954
1968
|
op: change.op,
|
|
1955
1969
|
version: batchVersion
|
|
@@ -2400,53 +2414,61 @@ var createSyncEngine = (options = {}) => {
|
|
|
2400
2414
|
const startedAt = Date.now();
|
|
2401
2415
|
let lastError;
|
|
2402
2416
|
let attemptsMade = 0;
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2417
|
+
mutationsInFlight += 1;
|
|
2418
|
+
try {
|
|
2419
|
+
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
2420
|
+
attemptsMade = attempt;
|
|
2421
|
+
try {
|
|
2422
|
+
const { buffered, result } = runInTransaction !== undefined ? await runInTransaction((tx) => runHandler(tx)) : await runHandler(undefined);
|
|
2423
|
+
await applyChangeBatch(buffered);
|
|
2424
|
+
mutationsCompleted += 1;
|
|
2425
|
+
emitActivity({
|
|
2426
|
+
type: "mutation",
|
|
2427
|
+
at: Date.now(),
|
|
2428
|
+
name,
|
|
2429
|
+
status: "ok"
|
|
2430
|
+
});
|
|
2431
|
+
return result;
|
|
2432
|
+
} catch (error) {
|
|
2433
|
+
lastError = error;
|
|
2434
|
+
const elapsedMs = Date.now() - startedAt;
|
|
2435
|
+
const canRetry = attempt < maxAttempts && isRetryable(error) && elapsedMs < maxElapsedMs;
|
|
2436
|
+
if (!canRetry)
|
|
2437
|
+
break;
|
|
2438
|
+
mutationsRetried += 1;
|
|
2439
|
+
const rawDelay = computeDelay(attempt);
|
|
2440
|
+
const remaining = maxElapsedMs - elapsedMs;
|
|
2441
|
+
if (remaining <= 0)
|
|
2442
|
+
break;
|
|
2443
|
+
const delayMs = Math.max(0, Math.min(rawDelay, remaining));
|
|
2444
|
+
emitActivity({
|
|
2445
|
+
type: "mutationRetry",
|
|
2446
|
+
at: Date.now(),
|
|
2447
|
+
name,
|
|
2448
|
+
attempt,
|
|
2449
|
+
delayMs,
|
|
2450
|
+
errorName: error instanceof Error ? error.name : "Error",
|
|
2451
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
2452
|
+
});
|
|
2453
|
+
if (delayMs > 0) {
|
|
2454
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
2455
|
+
}
|
|
2437
2456
|
}
|
|
2438
2457
|
}
|
|
2458
|
+
mutationsFailed += 1;
|
|
2459
|
+
emitActivity({
|
|
2460
|
+
type: "mutation",
|
|
2461
|
+
at: Date.now(),
|
|
2462
|
+
name,
|
|
2463
|
+
status: "error"
|
|
2464
|
+
});
|
|
2465
|
+
if (attemptsMade > 1) {
|
|
2466
|
+
throw new RetriesExhaustedError(attemptsMade, Date.now() - startedAt, lastError);
|
|
2467
|
+
}
|
|
2468
|
+
throw lastError;
|
|
2469
|
+
} finally {
|
|
2470
|
+
mutationsInFlight -= 1;
|
|
2439
2471
|
}
|
|
2440
|
-
emitActivity({
|
|
2441
|
-
type: "mutation",
|
|
2442
|
-
at: Date.now(),
|
|
2443
|
-
name,
|
|
2444
|
-
status: "error"
|
|
2445
|
-
});
|
|
2446
|
-
if (attemptsMade > 1) {
|
|
2447
|
-
throw new RetriesExhaustedError(attemptsMade, Date.now() - startedAt, lastError);
|
|
2448
|
-
}
|
|
2449
|
-
throw lastError;
|
|
2450
2472
|
},
|
|
2451
2473
|
runMutations: async (specs, ctx) => {
|
|
2452
2474
|
if (specs.length === 0)
|
|
@@ -2685,6 +2707,45 @@ var createSyncEngine = (options = {}) => {
|
|
|
2685
2707
|
}))
|
|
2686
2708
|
};
|
|
2687
2709
|
},
|
|
2710
|
+
metrics: () => {
|
|
2711
|
+
const now = Date.now();
|
|
2712
|
+
const byCollection = {};
|
|
2713
|
+
let totalSubscriptions = 0;
|
|
2714
|
+
for (const [name, subs] of active) {
|
|
2715
|
+
byCollection[name] = subs.size;
|
|
2716
|
+
totalSubscriptions += subs.size;
|
|
2717
|
+
}
|
|
2718
|
+
const oldest = changeLog[0];
|
|
2719
|
+
return {
|
|
2720
|
+
at: now,
|
|
2721
|
+
changeLog: {
|
|
2722
|
+
capacity: changeLogSize,
|
|
2723
|
+
entries: changeLog.length,
|
|
2724
|
+
oldestAgeMs: oldest ? now - oldest.at : null,
|
|
2725
|
+
oldestVersion: oldest ? oldest.version : null,
|
|
2726
|
+
retainMs: changeLogRetainMs
|
|
2727
|
+
},
|
|
2728
|
+
mutations: {
|
|
2729
|
+
completed: mutationsCompleted,
|
|
2730
|
+
failed: mutationsFailed,
|
|
2731
|
+
inFlight: mutationsInFlight,
|
|
2732
|
+
retried: mutationsRetried
|
|
2733
|
+
},
|
|
2734
|
+
reactiveCache: {
|
|
2735
|
+
capacity: reactiveCacheMax,
|
|
2736
|
+
entries: cachedReruns.size
|
|
2737
|
+
},
|
|
2738
|
+
schedules: {
|
|
2739
|
+
registered: schedules.size
|
|
2740
|
+
},
|
|
2741
|
+
subscriptions: {
|
|
2742
|
+
byCollection,
|
|
2743
|
+
total: totalSubscriptions
|
|
2744
|
+
},
|
|
2745
|
+
uptimeMs: now - engineStartedAt,
|
|
2746
|
+
version
|
|
2747
|
+
};
|
|
2748
|
+
},
|
|
2688
2749
|
onActivity: (listener) => {
|
|
2689
2750
|
activityListeners.add(listener);
|
|
2690
2751
|
return () => {
|
|
@@ -3161,5 +3222,5 @@ export {
|
|
|
3161
3222
|
CdcConsumerSlowError
|
|
3162
3223
|
};
|
|
3163
3224
|
|
|
3164
|
-
//# debugId=
|
|
3225
|
+
//# debugId=61A7A6BDD6B4D12064756E2164756E21
|
|
3165
3226
|
//# sourceMappingURL=index.js.map
|