@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/src/search.ts
CHANGED
|
@@ -1338,7 +1338,23 @@ export class DocumentIndex<
|
|
|
1338
1338
|
);
|
|
1339
1339
|
}
|
|
1340
1340
|
this.clearAllResultQueues();
|
|
1341
|
-
await this.
|
|
1341
|
+
await this._resumableIterators.clearAll();
|
|
1342
|
+
if (this.iteratorKeepAliveTimers) {
|
|
1343
|
+
for (const timer of this.iteratorKeepAliveTimers.values()) {
|
|
1344
|
+
clearTimeout(timer);
|
|
1345
|
+
}
|
|
1346
|
+
this.iteratorKeepAliveTimers.clear();
|
|
1347
|
+
}
|
|
1348
|
+
try {
|
|
1349
|
+
await this.index?.stop?.();
|
|
1350
|
+
} catch (error) {
|
|
1351
|
+
// Be defensive during teardown: stopping an already-stopped index shouldn't
|
|
1352
|
+
// prevent closing the program and releasing timers/iterators.
|
|
1353
|
+
if (error instanceof indexerTypes.NotStartedError) {
|
|
1354
|
+
return closed;
|
|
1355
|
+
}
|
|
1356
|
+
throw error;
|
|
1357
|
+
}
|
|
1342
1358
|
}
|
|
1343
1359
|
return closed;
|
|
1344
1360
|
}
|
|
@@ -1351,8 +1367,27 @@ export class DocumentIndex<
|
|
|
1351
1367
|
this.handleDocumentChange,
|
|
1352
1368
|
);
|
|
1353
1369
|
this.clearAllResultQueues();
|
|
1354
|
-
await this.
|
|
1355
|
-
|
|
1370
|
+
await this._resumableIterators.clearAll();
|
|
1371
|
+
if (this.iteratorKeepAliveTimers) {
|
|
1372
|
+
for (const timer of this.iteratorKeepAliveTimers.values()) {
|
|
1373
|
+
clearTimeout(timer);
|
|
1374
|
+
}
|
|
1375
|
+
this.iteratorKeepAliveTimers.clear();
|
|
1376
|
+
}
|
|
1377
|
+
try {
|
|
1378
|
+
await this.index?.drop?.();
|
|
1379
|
+
} catch (error) {
|
|
1380
|
+
if (!(error instanceof indexerTypes.NotStartedError)) {
|
|
1381
|
+
throw error;
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
try {
|
|
1385
|
+
await this.index?.stop?.();
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
if (!(error instanceof indexerTypes.NotStartedError)) {
|
|
1388
|
+
throw error;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1356
1391
|
}
|
|
1357
1392
|
return dropped;
|
|
1358
1393
|
}
|
|
@@ -1367,17 +1402,24 @@ export class DocumentIndex<
|
|
|
1367
1402
|
options?: Options,
|
|
1368
1403
|
): Promise<WithContext<I>>;
|
|
1369
1404
|
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1405
|
+
public async get<
|
|
1406
|
+
Options extends GetOptions<T, I, D, Resolve>,
|
|
1407
|
+
Resolve extends boolean | undefined = ExtractResolveFromOptions<Options>,
|
|
1408
|
+
>(key: indexerTypes.Ideable | indexerTypes.IdKey, options?: Options) {
|
|
1409
|
+
let deferred:
|
|
1410
|
+
| DeferredPromise<WithIndexedContext<T, I> | WithContext<I>>
|
|
1411
|
+
| undefined;
|
|
1412
|
+
let baseRemote:
|
|
1413
|
+
| RemoteQueryOptions<
|
|
1414
|
+
types.AbstractSearchRequest,
|
|
1415
|
+
types.AbstractSearchResult,
|
|
1416
|
+
D
|
|
1417
|
+
>
|
|
1418
|
+
| undefined;
|
|
1419
|
+
|
|
1420
|
+
// Normalize the id key early so listeners can use it
|
|
1421
|
+
let idKey =
|
|
1422
|
+
key instanceof indexerTypes.IdKey ? key : indexerTypes.toId(key);
|
|
1381
1423
|
|
|
1382
1424
|
if (options?.waitFor) {
|
|
1383
1425
|
// add change listener before query because we might get a concurrent change that matches the query,
|
|
@@ -1410,16 +1452,16 @@ export class DocumentIndex<
|
|
|
1410
1452
|
|
|
1411
1453
|
let timeout = setTimeout(resolveUndefined, options.waitFor);
|
|
1412
1454
|
this.events.addEventListener("close", resolveUndefined);
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1455
|
+
this.documentEvents.addEventListener("change", listener);
|
|
1456
|
+
deferred.promise.then(cleanup);
|
|
1457
|
+
|
|
1458
|
+
// Prepare remote options without mutating caller options
|
|
1459
|
+
baseRemote =
|
|
1460
|
+
options?.remote === false
|
|
1461
|
+
? undefined
|
|
1462
|
+
: typeof options?.remote === "object"
|
|
1463
|
+
? { ...options.remote }
|
|
1464
|
+
: {};
|
|
1423
1465
|
if (baseRemote) {
|
|
1424
1466
|
const waitPolicy = baseRemote.wait;
|
|
1425
1467
|
if (
|
|
@@ -1455,16 +1497,20 @@ export class DocumentIndex<
|
|
|
1455
1497
|
deferred!.resolve(first.value as any);
|
|
1456
1498
|
}
|
|
1457
1499
|
},
|
|
1458
|
-
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1459
1502
|
}
|
|
1460
|
-
}
|
|
1461
1503
|
|
|
1462
|
-
|
|
1504
|
+
const initialOptions = baseRemote
|
|
1505
|
+
? ({ ...(options as any), remote: baseRemote } as Options)
|
|
1506
|
+
: options;
|
|
1507
|
+
const result =
|
|
1508
|
+
(await this.getDetailed(idKey, initialOptions))?.[0]?.results[0];
|
|
1463
1509
|
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1510
|
+
// if no results, and we have remote joining options, we wait for the timout and if there are joining peers we re-query
|
|
1511
|
+
if (!result) {
|
|
1512
|
+
return deferred?.promise;
|
|
1513
|
+
} else if (deferred) {
|
|
1468
1514
|
deferred.resolve(undefined);
|
|
1469
1515
|
}
|
|
1470
1516
|
return result?.value;
|
|
@@ -1855,24 +1901,26 @@ export class DocumentIndex<
|
|
|
1855
1901
|
const resolveFlag = resolvesDocuments(
|
|
1856
1902
|
(fromQuery || query) as AnyIterationRequest,
|
|
1857
1903
|
);
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1904
|
+
prevQueued = {
|
|
1905
|
+
from,
|
|
1906
|
+
queue: [],
|
|
1907
|
+
timeout: setTimeout(() => {
|
|
1908
|
+
this._resultQueue.delete(query.idString);
|
|
1909
|
+
}, 6e4),
|
|
1910
|
+
keptInIndex: kept,
|
|
1911
|
+
fromQuery: (fromQuery || query) as
|
|
1912
|
+
| types.SearchRequest
|
|
1913
|
+
| types.SearchRequestIndexed
|
|
1914
|
+
| types.IterationRequest,
|
|
1915
|
+
resolveResults: resolveFlag,
|
|
1916
|
+
};
|
|
1917
|
+
// Don't keep Node alive just to GC old remote iterator state.
|
|
1918
|
+
prevQueued.timeout.unref?.();
|
|
1919
|
+
if (
|
|
1920
|
+
fromQuery instanceof types.IterationRequest &&
|
|
1921
|
+
fromQuery.pushUpdates
|
|
1922
|
+
) {
|
|
1923
|
+
prevQueued.pushMode = fromQuery.pushUpdates;
|
|
1876
1924
|
}
|
|
1877
1925
|
this._resultQueue.set(query.idString, prevQueued);
|
|
1878
1926
|
}
|
|
@@ -1970,17 +2018,19 @@ export class DocumentIndex<
|
|
|
1970
2018
|
string,
|
|
1971
2019
|
ReturnType<typeof setTimeout>
|
|
1972
2020
|
>());
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2021
|
+
const timer = setTimeout(() => {
|
|
2022
|
+
timers.delete(idString);
|
|
2023
|
+
const queued = this._resultQueue.get(idString);
|
|
2024
|
+
if (queued) {
|
|
2025
|
+
clearTimeout(queued.timeout);
|
|
2026
|
+
this._resultQueue.delete(idString);
|
|
2027
|
+
}
|
|
2028
|
+
this._resumableIterators.close({ idString });
|
|
2029
|
+
}, delay);
|
|
2030
|
+
// This is a best-effort cleanup timer; it should not keep Node alive.
|
|
2031
|
+
timer.unref?.();
|
|
2032
|
+
timers.set(idString, timer);
|
|
2033
|
+
}
|
|
1984
2034
|
|
|
1985
2035
|
private cancelIteratorKeepAlive(idString: string) {
|
|
1986
2036
|
const timers = this.iteratorKeepAliveTimers;
|
|
@@ -2213,30 +2263,35 @@ export class DocumentIndex<
|
|
|
2213
2263
|
queryRequest: R,
|
|
2214
2264
|
options?: QueryDetailedOptions<T, I, D, boolean | undefined>,
|
|
2215
2265
|
fetchFirstForRemote?: Set<string>,
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
remote = {};
|
|
2266
|
+
): Promise<types.Results<RT>[]> {
|
|
2267
|
+
const local = typeof options?.local === "boolean" ? options?.local : true;
|
|
2268
|
+
let remote:
|
|
2269
|
+
| RemoteQueryOptions<
|
|
2270
|
+
types.AbstractSearchRequest,
|
|
2271
|
+
types.AbstractSearchResult,
|
|
2272
|
+
D
|
|
2273
|
+
>
|
|
2274
|
+
| undefined = undefined;
|
|
2275
|
+
if (typeof options?.remote === "boolean") {
|
|
2276
|
+
remote = options.remote ? {} : undefined;
|
|
2228
2277
|
} else {
|
|
2229
|
-
remote =
|
|
2278
|
+
remote = options?.remote || {};
|
|
2230
2279
|
}
|
|
2231
|
-
} else {
|
|
2232
|
-
remote = options?.remote || {};
|
|
2233
|
-
}
|
|
2234
2280
|
if (remote && remote.priority == null) {
|
|
2235
2281
|
// give queries higher priority than other "normal" data activities
|
|
2236
2282
|
// without this, we might have a scenario that a peer joina network with large amount of data to be synced, but can not query anything before that is done
|
|
2237
2283
|
// this will lead to bad UX as you usually want to list/expore whats going on before doing any replication work
|
|
2238
2284
|
remote.priority = 2;
|
|
2239
2285
|
}
|
|
2286
|
+
if (remote && remote.timeout == null && options?.remote) {
|
|
2287
|
+
const waitPolicy =
|
|
2288
|
+
typeof options.remote === "object" ? options.remote.wait : undefined;
|
|
2289
|
+
const waitTimeout =
|
|
2290
|
+
typeof waitPolicy === "object" ? waitPolicy.timeout : undefined;
|
|
2291
|
+
if (waitTimeout != null) {
|
|
2292
|
+
remote.timeout = waitTimeout;
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2240
2295
|
|
|
2241
2296
|
if (!local && !remote) {
|
|
2242
2297
|
throw new Error(
|
|
@@ -2266,19 +2321,76 @@ export class DocumentIndex<
|
|
|
2266
2321
|
throw new Error("Unexpected");
|
|
2267
2322
|
}
|
|
2268
2323
|
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2324
|
+
const coverProps = remote.domain ?? { args: undefined };
|
|
2325
|
+
const isDefaultDomainArgs =
|
|
2326
|
+
!("range" in coverProps) &&
|
|
2327
|
+
(!("args" in coverProps) || (coverProps as any).args == null);
|
|
2328
|
+
|
|
2329
|
+
let replicatorGroups = options?.remote?.from
|
|
2330
|
+
? options?.remote?.from
|
|
2331
|
+
: await this._log.getCover(coverProps, {
|
|
2332
|
+
roleAge: remote.minAge,
|
|
2333
|
+
eager: remote.reach?.eager,
|
|
2334
|
+
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
|
|
2335
|
+
signal: options?.signal,
|
|
2336
|
+
});
|
|
2277
2337
|
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2338
|
+
// Cold start: cover can be temporarily empty/self-only while replication metadata
|
|
2339
|
+
// converges. For remote search, it's sometimes better to at least try currently
|
|
2340
|
+
// connected peers, but only if we have evidence that a remote replicator exists.
|
|
2341
|
+
if (!options?.remote?.from && isDefaultDomainArgs) {
|
|
2342
|
+
const selfHash = this.node.identity.publicKey.hashcode();
|
|
2343
|
+
const remoteCount = replicatorGroups.filter((h) => h !== selfHash).length;
|
|
2344
|
+
if (remoteCount === 0) {
|
|
2345
|
+
const waitEnabled = Boolean(remote.wait);
|
|
2346
|
+
const coverIsSelfOnly =
|
|
2347
|
+
replicatorGroups.length === 1 && replicatorGroups[0] === selfHash;
|
|
2348
|
+
|
|
2349
|
+
// If the cover is explicitly empty (no shards), don't override it unless
|
|
2350
|
+
// the caller requested waiting for joins (e.g. get(waitFor)).
|
|
2351
|
+
if (!waitEnabled && !coverIsSelfOnly) {
|
|
2352
|
+
// no-op
|
|
2353
|
+
} else {
|
|
2354
|
+
let hasKnownRemoteReplicator = false;
|
|
2355
|
+
if (!waitEnabled) {
|
|
2356
|
+
try {
|
|
2357
|
+
const replicators = await this._log.getReplicators();
|
|
2358
|
+
for (const hash of replicators.keys()) {
|
|
2359
|
+
if (hash !== selfHash) {
|
|
2360
|
+
hasKnownRemoteReplicator = true;
|
|
2361
|
+
break;
|
|
2362
|
+
}
|
|
2363
|
+
}
|
|
2364
|
+
} catch {
|
|
2365
|
+
// Best-effort only.
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
if (waitEnabled || hasKnownRemoteReplicator) {
|
|
2370
|
+
const peerMap: Map<string, unknown> | undefined = (this.node.services
|
|
2371
|
+
.pubsub as any)?.peers;
|
|
2372
|
+
if (peerMap?.keys) {
|
|
2373
|
+
const extra: string[] = [];
|
|
2374
|
+
for (const hash of peerMap.keys()) {
|
|
2375
|
+
if (!hash || hash === selfHash) continue;
|
|
2376
|
+
extra.push(hash);
|
|
2377
|
+
if (extra.length >= 8) break;
|
|
2378
|
+
}
|
|
2379
|
+
if (extra.length > 0) {
|
|
2380
|
+
replicatorGroups = [
|
|
2381
|
+
...new Set([...replicatorGroups, ...extra]),
|
|
2382
|
+
];
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
if (replicatorGroups) {
|
|
2391
|
+
const responseHandler = async (
|
|
2392
|
+
results: {
|
|
2393
|
+
response: types.AbstractSearchResult;
|
|
2282
2394
|
from?: PublicSignKey;
|
|
2283
2395
|
}[],
|
|
2284
2396
|
) => {
|
|
@@ -2464,18 +2576,17 @@ export class DocumentIndex<
|
|
|
2464
2576
|
options?: O,
|
|
2465
2577
|
): Promise<ValueTypeFromRequest<Resolve, T, I>[]> {
|
|
2466
2578
|
// Set fetch to search size, or max value (default to max u32 (4294967295))
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2579
|
+
const coercedRequest = coerceQuery(
|
|
2580
|
+
queryRequest,
|
|
2581
|
+
options,
|
|
2582
|
+
this.compatibility,
|
|
2583
|
+
);
|
|
2584
|
+
coercedRequest.fetch = coercedRequest.fetch ?? 0xffffffff;
|
|
2473
2585
|
|
|
2474
|
-
|
|
2475
|
-
|
|
2586
|
+
// Use an iterator so large results respect message size limits.
|
|
2587
|
+
const iterator = this.iterate<Resolve>(coercedRequest, options);
|
|
2476
2588
|
|
|
2477
|
-
|
|
2478
|
-
const allResults: ValueTypeFromRequest<Resolve, T, I>[] = [];
|
|
2589
|
+
const allResults: ValueTypeFromRequest<Resolve, T, I>[] = [];
|
|
2479
2590
|
|
|
2480
2591
|
while (
|
|
2481
2592
|
iterator.done() !== true &&
|
|
@@ -2876,37 +2987,43 @@ export class DocumentIndex<
|
|
|
2876
2987
|
|
|
2877
2988
|
if (typeof options?.remote === "object") {
|
|
2878
2989
|
let waitForTime: number | undefined = undefined;
|
|
2990
|
+
const waitPolicy =
|
|
2991
|
+
typeof options.remote.wait === "object"
|
|
2992
|
+
? options.remote.wait
|
|
2993
|
+
: undefined;
|
|
2994
|
+
const waitBehavior: WaitBehavior = waitPolicy?.behavior ?? "keep-open";
|
|
2879
2995
|
if (options.remote.wait) {
|
|
2880
|
-
let t0 = +new Date();
|
|
2881
|
-
|
|
2882
2996
|
waitForTime =
|
|
2883
2997
|
typeof options.remote.wait === "boolean"
|
|
2884
2998
|
? DEFAULT_TIMEOUT
|
|
2885
2999
|
: (options.remote.wait.timeout ?? DEFAULT_TIMEOUT);
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
3000
|
+
if (waitBehavior === "keep-open") {
|
|
3001
|
+
let t0 = +new Date();
|
|
3002
|
+
let setDoneIfTimeout = false;
|
|
3003
|
+
maybeSetDone = () => {
|
|
3004
|
+
if (t0 + waitForTime! < +new Date()) {
|
|
3005
|
+
cleanup();
|
|
3006
|
+
done = true;
|
|
3007
|
+
} else {
|
|
3008
|
+
setDoneIfTimeout = true;
|
|
3009
|
+
}
|
|
3010
|
+
};
|
|
3011
|
+
unsetDone = () => {
|
|
3012
|
+
setDoneIfTimeout = false;
|
|
3013
|
+
done = false;
|
|
3014
|
+
};
|
|
3015
|
+
let timeout = setTimeout(() => {
|
|
3016
|
+
if (setDoneIfTimeout) {
|
|
3017
|
+
cleanup();
|
|
3018
|
+
done = true;
|
|
3019
|
+
}
|
|
3020
|
+
}, waitForTime);
|
|
2905
3021
|
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
3022
|
+
cleanup = () => {
|
|
3023
|
+
this.clearResultsQueue(queryRequestCoerced);
|
|
3024
|
+
clearTimeout(timeout);
|
|
3025
|
+
};
|
|
3026
|
+
}
|
|
2910
3027
|
}
|
|
2911
3028
|
|
|
2912
3029
|
if (options.remote.reach?.discover) {
|
|
@@ -2933,10 +3050,6 @@ export class DocumentIndex<
|
|
|
2933
3050
|
options.remote.reach.eager = true; // include the results from the discovered peer even if it is not mature
|
|
2934
3051
|
}
|
|
2935
3052
|
|
|
2936
|
-
const waitPolicy =
|
|
2937
|
-
typeof options.remote.wait === "object"
|
|
2938
|
-
? options.remote.wait
|
|
2939
|
-
: undefined;
|
|
2940
3053
|
if (
|
|
2941
3054
|
waitPolicy?.behavior === "block" &&
|
|
2942
3055
|
(waitPolicy.until ?? "any") === "any"
|
|
@@ -2977,14 +3090,14 @@ export class DocumentIndex<
|
|
|
2977
3090
|
queryRequestCoerced.fetch = n;
|
|
2978
3091
|
await this.queryCommence(
|
|
2979
3092
|
queryRequestCoerced,
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
3093
|
+
{
|
|
3094
|
+
local: fetchOptions?.from != null ? false : options?.local,
|
|
3095
|
+
remote:
|
|
3096
|
+
options?.remote !== false && !skipRemoteDueToDiscovery
|
|
3097
|
+
? {
|
|
3098
|
+
...(typeof options?.remote === "object"
|
|
3099
|
+
? options.remote
|
|
3100
|
+
: {}),
|
|
2988
3101
|
from: fetchOptions?.from ?? initialRemoteTargets,
|
|
2989
3102
|
}
|
|
2990
3103
|
: false,
|
|
@@ -3533,32 +3646,27 @@ export class DocumentIndex<
|
|
|
3533
3646
|
done = true;
|
|
3534
3647
|
};
|
|
3535
3648
|
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
// send close to remote
|
|
3540
|
-
const closeRequest = new types.CloseIteratorRequest({
|
|
3541
|
-
id: queryRequestCoerced.id,
|
|
3542
|
-
});
|
|
3543
|
-
const promises: Promise<any>[] = [];
|
|
3649
|
+
let close = async () => {
|
|
3650
|
+
cleanupAndDone();
|
|
3544
3651
|
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3652
|
+
// send close to remote (only peers that actually served results / had an active buffer)
|
|
3653
|
+
const closeRequest = new types.CloseIteratorRequest({
|
|
3654
|
+
id: queryRequestCoerced.id,
|
|
3655
|
+
});
|
|
3656
|
+
const selfHash = this.node.identity.publicKey.hashcode();
|
|
3657
|
+
const remotePeers = [...peerBufferMap.entries()]
|
|
3658
|
+
.filter(([peer, buffer]) => peer !== selfHash && buffer.kept > 0)
|
|
3659
|
+
.map(([peer]) => peer);
|
|
3660
|
+
peerBufferMap.clear();
|
|
3661
|
+
await Promise.allSettled(
|
|
3662
|
+
remotePeers.map((peer) =>
|
|
3553
3663
|
this._query.send(closeRequest, {
|
|
3554
3664
|
...options,
|
|
3555
3665
|
mode: new SilentDelivery({ to: [peer], redundancy: 1 }),
|
|
3556
3666
|
}),
|
|
3557
|
-
)
|
|
3558
|
-
|
|
3559
|
-
}
|
|
3560
|
-
await Promise.all(promises);
|
|
3561
|
-
};
|
|
3667
|
+
),
|
|
3668
|
+
);
|
|
3669
|
+
};
|
|
3562
3670
|
options?.signal && options.signal.addEventListener("abort", close);
|
|
3563
3671
|
|
|
3564
3672
|
let doneFn = () => {
|
|
@@ -4144,13 +4252,24 @@ export class DocumentIndex<
|
|
|
4144
4252
|
};
|
|
4145
4253
|
}
|
|
4146
4254
|
|
|
4147
|
-
|
|
4255
|
+
const remoteConfig =
|
|
4256
|
+
options && typeof options.remote === "object" ? options.remote : undefined;
|
|
4257
|
+
const remoteWaitPolicy =
|
|
4258
|
+
remoteConfig && typeof remoteConfig.wait === "object"
|
|
4259
|
+
? remoteConfig.wait
|
|
4260
|
+
: undefined;
|
|
4261
|
+
const remoteWaitBehavior: WaitBehavior =
|
|
4262
|
+
remoteWaitPolicy?.behavior ?? "keep-open";
|
|
4263
|
+
const keepRemoteWaitOpen =
|
|
4264
|
+
!!remoteConfig?.wait &&
|
|
4265
|
+
remoteWaitBehavior === "keep-open";
|
|
4266
|
+
|
|
4267
|
+
if (keepRemoteWaitOpen) {
|
|
4148
4268
|
// was used to account for missed results when a peer joins; omitted in this minimal handler
|
|
4149
4269
|
|
|
4150
4270
|
updateDeferred = pDefer<void>();
|
|
4151
4271
|
|
|
4152
|
-
const waitForTime =
|
|
4153
|
-
typeof options.remote.wait === "object" && options.remote.wait.timeout;
|
|
4272
|
+
const waitForTime = remoteWaitPolicy?.timeout;
|
|
4154
4273
|
|
|
4155
4274
|
const prevMaybeSetDone = maybeSetDone;
|
|
4156
4275
|
maybeSetDone = () => {
|
|
@@ -4167,7 +4286,7 @@ export class DocumentIndex<
|
|
|
4167
4286
|
fetchedFirstForRemote = new Set<string>();
|
|
4168
4287
|
joinListener = this.createReplicatorJoinListener({
|
|
4169
4288
|
signal: ensureController().signal,
|
|
4170
|
-
eager:
|
|
4289
|
+
eager: remoteConfig?.reach?.eager,
|
|
4171
4290
|
onPeer: async (pk) => {
|
|
4172
4291
|
if (done) return;
|
|
4173
4292
|
const hash = pk.hashcode();
|
|
@@ -4240,8 +4359,7 @@ export class DocumentIndex<
|
|
|
4240
4359
|
}
|
|
4241
4360
|
};
|
|
4242
4361
|
}
|
|
4243
|
-
const remoteWaitActive =
|
|
4244
|
-
typeof options?.remote === "object" && !!options.remote.wait;
|
|
4362
|
+
const remoteWaitActive = keepRemoteWaitOpen;
|
|
4245
4363
|
|
|
4246
4364
|
const waitForUpdateAndResetDeferred = async () => {
|
|
4247
4365
|
if (remoteWaitActive) {
|