@frostpillar/frostpillar-btree 0.2.5 → 0.2.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-JA.md +117 -64
- package/README.md +117 -64
- package/dist/InMemoryBTree.d.ts +3 -3
- package/dist/btree/autoScale.d.ts +2 -0
- package/dist/btree/entry-lookup.d.ts +8 -0
- package/dist/btree/mutations.d.ts +1 -3
- package/dist/btree/node-ops.d.ts +15 -0
- package/dist/btree/rangeQuery.d.ts +5 -1
- package/dist/btree/rebalance-branch.d.ts +4 -0
- package/dist/btree/rebalance.d.ts +5 -2
- package/dist/btree/serialization.d.ts +3 -1
- package/dist/btree/split.d.ts +3 -0
- package/dist/btree/traversal.d.ts +7 -0
- package/dist/btree/types.d.ts +23 -26
- package/dist/{chunk-CZFRT2NN.js → chunk-UGGWGP4E.js} +823 -499
- package/dist/concurrency/ConcurrentInMemoryBTree.d.ts +22 -29
- package/dist/concurrency/coordinator.d.ts +21 -0
- package/dist/concurrency/helpers.d.ts +8 -2
- package/dist/concurrency/syncLogValidation.d.ts +2 -0
- package/dist/concurrency/types.d.ts +14 -1
- package/dist/concurrency/writeOps.d.ts +48 -0
- package/dist/core.cjs +823 -499
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/errors.d.ts +3 -0
- package/dist/frostpillar-btree-core.min.js +1 -1
- package/dist/frostpillar-btree.min.js +1 -1
- package/dist/index.cjs +1212 -670
- package/dist/index.d.ts +1 -1
- package/dist/index.js +397 -179
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
DEFAULT_MAX_LEAF_ENTRIES,
|
|
7
7
|
InMemoryBTree,
|
|
8
8
|
computeAutoScaleTier
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-UGGWGP4E.js";
|
|
10
10
|
|
|
11
11
|
// src/concurrency/helpers.ts
|
|
12
12
|
var DEFAULT_MAX_RETRIES = 16;
|
|
@@ -30,51 +30,110 @@ var assertNeverMutation = (mutation) => {
|
|
|
30
30
|
`Unsupported mutation type from shared store: ${String(unknownMutation.type)}`
|
|
31
31
|
);
|
|
32
32
|
};
|
|
33
|
-
var
|
|
34
|
-
for (const
|
|
35
|
-
if (typeof
|
|
36
|
-
throw new BTreeConcurrencyError(
|
|
33
|
+
var validatePutManyEntries = (entries) => {
|
|
34
|
+
for (const entry of entries) {
|
|
35
|
+
if (typeof entry !== "object" || entry === null || !("key" in entry) || !("value" in entry)) {
|
|
36
|
+
throw new BTreeConcurrencyError(
|
|
37
|
+
"Malformed putMany mutation: each entry must have key and value."
|
|
38
|
+
);
|
|
37
39
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (!("key" in m)) {
|
|
57
|
-
throw new BTreeConcurrencyError("Malformed remove mutation: missing key.");
|
|
58
|
-
}
|
|
59
|
-
break;
|
|
60
|
-
case "removeById":
|
|
61
|
-
if (!("entryId" in m)) {
|
|
62
|
-
throw new BTreeConcurrencyError("Malformed removeById mutation: missing entryId.");
|
|
63
|
-
}
|
|
64
|
-
break;
|
|
65
|
-
case "updateById":
|
|
66
|
-
if (!("entryId" in m) || !("value" in m)) {
|
|
67
|
-
throw new BTreeConcurrencyError("Malformed updateById mutation: missing entryId or value.");
|
|
68
|
-
}
|
|
69
|
-
break;
|
|
70
|
-
case "popFirst":
|
|
71
|
-
case "popLast":
|
|
72
|
-
break;
|
|
73
|
-
default:
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var validateInitMutation = (m, expectedConfigFingerprint) => {
|
|
43
|
+
if (typeof m.configFingerprint !== "string") {
|
|
44
|
+
throw new BTreeConcurrencyError(
|
|
45
|
+
"Malformed init mutation: missing configFingerprint."
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
if (expectedConfigFingerprint !== void 0 && m.configFingerprint !== expectedConfigFingerprint) {
|
|
49
|
+
throw new BTreeConcurrencyError(
|
|
50
|
+
"Config mismatch: store peers must share identical tree config."
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
var validateMutationFields = (m) => {
|
|
55
|
+
switch (m.type) {
|
|
56
|
+
case "put":
|
|
57
|
+
if (!("key" in m) || !("value" in m)) {
|
|
74
58
|
throw new BTreeConcurrencyError(
|
|
75
|
-
|
|
59
|
+
"Malformed put mutation: missing key or value."
|
|
76
60
|
);
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
case "remove":
|
|
64
|
+
if (!("key" in m)) {
|
|
65
|
+
throw new BTreeConcurrencyError(
|
|
66
|
+
"Malformed remove mutation: missing key."
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
case "removeById":
|
|
71
|
+
if (!("entryId" in m)) {
|
|
72
|
+
throw new BTreeConcurrencyError(
|
|
73
|
+
"Malformed removeById mutation: missing entryId."
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case "updateById":
|
|
78
|
+
if (!("entryId" in m) || !("value" in m)) {
|
|
79
|
+
throw new BTreeConcurrencyError(
|
|
80
|
+
"Malformed updateById mutation: missing entryId or value."
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
case "putMany":
|
|
85
|
+
if (!("entries" in m) || !Array.isArray(m.entries)) {
|
|
86
|
+
throw new BTreeConcurrencyError(
|
|
87
|
+
"Malformed putMany mutation: missing entries array."
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
validatePutManyEntries(m.entries);
|
|
91
|
+
break;
|
|
92
|
+
case "deleteRange":
|
|
93
|
+
if (!("startKey" in m) || !("endKey" in m)) {
|
|
94
|
+
throw new BTreeConcurrencyError(
|
|
95
|
+
"Malformed deleteRange mutation: missing startKey or endKey."
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var validateSingleMutation = (m, expectedConfigFingerprint) => {
|
|
104
|
+
switch (m.type) {
|
|
105
|
+
case "init":
|
|
106
|
+
validateInitMutation(m, expectedConfigFingerprint);
|
|
107
|
+
break;
|
|
108
|
+
case "put":
|
|
109
|
+
case "remove":
|
|
110
|
+
case "removeById":
|
|
111
|
+
case "updateById":
|
|
112
|
+
case "putMany":
|
|
113
|
+
case "deleteRange":
|
|
114
|
+
validateMutationFields(m);
|
|
115
|
+
break;
|
|
116
|
+
case "popFirst":
|
|
117
|
+
case "popLast":
|
|
118
|
+
case "clear":
|
|
119
|
+
break;
|
|
120
|
+
default:
|
|
121
|
+
throw new BTreeConcurrencyError(
|
|
122
|
+
`Unsupported mutation type from shared store: ${String(m.type)}`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var validateMutationBatch = (mutations, expectedConfigFingerprint) => {
|
|
127
|
+
for (const mutation of mutations) {
|
|
128
|
+
if (typeof mutation !== "object" || mutation === null) {
|
|
129
|
+
throw new BTreeConcurrencyError(
|
|
130
|
+
"Malformed mutation: expected an object."
|
|
131
|
+
);
|
|
77
132
|
}
|
|
133
|
+
validateSingleMutation(
|
|
134
|
+
mutation,
|
|
135
|
+
expectedConfigFingerprint
|
|
136
|
+
);
|
|
78
137
|
}
|
|
79
138
|
};
|
|
80
139
|
var normalizeMaxRetries = (value) => {
|
|
@@ -104,9 +163,7 @@ var normalizeReadMode = (value) => {
|
|
|
104
163
|
return "strong";
|
|
105
164
|
}
|
|
106
165
|
if (value !== "strong" && value !== "local") {
|
|
107
|
-
throw new BTreeConcurrencyError(
|
|
108
|
-
`readMode: must be 'strong' or 'local'.`
|
|
109
|
-
);
|
|
166
|
+
throw new BTreeConcurrencyError(`readMode: must be 'strong' or 'local'.`);
|
|
110
167
|
}
|
|
111
168
|
return value;
|
|
112
169
|
};
|
|
@@ -135,79 +192,170 @@ function assertAppendVersionContract(expectedVersion, appendResult) {
|
|
|
135
192
|
}
|
|
136
193
|
}
|
|
137
194
|
|
|
138
|
-
// src/concurrency/
|
|
139
|
-
var
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
195
|
+
// src/concurrency/writeOps.ts
|
|
196
|
+
var applyMutationLocal = (tree, mutation, onInit) => {
|
|
197
|
+
switch (mutation.type) {
|
|
198
|
+
case "init":
|
|
199
|
+
onInit();
|
|
200
|
+
return null;
|
|
201
|
+
case "put":
|
|
202
|
+
return tree.put(mutation.key, mutation.value);
|
|
203
|
+
case "putMany":
|
|
204
|
+
return tree.putMany(mutation.entries);
|
|
205
|
+
case "remove":
|
|
206
|
+
return tree.remove(mutation.key);
|
|
207
|
+
case "removeById":
|
|
208
|
+
return tree.removeById(mutation.entryId);
|
|
209
|
+
case "updateById":
|
|
210
|
+
return tree.updateById(mutation.entryId, mutation.value);
|
|
211
|
+
case "popFirst":
|
|
212
|
+
return tree.popFirst();
|
|
213
|
+
case "popLast":
|
|
214
|
+
return tree.popLast();
|
|
215
|
+
case "deleteRange":
|
|
216
|
+
return tree.deleteRange(
|
|
217
|
+
mutation.startKey,
|
|
218
|
+
mutation.endKey,
|
|
219
|
+
mutation.options
|
|
220
|
+
);
|
|
221
|
+
case "clear":
|
|
222
|
+
tree.clear();
|
|
223
|
+
return null;
|
|
224
|
+
default:
|
|
225
|
+
return assertNeverMutation(mutation);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
var createPutEvaluator = (duplicateKeys, key, value) => {
|
|
229
|
+
return (tree) => {
|
|
230
|
+
if (duplicateKeys === "reject" && tree.hasKey(key)) {
|
|
231
|
+
throw new BTreeValidationError("Duplicate key rejected.");
|
|
232
|
+
}
|
|
233
|
+
return { type: "put", key, value };
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
var createRemoveEvaluator = (key) => {
|
|
237
|
+
return (tree) => {
|
|
238
|
+
return tree.hasKey(key) ? { type: "remove", key } : null;
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
var createRemoveByIdEvaluator = (entryId) => {
|
|
242
|
+
return (tree) => {
|
|
243
|
+
return tree.peekById(entryId) !== null ? { type: "removeById", entryId } : null;
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
var createUpdateByIdEvaluator = (entryId, value) => {
|
|
247
|
+
return (tree) => {
|
|
248
|
+
return tree.peekById(entryId) !== null ? { type: "updateById", entryId, value } : null;
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
var createPopFirstEvaluator = () => {
|
|
252
|
+
return (tree) => {
|
|
253
|
+
return tree.peekFirst() !== null ? { type: "popFirst" } : null;
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
var createPopLastEvaluator = () => {
|
|
257
|
+
return (tree) => {
|
|
258
|
+
return tree.peekLast() !== null ? { type: "popLast" } : null;
|
|
259
|
+
};
|
|
260
|
+
};
|
|
261
|
+
var createPutManyEvaluator = (entries, duplicateKeys, compareKeys) => {
|
|
262
|
+
return (tree) => {
|
|
263
|
+
const strictlyAscending = duplicateKeys !== "allow";
|
|
264
|
+
for (let i = 1; i < entries.length; i += 1) {
|
|
265
|
+
const cmp = compareKeys(entries[i - 1].key, entries[i].key);
|
|
266
|
+
if (cmp > 0) {
|
|
267
|
+
throw new BTreeValidationError(
|
|
268
|
+
"putMany: entries not in ascending order."
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
if (strictlyAscending && cmp === 0) {
|
|
272
|
+
throw new BTreeValidationError(
|
|
273
|
+
duplicateKeys === "reject" ? "putMany: duplicate key rejected." : "putMany: equal keys not allowed in strict mode."
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (duplicateKeys === "reject") {
|
|
278
|
+
for (const entry of entries) {
|
|
279
|
+
if (tree.hasKey(entry.key)) {
|
|
280
|
+
throw new BTreeValidationError("Duplicate key rejected.");
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return { type: "putMany", entries };
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
var createDeleteRangeEvaluator = (startKey, endKey, options) => {
|
|
288
|
+
return (tree) => {
|
|
289
|
+
const count = tree.count(startKey, endKey, options);
|
|
290
|
+
if (count === 0) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
return { type: "deleteRange", startKey, endKey, options };
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
var createClearEvaluator = () => {
|
|
297
|
+
return () => ({ type: "clear" });
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// src/concurrency/syncLogValidation.ts
|
|
301
|
+
var validateSyncLog = (log, maxSyncMutationsPerBatch) => {
|
|
302
|
+
if (typeof log.version !== "bigint") {
|
|
303
|
+
throw new BTreeConcurrencyError("Store contract: version must be bigint.");
|
|
304
|
+
}
|
|
305
|
+
if (!Array.isArray(log.mutations)) {
|
|
306
|
+
throw new BTreeConcurrencyError(
|
|
307
|
+
"Store contract: mutations must be an array."
|
|
145
308
|
);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
309
|
+
}
|
|
310
|
+
if (log.mutations.length > maxSyncMutationsPerBatch) {
|
|
311
|
+
throw new BTreeConcurrencyError(
|
|
312
|
+
`Sync batch exceeded limit (${String(maxSyncMutationsPerBatch)}).`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// src/concurrency/coordinator.ts
|
|
318
|
+
var Coordinator = class {
|
|
319
|
+
constructor(tree, store, maxRetries, maxSyncMutationsPerBatch, configFingerprint, readMode) {
|
|
320
|
+
this.tree = tree;
|
|
321
|
+
this.store = store;
|
|
322
|
+
this.maxRetries = maxRetries;
|
|
323
|
+
this.maxSyncMutationsPerBatch = maxSyncMutationsPerBatch;
|
|
324
|
+
this.configFingerprint = configFingerprint;
|
|
325
|
+
this.readMode = readMode;
|
|
157
326
|
this.currentVersion = 0n;
|
|
158
327
|
this.operationQueue = Promise.resolve();
|
|
159
328
|
this.initSeen = false;
|
|
160
|
-
|
|
161
|
-
async sync() {
|
|
162
|
-
await this.runExclusive(async () => {
|
|
163
|
-
await this.syncUnlocked();
|
|
164
|
-
});
|
|
329
|
+
this.corrupted = false;
|
|
165
330
|
}
|
|
166
331
|
async syncUnlocked() {
|
|
167
332
|
const log = await this.store.getLogEntriesSince(this.currentVersion);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
if (!Array.isArray(log.mutations)) {
|
|
172
|
-
throw new BTreeConcurrencyError("Store contract: mutations must be an array.");
|
|
173
|
-
}
|
|
174
|
-
if (log.mutations.length > this.maxSyncMutationsPerBatch) {
|
|
175
|
-
throw new BTreeConcurrencyError(
|
|
176
|
-
`Sync batch exceeded limit (${String(this.maxSyncMutationsPerBatch)}).`
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
if (log.version <= this.currentVersion) {
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
333
|
+
validateSyncLog(log, this.maxSyncMutationsPerBatch);
|
|
334
|
+
if (log.version <= this.currentVersion) return;
|
|
182
335
|
validateMutationBatch(log.mutations, this.configFingerprint);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
this.currentVersion = log.version;
|
|
187
|
-
}
|
|
188
|
-
applyMutationLocal(mutation) {
|
|
189
|
-
switch (mutation.type) {
|
|
190
|
-
case "init":
|
|
336
|
+
try {
|
|
337
|
+
const markInit = () => {
|
|
191
338
|
this.initSeen = true;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return this.tree.popFirst();
|
|
203
|
-
case "popLast":
|
|
204
|
-
return this.tree.popLast();
|
|
205
|
-
default:
|
|
206
|
-
return assertNeverMutation(mutation);
|
|
339
|
+
};
|
|
340
|
+
for (const mutation of log.mutations)
|
|
341
|
+
applyMutationLocal(this.tree, mutation, markInit);
|
|
342
|
+
this.currentVersion = log.version;
|
|
343
|
+
} catch (error) {
|
|
344
|
+
this.corrupted = true;
|
|
345
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
346
|
+
throw new BTreeConcurrencyError(
|
|
347
|
+
`Replay failure: instance is permanently corrupted. Discard and create a new instance. Cause: ${cause}`
|
|
348
|
+
);
|
|
207
349
|
}
|
|
208
350
|
}
|
|
209
351
|
runExclusive(operation) {
|
|
210
|
-
const run = async () =>
|
|
352
|
+
const run = async () => {
|
|
353
|
+
if (this.corrupted)
|
|
354
|
+
throw new BTreeConcurrencyError(
|
|
355
|
+
"Instance is permanently corrupted. Discard and create a new instance."
|
|
356
|
+
);
|
|
357
|
+
return operation();
|
|
358
|
+
};
|
|
211
359
|
const result = this.operationQueue.then(run, run);
|
|
212
360
|
this.operationQueue = result.then(
|
|
213
361
|
() => void 0,
|
|
@@ -217,137 +365,207 @@ var ConcurrentInMemoryBTree = class {
|
|
|
217
365
|
}
|
|
218
366
|
readOp(fn) {
|
|
219
367
|
return this.runExclusive(async () => {
|
|
220
|
-
if (this.readMode === "strong")
|
|
221
|
-
await this.syncUnlocked();
|
|
222
|
-
}
|
|
368
|
+
if (this.readMode === "strong") await this.syncUnlocked();
|
|
223
369
|
return fn(this.tree);
|
|
224
370
|
});
|
|
225
371
|
}
|
|
226
|
-
async
|
|
372
|
+
async appendAndApply(evaluate) {
|
|
227
373
|
for (let attempt = 0; attempt < this.maxRetries; attempt += 1) {
|
|
228
374
|
await this.syncUnlocked();
|
|
229
375
|
const mutation = evaluate(this.tree);
|
|
230
|
-
if (mutation === null)
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
376
|
+
if (mutation === null) return null;
|
|
233
377
|
const expectedVersion = this.currentVersion;
|
|
234
|
-
const mutations = this.initSeen ? [mutation] : [
|
|
378
|
+
const mutations = this.initSeen ? [mutation] : [
|
|
379
|
+
{ type: "init", configFingerprint: this.configFingerprint },
|
|
380
|
+
mutation
|
|
381
|
+
];
|
|
235
382
|
const appendResult = await this.store.append(expectedVersion, mutations);
|
|
236
383
|
assertAppendVersionContract(expectedVersion, appendResult);
|
|
237
384
|
if (appendResult.applied) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
385
|
+
try {
|
|
386
|
+
const markInit = () => {
|
|
387
|
+
this.initSeen = true;
|
|
388
|
+
};
|
|
389
|
+
for (const m of mutations) {
|
|
390
|
+
if (m === mutation) break;
|
|
391
|
+
applyMutationLocal(this.tree, m, markInit);
|
|
392
|
+
}
|
|
393
|
+
const result = applyMutationLocal(
|
|
394
|
+
this.tree,
|
|
395
|
+
mutation,
|
|
396
|
+
markInit
|
|
397
|
+
);
|
|
398
|
+
this.currentVersion = appendResult.version;
|
|
399
|
+
return result;
|
|
400
|
+
} catch (error) {
|
|
401
|
+
this.corrupted = true;
|
|
402
|
+
const cause = error instanceof Error ? error.message : String(error);
|
|
403
|
+
throw new BTreeConcurrencyError(
|
|
404
|
+
`Local apply failure after successful append: instance is permanently corrupted. Discard and create a new instance. Cause: ${cause}`
|
|
405
|
+
);
|
|
241
406
|
}
|
|
242
|
-
const localResult = this.applyMutationLocal(mutation);
|
|
243
|
-
this.currentVersion = appendResult.version;
|
|
244
|
-
return localResult;
|
|
245
407
|
}
|
|
246
408
|
}
|
|
247
409
|
throw new BTreeConcurrencyError(
|
|
248
410
|
`Mutation failed after ${String(this.maxRetries)} retries.`
|
|
249
411
|
);
|
|
250
412
|
}
|
|
251
|
-
|
|
252
|
-
return this.runExclusive(async () =>
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
413
|
+
writeOp(evaluator) {
|
|
414
|
+
return this.runExclusive(async () => this.appendAndApply(evaluator));
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// src/concurrency/ConcurrentInMemoryBTree.ts
|
|
419
|
+
var ConcurrentInMemoryBTree = class {
|
|
420
|
+
constructor(config) {
|
|
421
|
+
this.compareKeys = config.compareKeys;
|
|
422
|
+
this.duplicateKeys = config.duplicateKeys ?? "replace";
|
|
423
|
+
const tree = new InMemoryBTree({
|
|
424
|
+
compareKeys: config.compareKeys,
|
|
425
|
+
maxLeafEntries: config.maxLeafEntries,
|
|
426
|
+
maxBranchChildren: config.maxBranchChildren,
|
|
427
|
+
duplicateKeys: config.duplicateKeys,
|
|
428
|
+
enableEntryIdLookup: config.enableEntryIdLookup,
|
|
429
|
+
autoScale: config.autoScale,
|
|
430
|
+
deleteRebalancePolicy: config.deleteRebalancePolicy
|
|
261
431
|
});
|
|
432
|
+
this.coord = new Coordinator(
|
|
433
|
+
tree,
|
|
434
|
+
config.store,
|
|
435
|
+
normalizeMaxRetries(config.maxRetries),
|
|
436
|
+
normalizeMaxSyncMutationsPerBatch(config.maxSyncMutationsPerBatch),
|
|
437
|
+
computeConfigFingerprint(config),
|
|
438
|
+
normalizeReadMode(config.readMode)
|
|
439
|
+
);
|
|
262
440
|
}
|
|
263
|
-
async
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
(tree) => {
|
|
267
|
-
return tree.hasKey(key) ? { type: "remove", key } : null;
|
|
268
|
-
}
|
|
269
|
-
);
|
|
441
|
+
async sync() {
|
|
442
|
+
await this.coord.runExclusive(async () => {
|
|
443
|
+
await this.coord.syncUnlocked();
|
|
270
444
|
});
|
|
271
445
|
}
|
|
272
|
-
async
|
|
273
|
-
return this.runExclusive(async () => {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
return tree.peekById(entryId) !== null ? { type: "removeById", entryId } : null;
|
|
277
|
-
}
|
|
278
|
-
);
|
|
446
|
+
async syncThenRead(fn) {
|
|
447
|
+
return this.coord.runExclusive(async () => {
|
|
448
|
+
await this.coord.syncUnlocked();
|
|
449
|
+
return fn(this.coord.tree);
|
|
279
450
|
});
|
|
280
451
|
}
|
|
452
|
+
async put(key, value) {
|
|
453
|
+
return this.coord.writeOp(
|
|
454
|
+
createPutEvaluator(this.duplicateKeys, key, value)
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
async remove(key) {
|
|
458
|
+
return this.coord.writeOp(createRemoveEvaluator(key));
|
|
459
|
+
}
|
|
460
|
+
async removeById(entryId) {
|
|
461
|
+
return this.coord.writeOp(createRemoveByIdEvaluator(entryId));
|
|
462
|
+
}
|
|
281
463
|
async updateById(entryId, value) {
|
|
282
|
-
return this.
|
|
283
|
-
return this.appendMutationAndApplyUnlocked(
|
|
284
|
-
(tree) => {
|
|
285
|
-
return tree.peekById(entryId) !== null ? { type: "updateById", entryId, value } : null;
|
|
286
|
-
}
|
|
287
|
-
);
|
|
288
|
-
});
|
|
464
|
+
return this.coord.writeOp(createUpdateByIdEvaluator(entryId, value));
|
|
289
465
|
}
|
|
290
466
|
async popFirst() {
|
|
291
|
-
return this.
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
467
|
+
return this.coord.writeOp(createPopFirstEvaluator());
|
|
468
|
+
}
|
|
469
|
+
async popLast() {
|
|
470
|
+
return this.coord.writeOp(createPopLastEvaluator());
|
|
471
|
+
}
|
|
472
|
+
async putMany(entries) {
|
|
473
|
+
if (entries.length === 0) return [];
|
|
474
|
+
return this.coord.writeOp(
|
|
475
|
+
createPutManyEvaluator(entries, this.duplicateKeys, this.compareKeys)
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
async deleteRange(startKey, endKey, options) {
|
|
479
|
+
const result = await this.coord.writeOp(
|
|
480
|
+
createDeleteRangeEvaluator(startKey, endKey, options)
|
|
481
|
+
);
|
|
482
|
+
return result ?? 0;
|
|
483
|
+
}
|
|
484
|
+
async clear() {
|
|
485
|
+
await this.coord.writeOp(createClearEvaluator());
|
|
296
486
|
}
|
|
297
487
|
async get(key) {
|
|
298
|
-
return this.readOp((
|
|
488
|
+
return this.coord.readOp((t) => t.get(key));
|
|
299
489
|
}
|
|
300
490
|
async hasKey(key) {
|
|
301
|
-
return this.readOp((
|
|
491
|
+
return this.coord.readOp((t) => t.hasKey(key));
|
|
302
492
|
}
|
|
303
493
|
async findFirst(key) {
|
|
304
|
-
return this.readOp((
|
|
494
|
+
return this.coord.readOp((t) => t.findFirst(key));
|
|
305
495
|
}
|
|
306
496
|
async findLast(key) {
|
|
307
|
-
return this.readOp((
|
|
497
|
+
return this.coord.readOp((t) => t.findLast(key));
|
|
308
498
|
}
|
|
309
499
|
async range(startKey, endKey, options) {
|
|
310
|
-
return this.readOp((
|
|
500
|
+
return this.coord.readOp((t) => t.range(startKey, endKey, options));
|
|
311
501
|
}
|
|
312
502
|
async snapshot() {
|
|
313
|
-
return this.readOp((
|
|
503
|
+
return this.coord.readOp((t) => t.snapshot());
|
|
314
504
|
}
|
|
315
505
|
async size() {
|
|
316
|
-
return this.readOp((
|
|
506
|
+
return this.coord.readOp((t) => t.size());
|
|
317
507
|
}
|
|
318
508
|
async assertInvariants() {
|
|
319
|
-
await this.readOp((
|
|
509
|
+
await this.coord.readOp((t) => t.assertInvariants());
|
|
320
510
|
}
|
|
321
511
|
async getStats() {
|
|
322
|
-
return this.readOp((
|
|
512
|
+
return this.coord.readOp((t) => t.getStats());
|
|
323
513
|
}
|
|
324
514
|
async peekFirst() {
|
|
325
|
-
return this.readOp((
|
|
515
|
+
return this.coord.readOp((t) => t.peekFirst());
|
|
326
516
|
}
|
|
327
517
|
async peekLast() {
|
|
328
|
-
return this.readOp((
|
|
329
|
-
}
|
|
330
|
-
async popLast() {
|
|
331
|
-
return this.runExclusive(async () => {
|
|
332
|
-
return this.appendMutationAndApplyUnlocked((tree) => {
|
|
333
|
-
return tree.peekLast() !== null ? { type: "popLast" } : null;
|
|
334
|
-
});
|
|
335
|
-
});
|
|
518
|
+
return this.coord.readOp((t) => t.peekLast());
|
|
336
519
|
}
|
|
337
520
|
async peekById(entryId) {
|
|
338
|
-
return this.readOp((
|
|
521
|
+
return this.coord.readOp((t) => t.peekById(entryId));
|
|
339
522
|
}
|
|
340
523
|
async count(startKey, endKey, options) {
|
|
341
|
-
return this.readOp((
|
|
524
|
+
return this.coord.readOp((t) => t.count(startKey, endKey, options));
|
|
342
525
|
}
|
|
343
526
|
async nextHigherKey(key) {
|
|
344
|
-
return this.readOp((
|
|
527
|
+
return this.coord.readOp((t) => t.nextHigherKey(key));
|
|
345
528
|
}
|
|
346
529
|
async nextLowerKey(key) {
|
|
347
|
-
return this.readOp((
|
|
530
|
+
return this.coord.readOp((t) => t.nextLowerKey(key));
|
|
348
531
|
}
|
|
349
532
|
async getPairOrNextLower(key) {
|
|
350
|
-
return this.readOp((
|
|
533
|
+
return this.coord.readOp((t) => t.getPairOrNextLower(key));
|
|
534
|
+
}
|
|
535
|
+
async entries() {
|
|
536
|
+
return this.coord.readOp((t) => Array.from(t.entries()));
|
|
537
|
+
}
|
|
538
|
+
async entriesReversed() {
|
|
539
|
+
return this.coord.readOp((t) => Array.from(t.entriesReversed()));
|
|
540
|
+
}
|
|
541
|
+
async keys() {
|
|
542
|
+
return this.coord.readOp((t) => Array.from(t.keys()));
|
|
543
|
+
}
|
|
544
|
+
async values() {
|
|
545
|
+
return this.coord.readOp((t) => Array.from(t.values()));
|
|
546
|
+
}
|
|
547
|
+
async forEach(callback) {
|
|
548
|
+
await this.coord.readOp((t) => {
|
|
549
|
+
t.forEach(callback);
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
async forEachRange(startKey, endKey, callback, options) {
|
|
553
|
+
await this.coord.readOp((t) => {
|
|
554
|
+
t.forEachRange(startKey, endKey, callback, options);
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
async *[Symbol.asyncIterator]() {
|
|
558
|
+
const all = await this.entries();
|
|
559
|
+
for (const entry of all) yield entry;
|
|
560
|
+
}
|
|
561
|
+
async clone() {
|
|
562
|
+
return this.coord.readOp((t) => t.clone());
|
|
563
|
+
}
|
|
564
|
+
async toJSON() {
|
|
565
|
+
return this.coord.readOp((t) => t.toJSON());
|
|
566
|
+
}
|
|
567
|
+
static fromJSON(json, compareKeys) {
|
|
568
|
+
return InMemoryBTree.fromJSON(json, compareKeys);
|
|
351
569
|
}
|
|
352
570
|
};
|
|
353
571
|
export {
|