@peerbit/document 15.1.6 → 15.1.7
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/README.md +30 -0
- 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/search.d.ts +5 -0
- package/dist/src/search.d.ts.map +1 -1
- package/dist/src/search.js +114 -3
- package/dist/src/search.js.map +1 -1
- package/package.json +13 -12
- package/src/program.ts +1 -0
- package/src/search.ts +158 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peerbit/document",
|
|
3
|
-
"version": "15.1.
|
|
3
|
+
"version": "15.1.7",
|
|
4
4
|
"description": "Document store implementation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -61,20 +61,21 @@
|
|
|
61
61
|
"p-defer": "^4.0.0",
|
|
62
62
|
"uint8arrays": "^5.1.0",
|
|
63
63
|
"@peerbit/cache": "3.1.1",
|
|
64
|
-
"@peerbit/document-interface": "3.2.79",
|
|
65
64
|
"@peerbit/crypto": "3.1.6",
|
|
65
|
+
"@peerbit/document-interface": "3.2.79",
|
|
66
66
|
"@peerbit/indexer-cache": "0.3.0",
|
|
67
|
-
"@peerbit/
|
|
67
|
+
"@peerbit/diagnostics": "0.1.0",
|
|
68
68
|
"@peerbit/indexer-simple": "1.3.0",
|
|
69
|
+
"@peerbit/log": "6.2.35",
|
|
69
70
|
"@peerbit/indexer-sqlite3": "3.0.21",
|
|
70
71
|
"@peerbit/logger": "2.0.2",
|
|
71
|
-
"@peerbit/
|
|
72
|
-
"@peerbit/
|
|
73
|
-
"@peerbit/
|
|
74
|
-
"@peerbit/pubsub": "5.4.8",
|
|
75
|
-
"@peerbit/shared-log": "16.0.35",
|
|
72
|
+
"@peerbit/program": "6.0.64",
|
|
73
|
+
"@peerbit/pubsub": "5.4.9",
|
|
74
|
+
"@peerbit/indexer-interface": "3.1.0",
|
|
76
75
|
"@peerbit/stream-interface": "6.0.16",
|
|
77
|
-
"@peerbit/time": "3.0.1"
|
|
76
|
+
"@peerbit/time": "3.0.1",
|
|
77
|
+
"@peerbit/shared-log": "16.0.36",
|
|
78
|
+
"@peerbit/rpc": "6.2.0"
|
|
78
79
|
},
|
|
79
80
|
"optionalDependencies": {
|
|
80
81
|
"@peerbit/document-rust": "0.1.1"
|
|
@@ -85,10 +86,10 @@
|
|
|
85
86
|
"@types/pidusage": "^2.0.5",
|
|
86
87
|
"pidusage": "^4.0.1",
|
|
87
88
|
"uuid": "^11.1.1",
|
|
88
|
-
"@peerbit/log-rust": "1.1.5",
|
|
89
89
|
"@peerbit/native-backbone": "0.2.17",
|
|
90
|
-
"@peerbit/test-utils": "3.1.
|
|
91
|
-
"peerbit": "
|
|
90
|
+
"@peerbit/test-utils": "3.1.45",
|
|
91
|
+
"@peerbit/log-rust": "1.1.5",
|
|
92
|
+
"peerbit": "5.4.6"
|
|
92
93
|
},
|
|
93
94
|
"repository": {
|
|
94
95
|
"type": "git",
|
package/src/program.ts
CHANGED
|
@@ -2622,6 +2622,7 @@ export class Documents<
|
|
|
2622
2622
|
prefetch: options.index?.prefetch,
|
|
2623
2623
|
includeIndexed: options.index?.includeIndexed,
|
|
2624
2624
|
immutable: this.immutable,
|
|
2625
|
+
profile: options.sync?.profile,
|
|
2625
2626
|
});
|
|
2626
2627
|
this._documentInternalChangeListenerCount = Math.max(
|
|
2627
2628
|
0,
|
package/src/search.ts
CHANGED
|
@@ -14,6 +14,10 @@ import {
|
|
|
14
14
|
PublicSignKey,
|
|
15
15
|
sha256Base64Sync,
|
|
16
16
|
} from "@peerbit/crypto";
|
|
17
|
+
import {
|
|
18
|
+
type DiagnosticSink,
|
|
19
|
+
createDiagnosticTrace,
|
|
20
|
+
} from "@peerbit/diagnostics";
|
|
17
21
|
import * as types from "@peerbit/document-interface";
|
|
18
22
|
import { CachedIndex, type QueryCacheOptions } from "@peerbit/indexer-cache";
|
|
19
23
|
import * as indexerTypes from "@peerbit/indexer-interface";
|
|
@@ -79,6 +83,11 @@ import {
|
|
|
79
83
|
} from "./transform.js";
|
|
80
84
|
|
|
81
85
|
const WARNING_WHEN_ITERATING_FOR_MORE_THAN = 1e5;
|
|
86
|
+
const QUERY_PROFILE_TARGET_LIMIT = 16;
|
|
87
|
+
type QueryProfile = {
|
|
88
|
+
trace: NonNullable<ReturnType<typeof createDiagnosticTrace>>;
|
|
89
|
+
missingGroups: number;
|
|
90
|
+
};
|
|
82
91
|
|
|
83
92
|
const logger = loggerFn("peerbit:program:document:search");
|
|
84
93
|
const warn = logger.newScope("warn");
|
|
@@ -838,6 +847,8 @@ export type OpenOptions<
|
|
|
838
847
|
prefetch?: boolean | Partial<PrefetchOptions>;
|
|
839
848
|
includeIndexed?: boolean; // if true, indexed representations will always be included in the search results
|
|
840
849
|
immutable?: boolean; // conflict rule of the owning store: oldest version wins when true (mirrors Documents "immutable")
|
|
850
|
+
/** Advisory query timing; no effect on coverage, authorization or deadlines. */
|
|
851
|
+
profile?: DiagnosticSink;
|
|
841
852
|
};
|
|
842
853
|
|
|
843
854
|
type IndexableClass<I> = new (
|
|
@@ -1089,6 +1100,7 @@ export class DocumentIndex<
|
|
|
1089
1100
|
) => Promise<void>;
|
|
1090
1101
|
|
|
1091
1102
|
private _log: SharedLog<Operation, D, any>;
|
|
1103
|
+
private _queryProfile?: DiagnosticSink;
|
|
1092
1104
|
|
|
1093
1105
|
private _resolverProgramCache?: Map<string | number | bigint, T>;
|
|
1094
1106
|
private _resolverCache?: Cache<T>;
|
|
@@ -1534,6 +1546,7 @@ export class DocumentIndex<
|
|
|
1534
1546
|
}
|
|
1535
1547
|
async open(properties: OpenOptions<T, I, D>) {
|
|
1536
1548
|
this._log = properties.log;
|
|
1549
|
+
this._queryProfile = properties.profile;
|
|
1537
1550
|
// Allow reopening with partial options (tests override the index transform)
|
|
1538
1551
|
const previousEvents = this.documentEvents;
|
|
1539
1552
|
this.documentEvents =
|
|
@@ -2305,6 +2318,7 @@ export class DocumentIndex<
|
|
|
2305
2318
|
async close(from?: Program): Promise<boolean> {
|
|
2306
2319
|
const closed = await super.close(from);
|
|
2307
2320
|
if (closed) {
|
|
2321
|
+
this._queryProfile = undefined;
|
|
2308
2322
|
if (this._joinListener) {
|
|
2309
2323
|
this._query.events.removeEventListener("join", this._joinListener);
|
|
2310
2324
|
}
|
|
@@ -2339,6 +2353,7 @@ export class DocumentIndex<
|
|
|
2339
2353
|
async drop(from?: Program): Promise<boolean> {
|
|
2340
2354
|
const dropped = await super.drop(from);
|
|
2341
2355
|
if (dropped) {
|
|
2356
|
+
this._queryProfile = undefined;
|
|
2342
2357
|
this.documentEvents?.removeEventListener(
|
|
2343
2358
|
"change",
|
|
2344
2359
|
this.handleDocumentChange,
|
|
@@ -4210,7 +4225,55 @@ export class DocumentIndex<
|
|
|
4210
4225
|
* @param options
|
|
4211
4226
|
* @returns
|
|
4212
4227
|
*/
|
|
4213
|
-
private
|
|
4228
|
+
private queryCommence<
|
|
4229
|
+
R extends
|
|
4230
|
+
| types.SearchRequest
|
|
4231
|
+
| types.SearchRequestIndexed
|
|
4232
|
+
| types.IterationRequest,
|
|
4233
|
+
RT extends types.Result = types.ResultTypeFromRequest<R, T, I>,
|
|
4234
|
+
>(
|
|
4235
|
+
queryRequest: R,
|
|
4236
|
+
options?: QueryDetailedOptions<T, I, D, boolean | undefined>,
|
|
4237
|
+
fetchFirstForRemote?: Set<string>,
|
|
4238
|
+
): Promise<types.Results<RT>[]> {
|
|
4239
|
+
const trace = createDiagnosticTrace(
|
|
4240
|
+
this._queryProfile,
|
|
4241
|
+
"document",
|
|
4242
|
+
"query",
|
|
4243
|
+
);
|
|
4244
|
+
if (!trace) {
|
|
4245
|
+
return this.queryCommenceWithProfile<R, RT>(
|
|
4246
|
+
queryRequest,
|
|
4247
|
+
options,
|
|
4248
|
+
fetchFirstForRemote,
|
|
4249
|
+
);
|
|
4250
|
+
}
|
|
4251
|
+
const profile: QueryProfile = { trace, missingGroups: 0 };
|
|
4252
|
+
let outcome = "rejected";
|
|
4253
|
+
trace.emit({
|
|
4254
|
+
name: "documents.query.start",
|
|
4255
|
+
details: { immutable: this.immutable },
|
|
4256
|
+
});
|
|
4257
|
+
return (async () => {
|
|
4258
|
+
try {
|
|
4259
|
+
const results = await this.queryCommenceWithProfile<R, RT>(
|
|
4260
|
+
queryRequest,
|
|
4261
|
+
options,
|
|
4262
|
+
fetchFirstForRemote,
|
|
4263
|
+
profile,
|
|
4264
|
+
);
|
|
4265
|
+
outcome = "fulfilled";
|
|
4266
|
+
return results;
|
|
4267
|
+
} finally {
|
|
4268
|
+
trace.finish({
|
|
4269
|
+
name: "documents.query.settle",
|
|
4270
|
+
details: { outcome, missingGroups: profile.missingGroups },
|
|
4271
|
+
});
|
|
4272
|
+
}
|
|
4273
|
+
})();
|
|
4274
|
+
}
|
|
4275
|
+
|
|
4276
|
+
private async queryCommenceWithProfile<
|
|
4214
4277
|
R extends
|
|
4215
4278
|
| types.SearchRequest
|
|
4216
4279
|
| types.SearchRequestIndexed
|
|
@@ -4220,7 +4283,9 @@ export class DocumentIndex<
|
|
|
4220
4283
|
queryRequest: R,
|
|
4221
4284
|
options?: QueryDetailedOptions<T, I, D, boolean | undefined>,
|
|
4222
4285
|
fetchFirstForRemote?: Set<string>,
|
|
4286
|
+
profile?: QueryProfile,
|
|
4223
4287
|
): Promise<types.Results<RT>[]> {
|
|
4288
|
+
const trace = profile?.trace;
|
|
4224
4289
|
const local = typeof options?.local === "boolean" ? options?.local : true;
|
|
4225
4290
|
let remote:
|
|
4226
4291
|
| RemoteQueryOptions<
|
|
@@ -4259,11 +4324,22 @@ export class DocumentIndex<
|
|
|
4259
4324
|
[];
|
|
4260
4325
|
|
|
4261
4326
|
if (local) {
|
|
4327
|
+
const startedAt = trace?.now();
|
|
4328
|
+
trace?.emit({
|
|
4329
|
+
name: "documents.query.local",
|
|
4330
|
+
details: { edge: "start" },
|
|
4331
|
+
});
|
|
4262
4332
|
const results = await this.processQuery(
|
|
4263
4333
|
queryRequest,
|
|
4264
4334
|
this.node.identity.publicKey,
|
|
4265
4335
|
true,
|
|
4266
4336
|
);
|
|
4337
|
+
trace?.emit({
|
|
4338
|
+
name: "documents.query.local",
|
|
4339
|
+
durationMs: trace.now() - startedAt!,
|
|
4340
|
+
entries: results.results.length,
|
|
4341
|
+
details: { edge: "end" },
|
|
4342
|
+
});
|
|
4267
4343
|
if (results.results.length > 0) {
|
|
4268
4344
|
options?.onResponse &&
|
|
4269
4345
|
(await options.onResponse(results, this.node.identity.publicKey));
|
|
@@ -4284,6 +4360,11 @@ export class DocumentIndex<
|
|
|
4284
4360
|
(!("args" in coverProps) || (coverProps as any).args == null);
|
|
4285
4361
|
const remoteWasExplicit = options?.remote != null;
|
|
4286
4362
|
|
|
4363
|
+
const coverStartedAt = trace?.now();
|
|
4364
|
+
trace?.emit({
|
|
4365
|
+
name: "documents.query.cover",
|
|
4366
|
+
details: { edge: "start" },
|
|
4367
|
+
});
|
|
4287
4368
|
let replicatorGroups = options?.remote?.from
|
|
4288
4369
|
? options?.remote?.from
|
|
4289
4370
|
: await this._log.getCover(coverProps, {
|
|
@@ -4292,6 +4373,16 @@ export class DocumentIndex<
|
|
|
4292
4373
|
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
|
|
4293
4374
|
signal: options?.signal,
|
|
4294
4375
|
});
|
|
4376
|
+
trace?.emit({
|
|
4377
|
+
name: "documents.query.cover",
|
|
4378
|
+
durationMs: trace.now() - coverStartedAt!,
|
|
4379
|
+
targets: replicatorGroups.length,
|
|
4380
|
+
details: {
|
|
4381
|
+
edge: "end",
|
|
4382
|
+
explicit: !!options?.remote?.from,
|
|
4383
|
+
reachableOnly: !!remote.wait,
|
|
4384
|
+
},
|
|
4385
|
+
});
|
|
4295
4386
|
|
|
4296
4387
|
// Cold start: cover can be temporarily self-only or empty while
|
|
4297
4388
|
// replication metadata converges. For explicit bounded remote searches,
|
|
@@ -4365,6 +4456,11 @@ export class DocumentIndex<
|
|
|
4365
4456
|
from?: PublicSignKey;
|
|
4366
4457
|
}[],
|
|
4367
4458
|
) => {
|
|
4459
|
+
const startedAt = trace?.now();
|
|
4460
|
+
trace?.emit({
|
|
4461
|
+
name: "documents.query.introduce",
|
|
4462
|
+
details: { edge: "start" },
|
|
4463
|
+
});
|
|
4368
4464
|
const resultInitialized = await introduceEntries(
|
|
4369
4465
|
queryRequest,
|
|
4370
4466
|
results,
|
|
@@ -4373,6 +4469,12 @@ export class DocumentIndex<
|
|
|
4373
4469
|
this._sync,
|
|
4374
4470
|
options,
|
|
4375
4471
|
);
|
|
4472
|
+
trace?.emit({
|
|
4473
|
+
name: "documents.query.introduce",
|
|
4474
|
+
durationMs: trace.now() - startedAt!,
|
|
4475
|
+
count: resultInitialized.length,
|
|
4476
|
+
details: { edge: "end" },
|
|
4477
|
+
});
|
|
4376
4478
|
for (const r of resultInitialized) {
|
|
4377
4479
|
resolved.push(r.response);
|
|
4378
4480
|
}
|
|
@@ -4425,9 +4527,54 @@ export class DocumentIndex<
|
|
|
4425
4527
|
.map((x) => [x]);
|
|
4426
4528
|
|
|
4427
4529
|
options?.onRemoteTargets?.(selectedRemoteHashes);
|
|
4530
|
+
if (trace) {
|
|
4531
|
+
trace.emit({
|
|
4532
|
+
name: "documents.query.targets",
|
|
4533
|
+
targets: selectedRemoteHashes.length,
|
|
4534
|
+
details: {
|
|
4535
|
+
requestTargets: groupHashes.length,
|
|
4536
|
+
sampledTargets: Math.min(
|
|
4537
|
+
QUERY_PROFILE_TARGET_LIMIT,
|
|
4538
|
+
selectedRemoteHashes.length,
|
|
4539
|
+
),
|
|
4540
|
+
},
|
|
4541
|
+
});
|
|
4542
|
+
for (
|
|
4543
|
+
let i = 0;
|
|
4544
|
+
i <
|
|
4545
|
+
Math.min(QUERY_PROFILE_TARGET_LIMIT, selectedRemoteHashes.length);
|
|
4546
|
+
i++
|
|
4547
|
+
) {
|
|
4548
|
+
const hash = selectedRemoteHashes[i];
|
|
4549
|
+
// Presence is only local direct-peer evidence, not reachability,
|
|
4550
|
+
// subscriber readiness, authority or receipt eligibility.
|
|
4551
|
+
const peers = (
|
|
4552
|
+
this.node.services.pubsub as { peers?: Map<string, unknown> }
|
|
4553
|
+
).peers;
|
|
4554
|
+
trace.emit({
|
|
4555
|
+
name: "documents.query.target",
|
|
4556
|
+
peer: hash,
|
|
4557
|
+
details: {
|
|
4558
|
+
index: i,
|
|
4559
|
+
directPeerPresent:
|
|
4560
|
+
peers instanceof Map ? peers.has(hash) : undefined,
|
|
4561
|
+
},
|
|
4562
|
+
});
|
|
4563
|
+
}
|
|
4564
|
+
}
|
|
4428
4565
|
extraPromises && (await Promise.all(extraPromises));
|
|
4429
4566
|
let tearDown: (() => void) | undefined = undefined;
|
|
4430
4567
|
const search = this;
|
|
4568
|
+
const requestOptions = trace
|
|
4569
|
+
? {
|
|
4570
|
+
...remote,
|
|
4571
|
+
profile: (event: Parameters<DiagnosticSink>[0]) =>
|
|
4572
|
+
trace.emit({
|
|
4573
|
+
...event,
|
|
4574
|
+
details: { ...event.details, requestTraceId: event.traceId },
|
|
4575
|
+
}),
|
|
4576
|
+
}
|
|
4577
|
+
: remote;
|
|
4431
4578
|
|
|
4432
4579
|
try {
|
|
4433
4580
|
groupHashes.length > 0 &&
|
|
@@ -4438,7 +4585,7 @@ export class DocumentIndex<
|
|
|
4438
4585
|
responseHandler,
|
|
4439
4586
|
search._prefetch?.accumulator
|
|
4440
4587
|
? {
|
|
4441
|
-
...
|
|
4588
|
+
...requestOptions,
|
|
4442
4589
|
responseInterceptor(fn) {
|
|
4443
4590
|
const listener = (evt: {
|
|
4444
4591
|
detail: {
|
|
@@ -4490,10 +4637,18 @@ export class DocumentIndex<
|
|
|
4490
4637
|
};
|
|
4491
4638
|
},
|
|
4492
4639
|
}
|
|
4493
|
-
:
|
|
4640
|
+
: requestOptions,
|
|
4494
4641
|
));
|
|
4495
4642
|
} catch (error) {
|
|
4496
4643
|
if (error instanceof MissingResponsesError) {
|
|
4644
|
+
if (profile) {
|
|
4645
|
+
profile.missingGroups = error.missingGroups.length;
|
|
4646
|
+
trace!.emit({
|
|
4647
|
+
name: "documents.query.missing",
|
|
4648
|
+
count: error.missingGroups.length,
|
|
4649
|
+
details: { tolerated: !remote.throwOnMissing },
|
|
4650
|
+
});
|
|
4651
|
+
}
|
|
4497
4652
|
warn("Did not reciveve responses from all shard");
|
|
4498
4653
|
if (options?.onMissingResponses) {
|
|
4499
4654
|
await options.onMissingResponses(error);
|