@absolutejs/sync 1.20.0 → 1.21.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 +8 -0
- package/dist/engine/index.d.ts +1 -1
- package/dist/engine/index.js +299 -152
- package/dist/engine/index.js.map +5 -4
- package/dist/engine/syncEngine.d.ts +58 -0
- package/dist/index.js +298 -152
- package/dist/index.js.map +5 -4
- package/dist/testing.js +298 -152
- 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) {
|
|
@@ -634,6 +687,19 @@ class MutationQueueOverflowError extends Error {
|
|
|
634
687
|
this.queueLimit = queueLimit;
|
|
635
688
|
}
|
|
636
689
|
}
|
|
690
|
+
|
|
691
|
+
class SubscriptionLimitError extends Error {
|
|
692
|
+
tenantKey;
|
|
693
|
+
limit;
|
|
694
|
+
active;
|
|
695
|
+
constructor(tenantKey, limit, active) {
|
|
696
|
+
super(`Tenant "${tenantKey}" is at the subscription cap ` + `(${active}/${limit}). Close an existing subscription before opening another.`);
|
|
697
|
+
this.name = "SubscriptionLimitError";
|
|
698
|
+
this.tenantKey = tenantKey;
|
|
699
|
+
this.limit = limit;
|
|
700
|
+
this.active = active;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
637
703
|
var defaultKey = (row) => row.id;
|
|
638
704
|
var shallowEqual3 = (a, b) => {
|
|
639
705
|
if (a === b) {
|
|
@@ -784,6 +850,31 @@ var createSyncEngine = (options = {}) => {
|
|
|
784
850
|
if (next !== undefined)
|
|
785
851
|
next();
|
|
786
852
|
};
|
|
853
|
+
const subscriptionsByTenant = new Map;
|
|
854
|
+
const acquireSubscriptionSlot = (ctx, args) => {
|
|
855
|
+
const cap = options.subscriptionLimit;
|
|
856
|
+
if (cap === undefined)
|
|
857
|
+
return;
|
|
858
|
+
const tenantKey = cap.key(ctx, args);
|
|
859
|
+
if (tenantKey === undefined)
|
|
860
|
+
return;
|
|
861
|
+
const active2 = subscriptionsByTenant.get(tenantKey) ?? 0;
|
|
862
|
+
if (active2 >= cap.max) {
|
|
863
|
+
throw new SubscriptionLimitError(tenantKey, cap.max, active2);
|
|
864
|
+
}
|
|
865
|
+
subscriptionsByTenant.set(tenantKey, active2 + 1);
|
|
866
|
+
return tenantKey;
|
|
867
|
+
};
|
|
868
|
+
const releaseSubscriptionSlot = (tenantKey) => {
|
|
869
|
+
if (tenantKey === undefined)
|
|
870
|
+
return;
|
|
871
|
+
const active2 = subscriptionsByTenant.get(tenantKey);
|
|
872
|
+
if (active2 === undefined || active2 <= 1) {
|
|
873
|
+
subscriptionsByTenant.delete(tenantKey);
|
|
874
|
+
} else {
|
|
875
|
+
subscriptionsByTenant.set(tenantKey, active2 - 1);
|
|
876
|
+
}
|
|
877
|
+
};
|
|
787
878
|
const reactiveCacheMax = options.reactiveCache?.max ?? 256;
|
|
788
879
|
const reactiveCacheTtlMs = options.reactiveCache?.ttlMs ?? 60000;
|
|
789
880
|
const cachedReruns = new Map;
|
|
@@ -825,6 +916,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
825
916
|
const runInTransaction = options.transaction;
|
|
826
917
|
const instanceId = options.instanceId ?? globalThis.crypto?.randomUUID?.() ?? `i${Math.random()}`;
|
|
827
918
|
let clusterBus;
|
|
919
|
+
const tracer = tracerOrNoop(options.tracerProvider, "@absolutejs/sync");
|
|
828
920
|
const importChangeLog = (snapshot) => {
|
|
829
921
|
if (version !== 0) {
|
|
830
922
|
throw new Error(`[sync] importChangeLog: engine already has version ${version}; ` + `restore must happen before any local writes commit.`);
|
|
@@ -1616,89 +1708,125 @@ var createSyncEngine = (options = {}) => {
|
|
|
1616
1708
|
registry.set(collection.name, collection);
|
|
1617
1709
|
},
|
|
1618
1710
|
subscribe: async ({ collection, params, ctx, onDiff, since, signal }) => {
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
}
|
|
1624
|
-
const typedOnDiff = onDiff;
|
|
1625
|
-
const subscribeSet = subsFor(collection);
|
|
1626
|
-
const wrapReturn = (sub) => {
|
|
1627
|
-
checkAborted(signal);
|
|
1628
|
-
linkAbortToUnsubscribe(signal, sub.unsubscribe);
|
|
1629
|
-
return sub;
|
|
1630
|
-
};
|
|
1631
|
-
const registeredKind = registered.kind;
|
|
1632
|
-
if (registeredKind === "join") {
|
|
1633
|
-
const joined = await subscribeJoin(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1634
|
-
return wrapReturn(joined);
|
|
1635
|
-
}
|
|
1636
|
-
if (registeredKind === "graph") {
|
|
1637
|
-
const graphed = await subscribeGraph(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1638
|
-
return wrapReturn(graphed);
|
|
1639
|
-
}
|
|
1640
|
-
if (registeredKind === "reactive") {
|
|
1641
|
-
const reactived = await subscribeReactive(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1642
|
-
return wrapReturn(reactived);
|
|
1643
|
-
}
|
|
1644
|
-
if (registeredKind === "search") {
|
|
1645
|
-
const searched = await subscribeSearch(collection, registered, params, ctx, typedOnDiff, subscribeSet);
|
|
1646
|
-
return wrapReturn(searched);
|
|
1647
|
-
}
|
|
1648
|
-
const definition = registered;
|
|
1649
|
-
if (definition.authorize !== undefined) {
|
|
1650
|
-
const allowed = await definition.authorize(params, ctx);
|
|
1651
|
-
if (!allowed) {
|
|
1652
|
-
throw new UnauthorizedError(`subscribe to collection "${collection}"`);
|
|
1711
|
+
const subscribeSpan = tracer.startSpan("sync.subscribe", {
|
|
1712
|
+
attributes: {
|
|
1713
|
+
[ABS_ATTRS.engineId]: instanceId,
|
|
1714
|
+
[ABS_ATTRS.collection]: collection
|
|
1653
1715
|
}
|
|
1654
|
-
}
|
|
1655
|
-
const key = definition.key ?? defaultKey;
|
|
1656
|
-
const match = definition.match;
|
|
1657
|
-
const tables = definition.tables ?? [collection];
|
|
1658
|
-
const scopedTable = tables.length === 1 ? tables[0] : undefined;
|
|
1659
|
-
const readRule = scopedTable !== undefined ? readRuleFor(scopedTable) : undefined;
|
|
1660
|
-
const rehydrate = async () => {
|
|
1661
|
-
const raw = [...await definition.hydrate(params, ctx)];
|
|
1662
|
-
const rows = scopedTable !== undefined ? raw.map((row) => migrateRow(scopedTable, row)) : raw;
|
|
1663
|
-
return readRule ? rows.filter((row) => readRule(ctx, row)) : rows;
|
|
1664
|
-
};
|
|
1665
|
-
const incremental = match !== undefined && tables.length === 1;
|
|
1666
|
-
const boundMatch = incremental ? (row) => match(row, params, ctx) && (readRule ? readRule(ctx, row) : true) : () => true;
|
|
1667
|
-
const view = createMaterializedView({
|
|
1668
|
-
key,
|
|
1669
|
-
match: boundMatch
|
|
1670
1716
|
});
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1717
|
+
try {
|
|
1718
|
+
checkAborted(signal);
|
|
1719
|
+
const registered = registry.get(collection);
|
|
1720
|
+
if (registered === undefined) {
|
|
1721
|
+
throw new Error(`Unknown collection "${collection}"`);
|
|
1722
|
+
}
|
|
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
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
return wrapReturn({
|
|
1810
|
+
initial: view.rows(),
|
|
1811
|
+
cursor: currentCursor(),
|
|
1812
|
+
version: atVersion,
|
|
1813
|
+
unsubscribe
|
|
1814
|
+
});
|
|
1815
|
+
} catch (error) {
|
|
1816
|
+
if (!slotHandedOff)
|
|
1817
|
+
releaseSubscriptionSlot(tenantSlot);
|
|
1818
|
+
throw error;
|
|
1819
|
+
}
|
|
1820
|
+
} catch (spanError) {
|
|
1821
|
+
subscribeSpan.recordException(spanError);
|
|
1822
|
+
subscribeSpan.setStatus({
|
|
1823
|
+
code: 2,
|
|
1824
|
+
message: spanError instanceof Error ? spanError.message : String(spanError)
|
|
1694
1825
|
});
|
|
1826
|
+
throw spanError;
|
|
1827
|
+
} finally {
|
|
1828
|
+
subscribeSpan.end();
|
|
1695
1829
|
}
|
|
1696
|
-
return wrapReturn({
|
|
1697
|
-
initial: view.rows(),
|
|
1698
|
-
cursor: currentCursor(),
|
|
1699
|
-
version: atVersion,
|
|
1700
|
-
unsubscribe
|
|
1701
|
-
});
|
|
1702
1830
|
},
|
|
1703
1831
|
hydrate: async (collection, params, ctx, options2) => {
|
|
1704
1832
|
const signal = options2?.signal;
|
|
@@ -1801,85 +1929,102 @@ var createSyncEngine = (options = {}) => {
|
|
|
1801
1929
|
},
|
|
1802
1930
|
migrate: (table, row) => migrateRow(table, row),
|
|
1803
1931
|
runMutation: async (name, args, ctx) => {
|
|
1804
|
-
const
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
if (mutation.authorize !== undefined) {
|
|
1809
|
-
const allowed = await mutation.authorize(args, ctx);
|
|
1810
|
-
if (!allowed) {
|
|
1811
|
-
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
|
|
1812
1936
|
}
|
|
1813
|
-
}
|
|
1814
|
-
await acquireMutationSlot();
|
|
1815
|
-
const sandboxRunner = sandboxRunners.get(name);
|
|
1816
|
-
const invokeHandler = sandboxRunner !== undefined ? sandboxRunner : (a, c, actions) => Promise.resolve(mutation.handler(a, c, actions));
|
|
1817
|
-
const runHandler = async (tx) => {
|
|
1818
|
-
const { actions, buffered } = makeActions(tx, ctx, true);
|
|
1819
|
-
const result = await invokeHandler(args, ctx, actions);
|
|
1820
|
-
return { buffered, result };
|
|
1821
|
-
};
|
|
1822
|
-
const retry = mutation.retry;
|
|
1823
|
-
const maxAttempts = retry === undefined ? 1 : retry.maxAttempts ?? 5;
|
|
1824
|
-
const isRetryable = retry?.isRetryable ?? isSerializationFailure;
|
|
1825
|
-
const computeDelay = retry?.backoff ?? exponentialBackoff();
|
|
1826
|
-
const maxElapsedMs = retry?.maxElapsedMs ?? 30000;
|
|
1827
|
-
const startedAt = Date.now();
|
|
1828
|
-
let lastError;
|
|
1829
|
-
let attemptsMade = 0;
|
|
1937
|
+
});
|
|
1830
1938
|
try {
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
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
|
+
}
|
|
1867
2003
|
}
|
|
1868
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();
|
|
1869
2018
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
status: "error"
|
|
2019
|
+
} catch (spanError) {
|
|
2020
|
+
span.recordException(spanError);
|
|
2021
|
+
span.setStatus({
|
|
2022
|
+
code: 2,
|
|
2023
|
+
message: spanError instanceof Error ? spanError.message : String(spanError)
|
|
1876
2024
|
});
|
|
1877
|
-
|
|
1878
|
-
throw new RetriesExhaustedError(attemptsMade, Date.now() - startedAt, lastError);
|
|
1879
|
-
}
|
|
1880
|
-
throw lastError;
|
|
2025
|
+
throw spanError;
|
|
1881
2026
|
} finally {
|
|
1882
|
-
|
|
2027
|
+
span.end();
|
|
1883
2028
|
}
|
|
1884
2029
|
},
|
|
1885
2030
|
runMutations: async (specs, ctx) => {
|
|
@@ -2163,6 +2308,7 @@ var createSyncEngine = (options = {}) => {
|
|
|
2163
2308
|
},
|
|
2164
2309
|
subscriptions: {
|
|
2165
2310
|
byCollection,
|
|
2311
|
+
byTenant: Object.fromEntries(subscriptionsByTenant),
|
|
2166
2312
|
total: totalSubscriptions
|
|
2167
2313
|
},
|
|
2168
2314
|
uptimeMs: now - engineStartedAt,
|
|
@@ -2272,5 +2418,5 @@ export {
|
|
|
2272
2418
|
createTestEngine
|
|
2273
2419
|
};
|
|
2274
2420
|
|
|
2275
|
-
//# debugId=
|
|
2421
|
+
//# debugId=ED4D54755D5C43CD64756E2164756E21
|
|
2276
2422
|
//# sourceMappingURL=testing.js.map
|