serializable-bptree 8.1.2 → 8.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/cjs/index.cjs +1020 -950
- package/dist/esm/index.mjs +1020 -950
- package/dist/types/base/BPTreeTransaction.d.ts +9 -0
- package/dist/types/transaction/BPTreeAsyncTransaction.d.ts +4 -0
- package/dist/types/transaction/BPTreeSyncTransaction.d.ts +1 -0
- package/package.json +5 -4
package/dist/esm/index.mjs
CHANGED
|
@@ -69,6 +69,7 @@ var MVCCTransaction = class {
|
|
|
69
69
|
// delete 시 삭제 전 값 저장
|
|
70
70
|
originallyExisted;
|
|
71
71
|
// 트랜잭션 시작 시점에 디스크에 존재했던 키 (deleted 결과 필터링용)
|
|
72
|
+
bufferHistory = /* @__PURE__ */ new Map();
|
|
72
73
|
// Nested Transaction Properties
|
|
73
74
|
parent;
|
|
74
75
|
localVersion;
|
|
@@ -123,26 +124,45 @@ var MVCCTransaction = class {
|
|
|
123
124
|
return false;
|
|
124
125
|
}
|
|
125
126
|
// --- Internal buffer manipulation helpers ---
|
|
126
|
-
|
|
127
|
-
this.
|
|
127
|
+
_recordHistory(key) {
|
|
128
|
+
const existsInWriteBuffer = this.writeBuffer.has(key);
|
|
129
|
+
const existsInDeleteBuffer = this.deleteBuffer.has(key);
|
|
130
|
+
const currentVer = this.keyVersions.get(key);
|
|
131
|
+
if (currentVer !== void 0) {
|
|
132
|
+
if (!this.bufferHistory.has(key)) this.bufferHistory.set(key, []);
|
|
133
|
+
this.bufferHistory.get(key).push({
|
|
134
|
+
value: existsInWriteBuffer ? this.writeBuffer.get(key) : this.deletedValues.get(key) ?? null,
|
|
135
|
+
exists: existsInWriteBuffer || !existsInDeleteBuffer,
|
|
136
|
+
version: currentVer
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
_bufferCreate(key, value, version) {
|
|
141
|
+
if (version === void 0) this.localVersion++;
|
|
142
|
+
const targetVersion = version ?? this.localVersion;
|
|
143
|
+
this._recordHistory(key);
|
|
128
144
|
this.writeBuffer.set(key, value);
|
|
129
145
|
this.createdKeys.add(key);
|
|
130
146
|
this.deleteBuffer.delete(key);
|
|
131
147
|
this.originallyExisted.delete(key);
|
|
132
|
-
this.keyVersions.set(key,
|
|
148
|
+
this.keyVersions.set(key, targetVersion);
|
|
133
149
|
}
|
|
134
|
-
_bufferWrite(key, value) {
|
|
135
|
-
this.localVersion++;
|
|
150
|
+
_bufferWrite(key, value, version) {
|
|
151
|
+
if (version === void 0) this.localVersion++;
|
|
152
|
+
const targetVersion = version ?? this.localVersion;
|
|
153
|
+
this._recordHistory(key);
|
|
136
154
|
this.writeBuffer.set(key, value);
|
|
137
155
|
this.deleteBuffer.delete(key);
|
|
138
|
-
this.keyVersions.set(key,
|
|
156
|
+
this.keyVersions.set(key, targetVersion);
|
|
139
157
|
}
|
|
140
|
-
_bufferDelete(key) {
|
|
141
|
-
this.localVersion++;
|
|
158
|
+
_bufferDelete(key, version) {
|
|
159
|
+
if (version === void 0) this.localVersion++;
|
|
160
|
+
const targetVersion = version ?? this.localVersion;
|
|
161
|
+
this._recordHistory(key);
|
|
142
162
|
this.deleteBuffer.add(key);
|
|
143
163
|
this.writeBuffer.delete(key);
|
|
144
164
|
this.createdKeys.delete(key);
|
|
145
|
-
this.keyVersions.set(key,
|
|
165
|
+
this.keyVersions.set(key, targetVersion);
|
|
146
166
|
}
|
|
147
167
|
/**
|
|
148
168
|
* Returns the entries that will be created, updated, and deleted by this transaction.
|
|
@@ -219,7 +239,9 @@ var MVCCTransaction = class {
|
|
|
219
239
|
break;
|
|
220
240
|
}
|
|
221
241
|
}
|
|
222
|
-
if (latestInSnapshotIdx
|
|
242
|
+
if (latestInSnapshotIdx === versions.length - 1) {
|
|
243
|
+
this.versionIndex.delete(key);
|
|
244
|
+
} else if (latestInSnapshotIdx > 0) {
|
|
223
245
|
versions.splice(0, latestInSnapshotIdx);
|
|
224
246
|
}
|
|
225
247
|
}
|
|
@@ -276,27 +298,68 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
276
298
|
if (this.committed) throw new Error("Transaction already committed");
|
|
277
299
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
278
300
|
if (this.deleteBuffer.has(key)) return null;
|
|
279
|
-
|
|
301
|
+
if (this.parent) {
|
|
302
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
303
|
+
}
|
|
304
|
+
return this._diskRead(key, this.snapshotVersion);
|
|
280
305
|
}
|
|
281
306
|
exists(key) {
|
|
282
307
|
if (this.committed) throw new Error("Transaction already committed");
|
|
283
308
|
if (this.deleteBuffer.has(key)) return false;
|
|
284
309
|
if (this.writeBuffer.has(key)) return true;
|
|
285
|
-
|
|
310
|
+
if (this.parent) {
|
|
311
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
312
|
+
}
|
|
313
|
+
return this._diskExists(key, this.snapshotVersion);
|
|
314
|
+
}
|
|
315
|
+
_existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
316
|
+
if (this.writeBuffer.has(key)) {
|
|
317
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
318
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
319
|
+
return true;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (this.deleteBuffer.has(key)) {
|
|
323
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
324
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const history = this.bufferHistory.get(key);
|
|
329
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
330
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
331
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
332
|
+
return history[i].exists;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (this.parent) {
|
|
337
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
338
|
+
} else {
|
|
339
|
+
return this._diskExists(key, snapshotVersion);
|
|
340
|
+
}
|
|
286
341
|
}
|
|
287
342
|
_readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
288
343
|
if (this.writeBuffer.has(key)) {
|
|
289
344
|
const keyModVersion = this.keyVersions.get(key);
|
|
290
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
345
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
291
346
|
return this.writeBuffer.get(key);
|
|
292
347
|
}
|
|
293
348
|
}
|
|
294
349
|
if (this.deleteBuffer.has(key)) {
|
|
295
350
|
const keyModVersion = this.keyVersions.get(key);
|
|
296
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
351
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
297
352
|
return null;
|
|
298
353
|
}
|
|
299
354
|
}
|
|
355
|
+
const history = this.bufferHistory.get(key);
|
|
356
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
357
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
358
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
359
|
+
return history[i].exists ? history[i].value : null;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
300
363
|
if (this.parent) {
|
|
301
364
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
302
365
|
} else {
|
|
@@ -306,54 +369,22 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
306
369
|
commit(label) {
|
|
307
370
|
const { created, updated, deleted } = this.getResultEntries();
|
|
308
371
|
if (this.committed) {
|
|
309
|
-
return {
|
|
310
|
-
label,
|
|
311
|
-
success: false,
|
|
312
|
-
error: "Transaction already committed",
|
|
313
|
-
conflict: void 0,
|
|
314
|
-
created,
|
|
315
|
-
updated,
|
|
316
|
-
deleted
|
|
317
|
-
};
|
|
372
|
+
return { label, success: false, error: "Transaction already committed", conflict: void 0, created, updated, deleted };
|
|
318
373
|
}
|
|
319
374
|
if (this.hasCommittedAncestor()) {
|
|
320
|
-
return {
|
|
321
|
-
label,
|
|
322
|
-
success: false,
|
|
323
|
-
error: "Ancestor transaction already committed",
|
|
324
|
-
conflict: void 0,
|
|
325
|
-
created,
|
|
326
|
-
updated,
|
|
327
|
-
deleted
|
|
328
|
-
};
|
|
375
|
+
return { label, success: false, error: "Ancestor transaction already committed", conflict: void 0, created, updated, deleted };
|
|
329
376
|
}
|
|
330
377
|
if (this.parent) {
|
|
331
378
|
const failure = this.parent._merge(this);
|
|
332
379
|
if (failure) {
|
|
333
|
-
return {
|
|
334
|
-
label,
|
|
335
|
-
success: false,
|
|
336
|
-
error: failure.error,
|
|
337
|
-
conflict: failure.conflict,
|
|
338
|
-
created,
|
|
339
|
-
updated,
|
|
340
|
-
deleted
|
|
341
|
-
};
|
|
380
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created, updated, deleted };
|
|
342
381
|
}
|
|
343
382
|
this.committed = true;
|
|
344
383
|
} else {
|
|
345
384
|
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
346
385
|
const failure = this._merge(this);
|
|
347
386
|
if (failure) {
|
|
348
|
-
return {
|
|
349
|
-
label,
|
|
350
|
-
success: false,
|
|
351
|
-
error: failure.error,
|
|
352
|
-
conflict: failure.conflict,
|
|
353
|
-
created: [],
|
|
354
|
-
updated: [],
|
|
355
|
-
deleted: []
|
|
356
|
-
};
|
|
387
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created: [], updated: [], deleted: [] };
|
|
357
388
|
}
|
|
358
389
|
this.writeBuffer.clear();
|
|
359
390
|
this.deleteBuffer.clear();
|
|
@@ -361,17 +392,12 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
361
392
|
this.deletedValues.clear();
|
|
362
393
|
this.originallyExisted.clear();
|
|
363
394
|
this.keyVersions.clear();
|
|
395
|
+
this.bufferHistory.clear();
|
|
364
396
|
this.localVersion = 0;
|
|
365
397
|
this.snapshotVersion = this.version;
|
|
366
398
|
}
|
|
367
399
|
}
|
|
368
|
-
return {
|
|
369
|
-
label,
|
|
370
|
-
success: true,
|
|
371
|
-
created,
|
|
372
|
-
updated,
|
|
373
|
-
deleted
|
|
374
|
-
};
|
|
400
|
+
return { label, success: true, created, updated, deleted };
|
|
375
401
|
}
|
|
376
402
|
_merge(child) {
|
|
377
403
|
if (this.parent) {
|
|
@@ -380,11 +406,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
380
406
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
381
407
|
return {
|
|
382
408
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
383
|
-
conflict: {
|
|
384
|
-
key,
|
|
385
|
-
parent: this.read(key),
|
|
386
|
-
child: child.read(key)
|
|
387
|
-
}
|
|
409
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
388
410
|
};
|
|
389
411
|
}
|
|
390
412
|
}
|
|
@@ -393,40 +415,26 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
393
415
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
394
416
|
return {
|
|
395
417
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
396
|
-
conflict: {
|
|
397
|
-
key,
|
|
398
|
-
parent: this.read(key),
|
|
399
|
-
child: child.read(key)
|
|
400
|
-
}
|
|
418
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
401
419
|
};
|
|
402
420
|
}
|
|
403
421
|
}
|
|
404
|
-
const
|
|
405
|
-
for (const key of child.writeBuffer
|
|
406
|
-
|
|
407
|
-
this.
|
|
408
|
-
this.
|
|
409
|
-
if (child.createdKeys.has(key)) {
|
|
410
|
-
this.createdKeys.add(key);
|
|
411
|
-
}
|
|
422
|
+
const mergeVersion = ++this.localVersion;
|
|
423
|
+
for (const [key, value] of child.writeBuffer) {
|
|
424
|
+
const wasCreated = child.createdKeys.has(key);
|
|
425
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
426
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
412
427
|
}
|
|
413
428
|
for (const key of child.deleteBuffer) {
|
|
414
|
-
this.deleteBuffer.add(key);
|
|
415
|
-
this.writeBuffer.delete(key);
|
|
416
|
-
this.createdKeys.delete(key);
|
|
417
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
418
429
|
const deletedValue = child.deletedValues.get(key);
|
|
419
|
-
if (deletedValue !== void 0)
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
if (child.originallyExisted.has(key)) {
|
|
430
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
431
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
423
432
|
this.originallyExisted.add(key);
|
|
424
433
|
}
|
|
434
|
+
this._bufferDelete(key, mergeVersion);
|
|
425
435
|
}
|
|
426
|
-
this.localVersion = newLocalVersion;
|
|
427
436
|
this.root.activeTransactions.delete(child);
|
|
428
437
|
} else {
|
|
429
|
-
const newVersion = this.version + 1;
|
|
430
438
|
if (child !== this) {
|
|
431
439
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
432
440
|
for (const key of modifiedKeys) {
|
|
@@ -436,58 +444,53 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
436
444
|
if (lastVer > child.snapshotVersion) {
|
|
437
445
|
return {
|
|
438
446
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
439
|
-
conflict: {
|
|
440
|
-
key,
|
|
441
|
-
parent: this.read(key),
|
|
442
|
-
child: child.read(key)
|
|
443
|
-
}
|
|
447
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
444
448
|
};
|
|
445
449
|
}
|
|
446
450
|
}
|
|
451
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
452
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
453
|
+
return {
|
|
454
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
455
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
456
|
+
};
|
|
457
|
+
}
|
|
447
458
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
for (const key of child.deleteBuffer) {
|
|
457
|
-
this.deleteBuffer.add(key);
|
|
458
|
-
this.writeBuffer.delete(key);
|
|
459
|
-
this.createdKeys.delete(key);
|
|
460
|
-
const deletedValue = child.deletedValues.get(key);
|
|
461
|
-
if (deletedValue !== void 0) {
|
|
462
|
-
this.deletedValues.set(key, deletedValue);
|
|
459
|
+
const mergeVersion = ++this.localVersion;
|
|
460
|
+
for (const [key, value] of child.writeBuffer) {
|
|
461
|
+
const wasCreated = child.createdKeys.has(key);
|
|
462
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
463
|
+
this.originallyExisted.add(key);
|
|
464
|
+
}
|
|
465
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
466
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
463
467
|
}
|
|
464
|
-
|
|
465
|
-
|
|
468
|
+
for (const key of child.deleteBuffer) {
|
|
469
|
+
const deletedValue = child.deletedValues.get(key);
|
|
470
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
471
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
472
|
+
this.originallyExisted.add(key);
|
|
473
|
+
}
|
|
474
|
+
this._bufferDelete(key, mergeVersion);
|
|
466
475
|
}
|
|
476
|
+
this.root.activeTransactions.delete(child);
|
|
477
|
+
} else {
|
|
478
|
+
const newVersion = this.version + 1;
|
|
479
|
+
for (const [key, value] of this.writeBuffer) this._diskWrite(key, value, newVersion);
|
|
480
|
+
for (const key of this.deleteBuffer) this._diskDelete(key, newVersion);
|
|
481
|
+
this.version = newVersion;
|
|
482
|
+
this._cleanupDeletedCache();
|
|
467
483
|
}
|
|
468
|
-
for (const [key, value] of child.writeBuffer) {
|
|
469
|
-
this._diskWrite(key, value, newVersion);
|
|
470
|
-
}
|
|
471
|
-
for (const key of child.deleteBuffer) {
|
|
472
|
-
this._diskDelete(key, newVersion);
|
|
473
|
-
}
|
|
474
|
-
this.version = newVersion;
|
|
475
|
-
this.root.activeTransactions.delete(child);
|
|
476
|
-
this._cleanupDeletedCache();
|
|
477
484
|
}
|
|
478
485
|
return null;
|
|
479
486
|
}
|
|
480
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
481
487
|
_diskWrite(key, value, version) {
|
|
482
488
|
const strategy = this.strategy;
|
|
483
489
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
484
490
|
if (strategy.exists(key)) {
|
|
485
491
|
const currentVal = strategy.read(key);
|
|
486
492
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
487
|
-
this.deletedCache.get(key).push({
|
|
488
|
-
value: currentVal,
|
|
489
|
-
deletedAtVersion: version
|
|
490
|
-
});
|
|
493
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
491
494
|
}
|
|
492
495
|
strategy.write(key, value);
|
|
493
496
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -503,9 +506,8 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
503
506
|
let targetVerObj = null;
|
|
504
507
|
let nextVerObj = null;
|
|
505
508
|
for (const v of versions) {
|
|
506
|
-
if (v.version <= snapshotVersion)
|
|
507
|
-
|
|
508
|
-
} else {
|
|
509
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
510
|
+
else {
|
|
509
511
|
nextVerObj = v;
|
|
510
512
|
break;
|
|
511
513
|
}
|
|
@@ -522,6 +524,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
522
524
|
}
|
|
523
525
|
if (!targetVerObj.exists) return null;
|
|
524
526
|
if (!nextVerObj) {
|
|
527
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
525
528
|
return strategy.read(key);
|
|
526
529
|
}
|
|
527
530
|
const cached = this.deletedCache.get(key);
|
|
@@ -539,14 +542,24 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
539
542
|
return strategy.exists(key);
|
|
540
543
|
}
|
|
541
544
|
let targetVerObj = null;
|
|
545
|
+
let nextVerObj = null;
|
|
542
546
|
for (const v of versions) {
|
|
543
|
-
if (v.version <= snapshotVersion)
|
|
544
|
-
|
|
545
|
-
|
|
547
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
548
|
+
else {
|
|
549
|
+
nextVerObj = v;
|
|
546
550
|
break;
|
|
547
551
|
}
|
|
548
552
|
}
|
|
549
|
-
if (!targetVerObj)
|
|
553
|
+
if (!targetVerObj) {
|
|
554
|
+
if (nextVerObj) {
|
|
555
|
+
const cached = this.deletedCache.get(key);
|
|
556
|
+
if (cached) {
|
|
557
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
558
|
+
if (match) return true;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
550
563
|
return targetVerObj.exists;
|
|
551
564
|
}
|
|
552
565
|
_diskDelete(key, snapshotVersion) {
|
|
@@ -555,12 +568,9 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
555
568
|
if (strategy.exists(key)) {
|
|
556
569
|
const currentVal = strategy.read(key);
|
|
557
570
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
558
|
-
this.deletedCache.get(key).push({
|
|
559
|
-
|
|
560
|
-
deletedAtVersion: snapshotVersion
|
|
561
|
-
});
|
|
571
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
572
|
+
strategy.delete(key);
|
|
562
573
|
}
|
|
563
|
-
strategy.delete(key);
|
|
564
574
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
565
575
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
566
576
|
}
|
|
@@ -881,84 +891,93 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
881
891
|
if (this.committed) throw new Error("Transaction already committed");
|
|
882
892
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
883
893
|
if (this.deleteBuffer.has(key)) return null;
|
|
884
|
-
|
|
894
|
+
if (this.parent) {
|
|
895
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
896
|
+
}
|
|
897
|
+
return await this._diskRead(key, this.snapshotVersion);
|
|
885
898
|
}
|
|
886
899
|
async exists(key) {
|
|
887
900
|
if (this.committed) throw new Error("Transaction already committed");
|
|
888
901
|
if (this.deleteBuffer.has(key)) return false;
|
|
889
902
|
if (this.writeBuffer.has(key)) return true;
|
|
890
|
-
|
|
903
|
+
if (this.parent) {
|
|
904
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
905
|
+
}
|
|
906
|
+
return await this._diskExists(key, this.snapshotVersion);
|
|
907
|
+
}
|
|
908
|
+
async _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
909
|
+
if (this.writeBuffer.has(key)) {
|
|
910
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
911
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
912
|
+
return true;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
if (this.deleteBuffer.has(key)) {
|
|
916
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
917
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
918
|
+
return false;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
const history = this.bufferHistory.get(key);
|
|
922
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
923
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
924
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
925
|
+
return history[i].exists;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
if (this.parent) {
|
|
930
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
931
|
+
} else {
|
|
932
|
+
return await this._diskExists(key, snapshotVersion);
|
|
933
|
+
}
|
|
891
934
|
}
|
|
892
935
|
async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
893
936
|
if (this.writeBuffer.has(key)) {
|
|
894
937
|
const keyModVersion = this.keyVersions.get(key);
|
|
895
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
938
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
896
939
|
return this.writeBuffer.get(key);
|
|
897
940
|
}
|
|
898
941
|
}
|
|
899
942
|
if (this.deleteBuffer.has(key)) {
|
|
900
943
|
const keyModVersion = this.keyVersions.get(key);
|
|
901
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
944
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
902
945
|
return null;
|
|
903
946
|
}
|
|
904
947
|
}
|
|
948
|
+
const history = this.bufferHistory.get(key);
|
|
949
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
950
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
951
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
952
|
+
return history[i].exists ? history[i].value : null;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
}
|
|
905
956
|
if (this.parent) {
|
|
906
957
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
907
958
|
} else {
|
|
908
|
-
return this._diskRead(key, snapshotVersion);
|
|
959
|
+
return await this._diskRead(key, snapshotVersion);
|
|
909
960
|
}
|
|
910
961
|
}
|
|
911
962
|
async commit(label) {
|
|
912
963
|
const { created, updated, deleted } = this.getResultEntries();
|
|
913
964
|
if (this.committed) {
|
|
914
|
-
return {
|
|
915
|
-
label,
|
|
916
|
-
success: false,
|
|
917
|
-
error: "Transaction already committed",
|
|
918
|
-
conflict: void 0,
|
|
919
|
-
created,
|
|
920
|
-
updated,
|
|
921
|
-
deleted
|
|
922
|
-
};
|
|
965
|
+
return { label, success: false, error: "Transaction already committed", conflict: void 0, created, updated, deleted };
|
|
923
966
|
}
|
|
924
967
|
if (this.hasCommittedAncestor()) {
|
|
925
|
-
return {
|
|
926
|
-
label,
|
|
927
|
-
success: false,
|
|
928
|
-
error: "Ancestor transaction already committed",
|
|
929
|
-
conflict: void 0,
|
|
930
|
-
created,
|
|
931
|
-
updated,
|
|
932
|
-
deleted
|
|
933
|
-
};
|
|
968
|
+
return { label, success: false, error: "Ancestor transaction already committed", conflict: void 0, created, updated, deleted };
|
|
934
969
|
}
|
|
935
970
|
if (this.parent) {
|
|
936
971
|
const failure = await this.parent._merge(this);
|
|
937
972
|
if (failure) {
|
|
938
|
-
return {
|
|
939
|
-
label,
|
|
940
|
-
success: false,
|
|
941
|
-
error: failure.error,
|
|
942
|
-
conflict: failure.conflict,
|
|
943
|
-
created,
|
|
944
|
-
updated,
|
|
945
|
-
deleted
|
|
946
|
-
};
|
|
973
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created, updated, deleted };
|
|
947
974
|
}
|
|
948
975
|
this.committed = true;
|
|
949
976
|
} else {
|
|
950
977
|
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
951
978
|
const failure = await this._merge(this);
|
|
952
979
|
if (failure) {
|
|
953
|
-
return {
|
|
954
|
-
label,
|
|
955
|
-
success: false,
|
|
956
|
-
error: failure.error,
|
|
957
|
-
conflict: failure.conflict,
|
|
958
|
-
created: [],
|
|
959
|
-
updated: [],
|
|
960
|
-
deleted: []
|
|
961
|
-
};
|
|
980
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created: [], updated: [], deleted: [] };
|
|
962
981
|
}
|
|
963
982
|
this.writeBuffer.clear();
|
|
964
983
|
this.deleteBuffer.clear();
|
|
@@ -966,17 +985,12 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
966
985
|
this.deletedValues.clear();
|
|
967
986
|
this.originallyExisted.clear();
|
|
968
987
|
this.keyVersions.clear();
|
|
988
|
+
this.bufferHistory.clear();
|
|
969
989
|
this.localVersion = 0;
|
|
970
990
|
this.snapshotVersion = this.version;
|
|
971
991
|
}
|
|
972
992
|
}
|
|
973
|
-
return {
|
|
974
|
-
label,
|
|
975
|
-
success: true,
|
|
976
|
-
created,
|
|
977
|
-
updated,
|
|
978
|
-
deleted
|
|
979
|
-
};
|
|
993
|
+
return { label, success: true, created, updated, deleted };
|
|
980
994
|
}
|
|
981
995
|
async _merge(child) {
|
|
982
996
|
return this.writeLock(async () => {
|
|
@@ -986,11 +1000,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
986
1000
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
987
1001
|
return {
|
|
988
1002
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
989
|
-
conflict: {
|
|
990
|
-
key,
|
|
991
|
-
parent: await this.read(key),
|
|
992
|
-
child: await child.read(key)
|
|
993
|
-
}
|
|
1003
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
994
1004
|
};
|
|
995
1005
|
}
|
|
996
1006
|
}
|
|
@@ -999,41 +1009,27 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
999
1009
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1000
1010
|
return {
|
|
1001
1011
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
1002
|
-
conflict: {
|
|
1003
|
-
key,
|
|
1004
|
-
parent: await this.read(key),
|
|
1005
|
-
child: await child.read(key)
|
|
1006
|
-
}
|
|
1012
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1007
1013
|
};
|
|
1008
1014
|
}
|
|
1009
1015
|
}
|
|
1010
|
-
const
|
|
1011
|
-
for (const key of child.writeBuffer
|
|
1012
|
-
|
|
1013
|
-
this.
|
|
1014
|
-
this.
|
|
1015
|
-
if (child.createdKeys.has(key)) {
|
|
1016
|
-
this.createdKeys.add(key);
|
|
1017
|
-
}
|
|
1016
|
+
const mergeVersion = ++this.localVersion;
|
|
1017
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1018
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1019
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1020
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1018
1021
|
}
|
|
1019
1022
|
for (const key of child.deleteBuffer) {
|
|
1020
|
-
this.deleteBuffer.add(key);
|
|
1021
|
-
this.writeBuffer.delete(key);
|
|
1022
|
-
this.createdKeys.delete(key);
|
|
1023
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
1024
1023
|
const deletedValue = child.deletedValues.get(key);
|
|
1025
|
-
if (deletedValue !== void 0)
|
|
1026
|
-
|
|
1027
|
-
}
|
|
1028
|
-
if (child.originallyExisted.has(key)) {
|
|
1024
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1025
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1029
1026
|
this.originallyExisted.add(key);
|
|
1030
1027
|
}
|
|
1028
|
+
this._bufferDelete(key, mergeVersion);
|
|
1031
1029
|
}
|
|
1032
|
-
this.localVersion = newLocalVersion;
|
|
1033
1030
|
this.root.activeTransactions.delete(child);
|
|
1034
1031
|
return null;
|
|
1035
1032
|
} else {
|
|
1036
|
-
const newVersion = this.version + 1;
|
|
1037
1033
|
if (child !== this) {
|
|
1038
1034
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
1039
1035
|
for (const key of modifiedKeys) {
|
|
@@ -1043,59 +1039,54 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1043
1039
|
if (lastVer > child.snapshotVersion) {
|
|
1044
1040
|
return {
|
|
1045
1041
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
1046
|
-
conflict: {
|
|
1047
|
-
key,
|
|
1048
|
-
parent: await this.read(key),
|
|
1049
|
-
child: await child.read(key)
|
|
1050
|
-
}
|
|
1042
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1051
1043
|
};
|
|
1052
1044
|
}
|
|
1053
1045
|
}
|
|
1046
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
1047
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1048
|
+
return {
|
|
1049
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
1050
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1054
1053
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
for (const key of child.deleteBuffer) {
|
|
1064
|
-
this.deleteBuffer.add(key);
|
|
1065
|
-
this.writeBuffer.delete(key);
|
|
1066
|
-
this.createdKeys.delete(key);
|
|
1067
|
-
const deletedValue = child.deletedValues.get(key);
|
|
1068
|
-
if (deletedValue !== void 0) {
|
|
1069
|
-
this.deletedValues.set(key, deletedValue);
|
|
1054
|
+
const mergeVersion = ++this.localVersion;
|
|
1055
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1056
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1057
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1058
|
+
this.originallyExisted.add(key);
|
|
1059
|
+
}
|
|
1060
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1061
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1070
1062
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1063
|
+
for (const key of child.deleteBuffer) {
|
|
1064
|
+
const deletedValue = child.deletedValues.get(key);
|
|
1065
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1066
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1067
|
+
this.originallyExisted.add(key);
|
|
1068
|
+
}
|
|
1069
|
+
this._bufferDelete(key, mergeVersion);
|
|
1073
1070
|
}
|
|
1071
|
+
this.root.activeTransactions.delete(child);
|
|
1072
|
+
} else {
|
|
1073
|
+
const newVersion = this.version + 1;
|
|
1074
|
+
for (const [key, value] of this.writeBuffer) await this._diskWrite(key, value, newVersion);
|
|
1075
|
+
for (const key of this.deleteBuffer) await this._diskDelete(key, newVersion);
|
|
1076
|
+
this.version = newVersion;
|
|
1077
|
+
this._cleanupDeletedCache();
|
|
1074
1078
|
}
|
|
1075
|
-
for (const [key, value] of child.writeBuffer) {
|
|
1076
|
-
await this._diskWrite(key, value, newVersion);
|
|
1077
|
-
}
|
|
1078
|
-
for (const key of child.deleteBuffer) {
|
|
1079
|
-
await this._diskDelete(key, newVersion);
|
|
1080
|
-
}
|
|
1081
|
-
this.version = newVersion;
|
|
1082
|
-
this.root.activeTransactions.delete(child);
|
|
1083
|
-
this._cleanupDeletedCache();
|
|
1084
1079
|
return null;
|
|
1085
1080
|
}
|
|
1086
1081
|
});
|
|
1087
1082
|
}
|
|
1088
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
1089
1083
|
async _diskWrite(key, value, version) {
|
|
1090
1084
|
const strategy = this.strategy;
|
|
1091
1085
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1092
1086
|
if (await strategy.exists(key)) {
|
|
1093
1087
|
const currentVal = await strategy.read(key);
|
|
1094
1088
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1095
|
-
this.deletedCache.get(key).push({
|
|
1096
|
-
value: currentVal,
|
|
1097
|
-
deletedAtVersion: version
|
|
1098
|
-
});
|
|
1089
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
1099
1090
|
}
|
|
1100
1091
|
await strategy.write(key, value);
|
|
1101
1092
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -1106,14 +1097,13 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1106
1097
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1107
1098
|
const versions = this.versionIndex.get(key);
|
|
1108
1099
|
if (!versions) {
|
|
1109
|
-
return await strategy.exists(key) ? strategy.read(key) : null;
|
|
1100
|
+
return await strategy.exists(key) ? await strategy.read(key) : null;
|
|
1110
1101
|
}
|
|
1111
1102
|
let targetVerObj = null;
|
|
1112
1103
|
let nextVerObj = null;
|
|
1113
1104
|
for (const v of versions) {
|
|
1114
|
-
if (v.version <= snapshotVersion)
|
|
1115
|
-
|
|
1116
|
-
} else {
|
|
1105
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
1106
|
+
else {
|
|
1117
1107
|
nextVerObj = v;
|
|
1118
1108
|
break;
|
|
1119
1109
|
}
|
|
@@ -1130,6 +1120,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1130
1120
|
}
|
|
1131
1121
|
if (!targetVerObj.exists) return null;
|
|
1132
1122
|
if (!nextVerObj) {
|
|
1123
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
1133
1124
|
return strategy.read(key);
|
|
1134
1125
|
}
|
|
1135
1126
|
const cached = this.deletedCache.get(key);
|
|
@@ -1144,17 +1135,27 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1144
1135
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1145
1136
|
const versions = this.versionIndex.get(key);
|
|
1146
1137
|
if (!versions) {
|
|
1147
|
-
return strategy.exists(key);
|
|
1138
|
+
return await strategy.exists(key);
|
|
1148
1139
|
}
|
|
1149
1140
|
let targetVerObj = null;
|
|
1141
|
+
let nextVerObj = null;
|
|
1150
1142
|
for (const v of versions) {
|
|
1151
|
-
if (v.version <= snapshotVersion)
|
|
1152
|
-
|
|
1153
|
-
|
|
1143
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
1144
|
+
else {
|
|
1145
|
+
nextVerObj = v;
|
|
1154
1146
|
break;
|
|
1155
1147
|
}
|
|
1156
1148
|
}
|
|
1157
|
-
if (!targetVerObj)
|
|
1149
|
+
if (!targetVerObj) {
|
|
1150
|
+
if (nextVerObj) {
|
|
1151
|
+
const cached = this.deletedCache.get(key);
|
|
1152
|
+
if (cached) {
|
|
1153
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
1154
|
+
if (match) return true;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
return false;
|
|
1158
|
+
}
|
|
1158
1159
|
return targetVerObj.exists;
|
|
1159
1160
|
}
|
|
1160
1161
|
async _diskDelete(key, snapshotVersion) {
|
|
@@ -1163,12 +1164,9 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1163
1164
|
if (await strategy.exists(key)) {
|
|
1164
1165
|
const currentVal = await strategy.read(key);
|
|
1165
1166
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1166
|
-
this.deletedCache.get(key).push({
|
|
1167
|
-
|
|
1168
|
-
deletedAtVersion: snapshotVersion
|
|
1169
|
-
});
|
|
1167
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
1168
|
+
await strategy.delete(key);
|
|
1170
1169
|
}
|
|
1171
|
-
await strategy.delete(key);
|
|
1172
1170
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
1173
1171
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
1174
1172
|
}
|
|
@@ -1510,6 +1508,8 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1510
1508
|
option;
|
|
1511
1509
|
order;
|
|
1512
1510
|
rootId;
|
|
1511
|
+
isInitialized = false;
|
|
1512
|
+
isDestroyed = false;
|
|
1513
1513
|
verifierMap = {
|
|
1514
1514
|
gt: (nv, v) => this.comparator.isHigher(nv, v),
|
|
1515
1515
|
gte: (nv, v) => this.comparator.isHigher(nv, v) || this.comparator.isSame(nv, v),
|
|
@@ -1774,13 +1774,26 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1774
1774
|
getResultEntries() {
|
|
1775
1775
|
return this.mvcc.getResultEntries();
|
|
1776
1776
|
}
|
|
1777
|
+
_clearCache() {
|
|
1778
|
+
this._cachedRegexp.clear();
|
|
1779
|
+
this.nodes.clear();
|
|
1780
|
+
}
|
|
1777
1781
|
/**
|
|
1778
1782
|
* Clears all cached nodes.
|
|
1779
1783
|
* This method is useful for freeing up memory when the tree is no longer needed.
|
|
1780
1784
|
*/
|
|
1781
1785
|
clear() {
|
|
1782
|
-
this.
|
|
1783
|
-
|
|
1786
|
+
if (this.rootTx !== this) {
|
|
1787
|
+
throw new Error("Cannot call clear on a nested transaction");
|
|
1788
|
+
}
|
|
1789
|
+
this._clearInternal();
|
|
1790
|
+
}
|
|
1791
|
+
_clearInternal() {
|
|
1792
|
+
if (this.isDestroyed) {
|
|
1793
|
+
throw new Error("Transaction already destroyed");
|
|
1794
|
+
}
|
|
1795
|
+
this._clearCache();
|
|
1796
|
+
this.isDestroyed = true;
|
|
1784
1797
|
}
|
|
1785
1798
|
_binarySearchValues(values, target, usePrimary = false, upperBound = false) {
|
|
1786
1799
|
let low = 0;
|
|
@@ -2086,28 +2099,46 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2086
2099
|
}
|
|
2087
2100
|
}
|
|
2088
2101
|
init() {
|
|
2089
|
-
this.
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
const { root, order } = head;
|
|
2101
|
-
this.strategy.head = head;
|
|
2102
|
-
this.order = order;
|
|
2103
|
-
this._writeHead({
|
|
2104
|
-
root,
|
|
2105
|
-
order: this.order,
|
|
2106
|
-
data: this.strategy.head.data
|
|
2107
|
-
});
|
|
2102
|
+
if (this.rootTx !== this) {
|
|
2103
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
2104
|
+
}
|
|
2105
|
+
this._initInternal();
|
|
2106
|
+
}
|
|
2107
|
+
_initInternal() {
|
|
2108
|
+
if (this.isInitialized) {
|
|
2109
|
+
throw new Error("Transaction already initialized");
|
|
2110
|
+
}
|
|
2111
|
+
if (this.isDestroyed) {
|
|
2112
|
+
throw new Error("Transaction already destroyed");
|
|
2108
2113
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2114
|
+
this.isInitialized = true;
|
|
2115
|
+
try {
|
|
2116
|
+
this._clearCache();
|
|
2117
|
+
const head = this._readHead();
|
|
2118
|
+
if (head === null) {
|
|
2119
|
+
this.order = this.strategy.order;
|
|
2120
|
+
const root = this._createNode(true, [], []);
|
|
2121
|
+
this._writeHead({
|
|
2122
|
+
root: root.id,
|
|
2123
|
+
order: this.order,
|
|
2124
|
+
data: this.strategy.head.data
|
|
2125
|
+
});
|
|
2126
|
+
} else {
|
|
2127
|
+
const { root, order } = head;
|
|
2128
|
+
this.strategy.head = head;
|
|
2129
|
+
this.order = order;
|
|
2130
|
+
this._writeHead({
|
|
2131
|
+
root,
|
|
2132
|
+
order: this.order,
|
|
2133
|
+
data: this.strategy.head.data
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
if (this.order < 3) {
|
|
2137
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
2138
|
+
}
|
|
2139
|
+
} catch (e) {
|
|
2140
|
+
this.isInitialized = false;
|
|
2141
|
+
throw e;
|
|
2111
2142
|
}
|
|
2112
2143
|
}
|
|
2113
2144
|
exists(key, value) {
|
|
@@ -2485,9 +2516,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2485
2516
|
commit(label) {
|
|
2486
2517
|
let result = this.mvcc.commit(label);
|
|
2487
2518
|
if (result.success) {
|
|
2488
|
-
|
|
2489
|
-
if (
|
|
2490
|
-
|
|
2519
|
+
const isRootTx = this.rootTx !== this;
|
|
2520
|
+
if (isRootTx) {
|
|
2521
|
+
result = this.rootTx.commit(label);
|
|
2522
|
+
if (result.success) {
|
|
2523
|
+
this.rootTx.rootId = this.rootId;
|
|
2524
|
+
}
|
|
2491
2525
|
}
|
|
2492
2526
|
if (result.success) {
|
|
2493
2527
|
for (const r of result.created) {
|
|
@@ -2570,7 +2604,7 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2570
2604
|
this.comparator,
|
|
2571
2605
|
this.option
|
|
2572
2606
|
);
|
|
2573
|
-
tx.
|
|
2607
|
+
tx._initInternal();
|
|
2574
2608
|
return tx;
|
|
2575
2609
|
}
|
|
2576
2610
|
insert(key, value) {
|
|
@@ -2580,7 +2614,6 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2580
2614
|
if (!result.success) {
|
|
2581
2615
|
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2582
2616
|
}
|
|
2583
|
-
this.rootId = tx.getRootId();
|
|
2584
2617
|
}
|
|
2585
2618
|
delete(key, value) {
|
|
2586
2619
|
const tx = this.createTransaction();
|
|
@@ -2589,244 +2622,512 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2589
2622
|
if (!result.success) {
|
|
2590
2623
|
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2591
2624
|
}
|
|
2592
|
-
this.rootId = tx.getRootId();
|
|
2593
2625
|
}
|
|
2594
2626
|
};
|
|
2595
2627
|
|
|
2596
|
-
//
|
|
2597
|
-
var
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
comparator,
|
|
2605
|
-
option
|
|
2606
|
-
);
|
|
2628
|
+
// node_modules/ryoiki/dist/esm/index.mjs
|
|
2629
|
+
var Ryoiki2 = class _Ryoiki2 {
|
|
2630
|
+
readings;
|
|
2631
|
+
writings;
|
|
2632
|
+
readQueue;
|
|
2633
|
+
writeQueue;
|
|
2634
|
+
static async CatchError(promise) {
|
|
2635
|
+
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
2607
2636
|
}
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2637
|
+
static IsRangeOverlap(a, b) {
|
|
2638
|
+
const [start1, end1] = a;
|
|
2639
|
+
const [start2, end2] = b;
|
|
2640
|
+
if (end1 <= start2 || end2 <= start1) {
|
|
2641
|
+
return false;
|
|
2611
2642
|
}
|
|
2612
|
-
return
|
|
2643
|
+
return true;
|
|
2644
|
+
}
|
|
2645
|
+
static ERR_ALREADY_EXISTS(lockId) {
|
|
2646
|
+
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
2647
|
+
}
|
|
2648
|
+
static ERR_NOT_EXISTS(lockId) {
|
|
2649
|
+
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
2650
|
+
}
|
|
2651
|
+
static ERR_TIMEOUT(lockId, timeout) {
|
|
2652
|
+
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
2613
2653
|
}
|
|
2614
2654
|
/**
|
|
2615
|
-
*
|
|
2655
|
+
* Constructs a new instance of the Ryoiki class.
|
|
2616
2656
|
*/
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
values,
|
|
2623
|
-
leaf,
|
|
2624
|
-
parent,
|
|
2625
|
-
next,
|
|
2626
|
-
prev
|
|
2627
|
-
};
|
|
2628
|
-
await this.mvcc.create(id, node);
|
|
2629
|
-
this.nodes.set(id, node);
|
|
2630
|
-
return node;
|
|
2657
|
+
constructor() {
|
|
2658
|
+
this.readings = /* @__PURE__ */ new Map();
|
|
2659
|
+
this.writings = /* @__PURE__ */ new Map();
|
|
2660
|
+
this.readQueue = /* @__PURE__ */ new Map();
|
|
2661
|
+
this.writeQueue = /* @__PURE__ */ new Map();
|
|
2631
2662
|
}
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2663
|
+
/**
|
|
2664
|
+
* Creates a range based on a start value and length.
|
|
2665
|
+
* @param start - The starting value of the range.
|
|
2666
|
+
* @param length - The length of the range.
|
|
2667
|
+
* @returns A range tuple [start, start + length].
|
|
2668
|
+
*/
|
|
2669
|
+
range(start, length) {
|
|
2670
|
+
return [start, start + length];
|
|
2635
2671
|
}
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
this.nodes.delete(node.id);
|
|
2672
|
+
rangeOverlapping(tasks, range) {
|
|
2673
|
+
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
2639
2674
|
}
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
const head = await this.mvcc.read("__HEAD__");
|
|
2645
|
-
return head ?? null;
|
|
2646
|
-
}
|
|
2647
|
-
async _writeHead(head) {
|
|
2648
|
-
if (!await this.mvcc.exists("__HEAD__")) {
|
|
2649
|
-
await this.mvcc.create("__HEAD__", head);
|
|
2650
|
-
} else {
|
|
2651
|
-
await this.mvcc.write("__HEAD__", head);
|
|
2652
|
-
}
|
|
2653
|
-
this.nodes.set("__HEAD__", head);
|
|
2654
|
-
this.rootId = head.root;
|
|
2675
|
+
isSameRange(a, b) {
|
|
2676
|
+
const [a1, a2] = a;
|
|
2677
|
+
const [b1, b2] = b;
|
|
2678
|
+
return a1 === b1 && a2 === b2;
|
|
2655
2679
|
}
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
2661
|
-
const keys = node.keys[i];
|
|
2662
|
-
if (keys.includes(key)) {
|
|
2663
|
-
break;
|
|
2664
|
-
}
|
|
2665
|
-
keys.push(key);
|
|
2666
|
-
await this._updateNode(node);
|
|
2667
|
-
return;
|
|
2668
|
-
} else if (this.comparator.isLower(value, nValue)) {
|
|
2669
|
-
node.values.splice(i, 0, value);
|
|
2670
|
-
node.keys.splice(i, 0, [key]);
|
|
2671
|
-
await this._updateNode(node);
|
|
2672
|
-
return;
|
|
2673
|
-
} else if (i + 1 === node.values.length) {
|
|
2674
|
-
node.values.push(value);
|
|
2675
|
-
node.keys.push([key]);
|
|
2676
|
-
await this._updateNode(node);
|
|
2677
|
-
return;
|
|
2678
|
-
}
|
|
2680
|
+
fetchUnitAndRun(queue, workspaces) {
|
|
2681
|
+
for (const [id, unit] of queue) {
|
|
2682
|
+
if (!unit.condition()) {
|
|
2683
|
+
continue;
|
|
2679
2684
|
}
|
|
2680
|
-
|
|
2681
|
-
node.values = [value];
|
|
2682
|
-
node.keys = [[key]];
|
|
2683
|
-
await this._updateNode(node);
|
|
2684
|
-
return;
|
|
2685
|
+
this._alloc(queue, workspaces, id);
|
|
2685
2686
|
}
|
|
2686
2687
|
}
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
node.parent = root.id;
|
|
2692
|
-
pointer.parent = root.id;
|
|
2693
|
-
if (pointer.leaf) {
|
|
2694
|
-
node.next = pointer.id;
|
|
2695
|
-
pointer.prev = node.id;
|
|
2696
|
-
}
|
|
2697
|
-
await this._writeHead({
|
|
2698
|
-
root: root.id,
|
|
2699
|
-
order: this.order,
|
|
2700
|
-
data: this.strategy.head.data
|
|
2701
|
-
});
|
|
2702
|
-
await this._updateNode(node);
|
|
2703
|
-
await this._updateNode(pointer);
|
|
2704
|
-
return;
|
|
2705
|
-
}
|
|
2706
|
-
const parentNode = await this.getNode(node.parent);
|
|
2707
|
-
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2708
|
-
if (nodeIndex === -1) {
|
|
2709
|
-
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
2710
|
-
}
|
|
2711
|
-
parentNode.values.splice(nodeIndex, 0, value);
|
|
2712
|
-
parentNode.keys.splice(nodeIndex + 1, 0, pointer.id);
|
|
2713
|
-
pointer.parent = parentNode.id;
|
|
2714
|
-
if (pointer.leaf) {
|
|
2715
|
-
const leftSibling = node;
|
|
2716
|
-
const oldNextId = leftSibling.next;
|
|
2717
|
-
pointer.prev = leftSibling.id;
|
|
2718
|
-
pointer.next = oldNextId;
|
|
2719
|
-
leftSibling.next = pointer.id;
|
|
2720
|
-
await this._updateNode(leftSibling);
|
|
2721
|
-
if (oldNextId) {
|
|
2722
|
-
const oldNext = await this.getNode(oldNextId);
|
|
2723
|
-
oldNext.prev = pointer.id;
|
|
2724
|
-
await this._updateNode(oldNext);
|
|
2725
|
-
}
|
|
2726
|
-
}
|
|
2727
|
-
await this._updateNode(parentNode);
|
|
2728
|
-
await this._updateNode(pointer);
|
|
2729
|
-
if (parentNode.keys.length > this.order) {
|
|
2730
|
-
const parentPointer = await this._createNode(false, [], []);
|
|
2731
|
-
parentPointer.parent = parentNode.parent;
|
|
2732
|
-
const mid = Math.ceil(this.order / 2) - 1;
|
|
2733
|
-
parentPointer.values = parentNode.values.slice(mid + 1);
|
|
2734
|
-
parentPointer.keys = parentNode.keys.slice(mid + 1);
|
|
2735
|
-
const midValue = parentNode.values[mid];
|
|
2736
|
-
parentNode.values = parentNode.values.slice(0, mid);
|
|
2737
|
-
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2738
|
-
for (const k of parentNode.keys) {
|
|
2739
|
-
const n = await this.getNode(k);
|
|
2740
|
-
n.parent = parentNode.id;
|
|
2741
|
-
await this._updateNode(n);
|
|
2742
|
-
}
|
|
2743
|
-
for (const k of parentPointer.keys) {
|
|
2744
|
-
const n = await this.getNode(k);
|
|
2745
|
-
n.parent = parentPointer.id;
|
|
2746
|
-
await this._updateNode(n);
|
|
2688
|
+
_handleOverload(args, handlers, argPatterns) {
|
|
2689
|
+
for (const [key, pattern] of Object.entries(argPatterns)) {
|
|
2690
|
+
if (this._matchArgs(args, pattern)) {
|
|
2691
|
+
return handlers[key](...args);
|
|
2747
2692
|
}
|
|
2748
|
-
await this._updateNode(parentNode);
|
|
2749
|
-
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2750
2693
|
}
|
|
2694
|
+
throw new Error("Invalid arguments");
|
|
2751
2695
|
}
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2696
|
+
_matchArgs(args, pattern) {
|
|
2697
|
+
return args.every((arg, index) => {
|
|
2698
|
+
const expectedType = pattern[index];
|
|
2699
|
+
if (expectedType === void 0) return typeof arg === "undefined";
|
|
2700
|
+
if (expectedType === Function) return typeof arg === "function";
|
|
2701
|
+
if (expectedType === Number) return typeof arg === "number";
|
|
2702
|
+
if (expectedType === Array) return Array.isArray(arg);
|
|
2703
|
+
return false;
|
|
2704
|
+
});
|
|
2759
2705
|
}
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
node = await this.getNode(node.keys[index]);
|
|
2765
|
-
}
|
|
2766
|
-
return node;
|
|
2706
|
+
_createRandomId() {
|
|
2707
|
+
const timestamp = Date.now().toString(36);
|
|
2708
|
+
const random = Math.random().toString(36).substring(2);
|
|
2709
|
+
return `${timestamp}${random}`;
|
|
2767
2710
|
}
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
node = await this.getNode(node.keys[index]);
|
|
2711
|
+
_alloc(queue, workspaces, lockId) {
|
|
2712
|
+
const unit = queue.get(lockId);
|
|
2713
|
+
if (!unit) {
|
|
2714
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2773
2715
|
}
|
|
2774
|
-
|
|
2716
|
+
workspaces.set(lockId, unit);
|
|
2717
|
+
queue.delete(lockId);
|
|
2718
|
+
unit.alloc();
|
|
2775
2719
|
}
|
|
2776
|
-
|
|
2777
|
-
const
|
|
2778
|
-
if (!
|
|
2779
|
-
|
|
2720
|
+
_free(workspaces, lockId) {
|
|
2721
|
+
const unit = workspaces.get(lockId);
|
|
2722
|
+
if (!unit) {
|
|
2723
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2780
2724
|
}
|
|
2781
|
-
|
|
2725
|
+
workspaces.delete(lockId);
|
|
2726
|
+
unit.free();
|
|
2782
2727
|
}
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2728
|
+
_lock(queue, range, timeout, task, condition) {
|
|
2729
|
+
return new Promise((resolve, reject) => {
|
|
2730
|
+
let timeoutId = null;
|
|
2731
|
+
if (timeout >= 0) {
|
|
2732
|
+
timeoutId = setTimeout(() => {
|
|
2733
|
+
reject(_Ryoiki2.ERR_TIMEOUT(id, timeout));
|
|
2734
|
+
}, timeout);
|
|
2735
|
+
}
|
|
2736
|
+
const id = this._createRandomId();
|
|
2737
|
+
const alloc = async () => {
|
|
2738
|
+
if (timeoutId !== null) {
|
|
2739
|
+
clearTimeout(timeoutId);
|
|
2740
|
+
}
|
|
2741
|
+
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
2742
|
+
if (err) reject(err);
|
|
2743
|
+
else resolve(v);
|
|
2744
|
+
};
|
|
2745
|
+
const fetch = () => {
|
|
2746
|
+
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
2747
|
+
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
2748
|
+
};
|
|
2749
|
+
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
2750
|
+
fetch();
|
|
2751
|
+
});
|
|
2752
|
+
}
|
|
2753
|
+
_checkWorking(range, workspaces) {
|
|
2754
|
+
let isLocked = false;
|
|
2755
|
+
for (const lock of workspaces.values()) {
|
|
2756
|
+
if (_Ryoiki2.IsRangeOverlap(range, lock.range)) {
|
|
2757
|
+
isLocked = true;
|
|
2792
2758
|
break;
|
|
2793
|
-
|
|
2794
|
-
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
2795
|
-
}
|
|
2796
|
-
const guessNode = insertableNode[key];
|
|
2797
|
-
if (!guessNode) {
|
|
2798
|
-
return null;
|
|
2759
|
+
}
|
|
2799
2760
|
}
|
|
2800
|
-
return
|
|
2761
|
+
return isLocked;
|
|
2801
2762
|
}
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
node = await this.getNode(keys[0]);
|
|
2810
|
-
}
|
|
2811
|
-
return node;
|
|
2763
|
+
/**
|
|
2764
|
+
* Checks if there is any active read lock within the specified range.
|
|
2765
|
+
* @param range The range to check for active read locks.
|
|
2766
|
+
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
2767
|
+
*/
|
|
2768
|
+
isReading(range) {
|
|
2769
|
+
return this._checkWorking(range, this.readings);
|
|
2812
2770
|
}
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
return
|
|
2771
|
+
/**
|
|
2772
|
+
* Checks if there is any active write lock within the specified range.
|
|
2773
|
+
* @param range The range to check for active write locks.
|
|
2774
|
+
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
2775
|
+
*/
|
|
2776
|
+
isWriting(range) {
|
|
2777
|
+
return this._checkWorking(range, this.writings);
|
|
2820
2778
|
}
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2779
|
+
/**
|
|
2780
|
+
* Checks if a read lock can be acquired within the specified range.
|
|
2781
|
+
* @param range The range to check for read lock availability.
|
|
2782
|
+
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
2783
|
+
*/
|
|
2784
|
+
canRead(range) {
|
|
2785
|
+
const writing = this.isWriting(range);
|
|
2786
|
+
return !writing;
|
|
2787
|
+
}
|
|
2788
|
+
/**
|
|
2789
|
+
* Checks if a write lock can be acquired within the specified range.
|
|
2790
|
+
* @param range The range to check for write lock availability.
|
|
2791
|
+
* @returns `true` if a write lock can be acquired, `false` otherwise.
|
|
2792
|
+
*/
|
|
2793
|
+
canWrite(range) {
|
|
2794
|
+
const reading = this.isReading(range);
|
|
2795
|
+
const writing = this.isWriting(range);
|
|
2796
|
+
return !reading && !writing;
|
|
2797
|
+
}
|
|
2798
|
+
/**
|
|
2799
|
+
* Internal implementation of the read lock. Handles both overloads.
|
|
2800
|
+
* @template T - The return type of the task.
|
|
2801
|
+
* @param arg0 - Either a range or a task callback.
|
|
2802
|
+
* If a range is provided, the task is the second argument.
|
|
2803
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2804
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2805
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2806
|
+
* If this value is not provided, no timeout will be set.
|
|
2807
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2808
|
+
*/
|
|
2809
|
+
readLock(arg0, arg1, arg2) {
|
|
2810
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2811
|
+
[arg0, arg1, arg2],
|
|
2812
|
+
{
|
|
2813
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2814
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2815
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2816
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2817
|
+
},
|
|
2818
|
+
{
|
|
2819
|
+
task: [Function],
|
|
2820
|
+
taskTimeout: [Function, Number],
|
|
2821
|
+
rangeTask: [Array, Function],
|
|
2822
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2823
|
+
}
|
|
2824
|
+
);
|
|
2825
|
+
return this._lock(
|
|
2826
|
+
this.readQueue,
|
|
2827
|
+
range,
|
|
2828
|
+
timeout,
|
|
2829
|
+
task,
|
|
2830
|
+
() => !this.rangeOverlapping(this.writings, range)
|
|
2831
|
+
);
|
|
2832
|
+
}
|
|
2833
|
+
/**
|
|
2834
|
+
* Internal implementation of the write lock. Handles both overloads.
|
|
2835
|
+
* @template T - The return type of the task.
|
|
2836
|
+
* @param arg0 - Either a range or a task callback.
|
|
2837
|
+
* If a range is provided, the task is the second argument.
|
|
2838
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2839
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2840
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2841
|
+
* If this value is not provided, no timeout will be set.
|
|
2842
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2843
|
+
*/
|
|
2844
|
+
writeLock(arg0, arg1, arg2) {
|
|
2845
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2846
|
+
[arg0, arg1, arg2],
|
|
2847
|
+
{
|
|
2848
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2849
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2850
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2851
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2852
|
+
},
|
|
2853
|
+
{
|
|
2854
|
+
task: [Function],
|
|
2855
|
+
taskTimeout: [Function, Number],
|
|
2856
|
+
rangeTask: [Array, Function],
|
|
2857
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2858
|
+
}
|
|
2859
|
+
);
|
|
2860
|
+
return this._lock(
|
|
2861
|
+
this.writeQueue,
|
|
2862
|
+
range,
|
|
2863
|
+
timeout,
|
|
2864
|
+
task,
|
|
2865
|
+
() => {
|
|
2866
|
+
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
2867
|
+
}
|
|
2868
|
+
);
|
|
2869
|
+
}
|
|
2870
|
+
/**
|
|
2871
|
+
* Releases a read lock by its lock ID.
|
|
2872
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2873
|
+
*/
|
|
2874
|
+
readUnlock(lockId) {
|
|
2875
|
+
this._free(this.readings, lockId);
|
|
2876
|
+
}
|
|
2877
|
+
/**
|
|
2878
|
+
* Releases a write lock by its lock ID.
|
|
2879
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2880
|
+
*/
|
|
2881
|
+
writeUnlock(lockId) {
|
|
2882
|
+
this._free(this.writings, lockId);
|
|
2883
|
+
}
|
|
2884
|
+
};
|
|
2885
|
+
|
|
2886
|
+
// src/transaction/BPTreeAsyncTransaction.ts
|
|
2887
|
+
var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
2888
|
+
lock;
|
|
2889
|
+
constructor(rootTx, mvccRoot, mvcc, strategy, comparator, option) {
|
|
2890
|
+
super(
|
|
2891
|
+
rootTx,
|
|
2892
|
+
mvccRoot,
|
|
2893
|
+
mvcc,
|
|
2894
|
+
strategy,
|
|
2895
|
+
comparator,
|
|
2896
|
+
option
|
|
2897
|
+
);
|
|
2898
|
+
this.lock = new Ryoiki2();
|
|
2899
|
+
}
|
|
2900
|
+
async writeLock(id, fn) {
|
|
2901
|
+
let lockId;
|
|
2902
|
+
return this.lock.writeLock([id, id + 0.1], async (_lockId) => {
|
|
2903
|
+
lockId = _lockId;
|
|
2904
|
+
return fn();
|
|
2905
|
+
}).finally(() => {
|
|
2906
|
+
this.lock.writeUnlock(lockId);
|
|
2907
|
+
});
|
|
2908
|
+
}
|
|
2909
|
+
async getNode(id) {
|
|
2910
|
+
if (this.nodes.has(id)) {
|
|
2911
|
+
return this.nodes.get(id);
|
|
2912
|
+
}
|
|
2913
|
+
return await this.mvcc.read(id);
|
|
2914
|
+
}
|
|
2915
|
+
/**
|
|
2916
|
+
* Create a new node with a unique ID.
|
|
2917
|
+
*/
|
|
2918
|
+
async _createNode(leaf, keys, values, parent = null, next = null, prev = null) {
|
|
2919
|
+
const id = await this.strategy.id(leaf);
|
|
2920
|
+
const node = {
|
|
2921
|
+
id,
|
|
2922
|
+
keys,
|
|
2923
|
+
values,
|
|
2924
|
+
leaf,
|
|
2925
|
+
parent,
|
|
2926
|
+
next,
|
|
2927
|
+
prev
|
|
2928
|
+
};
|
|
2929
|
+
await this.mvcc.create(id, node);
|
|
2930
|
+
this.nodes.set(id, node);
|
|
2931
|
+
return node;
|
|
2932
|
+
}
|
|
2933
|
+
async _updateNode(node) {
|
|
2934
|
+
await this.mvcc.write(node.id, node);
|
|
2935
|
+
this.nodes.set(node.id, node);
|
|
2936
|
+
}
|
|
2937
|
+
async _deleteNode(node) {
|
|
2938
|
+
await this.mvcc.delete(node.id);
|
|
2939
|
+
this.nodes.delete(node.id);
|
|
2940
|
+
}
|
|
2941
|
+
async _readHead() {
|
|
2942
|
+
if (this.nodes.has("__HEAD__")) {
|
|
2943
|
+
return this.nodes.get("__HEAD__") ?? null;
|
|
2944
|
+
}
|
|
2945
|
+
const head = await this.mvcc.read("__HEAD__");
|
|
2946
|
+
return head ?? null;
|
|
2947
|
+
}
|
|
2948
|
+
async _writeHead(head) {
|
|
2949
|
+
if (!await this.mvcc.exists("__HEAD__")) {
|
|
2950
|
+
await this.mvcc.create("__HEAD__", head);
|
|
2951
|
+
} else {
|
|
2952
|
+
await this.mvcc.write("__HEAD__", head);
|
|
2953
|
+
}
|
|
2954
|
+
this.nodes.set("__HEAD__", head);
|
|
2955
|
+
this.rootId = head.root;
|
|
2956
|
+
}
|
|
2957
|
+
async _insertAtLeaf(node, key, value) {
|
|
2958
|
+
if (node.values.length) {
|
|
2959
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2960
|
+
const nValue = node.values[i];
|
|
2961
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
2962
|
+
const keys = node.keys[i];
|
|
2963
|
+
if (keys.includes(key)) {
|
|
2964
|
+
break;
|
|
2965
|
+
}
|
|
2966
|
+
keys.push(key);
|
|
2967
|
+
await this._updateNode(node);
|
|
2968
|
+
return;
|
|
2969
|
+
} else if (this.comparator.isLower(value, nValue)) {
|
|
2970
|
+
node.values.splice(i, 0, value);
|
|
2971
|
+
node.keys.splice(i, 0, [key]);
|
|
2972
|
+
await this._updateNode(node);
|
|
2973
|
+
return;
|
|
2974
|
+
} else if (i + 1 === node.values.length) {
|
|
2975
|
+
node.values.push(value);
|
|
2976
|
+
node.keys.push([key]);
|
|
2977
|
+
await this._updateNode(node);
|
|
2978
|
+
return;
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
} else {
|
|
2982
|
+
node.values = [value];
|
|
2983
|
+
node.keys = [[key]];
|
|
2984
|
+
await this._updateNode(node);
|
|
2985
|
+
return;
|
|
2986
|
+
}
|
|
2987
|
+
}
|
|
2988
|
+
async _insertInParent(node, value, pointer) {
|
|
2989
|
+
if (this.rootId === node.id) {
|
|
2990
|
+
const root = await this._createNode(false, [node.id, pointer.id], [value]);
|
|
2991
|
+
this.rootId = root.id;
|
|
2992
|
+
node.parent = root.id;
|
|
2993
|
+
pointer.parent = root.id;
|
|
2994
|
+
if (pointer.leaf) {
|
|
2995
|
+
node.next = pointer.id;
|
|
2996
|
+
pointer.prev = node.id;
|
|
2997
|
+
}
|
|
2998
|
+
await this._writeHead({
|
|
2999
|
+
root: root.id,
|
|
3000
|
+
order: this.order,
|
|
3001
|
+
data: this.strategy.head.data
|
|
3002
|
+
});
|
|
3003
|
+
await this._updateNode(node);
|
|
3004
|
+
await this._updateNode(pointer);
|
|
3005
|
+
return;
|
|
3006
|
+
}
|
|
3007
|
+
const parentNode = await this.getNode(node.parent);
|
|
3008
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
3009
|
+
if (nodeIndex === -1) {
|
|
3010
|
+
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
3011
|
+
}
|
|
3012
|
+
parentNode.values.splice(nodeIndex, 0, value);
|
|
3013
|
+
parentNode.keys.splice(nodeIndex + 1, 0, pointer.id);
|
|
3014
|
+
pointer.parent = parentNode.id;
|
|
3015
|
+
if (pointer.leaf) {
|
|
3016
|
+
const leftSibling = node;
|
|
3017
|
+
const oldNextId = leftSibling.next;
|
|
3018
|
+
pointer.prev = leftSibling.id;
|
|
3019
|
+
pointer.next = oldNextId;
|
|
3020
|
+
leftSibling.next = pointer.id;
|
|
3021
|
+
await this._updateNode(leftSibling);
|
|
3022
|
+
if (oldNextId) {
|
|
3023
|
+
const oldNext = await this.getNode(oldNextId);
|
|
3024
|
+
oldNext.prev = pointer.id;
|
|
3025
|
+
await this._updateNode(oldNext);
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
await this._updateNode(parentNode);
|
|
3029
|
+
await this._updateNode(pointer);
|
|
3030
|
+
if (parentNode.keys.length > this.order) {
|
|
3031
|
+
const parentPointer = await this._createNode(false, [], []);
|
|
3032
|
+
parentPointer.parent = parentNode.parent;
|
|
3033
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
3034
|
+
parentPointer.values = parentNode.values.slice(mid + 1);
|
|
3035
|
+
parentPointer.keys = parentNode.keys.slice(mid + 1);
|
|
3036
|
+
const midValue = parentNode.values[mid];
|
|
3037
|
+
parentNode.values = parentNode.values.slice(0, mid);
|
|
3038
|
+
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
3039
|
+
for (const k of parentNode.keys) {
|
|
3040
|
+
const n = await this.getNode(k);
|
|
3041
|
+
n.parent = parentNode.id;
|
|
3042
|
+
await this._updateNode(n);
|
|
3043
|
+
}
|
|
3044
|
+
for (const k of parentPointer.keys) {
|
|
3045
|
+
const n = await this.getNode(k);
|
|
3046
|
+
n.parent = parentPointer.id;
|
|
3047
|
+
await this._updateNode(n);
|
|
3048
|
+
}
|
|
3049
|
+
await this._updateNode(parentNode);
|
|
3050
|
+
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
3051
|
+
}
|
|
3052
|
+
}
|
|
3053
|
+
async insertableNode(value) {
|
|
3054
|
+
let node = await this.getNode(this.rootId);
|
|
3055
|
+
while (!node.leaf) {
|
|
3056
|
+
const { index } = this._binarySearchValues(node.values, value, false, true);
|
|
3057
|
+
node = await this.getNode(node.keys[index]);
|
|
3058
|
+
}
|
|
3059
|
+
return node;
|
|
3060
|
+
}
|
|
3061
|
+
async insertableNodeByPrimary(value) {
|
|
3062
|
+
let node = await this.getNode(this.rootId);
|
|
3063
|
+
while (!node.leaf) {
|
|
3064
|
+
const { index } = this._binarySearchValues(node.values, value, true, false);
|
|
3065
|
+
node = await this.getNode(node.keys[index]);
|
|
3066
|
+
}
|
|
3067
|
+
return node;
|
|
3068
|
+
}
|
|
3069
|
+
async insertableRightestNodeByPrimary(value) {
|
|
3070
|
+
let node = await this.getNode(this.rootId);
|
|
3071
|
+
while (!node.leaf) {
|
|
3072
|
+
const { index } = this._binarySearchValues(node.values, value, true, true);
|
|
3073
|
+
node = await this.getNode(node.keys[index]);
|
|
3074
|
+
}
|
|
3075
|
+
return node;
|
|
3076
|
+
}
|
|
3077
|
+
async insertableRightestEndNodeByPrimary(value) {
|
|
3078
|
+
const node = await this.insertableRightestNodeByPrimary(value);
|
|
3079
|
+
if (!node.next) {
|
|
3080
|
+
return null;
|
|
3081
|
+
}
|
|
3082
|
+
return await this.getNode(node.next);
|
|
3083
|
+
}
|
|
3084
|
+
async insertableEndNode(value, direction) {
|
|
3085
|
+
const insertableNode = await this.insertableNode(value);
|
|
3086
|
+
let key;
|
|
3087
|
+
switch (direction) {
|
|
3088
|
+
case -1:
|
|
3089
|
+
key = "prev";
|
|
3090
|
+
break;
|
|
3091
|
+
case 1:
|
|
3092
|
+
key = "next";
|
|
3093
|
+
break;
|
|
3094
|
+
default:
|
|
3095
|
+
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
3096
|
+
}
|
|
3097
|
+
const guessNode = insertableNode[key];
|
|
3098
|
+
if (!guessNode) {
|
|
3099
|
+
return null;
|
|
3100
|
+
}
|
|
3101
|
+
return await this.getNode(guessNode);
|
|
3102
|
+
}
|
|
3103
|
+
async leftestNode() {
|
|
3104
|
+
let node = await this.getNode(this.rootId);
|
|
3105
|
+
if (node === null) {
|
|
3106
|
+
debugger;
|
|
3107
|
+
}
|
|
3108
|
+
while (!node.leaf) {
|
|
3109
|
+
const keys = node.keys;
|
|
3110
|
+
node = await this.getNode(keys[0]);
|
|
3111
|
+
}
|
|
3112
|
+
return node;
|
|
3113
|
+
}
|
|
3114
|
+
async rightestNode() {
|
|
3115
|
+
let node = await this.getNode(this.rootId);
|
|
3116
|
+
while (!node.leaf) {
|
|
3117
|
+
const keys = node.keys;
|
|
3118
|
+
node = await this.getNode(keys[keys.length - 1]);
|
|
3119
|
+
}
|
|
3120
|
+
return node;
|
|
3121
|
+
}
|
|
3122
|
+
async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
3123
|
+
let node = startNode;
|
|
3124
|
+
let done = false;
|
|
3125
|
+
let hasMatched = false;
|
|
3126
|
+
let nextNodePromise = null;
|
|
3127
|
+
while (!done) {
|
|
3128
|
+
if (endNode && node.id === endNode.id) {
|
|
3129
|
+
done = true;
|
|
3130
|
+
break;
|
|
2830
3131
|
}
|
|
2831
3132
|
if (direction === 1) {
|
|
2832
3133
|
if (node.next && !done) {
|
|
@@ -2882,28 +3183,46 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
2882
3183
|
}
|
|
2883
3184
|
}
|
|
2884
3185
|
async init() {
|
|
2885
|
-
this.
|
|
2886
|
-
|
|
2887
|
-
if (head === null) {
|
|
2888
|
-
this.order = this.strategy.order;
|
|
2889
|
-
const root = await this._createNode(true, [], []);
|
|
2890
|
-
await this._writeHead({
|
|
2891
|
-
root: root.id,
|
|
2892
|
-
order: this.order,
|
|
2893
|
-
data: this.strategy.head.data
|
|
2894
|
-
});
|
|
2895
|
-
} else {
|
|
2896
|
-
const { root, order } = head;
|
|
2897
|
-
this.strategy.head = head;
|
|
2898
|
-
this.order = order;
|
|
2899
|
-
await this._writeHead({
|
|
2900
|
-
root,
|
|
2901
|
-
order: this.order,
|
|
2902
|
-
data: this.strategy.head.data
|
|
2903
|
-
});
|
|
3186
|
+
if (this.rootTx !== this) {
|
|
3187
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
2904
3188
|
}
|
|
2905
|
-
|
|
2906
|
-
|
|
3189
|
+
return await this._initInternal();
|
|
3190
|
+
}
|
|
3191
|
+
async _initInternal() {
|
|
3192
|
+
if (this.isInitialized) {
|
|
3193
|
+
throw new Error("Transaction already initialized");
|
|
3194
|
+
}
|
|
3195
|
+
if (this.isDestroyed) {
|
|
3196
|
+
throw new Error("Transaction already destroyed");
|
|
3197
|
+
}
|
|
3198
|
+
this.isInitialized = true;
|
|
3199
|
+
try {
|
|
3200
|
+
this._clearCache();
|
|
3201
|
+
const head = await this._readHead();
|
|
3202
|
+
if (head === null) {
|
|
3203
|
+
this.order = this.strategy.order;
|
|
3204
|
+
const root = await this._createNode(true, [], []);
|
|
3205
|
+
await this._writeHead({
|
|
3206
|
+
root: root.id,
|
|
3207
|
+
order: this.order,
|
|
3208
|
+
data: this.strategy.head.data
|
|
3209
|
+
});
|
|
3210
|
+
} else {
|
|
3211
|
+
const { root, order } = head;
|
|
3212
|
+
this.strategy.head = head;
|
|
3213
|
+
this.order = order;
|
|
3214
|
+
await this._writeHead({
|
|
3215
|
+
root,
|
|
3216
|
+
order: this.order,
|
|
3217
|
+
data: this.strategy.head.data
|
|
3218
|
+
});
|
|
3219
|
+
}
|
|
3220
|
+
if (this.order < 3) {
|
|
3221
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
3222
|
+
}
|
|
3223
|
+
} catch (e) {
|
|
3224
|
+
this.isInitialized = false;
|
|
3225
|
+
throw e;
|
|
2907
3226
|
}
|
|
2908
3227
|
}
|
|
2909
3228
|
async exists(key, value) {
|
|
@@ -3003,38 +3322,40 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3003
3322
|
}
|
|
3004
3323
|
}
|
|
3005
3324
|
async keys(condition, filterValues, order = "asc") {
|
|
3006
|
-
const set = /* @__PURE__ */ new Set();
|
|
3007
|
-
for await (const key of this.keysStream(condition, filterValues, void 0, order)) {
|
|
3008
|
-
set.add(key);
|
|
3009
|
-
}
|
|
3010
|
-
return set;
|
|
3011
|
-
}
|
|
3012
|
-
async where(condition, order = "asc") {
|
|
3013
|
-
const map = /* @__PURE__ */ new Map();
|
|
3014
|
-
for await (const [key, value] of this.whereStream(condition, void 0, order)) {
|
|
3015
|
-
map.set(key, value);
|
|
3016
|
-
}
|
|
3017
|
-
return map;
|
|
3018
|
-
}
|
|
3019
|
-
async insert(key, value) {
|
|
3020
|
-
const before = await this.insertableNode(value);
|
|
3021
|
-
await this._insertAtLeaf(before, key, value);
|
|
3022
|
-
if (before.values.length === this.order) {
|
|
3023
|
-
const after = await this._createNode(
|
|
3024
|
-
true,
|
|
3025
|
-
[],
|
|
3026
|
-
[],
|
|
3027
|
-
before.parent,
|
|
3028
|
-
null,
|
|
3029
|
-
null
|
|
3030
|
-
);
|
|
3031
|
-
const mid = Math.ceil(this.order / 2) - 1;
|
|
3032
|
-
after.values = before.values.slice(mid + 1);
|
|
3033
|
-
after.keys = before.keys.slice(mid + 1);
|
|
3034
|
-
before.values = before.values.slice(0, mid + 1);
|
|
3035
|
-
before.keys = before.keys.slice(0, mid + 1);
|
|
3036
|
-
await this._insertInParent(before, after.values[0], after);
|
|
3325
|
+
const set = /* @__PURE__ */ new Set();
|
|
3326
|
+
for await (const key of this.keysStream(condition, filterValues, void 0, order)) {
|
|
3327
|
+
set.add(key);
|
|
3328
|
+
}
|
|
3329
|
+
return set;
|
|
3330
|
+
}
|
|
3331
|
+
async where(condition, order = "asc") {
|
|
3332
|
+
const map = /* @__PURE__ */ new Map();
|
|
3333
|
+
for await (const [key, value] of this.whereStream(condition, void 0, order)) {
|
|
3334
|
+
map.set(key, value);
|
|
3037
3335
|
}
|
|
3336
|
+
return map;
|
|
3337
|
+
}
|
|
3338
|
+
async insert(key, value) {
|
|
3339
|
+
return this.writeLock(0, async () => {
|
|
3340
|
+
const before = await this.insertableNode(value);
|
|
3341
|
+
await this._insertAtLeaf(before, key, value);
|
|
3342
|
+
if (before.values.length === this.order) {
|
|
3343
|
+
const after = await this._createNode(
|
|
3344
|
+
true,
|
|
3345
|
+
[],
|
|
3346
|
+
[],
|
|
3347
|
+
before.parent,
|
|
3348
|
+
null,
|
|
3349
|
+
null
|
|
3350
|
+
);
|
|
3351
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
3352
|
+
after.values = before.values.slice(mid + 1);
|
|
3353
|
+
after.keys = before.keys.slice(mid + 1);
|
|
3354
|
+
before.values = before.values.slice(0, mid + 1);
|
|
3355
|
+
before.keys = before.keys.slice(0, mid + 1);
|
|
3356
|
+
await this._insertInParent(before, after.values[0], after);
|
|
3357
|
+
}
|
|
3358
|
+
});
|
|
3038
3359
|
}
|
|
3039
3360
|
async _deleteEntry(node, key) {
|
|
3040
3361
|
if (!node.leaf) {
|
|
@@ -3227,35 +3548,37 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3227
3548
|
}
|
|
3228
3549
|
}
|
|
3229
3550
|
async delete(key, value) {
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3551
|
+
return this.writeLock(0, async () => {
|
|
3552
|
+
let node = await this.insertableNodeByPrimary(value);
|
|
3553
|
+
let found = false;
|
|
3554
|
+
while (true) {
|
|
3555
|
+
let i = node.values.length;
|
|
3556
|
+
while (i--) {
|
|
3557
|
+
const nValue = node.values[i];
|
|
3558
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
3559
|
+
const keys = node.keys[i];
|
|
3560
|
+
const keyIndex = keys.indexOf(key);
|
|
3561
|
+
if (keyIndex !== -1) {
|
|
3562
|
+
keys.splice(keyIndex, 1);
|
|
3563
|
+
if (keys.length === 0) {
|
|
3564
|
+
node.keys.splice(i, 1);
|
|
3565
|
+
node.values.splice(i, 1);
|
|
3566
|
+
}
|
|
3567
|
+
await this._updateNode(node);
|
|
3568
|
+
await this._deleteEntry(node, key);
|
|
3569
|
+
found = true;
|
|
3570
|
+
break;
|
|
3244
3571
|
}
|
|
3245
|
-
await this._updateNode(node);
|
|
3246
|
-
await this._deleteEntry(node, key);
|
|
3247
|
-
found = true;
|
|
3248
|
-
break;
|
|
3249
3572
|
}
|
|
3250
3573
|
}
|
|
3574
|
+
if (found) break;
|
|
3575
|
+
if (node.next) {
|
|
3576
|
+
node = await this.getNode(node.next);
|
|
3577
|
+
continue;
|
|
3578
|
+
}
|
|
3579
|
+
break;
|
|
3251
3580
|
}
|
|
3252
|
-
|
|
3253
|
-
if (node.next) {
|
|
3254
|
-
node = await this.getNode(node.next);
|
|
3255
|
-
continue;
|
|
3256
|
-
}
|
|
3257
|
-
break;
|
|
3258
|
-
}
|
|
3581
|
+
});
|
|
3259
3582
|
}
|
|
3260
3583
|
async getHeadData() {
|
|
3261
3584
|
const head = await this._readHead();
|
|
@@ -3278,9 +3601,12 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3278
3601
|
async commit(label) {
|
|
3279
3602
|
let result = await this.mvcc.commit(label);
|
|
3280
3603
|
if (result.success) {
|
|
3281
|
-
|
|
3282
|
-
if (
|
|
3283
|
-
|
|
3604
|
+
const isRootTx = this.rootTx === this;
|
|
3605
|
+
if (!isRootTx) {
|
|
3606
|
+
result = await this.rootTx.commit(label);
|
|
3607
|
+
if (result.success) {
|
|
3608
|
+
this.rootTx.rootId = this.rootId;
|
|
3609
|
+
}
|
|
3284
3610
|
}
|
|
3285
3611
|
if (result.success) {
|
|
3286
3612
|
for (const r of result.created) {
|
|
@@ -3316,397 +3642,141 @@ var BPTreeMVCCStrategyAsync = class extends AsyncMVCCStrategy {
|
|
|
3316
3642
|
async write(key, value) {
|
|
3317
3643
|
if (key === "__HEAD__") {
|
|
3318
3644
|
await this.strategy.writeHead(value);
|
|
3319
|
-
} else {
|
|
3320
|
-
await this.strategy.write(key, value);
|
|
3321
|
-
}
|
|
3322
|
-
}
|
|
3323
|
-
async delete(key) {
|
|
3324
|
-
await this.strategy.delete(key);
|
|
3325
|
-
}
|
|
3326
|
-
async exists(key) {
|
|
3327
|
-
if (key === "__HEAD__") {
|
|
3328
|
-
return await this.strategy.readHead() !== null;
|
|
3329
|
-
}
|
|
3330
|
-
try {
|
|
3331
|
-
const node = await this.strategy.read(key);
|
|
3332
|
-
return node !== null && node !== void 0;
|
|
3333
|
-
} catch {
|
|
3334
|
-
return false;
|
|
3335
|
-
}
|
|
3336
|
-
}
|
|
3337
|
-
};
|
|
3338
|
-
|
|
3339
|
-
// src/BPTreeAsync.ts
|
|
3340
|
-
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3341
|
-
constructor(strategy, comparator, option) {
|
|
3342
|
-
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy));
|
|
3343
|
-
super(
|
|
3344
|
-
null,
|
|
3345
|
-
mvccRoot,
|
|
3346
|
-
mvccRoot,
|
|
3347
|
-
strategy,
|
|
3348
|
-
comparator,
|
|
3349
|
-
option
|
|
3350
|
-
);
|
|
3351
|
-
}
|
|
3352
|
-
/**
|
|
3353
|
-
* Creates a new asynchronous transaction.
|
|
3354
|
-
* @returns A new BPTreeAsyncTransaction.
|
|
3355
|
-
*/
|
|
3356
|
-
async createTransaction() {
|
|
3357
|
-
const nestedTx = await this.mvcc.createNested();
|
|
3358
|
-
const tx = new BPTreeAsyncTransaction(
|
|
3359
|
-
this,
|
|
3360
|
-
this.mvcc,
|
|
3361
|
-
nestedTx,
|
|
3362
|
-
this.strategy,
|
|
3363
|
-
this.comparator,
|
|
3364
|
-
this.option
|
|
3365
|
-
);
|
|
3366
|
-
await tx.init();
|
|
3367
|
-
return tx;
|
|
3368
|
-
}
|
|
3369
|
-
async insert(key, value) {
|
|
3370
|
-
const tx = await this.createTransaction();
|
|
3371
|
-
await tx.insert(key, value);
|
|
3372
|
-
const result = await tx.commit();
|
|
3373
|
-
if (!result.success) {
|
|
3374
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3375
|
-
}
|
|
3376
|
-
this.rootId = tx.getRootId();
|
|
3377
|
-
}
|
|
3378
|
-
async delete(key, value) {
|
|
3379
|
-
const tx = await this.createTransaction();
|
|
3380
|
-
await tx.delete(key, value);
|
|
3381
|
-
const result = await tx.commit();
|
|
3382
|
-
if (!result.success) {
|
|
3383
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3384
|
-
}
|
|
3385
|
-
this.rootId = tx.getRootId();
|
|
3386
|
-
}
|
|
3387
|
-
};
|
|
3388
|
-
|
|
3389
|
-
// src/base/SerializeStrategy.ts
|
|
3390
|
-
var SerializeStrategy = class {
|
|
3391
|
-
order;
|
|
3392
|
-
head;
|
|
3393
|
-
constructor(order) {
|
|
3394
|
-
this.order = order;
|
|
3395
|
-
this.head = {
|
|
3396
|
-
order,
|
|
3397
|
-
root: null,
|
|
3398
|
-
data: {}
|
|
3399
|
-
};
|
|
3400
|
-
}
|
|
3401
|
-
};
|
|
3402
|
-
|
|
3403
|
-
// src/SerializeStrategySync.ts
|
|
3404
|
-
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3405
|
-
getHeadData(key, defaultValue) {
|
|
3406
|
-
if (!Object.hasOwn(this.head.data, key)) {
|
|
3407
|
-
this.setHeadData(key, defaultValue);
|
|
3408
|
-
}
|
|
3409
|
-
return this.head.data[key];
|
|
3410
|
-
}
|
|
3411
|
-
setHeadData(key, data) {
|
|
3412
|
-
this.head.data[key] = data;
|
|
3413
|
-
this.writeHead(this.head);
|
|
3414
|
-
}
|
|
3415
|
-
autoIncrement(key, defaultValue) {
|
|
3416
|
-
const current = this.getHeadData(key, defaultValue);
|
|
3417
|
-
const next = current + 1;
|
|
3418
|
-
this.setHeadData(key, next);
|
|
3419
|
-
return current;
|
|
3420
|
-
}
|
|
3421
|
-
};
|
|
3422
|
-
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3423
|
-
node;
|
|
3424
|
-
constructor(order) {
|
|
3425
|
-
super(order);
|
|
3426
|
-
this.node = {};
|
|
3427
|
-
}
|
|
3428
|
-
id(isLeaf) {
|
|
3429
|
-
return this.autoIncrement("index", 1).toString();
|
|
3430
|
-
}
|
|
3431
|
-
read(id) {
|
|
3432
|
-
if (!Object.hasOwn(this.node, id)) {
|
|
3433
|
-
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3434
|
-
}
|
|
3435
|
-
const node = this.node[id];
|
|
3436
|
-
return JSON.parse(JSON.stringify(node));
|
|
3437
|
-
}
|
|
3438
|
-
write(id, node) {
|
|
3439
|
-
this.node[id] = node;
|
|
3440
|
-
}
|
|
3441
|
-
delete(id) {
|
|
3442
|
-
delete this.node[id];
|
|
3443
|
-
}
|
|
3444
|
-
readHead() {
|
|
3445
|
-
if (this.head.root === null) {
|
|
3446
|
-
return null;
|
|
3447
|
-
}
|
|
3448
|
-
return this.head;
|
|
3449
|
-
}
|
|
3450
|
-
writeHead(head) {
|
|
3451
|
-
this.head = head;
|
|
3452
|
-
}
|
|
3453
|
-
};
|
|
3454
|
-
|
|
3455
|
-
// node_modules/ryoiki/dist/esm/index.mjs
|
|
3456
|
-
var Ryoiki2 = class _Ryoiki2 {
|
|
3457
|
-
readings;
|
|
3458
|
-
writings;
|
|
3459
|
-
readQueue;
|
|
3460
|
-
writeQueue;
|
|
3461
|
-
static async CatchError(promise) {
|
|
3462
|
-
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
3463
|
-
}
|
|
3464
|
-
static IsRangeOverlap(a, b) {
|
|
3465
|
-
const [start1, end1] = a;
|
|
3466
|
-
const [start2, end2] = b;
|
|
3467
|
-
if (end1 <= start2 || end2 <= start1) {
|
|
3468
|
-
return false;
|
|
3469
|
-
}
|
|
3470
|
-
return true;
|
|
3471
|
-
}
|
|
3472
|
-
static ERR_ALREADY_EXISTS(lockId) {
|
|
3473
|
-
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
3474
|
-
}
|
|
3475
|
-
static ERR_NOT_EXISTS(lockId) {
|
|
3476
|
-
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
3477
|
-
}
|
|
3478
|
-
static ERR_TIMEOUT(lockId, timeout) {
|
|
3479
|
-
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
3480
|
-
}
|
|
3481
|
-
/**
|
|
3482
|
-
* Constructs a new instance of the Ryoiki class.
|
|
3483
|
-
*/
|
|
3484
|
-
constructor() {
|
|
3485
|
-
this.readings = /* @__PURE__ */ new Map();
|
|
3486
|
-
this.writings = /* @__PURE__ */ new Map();
|
|
3487
|
-
this.readQueue = /* @__PURE__ */ new Map();
|
|
3488
|
-
this.writeQueue = /* @__PURE__ */ new Map();
|
|
3489
|
-
}
|
|
3490
|
-
/**
|
|
3491
|
-
* Creates a range based on a start value and length.
|
|
3492
|
-
* @param start - The starting value of the range.
|
|
3493
|
-
* @param length - The length of the range.
|
|
3494
|
-
* @returns A range tuple [start, start + length].
|
|
3495
|
-
*/
|
|
3496
|
-
range(start, length) {
|
|
3497
|
-
return [start, start + length];
|
|
3498
|
-
}
|
|
3499
|
-
rangeOverlapping(tasks, range) {
|
|
3500
|
-
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
3501
|
-
}
|
|
3502
|
-
isSameRange(a, b) {
|
|
3503
|
-
const [a1, a2] = a;
|
|
3504
|
-
const [b1, b2] = b;
|
|
3505
|
-
return a1 === b1 && a2 === b2;
|
|
3506
|
-
}
|
|
3507
|
-
fetchUnitAndRun(queue, workspaces) {
|
|
3508
|
-
for (const [id, unit] of queue) {
|
|
3509
|
-
if (!unit.condition()) {
|
|
3510
|
-
continue;
|
|
3511
|
-
}
|
|
3512
|
-
this._alloc(queue, workspaces, id);
|
|
3513
|
-
}
|
|
3514
|
-
}
|
|
3515
|
-
_handleOverload(args, handlers, argPatterns) {
|
|
3516
|
-
for (const [key, pattern] of Object.entries(argPatterns)) {
|
|
3517
|
-
if (this._matchArgs(args, pattern)) {
|
|
3518
|
-
return handlers[key](...args);
|
|
3519
|
-
}
|
|
3520
|
-
}
|
|
3521
|
-
throw new Error("Invalid arguments");
|
|
3522
|
-
}
|
|
3523
|
-
_matchArgs(args, pattern) {
|
|
3524
|
-
return args.every((arg, index) => {
|
|
3525
|
-
const expectedType = pattern[index];
|
|
3526
|
-
if (expectedType === void 0) return typeof arg === "undefined";
|
|
3527
|
-
if (expectedType === Function) return typeof arg === "function";
|
|
3528
|
-
if (expectedType === Number) return typeof arg === "number";
|
|
3529
|
-
if (expectedType === Array) return Array.isArray(arg);
|
|
3530
|
-
return false;
|
|
3531
|
-
});
|
|
3532
|
-
}
|
|
3533
|
-
_createRandomId() {
|
|
3534
|
-
const timestamp = Date.now().toString(36);
|
|
3535
|
-
const random = Math.random().toString(36).substring(2);
|
|
3536
|
-
return `${timestamp}${random}`;
|
|
3537
|
-
}
|
|
3538
|
-
_alloc(queue, workspaces, lockId) {
|
|
3539
|
-
const unit = queue.get(lockId);
|
|
3540
|
-
if (!unit) {
|
|
3541
|
-
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
3645
|
+
} else {
|
|
3646
|
+
await this.strategy.write(key, value);
|
|
3542
3647
|
}
|
|
3543
|
-
workspaces.set(lockId, unit);
|
|
3544
|
-
queue.delete(lockId);
|
|
3545
|
-
unit.alloc();
|
|
3546
3648
|
}
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3649
|
+
async delete(key) {
|
|
3650
|
+
await this.strategy.delete(key);
|
|
3651
|
+
}
|
|
3652
|
+
async exists(key) {
|
|
3653
|
+
if (key === "__HEAD__") {
|
|
3654
|
+
return await this.strategy.readHead() !== null;
|
|
3655
|
+
}
|
|
3656
|
+
try {
|
|
3657
|
+
const node = await this.strategy.read(key);
|
|
3658
|
+
return node !== null && node !== void 0;
|
|
3659
|
+
} catch {
|
|
3660
|
+
return false;
|
|
3551
3661
|
}
|
|
3552
|
-
workspaces.delete(lockId);
|
|
3553
|
-
unit.free();
|
|
3554
3662
|
}
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3663
|
+
};
|
|
3664
|
+
|
|
3665
|
+
// src/BPTreeAsync.ts
|
|
3666
|
+
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3667
|
+
constructor(strategy, comparator, option) {
|
|
3668
|
+
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy));
|
|
3669
|
+
super(
|
|
3670
|
+
null,
|
|
3671
|
+
mvccRoot,
|
|
3672
|
+
mvccRoot,
|
|
3673
|
+
strategy,
|
|
3674
|
+
comparator,
|
|
3675
|
+
option
|
|
3676
|
+
);
|
|
3677
|
+
}
|
|
3678
|
+
/**
|
|
3679
|
+
* Creates a new asynchronous transaction.
|
|
3680
|
+
* @returns A new BPTreeAsyncTransaction.
|
|
3681
|
+
*/
|
|
3682
|
+
async createTransaction() {
|
|
3683
|
+
const nestedTx = this.mvcc.createNested();
|
|
3684
|
+
const tx = new BPTreeAsyncTransaction(
|
|
3685
|
+
this,
|
|
3686
|
+
this.mvcc,
|
|
3687
|
+
nestedTx,
|
|
3688
|
+
this.strategy,
|
|
3689
|
+
this.comparator,
|
|
3690
|
+
this.option
|
|
3691
|
+
);
|
|
3692
|
+
await tx._initInternal();
|
|
3693
|
+
return tx;
|
|
3694
|
+
}
|
|
3695
|
+
async insert(key, value) {
|
|
3696
|
+
return this.writeLock(1, async () => {
|
|
3697
|
+
const tx = await this.createTransaction();
|
|
3698
|
+
await tx.insert(key, value);
|
|
3699
|
+
const result = await tx.commit();
|
|
3700
|
+
if (!result.success) {
|
|
3701
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3562
3702
|
}
|
|
3563
|
-
const id = this._createRandomId();
|
|
3564
|
-
const alloc = async () => {
|
|
3565
|
-
if (timeoutId !== null) {
|
|
3566
|
-
clearTimeout(timeoutId);
|
|
3567
|
-
}
|
|
3568
|
-
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
3569
|
-
if (err) reject(err);
|
|
3570
|
-
else resolve(v);
|
|
3571
|
-
};
|
|
3572
|
-
const fetch = () => {
|
|
3573
|
-
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
3574
|
-
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
3575
|
-
};
|
|
3576
|
-
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
3577
|
-
fetch();
|
|
3578
3703
|
});
|
|
3579
3704
|
}
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3705
|
+
async delete(key, value) {
|
|
3706
|
+
return this.writeLock(1, async () => {
|
|
3707
|
+
const tx = await this.createTransaction();
|
|
3708
|
+
await tx.delete(key, value);
|
|
3709
|
+
const result = await tx.commit();
|
|
3710
|
+
if (!result.success) {
|
|
3711
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3586
3712
|
}
|
|
3713
|
+
});
|
|
3714
|
+
}
|
|
3715
|
+
};
|
|
3716
|
+
|
|
3717
|
+
// src/base/SerializeStrategy.ts
|
|
3718
|
+
var SerializeStrategy = class {
|
|
3719
|
+
order;
|
|
3720
|
+
head;
|
|
3721
|
+
constructor(order) {
|
|
3722
|
+
this.order = order;
|
|
3723
|
+
this.head = {
|
|
3724
|
+
order,
|
|
3725
|
+
root: null,
|
|
3726
|
+
data: {}
|
|
3727
|
+
};
|
|
3728
|
+
}
|
|
3729
|
+
};
|
|
3730
|
+
|
|
3731
|
+
// src/SerializeStrategySync.ts
|
|
3732
|
+
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3733
|
+
getHeadData(key, defaultValue) {
|
|
3734
|
+
if (!Object.hasOwn(this.head.data, key)) {
|
|
3735
|
+
this.setHeadData(key, defaultValue);
|
|
3587
3736
|
}
|
|
3588
|
-
return
|
|
3737
|
+
return this.head.data[key];
|
|
3589
3738
|
}
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
3594
|
-
*/
|
|
3595
|
-
isReading(range) {
|
|
3596
|
-
return this._checkWorking(range, this.readings);
|
|
3739
|
+
setHeadData(key, data) {
|
|
3740
|
+
this.head.data[key] = data;
|
|
3741
|
+
this.writeHead(this.head);
|
|
3597
3742
|
}
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
isWriting(range) {
|
|
3604
|
-
return this._checkWorking(range, this.writings);
|
|
3743
|
+
autoIncrement(key, defaultValue) {
|
|
3744
|
+
const current = this.getHeadData(key, defaultValue);
|
|
3745
|
+
const next = current + 1;
|
|
3746
|
+
this.setHeadData(key, next);
|
|
3747
|
+
return current;
|
|
3605
3748
|
}
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
const writing = this.isWriting(range);
|
|
3613
|
-
return !writing;
|
|
3749
|
+
};
|
|
3750
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3751
|
+
node;
|
|
3752
|
+
constructor(order) {
|
|
3753
|
+
super(order);
|
|
3754
|
+
this.node = {};
|
|
3614
3755
|
}
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
* @param range The range to check for write lock availability.
|
|
3618
|
-
* @returns `true` if a write lock can be acquired, `false` otherwise.
|
|
3619
|
-
*/
|
|
3620
|
-
canWrite(range) {
|
|
3621
|
-
const reading = this.isReading(range);
|
|
3622
|
-
const writing = this.isWriting(range);
|
|
3623
|
-
return !reading && !writing;
|
|
3756
|
+
id(isLeaf) {
|
|
3757
|
+
return this.autoIncrement("index", 1).toString();
|
|
3624
3758
|
}
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
3632
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
3633
|
-
* If this value is not provided, no timeout will be set.
|
|
3634
|
-
* @returns A promise resolving to the result of the task execution.
|
|
3635
|
-
*/
|
|
3636
|
-
readLock(arg0, arg1, arg2) {
|
|
3637
|
-
const [range, task, timeout] = this._handleOverload(
|
|
3638
|
-
[arg0, arg1, arg2],
|
|
3639
|
-
{
|
|
3640
|
-
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
3641
|
-
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
3642
|
-
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
3643
|
-
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
3644
|
-
},
|
|
3645
|
-
{
|
|
3646
|
-
task: [Function],
|
|
3647
|
-
taskTimeout: [Function, Number],
|
|
3648
|
-
rangeTask: [Array, Function],
|
|
3649
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
3650
|
-
}
|
|
3651
|
-
);
|
|
3652
|
-
return this._lock(
|
|
3653
|
-
this.readQueue,
|
|
3654
|
-
range,
|
|
3655
|
-
timeout,
|
|
3656
|
-
task,
|
|
3657
|
-
() => !this.rangeOverlapping(this.writings, range)
|
|
3658
|
-
);
|
|
3759
|
+
read(id) {
|
|
3760
|
+
if (!Object.hasOwn(this.node, id)) {
|
|
3761
|
+
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3762
|
+
}
|
|
3763
|
+
const node = this.node[id];
|
|
3764
|
+
return JSON.parse(JSON.stringify(node));
|
|
3659
3765
|
}
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
* @template T - The return type of the task.
|
|
3663
|
-
* @param arg0 - Either a range or a task callback.
|
|
3664
|
-
* If a range is provided, the task is the second argument.
|
|
3665
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
3666
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
3667
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
3668
|
-
* If this value is not provided, no timeout will be set.
|
|
3669
|
-
* @returns A promise resolving to the result of the task execution.
|
|
3670
|
-
*/
|
|
3671
|
-
writeLock(arg0, arg1, arg2) {
|
|
3672
|
-
const [range, task, timeout] = this._handleOverload(
|
|
3673
|
-
[arg0, arg1, arg2],
|
|
3674
|
-
{
|
|
3675
|
-
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
3676
|
-
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
3677
|
-
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
3678
|
-
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
3679
|
-
},
|
|
3680
|
-
{
|
|
3681
|
-
task: [Function],
|
|
3682
|
-
taskTimeout: [Function, Number],
|
|
3683
|
-
rangeTask: [Array, Function],
|
|
3684
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
3685
|
-
}
|
|
3686
|
-
);
|
|
3687
|
-
return this._lock(
|
|
3688
|
-
this.writeQueue,
|
|
3689
|
-
range,
|
|
3690
|
-
timeout,
|
|
3691
|
-
task,
|
|
3692
|
-
() => {
|
|
3693
|
-
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
3694
|
-
}
|
|
3695
|
-
);
|
|
3766
|
+
write(id, node) {
|
|
3767
|
+
this.node[id] = node;
|
|
3696
3768
|
}
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
* @param lockId - The unique identifier for the lock to release.
|
|
3700
|
-
*/
|
|
3701
|
-
readUnlock(lockId) {
|
|
3702
|
-
this._free(this.readings, lockId);
|
|
3769
|
+
delete(id) {
|
|
3770
|
+
delete this.node[id];
|
|
3703
3771
|
}
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3772
|
+
readHead() {
|
|
3773
|
+
if (this.head.root === null) {
|
|
3774
|
+
return null;
|
|
3775
|
+
}
|
|
3776
|
+
return this.head;
|
|
3777
|
+
}
|
|
3778
|
+
writeHead(head) {
|
|
3779
|
+
this.head = head;
|
|
3710
3780
|
}
|
|
3711
3781
|
};
|
|
3712
3782
|
|