@peerbit/document 12.3.5-07ba572 → 12.3.5-cb91e7b
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/benchmark/iterate-replicate-2.js +0 -13
- package/dist/benchmark/iterate-replicate-2.js.map +1 -1
- package/dist/benchmark/iterate-replicate.js +0 -13
- package/dist/benchmark/iterate-replicate.js.map +1 -1
- package/dist/benchmark/replication-network.d.ts +2 -0
- package/dist/benchmark/replication-network.d.ts.map +1 -0
- package/dist/benchmark/replication-network.js +872 -0
- package/dist/benchmark/replication-network.js.map +1 -0
- package/dist/benchmark/replication.js +0 -19
- package/dist/benchmark/replication.js.map +1 -1
- package/dist/src/program.d.ts.map +1 -1
- package/dist/src/program.js +1 -0
- package/dist/src/program.js.map +1 -1
- package/dist/src/resumable-iterator.d.ts +1 -0
- package/dist/src/resumable-iterator.d.ts.map +1 -1
- package/dist/src/resumable-iterator.js +29 -0
- package/dist/src/resumable-iterator.js.map +1 -1
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +174 -67
- package/dist/src/search.js.map +1 -1
- package/package.json +18 -18
- package/src/program.ts +1 -0
- package/src/resumable-iterator.ts +33 -0
- package/src/search.ts +280 -162
package/dist/src/search.js
CHANGED
|
@@ -753,7 +753,24 @@ let DocumentIndex = (() => {
|
|
|
753
753
|
this.documentEvents.removeEventListener("change", this.handleDocumentChange);
|
|
754
754
|
}
|
|
755
755
|
this.clearAllResultQueues();
|
|
756
|
-
await this.
|
|
756
|
+
await this._resumableIterators.clearAll();
|
|
757
|
+
if (this.iteratorKeepAliveTimers) {
|
|
758
|
+
for (const timer of this.iteratorKeepAliveTimers.values()) {
|
|
759
|
+
clearTimeout(timer);
|
|
760
|
+
}
|
|
761
|
+
this.iteratorKeepAliveTimers.clear();
|
|
762
|
+
}
|
|
763
|
+
try {
|
|
764
|
+
await this.index?.stop?.();
|
|
765
|
+
}
|
|
766
|
+
catch (error) {
|
|
767
|
+
// Be defensive during teardown: stopping an already-stopped index shouldn't
|
|
768
|
+
// prevent closing the program and releasing timers/iterators.
|
|
769
|
+
if (error instanceof indexerTypes.NotStartedError) {
|
|
770
|
+
return closed;
|
|
771
|
+
}
|
|
772
|
+
throw error;
|
|
773
|
+
}
|
|
757
774
|
}
|
|
758
775
|
return closed;
|
|
759
776
|
}
|
|
@@ -762,13 +779,35 @@ let DocumentIndex = (() => {
|
|
|
762
779
|
if (dropped) {
|
|
763
780
|
this.documentEvents.removeEventListener("change", this.handleDocumentChange);
|
|
764
781
|
this.clearAllResultQueues();
|
|
765
|
-
await this.
|
|
766
|
-
|
|
782
|
+
await this._resumableIterators.clearAll();
|
|
783
|
+
if (this.iteratorKeepAliveTimers) {
|
|
784
|
+
for (const timer of this.iteratorKeepAliveTimers.values()) {
|
|
785
|
+
clearTimeout(timer);
|
|
786
|
+
}
|
|
787
|
+
this.iteratorKeepAliveTimers.clear();
|
|
788
|
+
}
|
|
789
|
+
try {
|
|
790
|
+
await this.index?.drop?.();
|
|
791
|
+
}
|
|
792
|
+
catch (error) {
|
|
793
|
+
if (!(error instanceof indexerTypes.NotStartedError)) {
|
|
794
|
+
throw error;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
try {
|
|
798
|
+
await this.index?.stop?.();
|
|
799
|
+
}
|
|
800
|
+
catch (error) {
|
|
801
|
+
if (!(error instanceof indexerTypes.NotStartedError)) {
|
|
802
|
+
throw error;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
767
805
|
}
|
|
768
806
|
return dropped;
|
|
769
807
|
}
|
|
770
808
|
async get(key, options) {
|
|
771
809
|
let deferred;
|
|
810
|
+
let baseRemote;
|
|
772
811
|
// Normalize the id key early so listeners can use it
|
|
773
812
|
let idKey = key instanceof indexerTypes.IdKey ? key : indexerTypes.toId(key);
|
|
774
813
|
if (options?.waitFor) {
|
|
@@ -801,11 +840,12 @@ let DocumentIndex = (() => {
|
|
|
801
840
|
this.documentEvents.addEventListener("change", listener);
|
|
802
841
|
deferred.promise.then(cleanup);
|
|
803
842
|
// Prepare remote options without mutating caller options
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
843
|
+
baseRemote =
|
|
844
|
+
options?.remote === false
|
|
845
|
+
? undefined
|
|
846
|
+
: typeof options?.remote === "object"
|
|
847
|
+
? { ...options.remote }
|
|
848
|
+
: {};
|
|
809
849
|
if (baseRemote) {
|
|
810
850
|
const waitPolicy = baseRemote.wait;
|
|
811
851
|
if (!waitPolicy ||
|
|
@@ -842,7 +882,10 @@ let DocumentIndex = (() => {
|
|
|
842
882
|
});
|
|
843
883
|
}
|
|
844
884
|
}
|
|
845
|
-
const
|
|
885
|
+
const initialOptions = baseRemote
|
|
886
|
+
? { ...options, remote: baseRemote }
|
|
887
|
+
: options;
|
|
888
|
+
const result = (await this.getDetailed(idKey, initialOptions))?.[0]?.results[0];
|
|
846
889
|
// if no results, and we have remote joining options, we wait for the timout and if there are joining peers we re-query
|
|
847
890
|
if (!result) {
|
|
848
891
|
return deferred?.promise;
|
|
@@ -1142,6 +1185,8 @@ let DocumentIndex = (() => {
|
|
|
1142
1185
|
fromQuery: (fromQuery || query),
|
|
1143
1186
|
resolveResults: resolveFlag,
|
|
1144
1187
|
};
|
|
1188
|
+
// Don't keep Node alive just to GC old remote iterator state.
|
|
1189
|
+
prevQueued.timeout.unref?.();
|
|
1145
1190
|
if (fromQuery instanceof types.IterationRequest &&
|
|
1146
1191
|
fromQuery.pushUpdates) {
|
|
1147
1192
|
prevQueued.pushMode = fromQuery.pushUpdates;
|
|
@@ -1228,6 +1273,8 @@ let DocumentIndex = (() => {
|
|
|
1228
1273
|
}
|
|
1229
1274
|
this._resumableIterators.close({ idString });
|
|
1230
1275
|
}, delay);
|
|
1276
|
+
// This is a best-effort cleanup timer; it should not keep Node alive.
|
|
1277
|
+
timer.unref?.();
|
|
1231
1278
|
timers.set(idString, timer);
|
|
1232
1279
|
}
|
|
1233
1280
|
cancelIteratorKeepAlive(idString) {
|
|
@@ -1409,12 +1456,7 @@ let DocumentIndex = (() => {
|
|
|
1409
1456
|
const local = typeof options?.local === "boolean" ? options?.local : true;
|
|
1410
1457
|
let remote = undefined;
|
|
1411
1458
|
if (typeof options?.remote === "boolean") {
|
|
1412
|
-
|
|
1413
|
-
remote = {};
|
|
1414
|
-
}
|
|
1415
|
-
else {
|
|
1416
|
-
remote = undefined;
|
|
1417
|
-
}
|
|
1459
|
+
remote = options.remote ? {} : undefined;
|
|
1418
1460
|
}
|
|
1419
1461
|
else {
|
|
1420
1462
|
remote = options?.remote || {};
|
|
@@ -1425,6 +1467,13 @@ let DocumentIndex = (() => {
|
|
|
1425
1467
|
// this will lead to bad UX as you usually want to list/expore whats going on before doing any replication work
|
|
1426
1468
|
remote.priority = 2;
|
|
1427
1469
|
}
|
|
1470
|
+
if (remote && remote.timeout == null && options?.remote) {
|
|
1471
|
+
const waitPolicy = typeof options.remote === "object" ? options.remote.wait : undefined;
|
|
1472
|
+
const waitTimeout = typeof waitPolicy === "object" ? waitPolicy.timeout : undefined;
|
|
1473
|
+
if (waitTimeout != null) {
|
|
1474
|
+
remote.timeout = waitTimeout;
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1428
1477
|
if (!local && !remote) {
|
|
1429
1478
|
throw new Error("Expecting either 'options.remote' or 'options.local' to be true");
|
|
1430
1479
|
}
|
|
@@ -1443,14 +1492,69 @@ let DocumentIndex = (() => {
|
|
|
1443
1492
|
// don't wait for responses
|
|
1444
1493
|
throw new Error("Unexpected");
|
|
1445
1494
|
}
|
|
1446
|
-
const
|
|
1495
|
+
const coverProps = remote.domain ?? { args: undefined };
|
|
1496
|
+
const isDefaultDomainArgs = !("range" in coverProps) &&
|
|
1497
|
+
(!("args" in coverProps) || coverProps.args == null);
|
|
1498
|
+
let replicatorGroups = options?.remote?.from
|
|
1447
1499
|
? options?.remote?.from
|
|
1448
|
-
: await this._log.getCover(
|
|
1500
|
+
: await this._log.getCover(coverProps, {
|
|
1449
1501
|
roleAge: remote.minAge,
|
|
1450
1502
|
eager: remote.reach?.eager,
|
|
1451
1503
|
reachableOnly: !!remote.wait, // when we want to merge joining we can ignore pending to be online peers and instead consider them once they become online
|
|
1452
1504
|
signal: options?.signal,
|
|
1453
1505
|
});
|
|
1506
|
+
// Cold start: cover can be temporarily empty/self-only while replication metadata
|
|
1507
|
+
// converges. For remote search, it's sometimes better to at least try currently
|
|
1508
|
+
// connected peers, but only if we have evidence that a remote replicator exists.
|
|
1509
|
+
if (!options?.remote?.from && isDefaultDomainArgs) {
|
|
1510
|
+
const selfHash = this.node.identity.publicKey.hashcode();
|
|
1511
|
+
const remoteCount = replicatorGroups.filter((h) => h !== selfHash).length;
|
|
1512
|
+
if (remoteCount === 0) {
|
|
1513
|
+
const waitEnabled = Boolean(remote.wait);
|
|
1514
|
+
const coverIsSelfOnly = replicatorGroups.length === 1 && replicatorGroups[0] === selfHash;
|
|
1515
|
+
// If the cover is explicitly empty (no shards), don't override it unless
|
|
1516
|
+
// the caller requested waiting for joins (e.g. get(waitFor)).
|
|
1517
|
+
if (!waitEnabled && !coverIsSelfOnly) {
|
|
1518
|
+
// no-op
|
|
1519
|
+
}
|
|
1520
|
+
else {
|
|
1521
|
+
let hasKnownRemoteReplicator = false;
|
|
1522
|
+
if (!waitEnabled) {
|
|
1523
|
+
try {
|
|
1524
|
+
const replicators = await this._log.getReplicators();
|
|
1525
|
+
for (const hash of replicators.keys()) {
|
|
1526
|
+
if (hash !== selfHash) {
|
|
1527
|
+
hasKnownRemoteReplicator = true;
|
|
1528
|
+
break;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
catch {
|
|
1533
|
+
// Best-effort only.
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
if (waitEnabled || hasKnownRemoteReplicator) {
|
|
1537
|
+
const peerMap = this.node.services
|
|
1538
|
+
.pubsub?.peers;
|
|
1539
|
+
if (peerMap?.keys) {
|
|
1540
|
+
const extra = [];
|
|
1541
|
+
for (const hash of peerMap.keys()) {
|
|
1542
|
+
if (!hash || hash === selfHash)
|
|
1543
|
+
continue;
|
|
1544
|
+
extra.push(hash);
|
|
1545
|
+
if (extra.length >= 8)
|
|
1546
|
+
break;
|
|
1547
|
+
}
|
|
1548
|
+
if (extra.length > 0) {
|
|
1549
|
+
replicatorGroups = [
|
|
1550
|
+
...new Set([...replicatorGroups, ...extra]),
|
|
1551
|
+
];
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1454
1558
|
if (replicatorGroups) {
|
|
1455
1559
|
const responseHandler = async (results) => {
|
|
1456
1560
|
const resultInitialized = await introduceEntries(queryRequest, results, this.documentType, this.indexedType, this._sync, options);
|
|
@@ -1575,9 +1679,8 @@ let DocumentIndex = (() => {
|
|
|
1575
1679
|
// Set fetch to search size, or max value (default to max u32 (4294967295))
|
|
1576
1680
|
const coercedRequest = coerceQuery(queryRequest, options, this.compatibility);
|
|
1577
1681
|
coercedRequest.fetch = coercedRequest.fetch ?? 0xffffffff;
|
|
1578
|
-
//
|
|
1682
|
+
// Use an iterator so large results respect message size limits.
|
|
1579
1683
|
const iterator = this.iterate(coercedRequest, options);
|
|
1580
|
-
// So that this call will not do any remote requests
|
|
1581
1684
|
const allResults = [];
|
|
1582
1685
|
while (iterator.done() !== true &&
|
|
1583
1686
|
coercedRequest.fetch > allResults.length) {
|
|
@@ -1838,36 +1941,42 @@ let DocumentIndex = (() => {
|
|
|
1838
1941
|
let discoveredTargetHashes;
|
|
1839
1942
|
if (typeof options?.remote === "object") {
|
|
1840
1943
|
let waitForTime = undefined;
|
|
1944
|
+
const waitPolicy = typeof options.remote.wait === "object"
|
|
1945
|
+
? options.remote.wait
|
|
1946
|
+
: undefined;
|
|
1947
|
+
const waitBehavior = waitPolicy?.behavior ?? "keep-open";
|
|
1841
1948
|
if (options.remote.wait) {
|
|
1842
|
-
let t0 = +new Date();
|
|
1843
1949
|
waitForTime =
|
|
1844
1950
|
typeof options.remote.wait === "boolean"
|
|
1845
1951
|
? DEFAULT_TIMEOUT
|
|
1846
1952
|
: (options.remote.wait.timeout ?? DEFAULT_TIMEOUT);
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1953
|
+
if (waitBehavior === "keep-open") {
|
|
1954
|
+
let t0 = +new Date();
|
|
1955
|
+
let setDoneIfTimeout = false;
|
|
1956
|
+
maybeSetDone = () => {
|
|
1957
|
+
if (t0 + waitForTime < +new Date()) {
|
|
1958
|
+
cleanup();
|
|
1959
|
+
done = true;
|
|
1960
|
+
}
|
|
1961
|
+
else {
|
|
1962
|
+
setDoneIfTimeout = true;
|
|
1963
|
+
}
|
|
1964
|
+
};
|
|
1965
|
+
unsetDone = () => {
|
|
1966
|
+
setDoneIfTimeout = false;
|
|
1967
|
+
done = false;
|
|
1968
|
+
};
|
|
1969
|
+
let timeout = setTimeout(() => {
|
|
1970
|
+
if (setDoneIfTimeout) {
|
|
1971
|
+
cleanup();
|
|
1972
|
+
done = true;
|
|
1973
|
+
}
|
|
1974
|
+
}, waitForTime);
|
|
1975
|
+
cleanup = () => {
|
|
1976
|
+
this.clearResultsQueue(queryRequestCoerced);
|
|
1977
|
+
clearTimeout(timeout);
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1871
1980
|
}
|
|
1872
1981
|
if (options.remote.reach?.discover) {
|
|
1873
1982
|
const discoverTimeout = waitForTime ??
|
|
@@ -1891,9 +2000,6 @@ let DocumentIndex = (() => {
|
|
|
1891
2000
|
warmupPromise = prior.then(() => discoverPromise);
|
|
1892
2001
|
options.remote.reach.eager = true; // include the results from the discovered peer even if it is not mature
|
|
1893
2002
|
}
|
|
1894
|
-
const waitPolicy = typeof options.remote.wait === "object"
|
|
1895
|
-
? options.remote.wait
|
|
1896
|
-
: undefined;
|
|
1897
2003
|
if (waitPolicy?.behavior === "block" &&
|
|
1898
2004
|
(waitPolicy.until ?? "any") === "any") {
|
|
1899
2005
|
const blockPromise = this.waitForCoverReady({
|
|
@@ -2300,25 +2406,19 @@ let DocumentIndex = (() => {
|
|
|
2300
2406
|
};
|
|
2301
2407
|
let close = async () => {
|
|
2302
2408
|
cleanupAndDone();
|
|
2303
|
-
// send close to remote
|
|
2409
|
+
// send close to remote (only peers that actually served results / had an active buffer)
|
|
2304
2410
|
const closeRequest = new types.CloseIteratorRequest({
|
|
2305
2411
|
id: queryRequestCoerced.id,
|
|
2306
2412
|
});
|
|
2307
|
-
const
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
...options,
|
|
2317
|
-
mode: new SilentDelivery({ to: [peer], redundancy: 1 }),
|
|
2318
|
-
}));
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
await Promise.all(promises);
|
|
2413
|
+
const selfHash = this.node.identity.publicKey.hashcode();
|
|
2414
|
+
const remotePeers = [...peerBufferMap.entries()]
|
|
2415
|
+
.filter(([peer, buffer]) => peer !== selfHash && buffer.kept > 0)
|
|
2416
|
+
.map(([peer]) => peer);
|
|
2417
|
+
peerBufferMap.clear();
|
|
2418
|
+
await Promise.allSettled(remotePeers.map((peer) => this._query.send(closeRequest, {
|
|
2419
|
+
...options,
|
|
2420
|
+
mode: new SilentDelivery({ to: [peer], redundancy: 1 }),
|
|
2421
|
+
})));
|
|
2322
2422
|
};
|
|
2323
2423
|
options?.signal && options.signal.addEventListener("abort", close);
|
|
2324
2424
|
let doneFn = () => {
|
|
@@ -2746,10 +2846,17 @@ let DocumentIndex = (() => {
|
|
|
2746
2846
|
return cleanupDefaultUpdates();
|
|
2747
2847
|
};
|
|
2748
2848
|
}
|
|
2749
|
-
|
|
2849
|
+
const remoteConfig = options && typeof options.remote === "object" ? options.remote : undefined;
|
|
2850
|
+
const remoteWaitPolicy = remoteConfig && typeof remoteConfig.wait === "object"
|
|
2851
|
+
? remoteConfig.wait
|
|
2852
|
+
: undefined;
|
|
2853
|
+
const remoteWaitBehavior = remoteWaitPolicy?.behavior ?? "keep-open";
|
|
2854
|
+
const keepRemoteWaitOpen = !!remoteConfig?.wait &&
|
|
2855
|
+
remoteWaitBehavior === "keep-open";
|
|
2856
|
+
if (keepRemoteWaitOpen) {
|
|
2750
2857
|
// was used to account for missed results when a peer joins; omitted in this minimal handler
|
|
2751
2858
|
updateDeferred = pDefer();
|
|
2752
|
-
const waitForTime =
|
|
2859
|
+
const waitForTime = remoteWaitPolicy?.timeout;
|
|
2753
2860
|
const prevMaybeSetDone = maybeSetDone;
|
|
2754
2861
|
maybeSetDone = () => {
|
|
2755
2862
|
prevMaybeSetDone();
|
|
@@ -2764,7 +2871,7 @@ let DocumentIndex = (() => {
|
|
|
2764
2871
|
fetchedFirstForRemote = new Set();
|
|
2765
2872
|
joinListener = this.createReplicatorJoinListener({
|
|
2766
2873
|
signal: ensureController().signal,
|
|
2767
|
-
eager:
|
|
2874
|
+
eager: remoteConfig?.reach?.eager,
|
|
2768
2875
|
onPeer: async (pk) => {
|
|
2769
2876
|
if (done)
|
|
2770
2877
|
return;
|
|
@@ -2823,7 +2930,7 @@ let DocumentIndex = (() => {
|
|
|
2823
2930
|
}
|
|
2824
2931
|
};
|
|
2825
2932
|
}
|
|
2826
|
-
const remoteWaitActive =
|
|
2933
|
+
const remoteWaitActive = keepRemoteWaitOpen;
|
|
2827
2934
|
const waitForUpdateAndResetDeferred = async () => {
|
|
2828
2935
|
if (remoteWaitActive) {
|
|
2829
2936
|
// wait until: join fetch adds results, cleanup runs, or the join-wait times out
|