@peerbit/log 6.1.0 → 6.2.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/benchmark/native-graph.js +447 -5
- package/dist/benchmark/native-graph.js.map +1 -1
- package/dist/src/clock.d.ts +1 -0
- package/dist/src/clock.d.ts.map +1 -1
- package/dist/src/clock.js +32 -0
- package/dist/src/clock.js.map +1 -1
- package/dist/src/entry-create.d.ts +1 -0
- package/dist/src/entry-create.d.ts.map +1 -1
- package/dist/src/entry-create.js.map +1 -1
- package/dist/src/entry-index.d.ts +288 -12
- package/dist/src/entry-index.d.ts.map +1 -1
- package/dist/src/entry-index.js +1478 -33
- package/dist/src/entry-index.js.map +1 -1
- package/dist/src/entry-v0.d.ts +184 -1
- package/dist/src/entry-v0.d.ts.map +1 -1
- package/dist/src/entry-v0.js +1021 -12
- package/dist/src/entry-v0.js.map +1 -1
- package/dist/src/entry.d.ts +73 -1
- package/dist/src/entry.d.ts.map +1 -1
- package/dist/src/entry.js +124 -1
- package/dist/src/entry.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/log.d.ts +62 -13
- package/dist/src/log.d.ts.map +1 -1
- package/dist/src/log.js +2282 -100
- package/dist/src/log.js.map +1 -1
- package/dist/src/runtime.d.ts +2 -0
- package/dist/src/runtime.d.ts.map +1 -0
- package/dist/src/runtime.js +10 -0
- package/dist/src/runtime.js.map +1 -0
- package/dist/src/trim.d.ts +14 -2
- package/dist/src/trim.d.ts.map +1 -1
- package/dist/src/trim.js +92 -4
- package/dist/src/trim.js.map +1 -1
- package/package.json +18 -15
- package/src/clock.ts +34 -0
- package/src/entry-create.ts +1 -0
- package/src/entry-index.ts +2232 -68
- package/src/entry-v0.ts +1488 -13
- package/src/entry.ts +235 -1
- package/src/index.ts +7 -1
- package/src/log.ts +3246 -206
- package/src/runtime.ts +16 -0
- package/src/trim.ts +133 -7
package/dist/src/entry-index.js
CHANGED
|
@@ -3,14 +3,49 @@ import {} from "@peerbit/blocks-interface";
|
|
|
3
3
|
import { Cache } from "@peerbit/cache";
|
|
4
4
|
import { BoolQuery, Not, Or, Sort, SortDirection, StringMatch, StringMatchMethod, toId, } from "@peerbit/indexer-interface";
|
|
5
5
|
import { FOREGROUND_READ_MESSAGE_PRIORITY } from "@peerbit/stream-interface";
|
|
6
|
+
import { LamportClock as Clock, Timestamp } from "./clock.js";
|
|
7
|
+
import { ShallowEntry, ShallowMeta } from "./entry-shallow.js";
|
|
6
8
|
import { EntryType } from "./entry-type.js";
|
|
7
|
-
import { Entry } from "./entry.js";
|
|
9
|
+
import { Entry, } from "./entry.js";
|
|
8
10
|
import { logger as baseLogger } from "./logger.js";
|
|
9
11
|
const log = baseLogger.newScope("entry-index");
|
|
10
12
|
const LOG_ENTRY_REMOTE_READ_PRIORITY = FOREGROUND_READ_MESSAGE_PRIORITY;
|
|
11
13
|
const ENTRY_CACHE_MAX_SIZE = 10; // TODO as param for log
|
|
12
14
|
const DEFERRED_INDEX_FLUSH_IDLE_MS = 250;
|
|
13
15
|
const NATIVE_GRAPH_REBUILD_BATCH_SIZE = 512;
|
|
16
|
+
const TIMESTAMP_WALL_TIME_KEY = ["meta", "clock", "timestamp", "wallTime"];
|
|
17
|
+
const TIMESTAMP_LOGICAL_KEY = ["meta", "clock", "timestamp", "logical"];
|
|
18
|
+
const entryIndexProfileNow = () => globalThis.performance?.now?.() ?? Date.now();
|
|
19
|
+
const entryIndexProfileStart = (sink) => sink ? entryIndexProfileNow() : 0;
|
|
20
|
+
const emitEntryIndexProfileDuration = (sink, startedAt, event) => {
|
|
21
|
+
if (!sink) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
sink({
|
|
25
|
+
...event,
|
|
26
|
+
durationMs: entryIndexProfileNow() - startedAt,
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
const hasRmMany = (store) => typeof store.rmMany === "function";
|
|
30
|
+
const hasExactDelete = (index) => typeof index.delIds === "function";
|
|
31
|
+
const hasExactDeleteCount = (index) => typeof index.delIdsCount === "function";
|
|
32
|
+
const putPreparedEntryBlock = async (store, prepared) => {
|
|
33
|
+
const storeWithKnown = store;
|
|
34
|
+
return typeof storeWithKnown.putKnown === "function"
|
|
35
|
+
? await storeWithKnown.putKnown(prepared.cid, prepared.block.bytes)
|
|
36
|
+
: await store.put(prepared);
|
|
37
|
+
};
|
|
38
|
+
const isPromiseLike = (value) => !!value && typeof value.then === "function";
|
|
39
|
+
const mapMaybePromise = (value, fn) => (isPromiseLike(value) ? value.then(fn) : fn(value));
|
|
40
|
+
const materializePreparedAppendShallowEntry = (entry, isHead) => {
|
|
41
|
+
const shallowEntry = entry.shallowEntry ?? entry.getShallowEntry?.(isHead);
|
|
42
|
+
if (!shallowEntry) {
|
|
43
|
+
throw new Error("Missing prepared append shallow entry");
|
|
44
|
+
}
|
|
45
|
+
shallowEntry.head = isHead;
|
|
46
|
+
entry.shallowEntry = shallowEntry;
|
|
47
|
+
return shallowEntry;
|
|
48
|
+
};
|
|
14
49
|
const withDefaultRemoteReadPriority = (options) => {
|
|
15
50
|
if (!options?.remote) {
|
|
16
51
|
return options;
|
|
@@ -32,6 +67,27 @@ const withDefaultRemoteReadPriority = (options) => {
|
|
|
32
67
|
},
|
|
33
68
|
};
|
|
34
69
|
};
|
|
70
|
+
const canBatchResolveFromStore = (store, options) => {
|
|
71
|
+
if (typeof store.getMany !== "function") {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return typeof options !== "object" || !options.remote;
|
|
75
|
+
};
|
|
76
|
+
const sortKeyEquals = (sort, key) => Array.isArray(sort?.key) &&
|
|
77
|
+
sort.key.length === key.length &&
|
|
78
|
+
sort.key.every((part, index) => part === key[index]);
|
|
79
|
+
const timestampSortDirection = (sort) => {
|
|
80
|
+
if (sort.length < 2) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const [wallTime, logical] = sort;
|
|
84
|
+
if (!sortKeyEquals(wallTime, TIMESTAMP_WALL_TIME_KEY) ||
|
|
85
|
+
!sortKeyEquals(logical, TIMESTAMP_LOGICAL_KEY) ||
|
|
86
|
+
wallTime.direction !== logical.direction) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
return wallTime.direction;
|
|
90
|
+
};
|
|
35
91
|
export class EntryIndex {
|
|
36
92
|
properties;
|
|
37
93
|
cache;
|
|
@@ -41,6 +97,7 @@ export class EntryIndex {
|
|
|
41
97
|
insertionPromises;
|
|
42
98
|
pendingIndexWrites;
|
|
43
99
|
pendingIndexFlushTimer;
|
|
100
|
+
pendingIndexFlushLastWriteMs = 0;
|
|
44
101
|
constructor(properties) {
|
|
45
102
|
this.properties = properties;
|
|
46
103
|
this.sortReversed = properties.sort.sort.map((x) => deserialize(serialize(x), Sort));
|
|
@@ -53,15 +110,39 @@ export class EntryIndex {
|
|
|
53
110
|
this.insertionPromises = new Map();
|
|
54
111
|
this.pendingIndexWrites = new Map();
|
|
55
112
|
}
|
|
113
|
+
materializePendingIndexWrite(hash, pending) {
|
|
114
|
+
if (typeof pending !== "function") {
|
|
115
|
+
return pending;
|
|
116
|
+
}
|
|
117
|
+
const shallowEntry = pending();
|
|
118
|
+
this.pendingIndexWrites.set(hash, shallowEntry);
|
|
119
|
+
return shallowEntry;
|
|
120
|
+
}
|
|
121
|
+
getPendingIndexWrite(hash) {
|
|
122
|
+
const pending = this.pendingIndexWrites.get(hash);
|
|
123
|
+
return pending
|
|
124
|
+
? this.materializePendingIndexWrite(hash, pending)
|
|
125
|
+
: undefined;
|
|
126
|
+
}
|
|
56
127
|
schedulePendingIndexWriteFlush() {
|
|
128
|
+
this.pendingIndexFlushLastWriteMs = Date.now();
|
|
57
129
|
if (this.pendingIndexFlushTimer) {
|
|
58
|
-
|
|
130
|
+
return;
|
|
59
131
|
}
|
|
60
|
-
|
|
132
|
+
const flushAfterIdle = () => {
|
|
133
|
+
const remainingMs = DEFERRED_INDEX_FLUSH_IDLE_MS -
|
|
134
|
+
(Date.now() - this.pendingIndexFlushLastWriteMs);
|
|
135
|
+
if (remainingMs > 0) {
|
|
136
|
+
this.pendingIndexFlushTimer = setTimeout(flushAfterIdle, remainingMs);
|
|
137
|
+
this.pendingIndexFlushTimer.unref?.();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
this.pendingIndexFlushTimer = undefined;
|
|
61
141
|
void this.flushPendingWrites().catch((error) => {
|
|
62
142
|
log.error("Failed to flush deferred entry-index writes", error);
|
|
63
143
|
});
|
|
64
|
-
}
|
|
144
|
+
};
|
|
145
|
+
this.pendingIndexFlushTimer = setTimeout(flushAfterIdle, DEFERRED_INDEX_FLUSH_IDLE_MS);
|
|
65
146
|
this.pendingIndexFlushTimer.unref?.();
|
|
66
147
|
}
|
|
67
148
|
clearPendingIndexFlushTimer() {
|
|
@@ -79,13 +160,27 @@ export class EntryIndex {
|
|
|
79
160
|
return;
|
|
80
161
|
}
|
|
81
162
|
this.clearPendingIndexFlushTimer();
|
|
163
|
+
const writes = [];
|
|
82
164
|
for (const hash of keys) {
|
|
83
165
|
const pending = this.pendingIndexWrites.get(hash);
|
|
84
166
|
if (!pending) {
|
|
85
167
|
continue;
|
|
86
168
|
}
|
|
87
|
-
|
|
88
|
-
|
|
169
|
+
writes.push(this.materializePendingIndexWrite(hash, pending));
|
|
170
|
+
}
|
|
171
|
+
if (writes.length === 0) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (writes.length > 1 && this.properties.index.putBatch) {
|
|
175
|
+
await this.properties.index.putBatch(writes);
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
for (const write of writes) {
|
|
179
|
+
await this.properties.index.put(write);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const write of writes) {
|
|
183
|
+
this.pendingIndexWrites.delete(write.hash);
|
|
89
184
|
}
|
|
90
185
|
if (this.pendingIndexWrites.size > 0) {
|
|
91
186
|
this.schedulePendingIndexWriteFlush();
|
|
@@ -93,6 +188,15 @@ export class EntryIndex {
|
|
|
93
188
|
}
|
|
94
189
|
getHeads(gid, resolve = false) {
|
|
95
190
|
if (this.properties.nativeGraph?.useHeads) {
|
|
191
|
+
const shape = getResolveShape(resolve);
|
|
192
|
+
if (shape && isHeadHashOnlyShape(shape)) {
|
|
193
|
+
return this.iterateNativeProjected(() => this.properties
|
|
194
|
+
.nativeGraph.graph.heads(gid)
|
|
195
|
+
.map((hash) => ({ hash })), shape);
|
|
196
|
+
}
|
|
197
|
+
if (shape && isHeadDataShape(shape)) {
|
|
198
|
+
return this.iterateNativeProjected(() => this.properties.nativeGraph.graph.headDataEntries(gid), shape);
|
|
199
|
+
}
|
|
96
200
|
return this.iterateNativeHashes(() => this.properties.nativeGraph.graph.heads(gid), resolve);
|
|
97
201
|
}
|
|
98
202
|
const query = [];
|
|
@@ -107,6 +211,110 @@ export class EntryIndex {
|
|
|
107
211
|
}
|
|
108
212
|
return this.iterate(query, undefined, resolve);
|
|
109
213
|
}
|
|
214
|
+
getHeadsForAppend(gid) {
|
|
215
|
+
if (!this.properties.nativeGraph?.useHeads) {
|
|
216
|
+
return undefined;
|
|
217
|
+
}
|
|
218
|
+
return this.properties.nativeGraph.graph.headEntries(gid);
|
|
219
|
+
}
|
|
220
|
+
async hasHead(gid) {
|
|
221
|
+
if (!this.properties.nativeGraph?.useHeads) {
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
return this.properties.nativeGraph.graph.hasHead(gid);
|
|
225
|
+
}
|
|
226
|
+
async hasAnyHead(gids) {
|
|
227
|
+
if (!this.properties.nativeGraph?.useHeads) {
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
const uniqueGids = new Set([...gids].filter(Boolean));
|
|
231
|
+
if (uniqueGids.size === 0) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
return this.properties.nativeGraph.graph.hasAnyHead(uniqueGids);
|
|
235
|
+
}
|
|
236
|
+
async hasAnyHeadBatch(gidSets) {
|
|
237
|
+
if (!this.properties.nativeGraph?.useHeads) {
|
|
238
|
+
return undefined;
|
|
239
|
+
}
|
|
240
|
+
const normalized = [...gidSets].map((gids) => new Set([...gids].filter(Boolean)));
|
|
241
|
+
if (normalized.length === 0) {
|
|
242
|
+
return [];
|
|
243
|
+
}
|
|
244
|
+
return this.properties.nativeGraph.graph.hasAnyHeadBatch(normalized);
|
|
245
|
+
}
|
|
246
|
+
async getMaxHeadDataU32(gid) {
|
|
247
|
+
if (!this.properties.nativeGraph?.useHeads) {
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
return this.properties.nativeGraph.graph.maxHeadDataU32(gid);
|
|
251
|
+
}
|
|
252
|
+
async getMaxHeadDataU32Batch(gids) {
|
|
253
|
+
if (!this.properties.nativeGraph?.useHeads) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
const normalized = [...gids];
|
|
257
|
+
if (normalized.length === 0) {
|
|
258
|
+
return [];
|
|
259
|
+
}
|
|
260
|
+
return this.properties.nativeGraph.graph.maxHeadDataU32Batch?.(normalized);
|
|
261
|
+
}
|
|
262
|
+
getJoinHeads(gid) {
|
|
263
|
+
if (this.properties.nativeGraph?.useHeads) {
|
|
264
|
+
return Promise.resolve(this.properties.nativeGraph.graph.joinHeadEntries(gid));
|
|
265
|
+
}
|
|
266
|
+
return this.getHeads(gid, {
|
|
267
|
+
type: "shape",
|
|
268
|
+
shape: {
|
|
269
|
+
hash: true,
|
|
270
|
+
meta: { type: true, next: true, gid: true, clock: true },
|
|
271
|
+
},
|
|
272
|
+
}).all();
|
|
273
|
+
}
|
|
274
|
+
getJoinChildren(next) {
|
|
275
|
+
if (this.properties.nativeGraph) {
|
|
276
|
+
return Promise.resolve(this.properties.nativeGraph.graph.childJoinEntries(next));
|
|
277
|
+
}
|
|
278
|
+
return this.getHasNext(next, {
|
|
279
|
+
type: "shape",
|
|
280
|
+
shape: {
|
|
281
|
+
hash: true,
|
|
282
|
+
meta: { type: true, next: true, gid: true, clock: true },
|
|
283
|
+
},
|
|
284
|
+
}).all();
|
|
285
|
+
}
|
|
286
|
+
getUniqueReferenceGids(hash) {
|
|
287
|
+
if (!this.properties.nativeGraph) {
|
|
288
|
+
return undefined;
|
|
289
|
+
}
|
|
290
|
+
return this.properties.nativeGraph.graph.uniqueReferenceGids(hash);
|
|
291
|
+
}
|
|
292
|
+
getUniqueReferenceGidRowsBatch(hashes) {
|
|
293
|
+
if (!this.properties.nativeGraph) {
|
|
294
|
+
return undefined;
|
|
295
|
+
}
|
|
296
|
+
const normalized = [...hashes];
|
|
297
|
+
if (normalized.length === 0) {
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
return this.properties.nativeGraph.graph.uniqueReferenceGidRowsBatch?.(normalized);
|
|
301
|
+
}
|
|
302
|
+
getUniqueReferenceGidRowsFlatBatch(hashes) {
|
|
303
|
+
if (!this.properties.nativeGraph) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
const normalized = [...hashes];
|
|
307
|
+
if (normalized.length === 0) {
|
|
308
|
+
return [];
|
|
309
|
+
}
|
|
310
|
+
return this.properties.nativeGraph.graph.uniqueReferenceGidRowsFlatBatch?.(normalized);
|
|
311
|
+
}
|
|
312
|
+
planDeleteRecursively(from, skipFirst = false) {
|
|
313
|
+
if (!this.properties.nativeGraph) {
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
return this.properties.nativeGraph.graph.planDeleteRecursively([...from].map((entry) => entry.hash), skipFirst);
|
|
317
|
+
}
|
|
110
318
|
getHasNext(next, resolve) {
|
|
111
319
|
const query = [
|
|
112
320
|
new StringMatch({
|
|
@@ -122,10 +330,10 @@ export class EntryIndex {
|
|
|
122
330
|
return this._countHasNext(next, excludeHash);
|
|
123
331
|
}
|
|
124
332
|
async _countHasNext(next, excludeHash = undefined) {
|
|
125
|
-
await this.flushPendingWrites();
|
|
126
333
|
if (this.properties.nativeGraph) {
|
|
127
334
|
return this.properties.nativeGraph.graph.countHasNext(next, excludeHash);
|
|
128
335
|
}
|
|
336
|
+
await this.flushPendingWrites();
|
|
129
337
|
const query = [
|
|
130
338
|
new StringMatch({
|
|
131
339
|
key: ["meta", "next"],
|
|
@@ -161,13 +369,13 @@ export class EntryIndex {
|
|
|
161
369
|
let complete = false;
|
|
162
370
|
const getHashes = async () => {
|
|
163
371
|
if (!hashPromise) {
|
|
164
|
-
hashPromise =
|
|
372
|
+
hashPromise = Promise.resolve(hashes());
|
|
165
373
|
}
|
|
166
374
|
return hashPromise;
|
|
167
375
|
};
|
|
168
376
|
const coerce = async (hashes) => {
|
|
169
377
|
if (resolveInFull) {
|
|
170
|
-
const resolved = await
|
|
378
|
+
const resolved = await this.resolveMany(hashes, resolveInFullOptions);
|
|
171
379
|
return resolved.filter((entry) => !!entry);
|
|
172
380
|
}
|
|
173
381
|
const shallow = await Promise.all(hashes.map((hash) => this.getShallow(hash)));
|
|
@@ -194,6 +402,36 @@ export class EntryIndex {
|
|
|
194
402
|
},
|
|
195
403
|
};
|
|
196
404
|
}
|
|
405
|
+
iterateNativeProjected(values, shape) {
|
|
406
|
+
let valuesPromise;
|
|
407
|
+
let offset = 0;
|
|
408
|
+
let complete = false;
|
|
409
|
+
const getValues = async () => {
|
|
410
|
+
if (!valuesPromise) {
|
|
411
|
+
valuesPromise = Promise.resolve(values());
|
|
412
|
+
}
|
|
413
|
+
return valuesPromise;
|
|
414
|
+
};
|
|
415
|
+
const coerce = (values) => values.map((value) => projectShape(value, shape));
|
|
416
|
+
return {
|
|
417
|
+
close: () => undefined,
|
|
418
|
+
done: () => complete,
|
|
419
|
+
next: async (amount) => {
|
|
420
|
+
const all = await getValues();
|
|
421
|
+
const batch = all.slice(offset, offset + amount);
|
|
422
|
+
offset += batch.length;
|
|
423
|
+
complete = offset >= all.length;
|
|
424
|
+
return coerce(batch);
|
|
425
|
+
},
|
|
426
|
+
all: async () => {
|
|
427
|
+
const all = await getValues();
|
|
428
|
+
const remaining = all.slice(offset);
|
|
429
|
+
offset = all.length;
|
|
430
|
+
complete = true;
|
|
431
|
+
return coerce(remaining);
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
}
|
|
197
435
|
iterate(query, sort = this.properties.sort.sort, options) {
|
|
198
436
|
let resolveInFull = options
|
|
199
437
|
? options === true
|
|
@@ -226,7 +464,7 @@ export class EntryIndex {
|
|
|
226
464
|
};
|
|
227
465
|
const coerce = async (results) => {
|
|
228
466
|
if (resolveInFull) {
|
|
229
|
-
const maybeResolved = await
|
|
467
|
+
const maybeResolved = await this.resolveMany(results.map((x) => x.value.hash), resolveInFullOptions);
|
|
230
468
|
return maybeResolved.filter((x) => !!x);
|
|
231
469
|
}
|
|
232
470
|
else {
|
|
@@ -241,17 +479,87 @@ export class EntryIndex {
|
|
|
241
479
|
};
|
|
242
480
|
}
|
|
243
481
|
async getOldest(resolve) {
|
|
482
|
+
const nativeHash = this.getNativeTimestampOrderedHash("oldest");
|
|
483
|
+
if (nativeHash) {
|
|
484
|
+
const nativeResult = resolve
|
|
485
|
+
? await this.resolve(nativeHash)
|
|
486
|
+
: (await this.getShallow(nativeHash))?.value;
|
|
487
|
+
if (nativeResult) {
|
|
488
|
+
return nativeResult;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
244
491
|
const iterator = this.iterate([], this.properties.sort.sort, resolve);
|
|
245
492
|
const results = await iterator.next(1);
|
|
246
493
|
await iterator.close();
|
|
247
494
|
return results[0];
|
|
248
495
|
}
|
|
496
|
+
async getOldestMany(limit, resolve) {
|
|
497
|
+
return this.getOldestManyMaybe(limit, resolve);
|
|
498
|
+
}
|
|
499
|
+
getOldestManyMaybe(limit, resolve) {
|
|
500
|
+
if (limit <= 0) {
|
|
501
|
+
return [];
|
|
502
|
+
}
|
|
503
|
+
const nativeEntries = this.getNativeOldestEntries(limit, resolve);
|
|
504
|
+
if (nativeEntries) {
|
|
505
|
+
return nativeEntries;
|
|
506
|
+
}
|
|
507
|
+
return this.getOldestManyFromIterator(limit, resolve);
|
|
508
|
+
}
|
|
509
|
+
async getOldestManyFromIterator(limit, resolve) {
|
|
510
|
+
const iterator = this.iterate([], this.properties.sort.sort, resolve);
|
|
511
|
+
try {
|
|
512
|
+
return (await iterator.next(limit));
|
|
513
|
+
}
|
|
514
|
+
finally {
|
|
515
|
+
await iterator.close();
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
getNativeOldestEntries(limit, resolve) {
|
|
519
|
+
if (resolve || limit <= 0) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const graph = this.properties.nativeGraph?.graph;
|
|
523
|
+
if (!graph?.oldestEntries) {
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const direction = timestampSortDirection(this.properties.sort.sort);
|
|
527
|
+
if (direction !== SortDirection.ASC) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
return graph
|
|
531
|
+
.oldestEntries(limit)
|
|
532
|
+
.map((entry) => this.nativeLogEntryToShallowEntry(entry));
|
|
533
|
+
}
|
|
249
534
|
async getNewest(resolve) {
|
|
535
|
+
const nativeHash = this.getNativeTimestampOrderedHash("newest");
|
|
536
|
+
if (nativeHash) {
|
|
537
|
+
const nativeResult = resolve
|
|
538
|
+
? await this.resolve(nativeHash)
|
|
539
|
+
: (await this.getShallow(nativeHash))?.value;
|
|
540
|
+
if (nativeResult) {
|
|
541
|
+
return nativeResult;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
250
544
|
const iterator = this.iterate([], this.sortReversed, resolve);
|
|
251
545
|
const results = await iterator.next(1);
|
|
252
546
|
await iterator.close();
|
|
253
547
|
return results[0];
|
|
254
548
|
}
|
|
549
|
+
getNativeTimestampOrderedHash(position) {
|
|
550
|
+
const graph = this.properties.nativeGraph?.graph;
|
|
551
|
+
if (!graph?.oldestHash || !graph.newestHash) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const direction = timestampSortDirection(this.properties.sort.sort);
|
|
555
|
+
if (direction == null) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
const useNewest = direction === SortDirection.ASC
|
|
559
|
+
? position === "newest"
|
|
560
|
+
: position === "oldest";
|
|
561
|
+
return useNewest ? graph.newestHash() : graph.oldestHash();
|
|
562
|
+
}
|
|
255
563
|
async getBefore(before, resolve) {
|
|
256
564
|
const iterator = this.iterate(this.properties.sort.before(before), this.sortReversed, resolve);
|
|
257
565
|
const results = await iterator.next(1);
|
|
@@ -267,17 +575,130 @@ export class EntryIndex {
|
|
|
267
575
|
async get(k, options) {
|
|
268
576
|
return this.resolve(k, options);
|
|
269
577
|
}
|
|
578
|
+
async getMany(k, options) {
|
|
579
|
+
return this.resolveMany(k, options);
|
|
580
|
+
}
|
|
581
|
+
async planJoin(entry, reset) {
|
|
582
|
+
if (this.properties.nativeGraph) {
|
|
583
|
+
const cutCheck = this.properties.nativeGraph.useHeads
|
|
584
|
+
? {
|
|
585
|
+
gid: entry.meta.gid,
|
|
586
|
+
wallTime: entry.meta.clock.timestamp.wallTime,
|
|
587
|
+
logical: entry.meta.clock.timestamp.logical,
|
|
588
|
+
}
|
|
589
|
+
: undefined;
|
|
590
|
+
return this.properties.nativeGraph.graph.planJoin(entry.hash, entry.meta.next, entry.meta.type, reset === true, cutCheck);
|
|
591
|
+
}
|
|
592
|
+
if (!reset && (await this.getShallow(entry.hash)) != null) {
|
|
593
|
+
return {
|
|
594
|
+
skip: true,
|
|
595
|
+
missingParents: [],
|
|
596
|
+
cutChecked: false,
|
|
597
|
+
coveredByCut: false,
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
if (entry.meta.type === EntryType.CUT) {
|
|
601
|
+
return {
|
|
602
|
+
skip: false,
|
|
603
|
+
missingParents: [],
|
|
604
|
+
cutChecked: false,
|
|
605
|
+
coveredByCut: false,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
const missingParents = [];
|
|
609
|
+
for (const next of entry.meta.next) {
|
|
610
|
+
if (reset || (await this.getShallow(next)) == null) {
|
|
611
|
+
missingParents.push(next);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return {
|
|
615
|
+
skip: false,
|
|
616
|
+
missingParents,
|
|
617
|
+
cutChecked: false,
|
|
618
|
+
coveredByCut: false,
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
async planJoinBatch(entries, reset, profile) {
|
|
622
|
+
if (entries.length === 0) {
|
|
623
|
+
return [];
|
|
624
|
+
}
|
|
625
|
+
if (this.properties.nativeGraph?.graph.planJoinBatch) {
|
|
626
|
+
const prepareStartedAt = entryIndexProfileStart(profile);
|
|
627
|
+
const nativeInputs = entries.map((entry) => ({
|
|
628
|
+
hash: entry.hash,
|
|
629
|
+
next: entry.meta.next,
|
|
630
|
+
type: entry.meta.type,
|
|
631
|
+
cutCheck: this.properties.nativeGraph.useHeads
|
|
632
|
+
? {
|
|
633
|
+
gid: entry.meta.gid,
|
|
634
|
+
wallTime: entry.meta.clock.timestamp.wallTime,
|
|
635
|
+
logical: entry.meta.clock.timestamp.logical,
|
|
636
|
+
}
|
|
637
|
+
: undefined,
|
|
638
|
+
}));
|
|
639
|
+
emitEntryIndexProfileDuration(profile, prepareStartedAt, {
|
|
640
|
+
name: "log.entryIndex.planJoinBatch.prepareNative",
|
|
641
|
+
component: "log",
|
|
642
|
+
entries: entries.length,
|
|
643
|
+
messages: 1,
|
|
644
|
+
details: {
|
|
645
|
+
cutChecks: nativeInputs.reduce((sum, input) => sum + (input.cutCheck ? 1 : 0), 0),
|
|
646
|
+
},
|
|
647
|
+
});
|
|
648
|
+
const nativeStartedAt = entryIndexProfileStart(profile);
|
|
649
|
+
const plans = this.properties.nativeGraph.graph.planJoinBatch(nativeInputs, reset === true);
|
|
650
|
+
emitEntryIndexProfileDuration(profile, nativeStartedAt, {
|
|
651
|
+
name: "log.entryIndex.planJoinBatch.nativeGraph",
|
|
652
|
+
component: "log",
|
|
653
|
+
entries: entries.length,
|
|
654
|
+
messages: 1,
|
|
655
|
+
});
|
|
656
|
+
return plans;
|
|
657
|
+
}
|
|
658
|
+
const fallbackStartedAt = entryIndexProfileStart(profile);
|
|
659
|
+
const plans = [];
|
|
660
|
+
for (const entry of entries) {
|
|
661
|
+
plans.push(await this.planJoin(entry, reset));
|
|
662
|
+
}
|
|
663
|
+
emitEntryIndexProfileDuration(profile, fallbackStartedAt, {
|
|
664
|
+
name: "log.entryIndex.planJoinBatch.fallback",
|
|
665
|
+
component: "log",
|
|
666
|
+
entries: entries.length,
|
|
667
|
+
messages: 1,
|
|
668
|
+
});
|
|
669
|
+
return plans;
|
|
670
|
+
}
|
|
270
671
|
async getShallow(k) {
|
|
271
|
-
const pending = this.
|
|
672
|
+
const pending = this.getPendingIndexWrite(k);
|
|
272
673
|
if (pending) {
|
|
273
674
|
return { id: toId(k), value: pending };
|
|
274
675
|
}
|
|
275
676
|
return this.properties.index.get(toId(k));
|
|
276
677
|
}
|
|
678
|
+
getNativeEntryMetadataBatch(hashes) {
|
|
679
|
+
if (!this.properties.nativeGraph) {
|
|
680
|
+
return undefined;
|
|
681
|
+
}
|
|
682
|
+
const normalized = [...hashes];
|
|
683
|
+
if (normalized.length === 0) {
|
|
684
|
+
return [];
|
|
685
|
+
}
|
|
686
|
+
return this.properties.nativeGraph.graph.entryMetadataBatch?.(normalized);
|
|
687
|
+
}
|
|
688
|
+
getNativeEntryMetadataHintsBatch(hashes) {
|
|
689
|
+
if (!this.properties.nativeGraph) {
|
|
690
|
+
return undefined;
|
|
691
|
+
}
|
|
692
|
+
const normalized = [...hashes];
|
|
693
|
+
if (normalized.length === 0) {
|
|
694
|
+
return [];
|
|
695
|
+
}
|
|
696
|
+
return (this.properties.nativeGraph.graph.entryMetadataHintsBatch?.(normalized) ??
|
|
697
|
+
this.properties.nativeGraph.graph.entryMetadataBatch?.(normalized));
|
|
698
|
+
}
|
|
277
699
|
async has(k) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
return true;
|
|
700
|
+
if (this.properties.nativeGraph) {
|
|
701
|
+
return this.properties.nativeGraph.graph.has(k);
|
|
281
702
|
}
|
|
282
703
|
if (this.pendingIndexWrites.has(k)) {
|
|
283
704
|
return true;
|
|
@@ -288,11 +709,14 @@ export class EntryIndex {
|
|
|
288
709
|
return result != null;
|
|
289
710
|
}
|
|
290
711
|
async hasMany(hashes) {
|
|
712
|
+
if (this.properties.nativeGraph) {
|
|
713
|
+
return this.properties.nativeGraph.graph.hasMany(new Set([...hashes].filter(Boolean)));
|
|
714
|
+
}
|
|
291
715
|
const batchSize = 64;
|
|
292
716
|
const existing = new Set();
|
|
293
717
|
const missing = [];
|
|
294
718
|
for (const hash of new Set([...hashes].filter(Boolean))) {
|
|
295
|
-
if (this.
|
|
719
|
+
if (this.pendingIndexWrites.has(hash)) {
|
|
296
720
|
existing.add(hash);
|
|
297
721
|
continue;
|
|
298
722
|
}
|
|
@@ -331,9 +755,17 @@ export class EntryIndex {
|
|
|
331
755
|
async put(entry, properties) {
|
|
332
756
|
if (properties.toMultiHash) {
|
|
333
757
|
const existingHash = entry.hash;
|
|
334
|
-
|
|
758
|
+
const preparedBlock = Entry.takePreparedBlock(entry);
|
|
335
759
|
try {
|
|
336
|
-
|
|
760
|
+
let hash;
|
|
761
|
+
if (preparedBlock) {
|
|
762
|
+
hash = await putPreparedEntryBlock(this.properties.store, preparedBlock);
|
|
763
|
+
entry.size = preparedBlock.block.bytes.length;
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
entry.hash = undefined;
|
|
767
|
+
hash = await Entry.toMultihash(this.properties.store, entry);
|
|
768
|
+
}
|
|
337
769
|
entry.hash = existingHash;
|
|
338
770
|
if (entry.hash === undefined) {
|
|
339
771
|
entry.hash = hash; // can happen if you sync entries that you load directly from ipfs
|
|
@@ -364,7 +796,10 @@ export class EntryIndex {
|
|
|
364
796
|
}
|
|
365
797
|
// add cache after .has check before .has uses the cache
|
|
366
798
|
this.cache.add(entry.hash, entry);
|
|
367
|
-
const shallowEntry =
|
|
799
|
+
const shallowEntry = Entry.takePreparedShallowEntry(entry, properties.isHead) ??
|
|
800
|
+
entry.toShallow(properties.isHead);
|
|
801
|
+
const nativeEntry = Entry.takePreparedNativeLogEntry(entry, properties.isHead) ??
|
|
802
|
+
toNativeLogEntry(shallowEntry);
|
|
368
803
|
const shouldDeferIndexWrite = properties.deferIndexWrite === true &&
|
|
369
804
|
properties.isHead &&
|
|
370
805
|
entry.meta.type !== EntryType.CUT &&
|
|
@@ -377,16 +812,9 @@ export class EntryIndex {
|
|
|
377
812
|
await this.flushPendingWrites(entry.meta.next);
|
|
378
813
|
await this.properties.index.put(shallowEntry);
|
|
379
814
|
}
|
|
380
|
-
this.properties.nativeGraph?.graph.put(
|
|
815
|
+
this.properties.nativeGraph?.graph.put(nativeEntry);
|
|
381
816
|
// check if gids has been shadowed, by query all nexts that have a different gid
|
|
382
|
-
|
|
383
|
-
const shadowedGids = this.properties.nativeGraph
|
|
384
|
-
? new Set(this.properties.nativeGraph.graph.shadowedGids(entry.meta.gid, entry.meta.next, entry.hash))
|
|
385
|
-
: await this.findShadowedGids(entry);
|
|
386
|
-
if (shadowedGids.size > 0) {
|
|
387
|
-
this.properties.onGidRemoved?.([...shadowedGids]);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
817
|
+
await this.notifyShadowedGids(entry);
|
|
390
818
|
// mark all next entries as not heads
|
|
391
819
|
await this.privateUpdateNextHeadProperty(entry, false);
|
|
392
820
|
this.insertionPromises.delete(entry.hash);
|
|
@@ -396,12 +824,560 @@ export class EntryIndex {
|
|
|
396
824
|
return promise;
|
|
397
825
|
}
|
|
398
826
|
}
|
|
827
|
+
async putAppendBatch(entries, properties) {
|
|
828
|
+
if (entries.length === 0) {
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
if (entries.length === 1 &&
|
|
832
|
+
properties.prepared?.nativeGraphUpdated !== true) {
|
|
833
|
+
return this.put(entries[0], {
|
|
834
|
+
unique: properties.unique,
|
|
835
|
+
isHead: true,
|
|
836
|
+
toMultiHash: false,
|
|
837
|
+
deferIndexWrite: properties.deferIndexWrite,
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
for (const entry of entries) {
|
|
841
|
+
if (!entry.hash) {
|
|
842
|
+
throw new Error("Missing hash");
|
|
843
|
+
}
|
|
844
|
+
const existingPromise = this.insertionPromises.get(entry.hash);
|
|
845
|
+
if (existingPromise) {
|
|
846
|
+
await existingPromise;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
const promise = (async () => {
|
|
850
|
+
const profile = properties.profile;
|
|
851
|
+
const prepareStartedAt = entryIndexProfileStart(profile);
|
|
852
|
+
const externalNexts = new Set(properties.externalNextHashes ?? []);
|
|
853
|
+
const shouldDiscoverExternalNexts = !properties.externalNextHashes;
|
|
854
|
+
const batchHashes = shouldDiscoverExternalNexts
|
|
855
|
+
? new Set(entries.map((entry) => entry.hash))
|
|
856
|
+
: undefined;
|
|
857
|
+
const putBatch = !this.properties.onGidRemoved && this.properties.index.putBatch;
|
|
858
|
+
const shallowEntries = [];
|
|
859
|
+
const nativeGraphUpdated = properties.prepared?.nativeGraphUpdated === true;
|
|
860
|
+
const nativeCommitOwnsHotIndex = nativeGraphUpdated &&
|
|
861
|
+
properties.prepared?.nativeBlocksCommitted === true &&
|
|
862
|
+
!this.properties.onGidRemoved;
|
|
863
|
+
const nativeGraphPutAppendChain = !nativeGraphUpdated &&
|
|
864
|
+
!this.properties.onGidRemoved &&
|
|
865
|
+
properties.externalNextHashes &&
|
|
866
|
+
this.properties.nativeGraph?.graph.putAppendChain
|
|
867
|
+
? this.properties.nativeGraph.graph.putAppendChain.bind(this.properties.nativeGraph.graph)
|
|
868
|
+
: undefined;
|
|
869
|
+
const nativeGraphPutBatch = !nativeGraphUpdated &&
|
|
870
|
+
!this.properties.onGidRemoved &&
|
|
871
|
+
this.properties.nativeGraph?.graph.putBatch
|
|
872
|
+
? this.properties.nativeGraph.graph.putBatch.bind(this.properties.nativeGraph.graph)
|
|
873
|
+
: undefined;
|
|
874
|
+
const deferBatchIndexWrite = properties.deferIndexWrite === true &&
|
|
875
|
+
!!putBatch &&
|
|
876
|
+
!!this.properties.nativeGraph &&
|
|
877
|
+
!this.properties.onGidRemoved &&
|
|
878
|
+
entries.every((entry) => entry.meta.type !== EntryType.CUT);
|
|
879
|
+
const nativeEntries = [];
|
|
880
|
+
for (let i = 0; i < entries.length; i++) {
|
|
881
|
+
const entry = entries[i];
|
|
882
|
+
const isHead = properties.heads?.[i] ?? i === entries.length - 1;
|
|
883
|
+
if (properties.unique === true || !(await this.has(entry.hash))) {
|
|
884
|
+
this._length++;
|
|
885
|
+
}
|
|
886
|
+
this.cache.add(entry.hash, entry);
|
|
887
|
+
const preparedShallowEntry = properties.prepared?.shallowEntries[i];
|
|
888
|
+
if (preparedShallowEntry) {
|
|
889
|
+
preparedShallowEntry.head = isHead;
|
|
890
|
+
}
|
|
891
|
+
const shallowEntry = preparedShallowEntry ??
|
|
892
|
+
Entry.takePreparedShallowEntry(entry, isHead) ??
|
|
893
|
+
entry.toShallow(isHead);
|
|
894
|
+
if (nativeCommitOwnsHotIndex) {
|
|
895
|
+
this.pendingIndexWrites.set(entry.hash, shallowEntry);
|
|
896
|
+
if (batchHashes) {
|
|
897
|
+
for (const next of entry.meta.next) {
|
|
898
|
+
if (!batchHashes.has(next)) {
|
|
899
|
+
externalNexts.add(next);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
continue;
|
|
904
|
+
}
|
|
905
|
+
const preparedNativeEntry = properties.prepared?.nativeEntries?.[i];
|
|
906
|
+
if (preparedNativeEntry) {
|
|
907
|
+
preparedNativeEntry.head = isHead;
|
|
908
|
+
}
|
|
909
|
+
const nativeEntry = !nativeGraphUpdated &&
|
|
910
|
+
this.properties.nativeGraph &&
|
|
911
|
+
(preparedNativeEntry ??
|
|
912
|
+
Entry.takePreparedNativeLogEntry(entry, isHead) ??
|
|
913
|
+
toNativeLogEntry(shallowEntry));
|
|
914
|
+
if (putBatch) {
|
|
915
|
+
shallowEntries.push(shallowEntry);
|
|
916
|
+
nativeEntry && nativeEntries.push(nativeEntry);
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
await this.properties.index.put(shallowEntry);
|
|
920
|
+
if (nativeGraphPutAppendChain || nativeGraphPutBatch) {
|
|
921
|
+
nativeEntry && nativeEntries.push(nativeEntry);
|
|
922
|
+
}
|
|
923
|
+
else {
|
|
924
|
+
if (nativeEntry) {
|
|
925
|
+
this.properties.nativeGraph?.graph.put(nativeEntry);
|
|
926
|
+
await this.notifyShadowedGids(entry);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
if (batchHashes) {
|
|
931
|
+
for (const next of entry.meta.next) {
|
|
932
|
+
if (!batchHashes.has(next)) {
|
|
933
|
+
externalNexts.add(next);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
emitEntryIndexProfileDuration(profile, prepareStartedAt, {
|
|
939
|
+
name: "log.entryIndex.putAppendBatch.prepare",
|
|
940
|
+
component: "log",
|
|
941
|
+
entries: entries.length,
|
|
942
|
+
messages: 1,
|
|
943
|
+
details: {
|
|
944
|
+
unique: properties.unique,
|
|
945
|
+
usedPreparedShallowEntries: properties.prepared?.shallowEntries.length ?? 0,
|
|
946
|
+
usedPreparedNativeEntries: properties.prepared?.nativeEntries?.length ?? 0,
|
|
947
|
+
nativeEntries: nativeEntries.length,
|
|
948
|
+
discoverExternalNexts: shouldDiscoverExternalNexts,
|
|
949
|
+
},
|
|
950
|
+
});
|
|
951
|
+
const putNativeEntries = (allowLoopFallback) => {
|
|
952
|
+
if (nativeEntries.length === 0) {
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
if (nativeGraphPutAppendChain) {
|
|
956
|
+
const nativePutStartedAt = entryIndexProfileStart(profile);
|
|
957
|
+
nativeGraphPutAppendChain(nativeEntries);
|
|
958
|
+
emitEntryIndexProfileDuration(profile, nativePutStartedAt, {
|
|
959
|
+
name: "log.entryIndex.putAppendBatch.nativeGraphPut",
|
|
960
|
+
component: "log",
|
|
961
|
+
entries: nativeEntries.length,
|
|
962
|
+
messages: 1,
|
|
963
|
+
details: { method: "putAppendChain" },
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
else if (nativeGraphPutBatch) {
|
|
967
|
+
const nativePutStartedAt = entryIndexProfileStart(profile);
|
|
968
|
+
nativeGraphPutBatch(nativeEntries);
|
|
969
|
+
emitEntryIndexProfileDuration(profile, nativePutStartedAt, {
|
|
970
|
+
name: "log.entryIndex.putAppendBatch.nativeGraphPut",
|
|
971
|
+
component: "log",
|
|
972
|
+
entries: nativeEntries.length,
|
|
973
|
+
messages: 1,
|
|
974
|
+
details: { method: "putBatch" },
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
else if (allowLoopFallback) {
|
|
978
|
+
const nativePutStartedAt = entryIndexProfileStart(profile);
|
|
979
|
+
for (const nativeEntry of nativeEntries) {
|
|
980
|
+
this.properties.nativeGraph?.graph.put(nativeEntry);
|
|
981
|
+
}
|
|
982
|
+
emitEntryIndexProfileDuration(profile, nativePutStartedAt, {
|
|
983
|
+
name: "log.entryIndex.putAppendBatch.nativeGraphPut",
|
|
984
|
+
component: "log",
|
|
985
|
+
entries: nativeEntries.length,
|
|
986
|
+
messages: 1,
|
|
987
|
+
details: { method: "putLoop" },
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
if (nativeCommitOwnsHotIndex) {
|
|
992
|
+
this.schedulePendingIndexWriteFlush();
|
|
993
|
+
}
|
|
994
|
+
else if (deferBatchIndexWrite) {
|
|
995
|
+
const indexPutStartedAt = entryIndexProfileStart(profile);
|
|
996
|
+
for (const shallowEntry of shallowEntries) {
|
|
997
|
+
this.pendingIndexWrites.set(shallowEntry.hash, shallowEntry);
|
|
998
|
+
}
|
|
999
|
+
this.schedulePendingIndexWriteFlush();
|
|
1000
|
+
emitEntryIndexProfileDuration(profile, indexPutStartedAt, {
|
|
1001
|
+
name: "log.entryIndex.putAppendBatch.indexPut",
|
|
1002
|
+
component: "log",
|
|
1003
|
+
entries: shallowEntries.length,
|
|
1004
|
+
messages: 1,
|
|
1005
|
+
details: { deferred: true },
|
|
1006
|
+
});
|
|
1007
|
+
putNativeEntries(true);
|
|
1008
|
+
}
|
|
1009
|
+
else if (putBatch) {
|
|
1010
|
+
const indexPutStartedAt = entryIndexProfileStart(profile);
|
|
1011
|
+
await putBatch.call(this.properties.index, shallowEntries);
|
|
1012
|
+
emitEntryIndexProfileDuration(profile, indexPutStartedAt, {
|
|
1013
|
+
name: "log.entryIndex.putAppendBatch.indexPut",
|
|
1014
|
+
component: "log",
|
|
1015
|
+
entries: shallowEntries.length,
|
|
1016
|
+
messages: 1,
|
|
1017
|
+
});
|
|
1018
|
+
putNativeEntries(true);
|
|
1019
|
+
}
|
|
1020
|
+
else if (nativeEntries.length > 0) {
|
|
1021
|
+
putNativeEntries(false);
|
|
1022
|
+
}
|
|
1023
|
+
if (externalNexts.size > 0) {
|
|
1024
|
+
const externalNextStartedAt = entryIndexProfileStart(profile);
|
|
1025
|
+
await this.privateUpdateNextHeadHashes([...externalNexts], false);
|
|
1026
|
+
emitEntryIndexProfileDuration(profile, externalNextStartedAt, {
|
|
1027
|
+
name: "log.entryIndex.putAppendBatch.externalNexts",
|
|
1028
|
+
component: "log",
|
|
1029
|
+
entries: externalNexts.size,
|
|
1030
|
+
messages: 1,
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
})().finally(() => {
|
|
1034
|
+
for (const entry of entries) {
|
|
1035
|
+
this.insertionPromises.delete(entry.hash);
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
for (const entry of entries) {
|
|
1039
|
+
this.insertionPromises.set(entry.hash, promise);
|
|
1040
|
+
}
|
|
1041
|
+
return promise;
|
|
1042
|
+
}
|
|
1043
|
+
// Internal trusted receive path for callers that can supply prepared append facts.
|
|
1044
|
+
async putAppendFactsBatch(entries, properties) {
|
|
1045
|
+
if (entries.length === 0) {
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
if (this.properties.onGidRemoved) {
|
|
1049
|
+
throw new Error("Prepared append facts batch requires no onGidRemoved hook");
|
|
1050
|
+
}
|
|
1051
|
+
for (const entry of entries) {
|
|
1052
|
+
if (!entry.hash) {
|
|
1053
|
+
throw new Error("Missing hash");
|
|
1054
|
+
}
|
|
1055
|
+
const existingPromise = this.insertionPromises.get(entry.hash);
|
|
1056
|
+
if (existingPromise) {
|
|
1057
|
+
await existingPromise;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
const promise = (async () => {
|
|
1061
|
+
const profile = properties.profile;
|
|
1062
|
+
const prepareStartedAt = entryIndexProfileStart(profile);
|
|
1063
|
+
const externalNexts = new Set(properties.externalNextHashes ?? []);
|
|
1064
|
+
const shouldDiscoverExternalNexts = !properties.externalNextHashes;
|
|
1065
|
+
const batchHashes = shouldDiscoverExternalNexts
|
|
1066
|
+
? new Set(entries.map((entry) => entry.hash))
|
|
1067
|
+
: undefined;
|
|
1068
|
+
const putBatch = this.properties.index.putBatch;
|
|
1069
|
+
const shallowEntries = [];
|
|
1070
|
+
const nativeEntries = [];
|
|
1071
|
+
const nativeGraphUpdated = properties.nativeGraphUpdated === true;
|
|
1072
|
+
const nativeGraphPutAppendChain = !nativeGraphUpdated &&
|
|
1073
|
+
properties.externalNextHashes &&
|
|
1074
|
+
this.properties.nativeGraph?.graph.putAppendChain
|
|
1075
|
+
? this.properties.nativeGraph.graph.putAppendChain.bind(this.properties.nativeGraph.graph)
|
|
1076
|
+
: undefined;
|
|
1077
|
+
const nativeGraphPutBatch = !nativeGraphUpdated && this.properties.nativeGraph?.graph.putBatch
|
|
1078
|
+
? this.properties.nativeGraph.graph.putBatch.bind(this.properties.nativeGraph.graph)
|
|
1079
|
+
: undefined;
|
|
1080
|
+
const deferBatchIndexWrite = properties.deferIndexWrite === true &&
|
|
1081
|
+
!!this.properties.nativeGraph &&
|
|
1082
|
+
entries.every((entry) => entry.meta.type !== EntryType.CUT);
|
|
1083
|
+
const lazyNativeGraphUpdatedDeferredIndexWrite = nativeGraphUpdated && deferBatchIndexWrite;
|
|
1084
|
+
for (let i = 0; i < entries.length; i++) {
|
|
1085
|
+
const entry = entries[i];
|
|
1086
|
+
const isHead = properties.heads?.[i] ?? i === entries.length - 1;
|
|
1087
|
+
if (properties.unique === true || !(await this.has(entry.hash))) {
|
|
1088
|
+
this._length++;
|
|
1089
|
+
}
|
|
1090
|
+
let shallowEntry;
|
|
1091
|
+
if (lazyNativeGraphUpdatedDeferredIndexWrite) {
|
|
1092
|
+
this.pendingIndexWrites.set(entry.hash, () => materializePreparedAppendShallowEntry(entry, isHead));
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
shallowEntry = materializePreparedAppendShallowEntry(entry, isHead);
|
|
1096
|
+
}
|
|
1097
|
+
const nativeEntry = !nativeGraphUpdated &&
|
|
1098
|
+
this.properties.nativeGraph &&
|
|
1099
|
+
(entry.nativeEntry ??
|
|
1100
|
+
toNativeLogEntry(shallowEntry ??
|
|
1101
|
+
materializePreparedAppendShallowEntry(entry, isHead)));
|
|
1102
|
+
if (nativeEntry) {
|
|
1103
|
+
nativeEntry.head = isHead;
|
|
1104
|
+
}
|
|
1105
|
+
if (lazyNativeGraphUpdatedDeferredIndexWrite) {
|
|
1106
|
+
// The native commit already updated blocks/graph; keep the JS
|
|
1107
|
+
// compatibility index write lazy for later flushes.
|
|
1108
|
+
}
|
|
1109
|
+
else if (deferBatchIndexWrite || putBatch) {
|
|
1110
|
+
shallowEntries.push(shallowEntry);
|
|
1111
|
+
nativeEntry && nativeEntries.push(nativeEntry);
|
|
1112
|
+
}
|
|
1113
|
+
else {
|
|
1114
|
+
await this.properties.index.put(shallowEntry);
|
|
1115
|
+
nativeEntry && nativeEntries.push(nativeEntry);
|
|
1116
|
+
}
|
|
1117
|
+
if (batchHashes) {
|
|
1118
|
+
for (const next of entry.meta.next) {
|
|
1119
|
+
if (!batchHashes.has(next)) {
|
|
1120
|
+
externalNexts.add(next);
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
emitEntryIndexProfileDuration(profile, prepareStartedAt, {
|
|
1126
|
+
name: "log.entryIndex.putAppendFactsBatch.prepare",
|
|
1127
|
+
component: "log",
|
|
1128
|
+
entries: entries.length,
|
|
1129
|
+
messages: 1,
|
|
1130
|
+
details: {
|
|
1131
|
+
unique: properties.unique,
|
|
1132
|
+
nativeEntries: nativeEntries.length,
|
|
1133
|
+
discoverExternalNexts: shouldDiscoverExternalNexts,
|
|
1134
|
+
nativeGraphUpdated,
|
|
1135
|
+
},
|
|
1136
|
+
});
|
|
1137
|
+
const putNativeEntries = (allowLoopFallback) => {
|
|
1138
|
+
if (nativeEntries.length === 0) {
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
if (nativeGraphPutAppendChain) {
|
|
1142
|
+
const nativePutStartedAt = entryIndexProfileStart(profile);
|
|
1143
|
+
nativeGraphPutAppendChain(nativeEntries);
|
|
1144
|
+
emitEntryIndexProfileDuration(profile, nativePutStartedAt, {
|
|
1145
|
+
name: "log.entryIndex.putAppendFactsBatch.nativeGraphPut",
|
|
1146
|
+
component: "log",
|
|
1147
|
+
entries: nativeEntries.length,
|
|
1148
|
+
messages: 1,
|
|
1149
|
+
details: { method: "putAppendChain" },
|
|
1150
|
+
});
|
|
1151
|
+
}
|
|
1152
|
+
else if (nativeGraphPutBatch) {
|
|
1153
|
+
const nativePutStartedAt = entryIndexProfileStart(profile);
|
|
1154
|
+
nativeGraphPutBatch(nativeEntries);
|
|
1155
|
+
emitEntryIndexProfileDuration(profile, nativePutStartedAt, {
|
|
1156
|
+
name: "log.entryIndex.putAppendFactsBatch.nativeGraphPut",
|
|
1157
|
+
component: "log",
|
|
1158
|
+
entries: nativeEntries.length,
|
|
1159
|
+
messages: 1,
|
|
1160
|
+
details: { method: "putBatch" },
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1163
|
+
else if (allowLoopFallback) {
|
|
1164
|
+
const nativePutStartedAt = entryIndexProfileStart(profile);
|
|
1165
|
+
for (const nativeEntry of nativeEntries) {
|
|
1166
|
+
this.properties.nativeGraph?.graph.put(nativeEntry);
|
|
1167
|
+
}
|
|
1168
|
+
emitEntryIndexProfileDuration(profile, nativePutStartedAt, {
|
|
1169
|
+
name: "log.entryIndex.putAppendFactsBatch.nativeGraphPut",
|
|
1170
|
+
component: "log",
|
|
1171
|
+
entries: nativeEntries.length,
|
|
1172
|
+
messages: 1,
|
|
1173
|
+
details: { method: "putLoop" },
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
if (deferBatchIndexWrite) {
|
|
1178
|
+
const indexPutStartedAt = entryIndexProfileStart(profile);
|
|
1179
|
+
if (!lazyNativeGraphUpdatedDeferredIndexWrite) {
|
|
1180
|
+
for (const shallowEntry of shallowEntries) {
|
|
1181
|
+
this.pendingIndexWrites.set(shallowEntry.hash, shallowEntry);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
this.schedulePendingIndexWriteFlush();
|
|
1185
|
+
emitEntryIndexProfileDuration(profile, indexPutStartedAt, {
|
|
1186
|
+
name: "log.entryIndex.putAppendFactsBatch.indexPut",
|
|
1187
|
+
component: "log",
|
|
1188
|
+
entries: lazyNativeGraphUpdatedDeferredIndexWrite
|
|
1189
|
+
? entries.length
|
|
1190
|
+
: shallowEntries.length,
|
|
1191
|
+
messages: 1,
|
|
1192
|
+
details: {
|
|
1193
|
+
deferred: true,
|
|
1194
|
+
lazy: lazyNativeGraphUpdatedDeferredIndexWrite,
|
|
1195
|
+
},
|
|
1196
|
+
});
|
|
1197
|
+
putNativeEntries(true);
|
|
1198
|
+
}
|
|
1199
|
+
else if (putBatch) {
|
|
1200
|
+
const indexPutStartedAt = entryIndexProfileStart(profile);
|
|
1201
|
+
await putBatch.call(this.properties.index, shallowEntries);
|
|
1202
|
+
emitEntryIndexProfileDuration(profile, indexPutStartedAt, {
|
|
1203
|
+
name: "log.entryIndex.putAppendFactsBatch.indexPut",
|
|
1204
|
+
component: "log",
|
|
1205
|
+
entries: shallowEntries.length,
|
|
1206
|
+
messages: 1,
|
|
1207
|
+
});
|
|
1208
|
+
putNativeEntries(true);
|
|
1209
|
+
}
|
|
1210
|
+
else if (nativeEntries.length > 0) {
|
|
1211
|
+
putNativeEntries(true);
|
|
1212
|
+
}
|
|
1213
|
+
if (externalNexts.size > 0) {
|
|
1214
|
+
const externalNextStartedAt = entryIndexProfileStart(profile);
|
|
1215
|
+
await this.privateUpdateNextHeadHashes([...externalNexts], false);
|
|
1216
|
+
emitEntryIndexProfileDuration(profile, externalNextStartedAt, {
|
|
1217
|
+
name: "log.entryIndex.putAppendFactsBatch.externalNexts",
|
|
1218
|
+
component: "log",
|
|
1219
|
+
entries: externalNexts.size,
|
|
1220
|
+
messages: 1,
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
})().finally(() => {
|
|
1224
|
+
for (const entry of entries) {
|
|
1225
|
+
this.insertionPromises.delete(entry.hash);
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
for (const entry of entries) {
|
|
1229
|
+
this.insertionPromises.set(entry.hash, promise);
|
|
1230
|
+
}
|
|
1231
|
+
return promise;
|
|
1232
|
+
}
|
|
1233
|
+
/** @internal */
|
|
1234
|
+
async putNativeCommittedAppend(entry, properties) {
|
|
1235
|
+
if (!entry.hash) {
|
|
1236
|
+
throw new Error("Missing hash");
|
|
1237
|
+
}
|
|
1238
|
+
const existingPromise = this.insertionPromises.get(entry.hash);
|
|
1239
|
+
if (existingPromise) {
|
|
1240
|
+
await existingPromise;
|
|
1241
|
+
}
|
|
1242
|
+
const promise = (async () => {
|
|
1243
|
+
const isHead = properties.isHead ?? true;
|
|
1244
|
+
if (properties.unique === true || !(await this.has(entry.hash))) {
|
|
1245
|
+
this._length++;
|
|
1246
|
+
}
|
|
1247
|
+
this.cache.add(entry.hash, entry);
|
|
1248
|
+
const shallowEntry = properties.shallowEntry ??
|
|
1249
|
+
Entry.takePreparedShallowEntry(entry, isHead) ??
|
|
1250
|
+
entry.toShallow(isHead);
|
|
1251
|
+
shallowEntry.head = isHead;
|
|
1252
|
+
this.pendingIndexWrites.set(entry.hash, shallowEntry);
|
|
1253
|
+
this.schedulePendingIndexWriteFlush();
|
|
1254
|
+
if (properties.externalNextHashes.length > 0) {
|
|
1255
|
+
await this.privateUpdateNextHeadHashes(properties.externalNextHashes, false);
|
|
1256
|
+
}
|
|
1257
|
+
})().finally(() => {
|
|
1258
|
+
this.insertionPromises.delete(entry.hash);
|
|
1259
|
+
});
|
|
1260
|
+
this.insertionPromises.set(entry.hash, promise);
|
|
1261
|
+
return promise;
|
|
1262
|
+
}
|
|
1263
|
+
/** @internal */
|
|
1264
|
+
putNativeCommittedAppendFacts(properties) {
|
|
1265
|
+
if (!properties.hash) {
|
|
1266
|
+
throw new Error("Missing hash");
|
|
1267
|
+
}
|
|
1268
|
+
const existingPromise = this.insertionPromises.get(properties.hash);
|
|
1269
|
+
if (!existingPromise &&
|
|
1270
|
+
properties.unique === true &&
|
|
1271
|
+
properties.externalNextHashes.length === 0) {
|
|
1272
|
+
const isHead = properties.isHead ?? true;
|
|
1273
|
+
this._length++;
|
|
1274
|
+
if (!properties.shallowEntry && !properties.getShallowEntry) {
|
|
1275
|
+
throw new Error("Missing shallow entry");
|
|
1276
|
+
}
|
|
1277
|
+
const pending = properties.shallowEntry ??
|
|
1278
|
+
(() => {
|
|
1279
|
+
const shallowEntry = properties.getShallowEntry();
|
|
1280
|
+
shallowEntry.head = isHead;
|
|
1281
|
+
return shallowEntry;
|
|
1282
|
+
});
|
|
1283
|
+
if (properties.shallowEntry) {
|
|
1284
|
+
properties.shallowEntry.head = isHead;
|
|
1285
|
+
}
|
|
1286
|
+
this.pendingIndexWrites.set(properties.hash, pending);
|
|
1287
|
+
this.schedulePendingIndexWriteFlush();
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
return this.putNativeCommittedAppendFactsAsync(properties, existingPromise);
|
|
1291
|
+
}
|
|
1292
|
+
/** @internal */
|
|
1293
|
+
putNativeCommittedAppendFactsBatch(rows) {
|
|
1294
|
+
if (rows.length === 0) {
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
const asyncRows = [];
|
|
1298
|
+
const externalNextHashes = [];
|
|
1299
|
+
let scheduledPendingFlush = false;
|
|
1300
|
+
for (const row of rows) {
|
|
1301
|
+
if (!row.hash) {
|
|
1302
|
+
throw new Error("Missing hash");
|
|
1303
|
+
}
|
|
1304
|
+
const existingPromise = this.insertionPromises.get(row.hash);
|
|
1305
|
+
if (!existingPromise && row.unique === true) {
|
|
1306
|
+
const isHead = row.isHead ?? true;
|
|
1307
|
+
this._length++;
|
|
1308
|
+
if (!row.shallowEntry && !row.getShallowEntry) {
|
|
1309
|
+
throw new Error("Missing shallow entry");
|
|
1310
|
+
}
|
|
1311
|
+
const pending = row.shallowEntry ??
|
|
1312
|
+
(() => {
|
|
1313
|
+
const shallowEntry = row.getShallowEntry();
|
|
1314
|
+
shallowEntry.head = isHead;
|
|
1315
|
+
return shallowEntry;
|
|
1316
|
+
});
|
|
1317
|
+
if (row.shallowEntry) {
|
|
1318
|
+
row.shallowEntry.head = isHead;
|
|
1319
|
+
}
|
|
1320
|
+
this.pendingIndexWrites.set(row.hash, pending);
|
|
1321
|
+
if (row.externalNextHashes.length > 0) {
|
|
1322
|
+
externalNextHashes.push(...row.externalNextHashes);
|
|
1323
|
+
}
|
|
1324
|
+
scheduledPendingFlush = true;
|
|
1325
|
+
}
|
|
1326
|
+
else {
|
|
1327
|
+
asyncRows.push(row);
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
if (scheduledPendingFlush) {
|
|
1331
|
+
this.schedulePendingIndexWriteFlush();
|
|
1332
|
+
}
|
|
1333
|
+
const batchedNextUpdate = externalNextHashes.length > 0
|
|
1334
|
+
? this.privateUpdateNextHeadHashes(externalNextHashes, false)
|
|
1335
|
+
: undefined;
|
|
1336
|
+
if (asyncRows.length > 0 || batchedNextUpdate) {
|
|
1337
|
+
return Promise.all([
|
|
1338
|
+
batchedNextUpdate,
|
|
1339
|
+
...asyncRows.map((row) => this.putNativeCommittedAppendFacts(row)),
|
|
1340
|
+
]).then(() => undefined);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
async putNativeCommittedAppendFactsAsync(properties, existingPromise) {
|
|
1344
|
+
if (existingPromise) {
|
|
1345
|
+
await existingPromise;
|
|
1346
|
+
}
|
|
1347
|
+
const promise = (async () => {
|
|
1348
|
+
const isHead = properties.isHead ?? true;
|
|
1349
|
+
if (properties.unique === true || !(await this.has(properties.hash))) {
|
|
1350
|
+
this._length++;
|
|
1351
|
+
}
|
|
1352
|
+
if (!properties.shallowEntry && !properties.getShallowEntry) {
|
|
1353
|
+
throw new Error("Missing shallow entry");
|
|
1354
|
+
}
|
|
1355
|
+
const pending = properties.shallowEntry ??
|
|
1356
|
+
(() => {
|
|
1357
|
+
const shallowEntry = properties.getShallowEntry();
|
|
1358
|
+
shallowEntry.head = isHead;
|
|
1359
|
+
return shallowEntry;
|
|
1360
|
+
});
|
|
1361
|
+
if (properties.shallowEntry) {
|
|
1362
|
+
properties.shallowEntry.head = isHead;
|
|
1363
|
+
}
|
|
1364
|
+
this.pendingIndexWrites.set(properties.hash, pending);
|
|
1365
|
+
this.schedulePendingIndexWriteFlush();
|
|
1366
|
+
if (properties.externalNextHashes.length > 0) {
|
|
1367
|
+
await this.privateUpdateNextHeadHashes(properties.externalNextHashes, false);
|
|
1368
|
+
}
|
|
1369
|
+
})().finally(() => {
|
|
1370
|
+
this.insertionPromises.delete(properties.hash);
|
|
1371
|
+
});
|
|
1372
|
+
this.insertionPromises.set(properties.hash, promise);
|
|
1373
|
+
return promise;
|
|
1374
|
+
}
|
|
399
1375
|
async delete(k, from) {
|
|
400
1376
|
this.cache.del(k);
|
|
401
1377
|
if (from && from.hash !== k) {
|
|
402
1378
|
throw new Error("Shallow hash doesn't match the key");
|
|
403
1379
|
}
|
|
404
|
-
const pending = this.
|
|
1380
|
+
const pending = this.getPendingIndexWrite(k);
|
|
405
1381
|
from = from || pending || (await this.getShallow(k))?.value;
|
|
406
1382
|
if (!from) {
|
|
407
1383
|
return; // already deleted
|
|
@@ -424,18 +1400,397 @@ export class EntryIndex {
|
|
|
424
1400
|
return from;
|
|
425
1401
|
}
|
|
426
1402
|
}
|
|
1403
|
+
canDeleteMany() {
|
|
1404
|
+
return (!!this.properties.nativeGraph?.graph.deleteMany ||
|
|
1405
|
+
hasRmMany(this.properties.store));
|
|
1406
|
+
}
|
|
1407
|
+
async deleteMany(from, options) {
|
|
1408
|
+
return this.deleteManyMaybe(from, options);
|
|
1409
|
+
}
|
|
1410
|
+
deleteManyMaybe(from, options) {
|
|
1411
|
+
if (from.length === 0) {
|
|
1412
|
+
return [];
|
|
1413
|
+
}
|
|
1414
|
+
if (from.length === 1) {
|
|
1415
|
+
return this.deleteSingleMaybe(from[0], options);
|
|
1416
|
+
}
|
|
1417
|
+
const nodes = [];
|
|
1418
|
+
const seen = new Set();
|
|
1419
|
+
for (const node of from) {
|
|
1420
|
+
if (seen.has(node.hash)) {
|
|
1421
|
+
continue;
|
|
1422
|
+
}
|
|
1423
|
+
seen.add(node.hash);
|
|
1424
|
+
nodes.push(node);
|
|
1425
|
+
}
|
|
1426
|
+
const indexedByHash = new Map(nodes.map((node) => [node.hash, node]));
|
|
1427
|
+
const deletedByHash = new Map();
|
|
1428
|
+
const indexedHashes = [];
|
|
1429
|
+
const storeHashes = [];
|
|
1430
|
+
for (const node of nodes) {
|
|
1431
|
+
this.cache.del(node.hash);
|
|
1432
|
+
const pending = this.getPendingIndexWrite(node.hash);
|
|
1433
|
+
if (pending) {
|
|
1434
|
+
this.pendingIndexWrites.delete(node.hash);
|
|
1435
|
+
deletedByHash.set(node.hash, pending);
|
|
1436
|
+
storeHashes.push(node.hash);
|
|
1437
|
+
}
|
|
1438
|
+
else {
|
|
1439
|
+
indexedHashes.push(node.hash);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
const exactDeleteIndex = hasExactDelete(this.properties.index)
|
|
1443
|
+
? this.properties.index
|
|
1444
|
+
: undefined;
|
|
1445
|
+
if (indexedHashes.length > 0 && exactDeleteIndex) {
|
|
1446
|
+
return mapMaybePromise(exactDeleteIndex.delIds(indexedHashes), (deleted) => {
|
|
1447
|
+
for (const id of deleted) {
|
|
1448
|
+
const hash = String(id.primitive);
|
|
1449
|
+
const node = indexedByHash.get(hash);
|
|
1450
|
+
if (!node || deletedByHash.has(hash)) {
|
|
1451
|
+
continue;
|
|
1452
|
+
}
|
|
1453
|
+
deletedByHash.set(hash, node);
|
|
1454
|
+
storeHashes.push(hash);
|
|
1455
|
+
}
|
|
1456
|
+
return this.finishDeleteMany(deletedByHash, nodes, storeHashes, options);
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
if (indexedHashes.length > 0) {
|
|
1460
|
+
return this.deleteManyByQuery(indexedHashes, indexedByHash, deletedByHash, nodes, storeHashes, options);
|
|
1461
|
+
}
|
|
1462
|
+
return this.finishDeleteMany(deletedByHash, nodes, storeHashes, options);
|
|
1463
|
+
}
|
|
1464
|
+
consumeNativeTrimmedEntriesMaybe(from, options) {
|
|
1465
|
+
if (from.length === 0) {
|
|
1466
|
+
return [];
|
|
1467
|
+
}
|
|
1468
|
+
const nodes = [];
|
|
1469
|
+
const seen = new Set();
|
|
1470
|
+
for (const node of from) {
|
|
1471
|
+
if (seen.has(node.hash)) {
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1474
|
+
seen.add(node.hash);
|
|
1475
|
+
nodes.push(node);
|
|
1476
|
+
}
|
|
1477
|
+
return this.consumeNativeTrimmedEntryNodesMaybe(nodes, options);
|
|
1478
|
+
}
|
|
1479
|
+
consumeNativeTrimmedEntryHashesMaybe(hashes, options) {
|
|
1480
|
+
if (hashes.length === 0) {
|
|
1481
|
+
return [];
|
|
1482
|
+
}
|
|
1483
|
+
const nodes = [];
|
|
1484
|
+
const seen = new Set();
|
|
1485
|
+
for (const hash of hashes) {
|
|
1486
|
+
if (seen.has(hash)) {
|
|
1487
|
+
continue;
|
|
1488
|
+
}
|
|
1489
|
+
seen.add(hash);
|
|
1490
|
+
nodes.push(this.nativeTrimmedHashToShallowEntry(hash));
|
|
1491
|
+
}
|
|
1492
|
+
return this.consumeNativeTrimmedEntryNodesMaybe(nodes, options);
|
|
1493
|
+
}
|
|
1494
|
+
consumeNativeTrimmedEntryHashesNoReturnMaybe(hashes, options) {
|
|
1495
|
+
if (!options?.skipNextHeadUpdates ||
|
|
1496
|
+
options.deleteBlocks !== false ||
|
|
1497
|
+
hashes.length === 0) {
|
|
1498
|
+
return undefined;
|
|
1499
|
+
}
|
|
1500
|
+
const indexedHashes = [];
|
|
1501
|
+
let pendingDeleted = 0;
|
|
1502
|
+
const seen = new Set();
|
|
1503
|
+
for (const hash of hashes) {
|
|
1504
|
+
if (seen.has(hash)) {
|
|
1505
|
+
continue;
|
|
1506
|
+
}
|
|
1507
|
+
seen.add(hash);
|
|
1508
|
+
this.cache.del(hash);
|
|
1509
|
+
if (this.pendingIndexWrites.delete(hash)) {
|
|
1510
|
+
pendingDeleted++;
|
|
1511
|
+
}
|
|
1512
|
+
else {
|
|
1513
|
+
indexedHashes.push(hash);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
const finish = (indexedDeleted) => {
|
|
1517
|
+
this._length -= pendingDeleted + indexedDeleted;
|
|
1518
|
+
return true;
|
|
1519
|
+
};
|
|
1520
|
+
if (indexedHashes.length === 0) {
|
|
1521
|
+
return finish(0);
|
|
1522
|
+
}
|
|
1523
|
+
const exactDeleteCountIndex = hasExactDeleteCount(this.properties.index)
|
|
1524
|
+
? this.properties.index
|
|
1525
|
+
: undefined;
|
|
1526
|
+
if (exactDeleteCountIndex) {
|
|
1527
|
+
return mapMaybePromise(exactDeleteCountIndex.delIdsCount(indexedHashes), finish);
|
|
1528
|
+
}
|
|
1529
|
+
const exactDeleteIndex = hasExactDelete(this.properties.index)
|
|
1530
|
+
? this.properties.index
|
|
1531
|
+
: undefined;
|
|
1532
|
+
if (exactDeleteIndex) {
|
|
1533
|
+
return mapMaybePromise(exactDeleteIndex.delIds(indexedHashes), (deleted) => finish(deleted.length));
|
|
1534
|
+
}
|
|
1535
|
+
return this.consumeNativeTrimmedEntryHashesNoReturnByQuery(indexedHashes, finish);
|
|
1536
|
+
}
|
|
1537
|
+
async consumeNativeTrimmedEntryHashesNoReturnByQuery(indexedHashes, finish) {
|
|
1538
|
+
const batchSize = 64;
|
|
1539
|
+
let deletedCount = 0;
|
|
1540
|
+
for (let i = 0; i < indexedHashes.length; i += batchSize) {
|
|
1541
|
+
const hashes = indexedHashes.slice(i, i + batchSize);
|
|
1542
|
+
const deleted = await this.properties.index.del({
|
|
1543
|
+
query: createHashMatchQuery(hashes),
|
|
1544
|
+
});
|
|
1545
|
+
deletedCount += deleted.length;
|
|
1546
|
+
}
|
|
1547
|
+
return finish(deletedCount);
|
|
1548
|
+
}
|
|
1549
|
+
consumeNativeTrimmedEntryNodesMaybe(nodes, options) {
|
|
1550
|
+
const indexedByHash = new Map(nodes.map((node) => [node.hash, node]));
|
|
1551
|
+
const deletedByHash = new Map();
|
|
1552
|
+
const indexedHashes = [];
|
|
1553
|
+
for (const node of nodes) {
|
|
1554
|
+
this.cache.del(node.hash);
|
|
1555
|
+
const pending = this.getPendingIndexWrite(node.hash);
|
|
1556
|
+
if (pending) {
|
|
1557
|
+
this.pendingIndexWrites.delete(node.hash);
|
|
1558
|
+
deletedByHash.set(node.hash, pending);
|
|
1559
|
+
}
|
|
1560
|
+
else {
|
|
1561
|
+
indexedHashes.push(node.hash);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
const finish = () => this.finishConsumeNativeTrimmedEntries(deletedByHash, nodes, options);
|
|
1565
|
+
const exactDeleteIndex = hasExactDelete(this.properties.index)
|
|
1566
|
+
? this.properties.index
|
|
1567
|
+
: undefined;
|
|
1568
|
+
if (indexedHashes.length > 0 && exactDeleteIndex) {
|
|
1569
|
+
return mapMaybePromise(exactDeleteIndex.delIds(indexedHashes), (deleted) => {
|
|
1570
|
+
for (const id of deleted) {
|
|
1571
|
+
const hash = String(id.primitive);
|
|
1572
|
+
const node = indexedByHash.get(hash);
|
|
1573
|
+
if (!node || deletedByHash.has(hash)) {
|
|
1574
|
+
continue;
|
|
1575
|
+
}
|
|
1576
|
+
deletedByHash.set(hash, node);
|
|
1577
|
+
}
|
|
1578
|
+
return finish();
|
|
1579
|
+
});
|
|
1580
|
+
}
|
|
1581
|
+
if (indexedHashes.length > 0) {
|
|
1582
|
+
return this.consumeNativeTrimmedEntriesByQuery(indexedHashes, indexedByHash, deletedByHash, nodes, options);
|
|
1583
|
+
}
|
|
1584
|
+
return finish();
|
|
1585
|
+
}
|
|
1586
|
+
deleteSingleMaybe(node, options) {
|
|
1587
|
+
this.cache.del(node.hash);
|
|
1588
|
+
const pending = this.getPendingIndexWrite(node.hash);
|
|
1589
|
+
if (pending) {
|
|
1590
|
+
this.pendingIndexWrites.delete(node.hash);
|
|
1591
|
+
return this.finishDeleteSingle(pending, options);
|
|
1592
|
+
}
|
|
1593
|
+
const exactDeleteIndex = hasExactDelete(this.properties.index)
|
|
1594
|
+
? this.properties.index
|
|
1595
|
+
: undefined;
|
|
1596
|
+
if (exactDeleteIndex) {
|
|
1597
|
+
return mapMaybePromise(exactDeleteIndex.delIds([node.hash]), (deleted) => deleted.some((id) => String(id.primitive) === node.hash)
|
|
1598
|
+
? this.finishDeleteSingle(node, options)
|
|
1599
|
+
: []);
|
|
1600
|
+
}
|
|
1601
|
+
return mapMaybePromise(this.properties.index.del({ query: createHashMatchQuery([node.hash]) }), (deleted) => deleted.some((id) => String(id.primitive) === node.hash)
|
|
1602
|
+
? this.finishDeleteSingle(node, options)
|
|
1603
|
+
: []);
|
|
1604
|
+
}
|
|
1605
|
+
finishDeleteSingle(node, options) {
|
|
1606
|
+
const afterStoreDelete = () => {
|
|
1607
|
+
this._length--;
|
|
1608
|
+
this.properties.nativeGraph?.graph.delete(node.hash);
|
|
1609
|
+
if (!options?.skipNextHeadUpdates && node.meta.type !== EntryType.CUT) {
|
|
1610
|
+
return mapMaybePromise(this.privateUpdateNextHeadHashes(node.meta.next, true), () => [node]);
|
|
1611
|
+
}
|
|
1612
|
+
return [node];
|
|
1613
|
+
};
|
|
1614
|
+
return mapMaybePromise(this.properties.store.rm(node.hash), afterStoreDelete);
|
|
1615
|
+
}
|
|
1616
|
+
async deleteManyByQuery(indexedHashes, indexedByHash, deletedByHash, nodes, storeHashes, options) {
|
|
1617
|
+
const batchSize = 64;
|
|
1618
|
+
for (let i = 0; i < indexedHashes.length; i += batchSize) {
|
|
1619
|
+
const hashes = indexedHashes.slice(i, i + batchSize);
|
|
1620
|
+
const deleted = await this.properties.index.del({
|
|
1621
|
+
query: createHashMatchQuery(hashes),
|
|
1622
|
+
});
|
|
1623
|
+
for (const id of deleted) {
|
|
1624
|
+
const hash = String(id.primitive);
|
|
1625
|
+
const node = indexedByHash.get(hash);
|
|
1626
|
+
if (!node || deletedByHash.has(hash)) {
|
|
1627
|
+
continue;
|
|
1628
|
+
}
|
|
1629
|
+
deletedByHash.set(hash, node);
|
|
1630
|
+
storeHashes.push(hash);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
return this.finishDeleteMany(deletedByHash, nodes, storeHashes, options);
|
|
1634
|
+
}
|
|
1635
|
+
async consumeNativeTrimmedEntriesByQuery(indexedHashes, indexedByHash, deletedByHash, nodes, options) {
|
|
1636
|
+
const batchSize = 64;
|
|
1637
|
+
for (let i = 0; i < indexedHashes.length; i += batchSize) {
|
|
1638
|
+
const hashes = indexedHashes.slice(i, i + batchSize);
|
|
1639
|
+
const deleted = await this.properties.index.del({
|
|
1640
|
+
query: createHashMatchQuery(hashes),
|
|
1641
|
+
});
|
|
1642
|
+
for (const id of deleted) {
|
|
1643
|
+
const hash = String(id.primitive);
|
|
1644
|
+
const node = indexedByHash.get(hash);
|
|
1645
|
+
if (!node || deletedByHash.has(hash)) {
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
deletedByHash.set(hash, node);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
return this.finishConsumeNativeTrimmedEntries(deletedByHash, nodes, options);
|
|
1652
|
+
}
|
|
1653
|
+
finishConsumeNativeTrimmedEntries(deletedByHash, nodes, options) {
|
|
1654
|
+
const deleted = nodes
|
|
1655
|
+
.map((node) => deletedByHash.get(node.hash))
|
|
1656
|
+
.filter((node) => !!node);
|
|
1657
|
+
if (deleted.length === 0) {
|
|
1658
|
+
return [];
|
|
1659
|
+
}
|
|
1660
|
+
const afterStoreDelete = () => {
|
|
1661
|
+
this._length -= deleted.length;
|
|
1662
|
+
if (!options?.skipNextHeadUpdates) {
|
|
1663
|
+
const deletedNexts = [];
|
|
1664
|
+
for (const node of deleted) {
|
|
1665
|
+
if (node.meta.type !== EntryType.CUT) {
|
|
1666
|
+
deletedNexts.push(...node.meta.next);
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
return mapMaybePromise(this.privateUpdateNextHeadHashes(deletedNexts, true), () => deleted);
|
|
1670
|
+
}
|
|
1671
|
+
return deleted;
|
|
1672
|
+
};
|
|
1673
|
+
if (options?.deleteBlocks) {
|
|
1674
|
+
const store = this.properties.store;
|
|
1675
|
+
const hashes = deleted.map((node) => node.hash);
|
|
1676
|
+
const deleteResult = hasRmMany(store) && store.rmMany
|
|
1677
|
+
? store.rmMany(hashes)
|
|
1678
|
+
: Promise.all(hashes.map((hash) => store.rm(hash))).then(() => { });
|
|
1679
|
+
return mapMaybePromise(deleteResult, afterStoreDelete);
|
|
1680
|
+
}
|
|
1681
|
+
return afterStoreDelete();
|
|
1682
|
+
}
|
|
1683
|
+
finishDeleteMany(deletedByHash, nodes, storeHashes, options) {
|
|
1684
|
+
const deleted = nodes
|
|
1685
|
+
.map((node) => deletedByHash.get(node.hash))
|
|
1686
|
+
.filter((node) => !!node);
|
|
1687
|
+
if (deleted.length === 0) {
|
|
1688
|
+
return [];
|
|
1689
|
+
}
|
|
1690
|
+
const store = this.properties.store;
|
|
1691
|
+
const afterStoreDelete = () => {
|
|
1692
|
+
this._length -= deleted.length;
|
|
1693
|
+
const graph = this.properties.nativeGraph?.graph;
|
|
1694
|
+
if (graph?.deleteMany) {
|
|
1695
|
+
graph.deleteMany(storeHashes);
|
|
1696
|
+
}
|
|
1697
|
+
else {
|
|
1698
|
+
for (const hash of storeHashes) {
|
|
1699
|
+
graph?.delete(hash);
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
if (!options?.skipNextHeadUpdates) {
|
|
1703
|
+
const deletedNexts = [];
|
|
1704
|
+
for (const node of deleted) {
|
|
1705
|
+
if (node.meta.type !== EntryType.CUT) {
|
|
1706
|
+
deletedNexts.push(...node.meta.next);
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
return mapMaybePromise(this.privateUpdateNextHeadHashes(deletedNexts, true), () => deleted);
|
|
1710
|
+
}
|
|
1711
|
+
return deleted;
|
|
1712
|
+
};
|
|
1713
|
+
if (hasRmMany(store) && store.rmMany) {
|
|
1714
|
+
return mapMaybePromise(store.rmMany(storeHashes), afterStoreDelete);
|
|
1715
|
+
}
|
|
1716
|
+
return Promise.all(storeHashes.map((hash) => store.rm(hash))).then(afterStoreDelete);
|
|
1717
|
+
}
|
|
1718
|
+
nativeLogEntryToShallowEntry(entry) {
|
|
1719
|
+
return new ShallowEntry({
|
|
1720
|
+
hash: entry.hash,
|
|
1721
|
+
head: entry.head ?? false,
|
|
1722
|
+
payloadSize: entry.payloadSize ?? 0,
|
|
1723
|
+
meta: new ShallowMeta({
|
|
1724
|
+
gid: entry.gid,
|
|
1725
|
+
next: entry.next,
|
|
1726
|
+
type: entry.type,
|
|
1727
|
+
data: entry.data,
|
|
1728
|
+
clock: new Clock({
|
|
1729
|
+
id: this.properties.publicKey.bytes,
|
|
1730
|
+
timestamp: new Timestamp({
|
|
1731
|
+
wallTime: BigInt(entry.clock.timestamp.wallTime),
|
|
1732
|
+
logical: entry.clock.timestamp.logical ?? 0,
|
|
1733
|
+
}),
|
|
1734
|
+
}),
|
|
1735
|
+
}),
|
|
1736
|
+
});
|
|
1737
|
+
}
|
|
1738
|
+
nativeTrimmedHashToShallowEntry(hash) {
|
|
1739
|
+
return new ShallowEntry({
|
|
1740
|
+
hash,
|
|
1741
|
+
head: false,
|
|
1742
|
+
payloadSize: 0,
|
|
1743
|
+
meta: new ShallowMeta({
|
|
1744
|
+
gid: "",
|
|
1745
|
+
next: [],
|
|
1746
|
+
type: EntryType.APPEND,
|
|
1747
|
+
clock: new Clock({
|
|
1748
|
+
id: this.properties.publicKey.bytes,
|
|
1749
|
+
timestamp: new Timestamp({
|
|
1750
|
+
wallTime: 0n,
|
|
1751
|
+
logical: 0,
|
|
1752
|
+
}),
|
|
1753
|
+
}),
|
|
1754
|
+
}),
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
nativeLogEntriesToShallowEntries(entries) {
|
|
1758
|
+
return entries.map((entry) => this.nativeLogEntryToShallowEntry(entry));
|
|
1759
|
+
}
|
|
427
1760
|
async getMemoryUsage() {
|
|
1761
|
+
if (this.properties.nativeGraph) {
|
|
1762
|
+
return this.properties.nativeGraph.graph.payloadSizeSum();
|
|
1763
|
+
}
|
|
428
1764
|
const indexed = (await this.properties.index.sum({ key: "payloadSize" })) || 0;
|
|
429
|
-
|
|
430
|
-
|
|
1765
|
+
let pending = 0;
|
|
1766
|
+
for (const [hash, write] of this.pendingIndexWrites) {
|
|
1767
|
+
pending +=
|
|
1768
|
+
this.materializePendingIndexWrite(hash, write).payloadSize || 0;
|
|
1769
|
+
}
|
|
1770
|
+
return typeof indexed === "bigint"
|
|
1771
|
+
? indexed + BigInt(pending)
|
|
1772
|
+
: indexed + pending;
|
|
431
1773
|
}
|
|
432
1774
|
async privateUpdateNextHeadProperty(from, isHead) {
|
|
433
1775
|
if (from.meta.type === EntryType.CUT) {
|
|
434
1776
|
// if the next is a cut, we can't update it, since it's not in the index
|
|
435
1777
|
return;
|
|
436
1778
|
}
|
|
437
|
-
|
|
438
|
-
|
|
1779
|
+
await this.privateUpdateNextHeadHashes(from.meta.next, isHead);
|
|
1780
|
+
}
|
|
1781
|
+
async privateUpdateNextHeadHashes(nexts, isHead) {
|
|
1782
|
+
const hashes = [...new Set(nexts.filter(Boolean))];
|
|
1783
|
+
if (hashes.length === 0) {
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
const existingNexts = isHead && this.properties.nativeGraph
|
|
1787
|
+
? this.properties.nativeGraph.graph.hasMany(hashes)
|
|
1788
|
+
: undefined;
|
|
1789
|
+
for (const next of hashes) {
|
|
1790
|
+
const pending = this.getPendingIndexWrite(next);
|
|
1791
|
+
if (!pending && existingNexts && !existingNexts.has(next)) {
|
|
1792
|
+
continue;
|
|
1793
|
+
}
|
|
439
1794
|
const indexedEntry = pending
|
|
440
1795
|
? { id: toId(next), value: pending }
|
|
441
1796
|
: await this.properties.index.get(toId(next));
|
|
@@ -465,6 +1820,17 @@ export class EntryIndex {
|
|
|
465
1820
|
}
|
|
466
1821
|
}
|
|
467
1822
|
}
|
|
1823
|
+
async notifyShadowedGids(entry) {
|
|
1824
|
+
if (!this.properties.onGidRemoved || entry.meta.next.length === 0) {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
const shadowedGids = this.properties.nativeGraph
|
|
1828
|
+
? new Set(this.properties.nativeGraph.graph.shadowedGids(entry.meta.gid, entry.meta.next, entry.hash))
|
|
1829
|
+
: await this.findShadowedGids(entry);
|
|
1830
|
+
if (shadowedGids.size > 0) {
|
|
1831
|
+
this.properties.onGidRemoved([...shadowedGids]);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
468
1834
|
async clear() {
|
|
469
1835
|
this.clearPendingIndexFlushTimer();
|
|
470
1836
|
const hashes = new Set(this.pendingIndexWrites.keys());
|
|
@@ -571,7 +1937,50 @@ export class EntryIndex {
|
|
|
571
1937
|
}
|
|
572
1938
|
return mem ? mem : undefined;
|
|
573
1939
|
/* }
|
|
574
|
-
|
|
1940
|
+
return undefined; */
|
|
1941
|
+
}
|
|
1942
|
+
async resolveMany(hashes, options) {
|
|
1943
|
+
if (hashes.length === 0) {
|
|
1944
|
+
return [];
|
|
1945
|
+
}
|
|
1946
|
+
if (!canBatchResolveFromStore(this.properties.store, options)) {
|
|
1947
|
+
return Promise.all(hashes.map((hash) => this.resolve(hash, options)));
|
|
1948
|
+
}
|
|
1949
|
+
const coercedOptions = typeof options === "object" ? options : undefined;
|
|
1950
|
+
const resolved = new Array(hashes.length);
|
|
1951
|
+
const missingHashes = [];
|
|
1952
|
+
const missingPositions = [];
|
|
1953
|
+
for (let i = 0; i < hashes.length; i++) {
|
|
1954
|
+
const hash = hashes[i];
|
|
1955
|
+
const mem = this.cache.get(hash);
|
|
1956
|
+
if (mem !== undefined) {
|
|
1957
|
+
resolved[i] = mem ? mem : undefined;
|
|
1958
|
+
continue;
|
|
1959
|
+
}
|
|
1960
|
+
missingHashes.push(hash);
|
|
1961
|
+
missingPositions.push(i);
|
|
1962
|
+
}
|
|
1963
|
+
if (missingHashes.length === 0) {
|
|
1964
|
+
return resolved;
|
|
1965
|
+
}
|
|
1966
|
+
const values = await this.properties.store.getMany(missingHashes, withDefaultRemoteReadPriority(coercedOptions));
|
|
1967
|
+
for (let i = 0; i < values.length; i++) {
|
|
1968
|
+
const hash = missingHashes[i];
|
|
1969
|
+
const value = values[i];
|
|
1970
|
+
if (!value) {
|
|
1971
|
+
if (coercedOptions?.ignoreMissing !== true) {
|
|
1972
|
+
throw new Error("Failed to load entry from head with hash: " + hash);
|
|
1973
|
+
}
|
|
1974
|
+
continue;
|
|
1975
|
+
}
|
|
1976
|
+
const entry = deserialize(value, Entry);
|
|
1977
|
+
this.properties.init(entry);
|
|
1978
|
+
entry.hash = hash;
|
|
1979
|
+
entry.size = value.length;
|
|
1980
|
+
this.cache.add(hash, entry);
|
|
1981
|
+
resolved[missingPositions[i]] = entry;
|
|
1982
|
+
}
|
|
1983
|
+
return resolved;
|
|
575
1984
|
}
|
|
576
1985
|
async resolveFromStore(k, options) {
|
|
577
1986
|
let coercedOptions = options;
|
|
@@ -614,6 +2023,19 @@ export class EntryIndex {
|
|
|
614
2023
|
return null;
|
|
615
2024
|
}
|
|
616
2025
|
}
|
|
2026
|
+
const createHashMatchQuery = (hashes) => hashes.length === 1
|
|
2027
|
+
? new StringMatch({
|
|
2028
|
+
key: "hash",
|
|
2029
|
+
value: hashes[0],
|
|
2030
|
+
caseInsensitive: false,
|
|
2031
|
+
method: StringMatchMethod.exact,
|
|
2032
|
+
})
|
|
2033
|
+
: new Or(hashes.map((hash) => new StringMatch({
|
|
2034
|
+
key: "hash",
|
|
2035
|
+
value: hash,
|
|
2036
|
+
caseInsensitive: false,
|
|
2037
|
+
method: StringMatchMethod.exact,
|
|
2038
|
+
})));
|
|
617
2039
|
const toNativeLogEntry = (entry) => ({
|
|
618
2040
|
hash: entry.hash,
|
|
619
2041
|
gid: entry.meta.gid,
|
|
@@ -621,6 +2043,7 @@ const toNativeLogEntry = (entry) => ({
|
|
|
621
2043
|
type: entry.meta.type,
|
|
622
2044
|
head: entry.head,
|
|
623
2045
|
payloadSize: entry.payloadSize,
|
|
2046
|
+
data: entry.meta.data,
|
|
624
2047
|
clock: {
|
|
625
2048
|
timestamp: {
|
|
626
2049
|
wallTime: entry.meta.clock.timestamp.wallTime,
|
|
@@ -628,6 +2051,28 @@ const toNativeLogEntry = (entry) => ({
|
|
|
628
2051
|
},
|
|
629
2052
|
},
|
|
630
2053
|
});
|
|
2054
|
+
const getResolveShape = (options) => options && options !== true && options.type === "shape"
|
|
2055
|
+
? options.shape
|
|
2056
|
+
: undefined;
|
|
2057
|
+
const isHeadHashOnlyShape = (shape) => {
|
|
2058
|
+
const keys = Object.keys(shape);
|
|
2059
|
+
return keys.length === 1 && shape.hash === true;
|
|
2060
|
+
};
|
|
2061
|
+
const isHeadDataShape = (shape) => {
|
|
2062
|
+
const keys = Object.keys(shape);
|
|
2063
|
+
if (keys.some((key) => key !== "hash" && key !== "meta")) {
|
|
2064
|
+
return false;
|
|
2065
|
+
}
|
|
2066
|
+
if (shape.hash !== undefined && shape.hash !== true) {
|
|
2067
|
+
return false;
|
|
2068
|
+
}
|
|
2069
|
+
const meta = shape.meta;
|
|
2070
|
+
if (!meta || typeof meta !== "object") {
|
|
2071
|
+
return false;
|
|
2072
|
+
}
|
|
2073
|
+
const metaKeys = Object.keys(meta);
|
|
2074
|
+
return metaKeys.length === 1 && meta.data === true;
|
|
2075
|
+
};
|
|
631
2076
|
const projectShape = (value, shape) => {
|
|
632
2077
|
const out = {};
|
|
633
2078
|
for (const [key, selector] of Object.entries(shape)) {
|