@absolutejs/sync 1.20.1 → 1.22.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 +289 -164
- package/dist/engine/index.js.map +5 -4
- package/dist/engine/syncEngine.d.ts +91 -0
- package/dist/index.js +289 -164
- package/dist/index.js.map +5 -4
- package/dist/testing.js +289 -164
- package/dist/testing.js.map +5 -4
- package/package.json +5 -1
package/dist/testing.js
CHANGED
|
@@ -15,6 +15,59 @@ var __export = (target, all) => {
|
|
|
15
15
|
};
|
|
16
16
|
var __require = import.meta.require;
|
|
17
17
|
|
|
18
|
+
// node_modules/@absolutejs/telemetry/dist/index.js
|
|
19
|
+
var NOOP_SPAN_CONTEXT = {
|
|
20
|
+
spanId: "0000000000000000",
|
|
21
|
+
traceFlags: 0,
|
|
22
|
+
traceId: "00000000000000000000000000000000"
|
|
23
|
+
};
|
|
24
|
+
var noopSpan = {
|
|
25
|
+
addEvent: () => noopSpan,
|
|
26
|
+
end: () => {},
|
|
27
|
+
isRecording: () => false,
|
|
28
|
+
recordException: () => {},
|
|
29
|
+
setAttribute: () => noopSpan,
|
|
30
|
+
setAttributes: () => noopSpan,
|
|
31
|
+
setStatus: () => noopSpan,
|
|
32
|
+
spanContext: () => NOOP_SPAN_CONTEXT,
|
|
33
|
+
updateName: () => noopSpan
|
|
34
|
+
};
|
|
35
|
+
var startActiveSpanNoop = (_name, optionsOrFn, maybeFn) => {
|
|
36
|
+
const fn = typeof optionsOrFn === "function" ? optionsOrFn : maybeFn;
|
|
37
|
+
return fn(noopSpan);
|
|
38
|
+
};
|
|
39
|
+
var noopTracer = {
|
|
40
|
+
startActiveSpan: startActiveSpanNoop,
|
|
41
|
+
startSpan: () => noopSpan
|
|
42
|
+
};
|
|
43
|
+
var tracerOrNoop = (provider, name, version) => provider !== undefined ? provider.getTracer(name, version) : noopTracer;
|
|
44
|
+
var ABS_ATTRS = {
|
|
45
|
+
tenant: "abs.tenant",
|
|
46
|
+
shardId: "abs.shard.id",
|
|
47
|
+
engineId: "abs.engine.id",
|
|
48
|
+
collection: "abs.collection",
|
|
49
|
+
mutation: "abs.mutation",
|
|
50
|
+
mutationAttempt: "abs.mutation.attempt",
|
|
51
|
+
subscriptionId: "abs.subscription.id",
|
|
52
|
+
batchSize: "abs.batch.size",
|
|
53
|
+
clusterMessageOrigin: "abs.cluster.origin",
|
|
54
|
+
jobId: "abs.job.id",
|
|
55
|
+
jobKind: "abs.job.kind",
|
|
56
|
+
jobAttempt: "abs.job.attempt",
|
|
57
|
+
jobMaxAttempts: "abs.job.max_attempts",
|
|
58
|
+
workerId: "abs.worker.id",
|
|
59
|
+
runtimeKey: "abs.runtime.key",
|
|
60
|
+
runtimePid: "abs.runtime.pid",
|
|
61
|
+
runtimePort: "abs.runtime.port",
|
|
62
|
+
runtimeExitReason: "abs.runtime.exit_reason",
|
|
63
|
+
runtimeReadinessMs: "abs.runtime.readiness_ms",
|
|
64
|
+
routeShard: "abs.route.shard",
|
|
65
|
+
routeDecision: "abs.route.decision",
|
|
66
|
+
secretName: "abs.secret.name",
|
|
67
|
+
secretFingerprint: "abs.secret.fingerprint",
|
|
68
|
+
auditKind: "abs.audit.kind"
|
|
69
|
+
};
|
|
70
|
+
|
|
18
71
|
// src/engine/equiJoin.ts
|
|
19
72
|
var shallowEqual = (a, b) => {
|
|
20
73
|
if (a === b) {
|
|
@@ -863,6 +916,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
863
916
|
const runInTransaction = options.transaction;
|
|
864
917
|
const instanceId = options.instanceId ?? globalThis.crypto?.randomUUID?.() ?? `i${Math.random()}`;
|
|
865
918
|
let clusterBus;
|
|
919
|
+
const tracer = tracerOrNoop(options.tracerProvider, "@absolutejs/sync");
|
|
866
920
|
const importChangeLog = (snapshot) => {
|
|
867
921
|
if (version !== 0) {
|
|
868
922
|
throw new Error(`[sync] importChangeLog: engine already has version ${version}; ` + `restore must happen before any local writes commit.`);
|
|
@@ -1654,107 +1708,124 @@ var createSyncEngine = (options = {}) => {
|
|
|
1654
1708
|
registry.set(collection.name, collection);
|
|
1655
1709
|
},
|
|
1656
1710
|
subscribe: async ({ collection, params, ctx, onDiff, since, signal }) => {
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
}
|
|
1662
|
-
const tenantSlot = acquireSubscriptionSlot(ctx, { collection });
|
|
1663
|
-
let slotHandedOff = false;
|
|
1664
|
-
try {
|
|
1665
|
-
const typedOnDiff = onDiff;
|
|
1666
|
-
const subscribeSet = subsFor(collection);
|
|
1667
|
-
const wrapReturn = (sub) => {
|
|
1668
|
-
checkAborted(signal);
|
|
1669
|
-
const innerUnsubscribe = sub.unsubscribe;
|
|
1670
|
-
let released = false;
|
|
1671
|
-
const wrappedUnsubscribe = () => {
|
|
1672
|
-
if (released)
|
|
1673
|
-
return;
|
|
1674
|
-
released = true;
|
|
1675
|
-
releaseSubscriptionSlot(tenantSlot);
|
|
1676
|
-
innerUnsubscribe();
|
|
1677
|
-
};
|
|
1678
|
-
const wrapped = { ...sub, unsubscribe: wrappedUnsubscribe };
|
|
1679
|
-
linkAbortToUnsubscribe(signal, wrappedUnsubscribe);
|
|
1680
|
-
slotHandedOff = true;
|
|
1681
|
-
return wrapped;
|
|
1682
|
-
};
|
|
1683
|
-
const registeredKind = registered.kind;
|
|
1684
|
-
if (registeredKind === "join") {
|
|
1685
|
-
const joined = await subscribeJoin(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1686
|
-
return wrapReturn(joined);
|
|
1687
|
-
}
|
|
1688
|
-
if (registeredKind === "graph") {
|
|
1689
|
-
const graphed = await subscribeGraph(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1690
|
-
return wrapReturn(graphed);
|
|
1691
|
-
}
|
|
1692
|
-
if (registeredKind === "reactive") {
|
|
1693
|
-
const reactived = await subscribeReactive(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1694
|
-
return wrapReturn(reactived);
|
|
1711
|
+
const subscribeSpan = tracer.startSpan("sync.subscribe", {
|
|
1712
|
+
attributes: {
|
|
1713
|
+
[ABS_ATTRS.engineId]: instanceId,
|
|
1714
|
+
[ABS_ATTRS.collection]: collection
|
|
1695
1715
|
}
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1716
|
+
});
|
|
1717
|
+
try {
|
|
1718
|
+
checkAborted(signal);
|
|
1719
|
+
const registered = registry.get(collection);
|
|
1720
|
+
if (registered === undefined) {
|
|
1721
|
+
throw new Error(`Unknown collection "${collection}"`);
|
|
1699
1722
|
}
|
|
1700
|
-
const
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1723
|
+
const tenantSlot = acquireSubscriptionSlot(ctx, { collection });
|
|
1724
|
+
let slotHandedOff = false;
|
|
1725
|
+
try {
|
|
1726
|
+
const typedOnDiff = onDiff;
|
|
1727
|
+
const subscribeSet = subsFor(collection);
|
|
1728
|
+
const wrapReturn = (sub) => {
|
|
1729
|
+
checkAborted(signal);
|
|
1730
|
+
const innerUnsubscribe = sub.unsubscribe;
|
|
1731
|
+
let released = false;
|
|
1732
|
+
const wrappedUnsubscribe = () => {
|
|
1733
|
+
if (released)
|
|
1734
|
+
return;
|
|
1735
|
+
released = true;
|
|
1736
|
+
releaseSubscriptionSlot(tenantSlot);
|
|
1737
|
+
innerUnsubscribe();
|
|
1738
|
+
};
|
|
1739
|
+
const wrapped = { ...sub, unsubscribe: wrappedUnsubscribe };
|
|
1740
|
+
linkAbortToUnsubscribe(signal, wrappedUnsubscribe);
|
|
1741
|
+
slotHandedOff = true;
|
|
1742
|
+
return wrapped;
|
|
1743
|
+
};
|
|
1744
|
+
const registeredKind = registered.kind;
|
|
1745
|
+
if (registeredKind === "join") {
|
|
1746
|
+
const joined = await subscribeJoin(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1747
|
+
return wrapReturn(joined);
|
|
1748
|
+
}
|
|
1749
|
+
if (registeredKind === "graph") {
|
|
1750
|
+
const graphed = await subscribeGraph(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1751
|
+
return wrapReturn(graphed);
|
|
1752
|
+
}
|
|
1753
|
+
if (registeredKind === "reactive") {
|
|
1754
|
+
const reactived = await subscribeReactive(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1755
|
+
return wrapReturn(reactived);
|
|
1756
|
+
}
|
|
1757
|
+
if (registeredKind === "search") {
|
|
1758
|
+
const searched = await subscribeSearch(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1759
|
+
return wrapReturn(searched);
|
|
1760
|
+
}
|
|
1761
|
+
const definition = registered;
|
|
1762
|
+
if (definition.authorize !== undefined) {
|
|
1763
|
+
const allowed = await definition.authorize(params, ctx);
|
|
1764
|
+
if (!allowed) {
|
|
1765
|
+
throw new UnauthorizedError(`subscribe to collection "${collection}"`);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
const key = definition.key ?? defaultKey;
|
|
1769
|
+
const match = definition.match;
|
|
1770
|
+
const tables = definition.tables ?? [collection];
|
|
1771
|
+
const scopedTable = tables.length === 1 ? tables[0] : undefined;
|
|
1772
|
+
const readRule = scopedTable !== undefined ? readRuleFor(scopedTable) : undefined;
|
|
1773
|
+
const rehydrate = async () => {
|
|
1774
|
+
const raw = [...await definition.hydrate(params, ctx)];
|
|
1775
|
+
const rows = scopedTable !== undefined ? raw.map((row) => migrateRow(scopedTable, row)) : raw;
|
|
1776
|
+
return readRule ? rows.filter((row) => readRule(ctx, row)) : rows;
|
|
1777
|
+
};
|
|
1778
|
+
const incremental = match !== undefined && tables.length === 1;
|
|
1779
|
+
const boundMatch = incremental ? (row) => match(row, params, ctx) && (readRule ? readRule(ctx, row) : true) : () => true;
|
|
1780
|
+
const view = createMaterializedView({
|
|
1781
|
+
key,
|
|
1782
|
+
match: boundMatch
|
|
1783
|
+
});
|
|
1784
|
+
const resuming = since !== undefined && canResume(since, incremental);
|
|
1785
|
+
view.hydrate([...await rehydrate()]);
|
|
1786
|
+
const atVersion = version;
|
|
1787
|
+
const subscription = {
|
|
1788
|
+
kind: "view",
|
|
1789
|
+
collection,
|
|
1790
|
+
view,
|
|
1791
|
+
incremental,
|
|
1792
|
+
rehydrate,
|
|
1793
|
+
key,
|
|
1794
|
+
onDiff: typedOnDiff
|
|
1795
|
+
};
|
|
1796
|
+
subscribeSet.add(subscription);
|
|
1797
|
+
const unsubscribe = () => {
|
|
1798
|
+
subscribeSet.delete(subscription);
|
|
1799
|
+
};
|
|
1800
|
+
if (resuming) {
|
|
1801
|
+
return wrapReturn({
|
|
1802
|
+
initial: [],
|
|
1803
|
+
catchup: buildCatchup(since, tables, key, boundMatch),
|
|
1804
|
+
cursor: currentCursor(),
|
|
1805
|
+
version: atVersion,
|
|
1806
|
+
unsubscribe
|
|
1807
|
+
});
|
|
1705
1808
|
}
|
|
1706
|
-
}
|
|
1707
|
-
const key = definition.key ?? defaultKey;
|
|
1708
|
-
const match = definition.match;
|
|
1709
|
-
const tables = definition.tables ?? [collection];
|
|
1710
|
-
const scopedTable = tables.length === 1 ? tables[0] : undefined;
|
|
1711
|
-
const readRule = scopedTable !== undefined ? readRuleFor(scopedTable) : undefined;
|
|
1712
|
-
const rehydrate = async () => {
|
|
1713
|
-
const raw = [...await definition.hydrate(params, ctx)];
|
|
1714
|
-
const rows = scopedTable !== undefined ? raw.map((row) => migrateRow(scopedTable, row)) : raw;
|
|
1715
|
-
return readRule ? rows.filter((row) => readRule(ctx, row)) : rows;
|
|
1716
|
-
};
|
|
1717
|
-
const incremental = match !== undefined && tables.length === 1;
|
|
1718
|
-
const boundMatch = incremental ? (row) => match(row, params, ctx) && (readRule ? readRule(ctx, row) : true) : () => true;
|
|
1719
|
-
const view = createMaterializedView({
|
|
1720
|
-
key,
|
|
1721
|
-
match: boundMatch
|
|
1722
|
-
});
|
|
1723
|
-
const resuming = since !== undefined && canResume(since, incremental);
|
|
1724
|
-
view.hydrate([...await rehydrate()]);
|
|
1725
|
-
const atVersion = version;
|
|
1726
|
-
const subscription = {
|
|
1727
|
-
kind: "view",
|
|
1728
|
-
collection,
|
|
1729
|
-
view,
|
|
1730
|
-
incremental,
|
|
1731
|
-
rehydrate,
|
|
1732
|
-
key,
|
|
1733
|
-
onDiff: typedOnDiff
|
|
1734
|
-
};
|
|
1735
|
-
subscribeSet.add(subscription);
|
|
1736
|
-
const unsubscribe = () => {
|
|
1737
|
-
subscribeSet.delete(subscription);
|
|
1738
|
-
};
|
|
1739
|
-
if (resuming) {
|
|
1740
1809
|
return wrapReturn({
|
|
1741
|
-
initial:
|
|
1742
|
-
catchup: buildCatchup(since, tables, key, boundMatch),
|
|
1810
|
+
initial: view.rows(),
|
|
1743
1811
|
cursor: currentCursor(),
|
|
1744
1812
|
version: atVersion,
|
|
1745
1813
|
unsubscribe
|
|
1746
1814
|
});
|
|
1815
|
+
} catch (error) {
|
|
1816
|
+
if (!slotHandedOff)
|
|
1817
|
+
releaseSubscriptionSlot(tenantSlot);
|
|
1818
|
+
throw error;
|
|
1747
1819
|
}
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1820
|
+
} catch (spanError) {
|
|
1821
|
+
subscribeSpan.recordException(spanError);
|
|
1822
|
+
subscribeSpan.setStatus({
|
|
1823
|
+
code: 2,
|
|
1824
|
+
message: spanError instanceof Error ? spanError.message : String(spanError)
|
|
1753
1825
|
});
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
throw error;
|
|
1826
|
+
throw spanError;
|
|
1827
|
+
} finally {
|
|
1828
|
+
subscribeSpan.end();
|
|
1758
1829
|
}
|
|
1759
1830
|
},
|
|
1760
1831
|
hydrate: async (collection, params, ctx, options2) => {
|
|
@@ -1858,85 +1929,102 @@ var createSyncEngine = (options = {}) => {
|
|
|
1858
1929
|
},
|
|
1859
1930
|
migrate: (table, row) => migrateRow(table, row),
|
|
1860
1931
|
runMutation: async (name, args, ctx) => {
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
if (mutation.authorize !== undefined) {
|
|
1866
|
-
const allowed = await mutation.authorize(args, ctx);
|
|
1867
|
-
if (!allowed) {
|
|
1868
|
-
throw new UnauthorizedError(`run mutation "${name}"`);
|
|
1932
|
+
const span = tracer.startSpan("sync.runMutation", {
|
|
1933
|
+
attributes: {
|
|
1934
|
+
[ABS_ATTRS.engineId]: instanceId,
|
|
1935
|
+
[ABS_ATTRS.mutation]: name
|
|
1869
1936
|
}
|
|
1870
|
-
}
|
|
1871
|
-
await acquireMutationSlot();
|
|
1872
|
-
const sandboxRunner = sandboxRunners.get(name);
|
|
1873
|
-
const invokeHandler = sandboxRunner !== undefined ? sandboxRunner : (a, c, actions) => Promise.resolve(mutation.handler(a, c, actions));
|
|
1874
|
-
const runHandler = async (tx) => {
|
|
1875
|
-
const { actions, buffered } = makeActions(tx, ctx, true);
|
|
1876
|
-
const result = await invokeHandler(args, ctx, actions);
|
|
1877
|
-
return { buffered, result };
|
|
1878
|
-
};
|
|
1879
|
-
const retry = mutation.retry;
|
|
1880
|
-
const maxAttempts = retry === undefined ? 1 : retry.maxAttempts ?? 5;
|
|
1881
|
-
const isRetryable = retry?.isRetryable ?? isSerializationFailure;
|
|
1882
|
-
const computeDelay = retry?.backoff ?? exponentialBackoff();
|
|
1883
|
-
const maxElapsedMs = retry?.maxElapsedMs ?? 30000;
|
|
1884
|
-
const startedAt = Date.now();
|
|
1885
|
-
let lastError;
|
|
1886
|
-
let attemptsMade = 0;
|
|
1937
|
+
});
|
|
1887
1938
|
try {
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1939
|
+
const mutation = mutations.get(name);
|
|
1940
|
+
if (mutation === undefined) {
|
|
1941
|
+
throw new Error(`Unknown mutation "${name}"`);
|
|
1942
|
+
}
|
|
1943
|
+
if (mutation.authorize !== undefined) {
|
|
1944
|
+
const allowed = await mutation.authorize(args, ctx);
|
|
1945
|
+
if (!allowed) {
|
|
1946
|
+
throw new UnauthorizedError(`run mutation "${name}"`);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
await acquireMutationSlot();
|
|
1950
|
+
const sandboxRunner = sandboxRunners.get(name);
|
|
1951
|
+
const invokeHandler = sandboxRunner !== undefined ? sandboxRunner : (a, c, actions) => Promise.resolve(mutation.handler(a, c, actions));
|
|
1952
|
+
const runHandler = async (tx) => {
|
|
1953
|
+
const { actions, buffered } = makeActions(tx, ctx, true);
|
|
1954
|
+
const result = await invokeHandler(args, ctx, actions);
|
|
1955
|
+
return { buffered, result };
|
|
1956
|
+
};
|
|
1957
|
+
const retry = mutation.retry;
|
|
1958
|
+
const maxAttempts = retry === undefined ? 1 : retry.maxAttempts ?? 5;
|
|
1959
|
+
const isRetryable = retry?.isRetryable ?? isSerializationFailure;
|
|
1960
|
+
const computeDelay = retry?.backoff ?? exponentialBackoff();
|
|
1961
|
+
const maxElapsedMs = retry?.maxElapsedMs ?? 30000;
|
|
1962
|
+
const startedAt = Date.now();
|
|
1963
|
+
let lastError;
|
|
1964
|
+
let attemptsMade = 0;
|
|
1965
|
+
try {
|
|
1966
|
+
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
1967
|
+
attemptsMade = attempt;
|
|
1968
|
+
try {
|
|
1969
|
+
const { buffered, result } = runInTransaction !== undefined ? await runInTransaction((tx) => runHandler(tx)) : await runHandler(undefined);
|
|
1970
|
+
await applyChangeBatch(buffered);
|
|
1971
|
+
mutationsCompleted += 1;
|
|
1972
|
+
emitActivity({
|
|
1973
|
+
type: "mutation",
|
|
1974
|
+
at: Date.now(),
|
|
1975
|
+
name,
|
|
1976
|
+
status: "ok"
|
|
1977
|
+
});
|
|
1978
|
+
return result;
|
|
1979
|
+
} catch (error) {
|
|
1980
|
+
lastError = error;
|
|
1981
|
+
const elapsedMs = Date.now() - startedAt;
|
|
1982
|
+
const canRetry = attempt < maxAttempts && isRetryable(error) && elapsedMs < maxElapsedMs;
|
|
1983
|
+
if (!canRetry)
|
|
1984
|
+
break;
|
|
1985
|
+
mutationsRetried += 1;
|
|
1986
|
+
const rawDelay = computeDelay(attempt);
|
|
1987
|
+
const remaining = maxElapsedMs - elapsedMs;
|
|
1988
|
+
if (remaining <= 0)
|
|
1989
|
+
break;
|
|
1990
|
+
const delayMs = Math.max(0, Math.min(rawDelay, remaining));
|
|
1991
|
+
emitActivity({
|
|
1992
|
+
type: "mutationRetry",
|
|
1993
|
+
at: Date.now(),
|
|
1994
|
+
name,
|
|
1995
|
+
attempt,
|
|
1996
|
+
delayMs,
|
|
1997
|
+
errorName: error instanceof Error ? error.name : "Error",
|
|
1998
|
+
errorMessage: error instanceof Error ? error.message : String(error)
|
|
1999
|
+
});
|
|
2000
|
+
if (delayMs > 0) {
|
|
2001
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
2002
|
+
}
|
|
1924
2003
|
}
|
|
1925
2004
|
}
|
|
2005
|
+
mutationsFailed += 1;
|
|
2006
|
+
emitActivity({
|
|
2007
|
+
type: "mutation",
|
|
2008
|
+
at: Date.now(),
|
|
2009
|
+
name,
|
|
2010
|
+
status: "error"
|
|
2011
|
+
});
|
|
2012
|
+
if (attemptsMade > 1) {
|
|
2013
|
+
throw new RetriesExhaustedError(attemptsMade, Date.now() - startedAt, lastError);
|
|
2014
|
+
}
|
|
2015
|
+
throw lastError;
|
|
2016
|
+
} finally {
|
|
2017
|
+
releaseMutationSlot();
|
|
1926
2018
|
}
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
status: "error"
|
|
2019
|
+
} catch (spanError) {
|
|
2020
|
+
span.recordException(spanError);
|
|
2021
|
+
span.setStatus({
|
|
2022
|
+
code: 2,
|
|
2023
|
+
message: spanError instanceof Error ? spanError.message : String(spanError)
|
|
1933
2024
|
});
|
|
1934
|
-
|
|
1935
|
-
throw new RetriesExhaustedError(attemptsMade, Date.now() - startedAt, lastError);
|
|
1936
|
-
}
|
|
1937
|
-
throw lastError;
|
|
2025
|
+
throw spanError;
|
|
1938
2026
|
} finally {
|
|
1939
|
-
|
|
2027
|
+
span.end();
|
|
1940
2028
|
}
|
|
1941
2029
|
},
|
|
1942
2030
|
runMutations: async (specs, ctx) => {
|
|
@@ -2186,6 +2274,43 @@ var createSyncEngine = (options = {}) => {
|
|
|
2186
2274
|
version
|
|
2187
2275
|
}),
|
|
2188
2276
|
importChangeLog,
|
|
2277
|
+
replayTo: async ({ at, tables }) => {
|
|
2278
|
+
const filterTables = tables !== undefined ? new Set(tables) : undefined;
|
|
2279
|
+
const state = new Map;
|
|
2280
|
+
let asOfVersion = 0;
|
|
2281
|
+
let asOfAt = 0;
|
|
2282
|
+
const oldest = changeLog[0];
|
|
2283
|
+
const truncated = oldest !== undefined && oldest.version > 1 && oldest.at > at;
|
|
2284
|
+
for (const entry of changeLog) {
|
|
2285
|
+
if (entry.at > at)
|
|
2286
|
+
break;
|
|
2287
|
+
if (filterTables !== undefined && !filterTables.has(entry.table)) {
|
|
2288
|
+
continue;
|
|
2289
|
+
}
|
|
2290
|
+
let tableState = state.get(entry.table);
|
|
2291
|
+
if (tableState === undefined) {
|
|
2292
|
+
tableState = new Map;
|
|
2293
|
+
state.set(entry.table, tableState);
|
|
2294
|
+
}
|
|
2295
|
+
const reader = readers.get(entry.table);
|
|
2296
|
+
const key = reader?.key?.(entry.change.row) ?? entry.change.row?.id;
|
|
2297
|
+
if (key === undefined) {
|
|
2298
|
+
continue;
|
|
2299
|
+
}
|
|
2300
|
+
if (entry.change.op === "delete") {
|
|
2301
|
+
tableState.delete(key);
|
|
2302
|
+
} else {
|
|
2303
|
+
tableState.set(key, entry.change.row);
|
|
2304
|
+
}
|
|
2305
|
+
asOfVersion = entry.version;
|
|
2306
|
+
asOfAt = entry.at;
|
|
2307
|
+
}
|
|
2308
|
+
const rows = {};
|
|
2309
|
+
for (const [table, map] of state) {
|
|
2310
|
+
rows[table] = [...map.values()];
|
|
2311
|
+
}
|
|
2312
|
+
return { asOfAt, asOfVersion, rows, truncated };
|
|
2313
|
+
},
|
|
2189
2314
|
metrics: () => {
|
|
2190
2315
|
const now = Date.now();
|
|
2191
2316
|
const byCollection = {};
|
|
@@ -2330,5 +2455,5 @@ export {
|
|
|
2330
2455
|
createTestEngine
|
|
2331
2456
|
};
|
|
2332
2457
|
|
|
2333
|
-
//# debugId=
|
|
2458
|
+
//# debugId=19605A03FFBA9EF964756E2164756E21
|
|
2334
2459
|
//# sourceMappingURL=testing.js.map
|