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/cjs/index.cjs
CHANGED
|
@@ -105,6 +105,7 @@ var MVCCTransaction = class {
|
|
|
105
105
|
// delete 시 삭제 전 값 저장
|
|
106
106
|
originallyExisted;
|
|
107
107
|
// 트랜잭션 시작 시점에 디스크에 존재했던 키 (deleted 결과 필터링용)
|
|
108
|
+
bufferHistory = /* @__PURE__ */ new Map();
|
|
108
109
|
// Nested Transaction Properties
|
|
109
110
|
parent;
|
|
110
111
|
localVersion;
|
|
@@ -159,26 +160,45 @@ var MVCCTransaction = class {
|
|
|
159
160
|
return false;
|
|
160
161
|
}
|
|
161
162
|
// --- Internal buffer manipulation helpers ---
|
|
162
|
-
|
|
163
|
-
this.
|
|
163
|
+
_recordHistory(key) {
|
|
164
|
+
const existsInWriteBuffer = this.writeBuffer.has(key);
|
|
165
|
+
const existsInDeleteBuffer = this.deleteBuffer.has(key);
|
|
166
|
+
const currentVer = this.keyVersions.get(key);
|
|
167
|
+
if (currentVer !== void 0) {
|
|
168
|
+
if (!this.bufferHistory.has(key)) this.bufferHistory.set(key, []);
|
|
169
|
+
this.bufferHistory.get(key).push({
|
|
170
|
+
value: existsInWriteBuffer ? this.writeBuffer.get(key) : this.deletedValues.get(key) ?? null,
|
|
171
|
+
exists: existsInWriteBuffer || !existsInDeleteBuffer,
|
|
172
|
+
version: currentVer
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
_bufferCreate(key, value, version) {
|
|
177
|
+
if (version === void 0) this.localVersion++;
|
|
178
|
+
const targetVersion = version ?? this.localVersion;
|
|
179
|
+
this._recordHistory(key);
|
|
164
180
|
this.writeBuffer.set(key, value);
|
|
165
181
|
this.createdKeys.add(key);
|
|
166
182
|
this.deleteBuffer.delete(key);
|
|
167
183
|
this.originallyExisted.delete(key);
|
|
168
|
-
this.keyVersions.set(key,
|
|
184
|
+
this.keyVersions.set(key, targetVersion);
|
|
169
185
|
}
|
|
170
|
-
_bufferWrite(key, value) {
|
|
171
|
-
this.localVersion++;
|
|
186
|
+
_bufferWrite(key, value, version) {
|
|
187
|
+
if (version === void 0) this.localVersion++;
|
|
188
|
+
const targetVersion = version ?? this.localVersion;
|
|
189
|
+
this._recordHistory(key);
|
|
172
190
|
this.writeBuffer.set(key, value);
|
|
173
191
|
this.deleteBuffer.delete(key);
|
|
174
|
-
this.keyVersions.set(key,
|
|
192
|
+
this.keyVersions.set(key, targetVersion);
|
|
175
193
|
}
|
|
176
|
-
_bufferDelete(key) {
|
|
177
|
-
this.localVersion++;
|
|
194
|
+
_bufferDelete(key, version) {
|
|
195
|
+
if (version === void 0) this.localVersion++;
|
|
196
|
+
const targetVersion = version ?? this.localVersion;
|
|
197
|
+
this._recordHistory(key);
|
|
178
198
|
this.deleteBuffer.add(key);
|
|
179
199
|
this.writeBuffer.delete(key);
|
|
180
200
|
this.createdKeys.delete(key);
|
|
181
|
-
this.keyVersions.set(key,
|
|
201
|
+
this.keyVersions.set(key, targetVersion);
|
|
182
202
|
}
|
|
183
203
|
/**
|
|
184
204
|
* Returns the entries that will be created, updated, and deleted by this transaction.
|
|
@@ -255,7 +275,9 @@ var MVCCTransaction = class {
|
|
|
255
275
|
break;
|
|
256
276
|
}
|
|
257
277
|
}
|
|
258
|
-
if (latestInSnapshotIdx
|
|
278
|
+
if (latestInSnapshotIdx === versions.length - 1) {
|
|
279
|
+
this.versionIndex.delete(key);
|
|
280
|
+
} else if (latestInSnapshotIdx > 0) {
|
|
259
281
|
versions.splice(0, latestInSnapshotIdx);
|
|
260
282
|
}
|
|
261
283
|
}
|
|
@@ -312,27 +334,68 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
312
334
|
if (this.committed) throw new Error("Transaction already committed");
|
|
313
335
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
314
336
|
if (this.deleteBuffer.has(key)) return null;
|
|
315
|
-
|
|
337
|
+
if (this.parent) {
|
|
338
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
339
|
+
}
|
|
340
|
+
return this._diskRead(key, this.snapshotVersion);
|
|
316
341
|
}
|
|
317
342
|
exists(key) {
|
|
318
343
|
if (this.committed) throw new Error("Transaction already committed");
|
|
319
344
|
if (this.deleteBuffer.has(key)) return false;
|
|
320
345
|
if (this.writeBuffer.has(key)) return true;
|
|
321
|
-
|
|
346
|
+
if (this.parent) {
|
|
347
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
348
|
+
}
|
|
349
|
+
return this._diskExists(key, this.snapshotVersion);
|
|
350
|
+
}
|
|
351
|
+
_existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
352
|
+
if (this.writeBuffer.has(key)) {
|
|
353
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
354
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (this.deleteBuffer.has(key)) {
|
|
359
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
360
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
const history = this.bufferHistory.get(key);
|
|
365
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
366
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
367
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
368
|
+
return history[i].exists;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (this.parent) {
|
|
373
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
374
|
+
} else {
|
|
375
|
+
return this._diskExists(key, snapshotVersion);
|
|
376
|
+
}
|
|
322
377
|
}
|
|
323
378
|
_readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
324
379
|
if (this.writeBuffer.has(key)) {
|
|
325
380
|
const keyModVersion = this.keyVersions.get(key);
|
|
326
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
381
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
327
382
|
return this.writeBuffer.get(key);
|
|
328
383
|
}
|
|
329
384
|
}
|
|
330
385
|
if (this.deleteBuffer.has(key)) {
|
|
331
386
|
const keyModVersion = this.keyVersions.get(key);
|
|
332
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
387
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
333
388
|
return null;
|
|
334
389
|
}
|
|
335
390
|
}
|
|
391
|
+
const history = this.bufferHistory.get(key);
|
|
392
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
393
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
394
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
395
|
+
return history[i].exists ? history[i].value : null;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
336
399
|
if (this.parent) {
|
|
337
400
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
338
401
|
} else {
|
|
@@ -342,54 +405,22 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
342
405
|
commit(label) {
|
|
343
406
|
const { created, updated, deleted } = this.getResultEntries();
|
|
344
407
|
if (this.committed) {
|
|
345
|
-
return {
|
|
346
|
-
label,
|
|
347
|
-
success: false,
|
|
348
|
-
error: "Transaction already committed",
|
|
349
|
-
conflict: void 0,
|
|
350
|
-
created,
|
|
351
|
-
updated,
|
|
352
|
-
deleted
|
|
353
|
-
};
|
|
408
|
+
return { label, success: false, error: "Transaction already committed", conflict: void 0, created, updated, deleted };
|
|
354
409
|
}
|
|
355
410
|
if (this.hasCommittedAncestor()) {
|
|
356
|
-
return {
|
|
357
|
-
label,
|
|
358
|
-
success: false,
|
|
359
|
-
error: "Ancestor transaction already committed",
|
|
360
|
-
conflict: void 0,
|
|
361
|
-
created,
|
|
362
|
-
updated,
|
|
363
|
-
deleted
|
|
364
|
-
};
|
|
411
|
+
return { label, success: false, error: "Ancestor transaction already committed", conflict: void 0, created, updated, deleted };
|
|
365
412
|
}
|
|
366
413
|
if (this.parent) {
|
|
367
414
|
const failure = this.parent._merge(this);
|
|
368
415
|
if (failure) {
|
|
369
|
-
return {
|
|
370
|
-
label,
|
|
371
|
-
success: false,
|
|
372
|
-
error: failure.error,
|
|
373
|
-
conflict: failure.conflict,
|
|
374
|
-
created,
|
|
375
|
-
updated,
|
|
376
|
-
deleted
|
|
377
|
-
};
|
|
416
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created, updated, deleted };
|
|
378
417
|
}
|
|
379
418
|
this.committed = true;
|
|
380
419
|
} else {
|
|
381
420
|
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
382
421
|
const failure = this._merge(this);
|
|
383
422
|
if (failure) {
|
|
384
|
-
return {
|
|
385
|
-
label,
|
|
386
|
-
success: false,
|
|
387
|
-
error: failure.error,
|
|
388
|
-
conflict: failure.conflict,
|
|
389
|
-
created: [],
|
|
390
|
-
updated: [],
|
|
391
|
-
deleted: []
|
|
392
|
-
};
|
|
423
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created: [], updated: [], deleted: [] };
|
|
393
424
|
}
|
|
394
425
|
this.writeBuffer.clear();
|
|
395
426
|
this.deleteBuffer.clear();
|
|
@@ -397,17 +428,12 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
397
428
|
this.deletedValues.clear();
|
|
398
429
|
this.originallyExisted.clear();
|
|
399
430
|
this.keyVersions.clear();
|
|
431
|
+
this.bufferHistory.clear();
|
|
400
432
|
this.localVersion = 0;
|
|
401
433
|
this.snapshotVersion = this.version;
|
|
402
434
|
}
|
|
403
435
|
}
|
|
404
|
-
return {
|
|
405
|
-
label,
|
|
406
|
-
success: true,
|
|
407
|
-
created,
|
|
408
|
-
updated,
|
|
409
|
-
deleted
|
|
410
|
-
};
|
|
436
|
+
return { label, success: true, created, updated, deleted };
|
|
411
437
|
}
|
|
412
438
|
_merge(child) {
|
|
413
439
|
if (this.parent) {
|
|
@@ -416,11 +442,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
416
442
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
417
443
|
return {
|
|
418
444
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
419
|
-
conflict: {
|
|
420
|
-
key,
|
|
421
|
-
parent: this.read(key),
|
|
422
|
-
child: child.read(key)
|
|
423
|
-
}
|
|
445
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
424
446
|
};
|
|
425
447
|
}
|
|
426
448
|
}
|
|
@@ -429,40 +451,26 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
429
451
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
430
452
|
return {
|
|
431
453
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
432
|
-
conflict: {
|
|
433
|
-
key,
|
|
434
|
-
parent: this.read(key),
|
|
435
|
-
child: child.read(key)
|
|
436
|
-
}
|
|
454
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
437
455
|
};
|
|
438
456
|
}
|
|
439
457
|
}
|
|
440
|
-
const
|
|
441
|
-
for (const key of child.writeBuffer
|
|
442
|
-
|
|
443
|
-
this.
|
|
444
|
-
this.
|
|
445
|
-
if (child.createdKeys.has(key)) {
|
|
446
|
-
this.createdKeys.add(key);
|
|
447
|
-
}
|
|
458
|
+
const mergeVersion = ++this.localVersion;
|
|
459
|
+
for (const [key, value] of child.writeBuffer) {
|
|
460
|
+
const wasCreated = child.createdKeys.has(key);
|
|
461
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
462
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
448
463
|
}
|
|
449
464
|
for (const key of child.deleteBuffer) {
|
|
450
|
-
this.deleteBuffer.add(key);
|
|
451
|
-
this.writeBuffer.delete(key);
|
|
452
|
-
this.createdKeys.delete(key);
|
|
453
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
454
465
|
const deletedValue = child.deletedValues.get(key);
|
|
455
|
-
if (deletedValue !== void 0)
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
if (child.originallyExisted.has(key)) {
|
|
466
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
467
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
459
468
|
this.originallyExisted.add(key);
|
|
460
469
|
}
|
|
470
|
+
this._bufferDelete(key, mergeVersion);
|
|
461
471
|
}
|
|
462
|
-
this.localVersion = newLocalVersion;
|
|
463
472
|
this.root.activeTransactions.delete(child);
|
|
464
473
|
} else {
|
|
465
|
-
const newVersion = this.version + 1;
|
|
466
474
|
if (child !== this) {
|
|
467
475
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
468
476
|
for (const key of modifiedKeys) {
|
|
@@ -472,58 +480,53 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
472
480
|
if (lastVer > child.snapshotVersion) {
|
|
473
481
|
return {
|
|
474
482
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
475
|
-
conflict: {
|
|
476
|
-
key,
|
|
477
|
-
parent: this.read(key),
|
|
478
|
-
child: child.read(key)
|
|
479
|
-
}
|
|
483
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
480
484
|
};
|
|
481
485
|
}
|
|
482
486
|
}
|
|
487
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
488
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
489
|
+
return {
|
|
490
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
491
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
492
|
+
};
|
|
493
|
+
}
|
|
483
494
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
for (const key of child.deleteBuffer) {
|
|
493
|
-
this.deleteBuffer.add(key);
|
|
494
|
-
this.writeBuffer.delete(key);
|
|
495
|
-
this.createdKeys.delete(key);
|
|
496
|
-
const deletedValue = child.deletedValues.get(key);
|
|
497
|
-
if (deletedValue !== void 0) {
|
|
498
|
-
this.deletedValues.set(key, deletedValue);
|
|
495
|
+
const mergeVersion = ++this.localVersion;
|
|
496
|
+
for (const [key, value] of child.writeBuffer) {
|
|
497
|
+
const wasCreated = child.createdKeys.has(key);
|
|
498
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
499
|
+
this.originallyExisted.add(key);
|
|
500
|
+
}
|
|
501
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
502
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
499
503
|
}
|
|
500
|
-
|
|
501
|
-
|
|
504
|
+
for (const key of child.deleteBuffer) {
|
|
505
|
+
const deletedValue = child.deletedValues.get(key);
|
|
506
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
507
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
508
|
+
this.originallyExisted.add(key);
|
|
509
|
+
}
|
|
510
|
+
this._bufferDelete(key, mergeVersion);
|
|
502
511
|
}
|
|
512
|
+
this.root.activeTransactions.delete(child);
|
|
513
|
+
} else {
|
|
514
|
+
const newVersion = this.version + 1;
|
|
515
|
+
for (const [key, value] of this.writeBuffer) this._diskWrite(key, value, newVersion);
|
|
516
|
+
for (const key of this.deleteBuffer) this._diskDelete(key, newVersion);
|
|
517
|
+
this.version = newVersion;
|
|
518
|
+
this._cleanupDeletedCache();
|
|
503
519
|
}
|
|
504
|
-
for (const [key, value] of child.writeBuffer) {
|
|
505
|
-
this._diskWrite(key, value, newVersion);
|
|
506
|
-
}
|
|
507
|
-
for (const key of child.deleteBuffer) {
|
|
508
|
-
this._diskDelete(key, newVersion);
|
|
509
|
-
}
|
|
510
|
-
this.version = newVersion;
|
|
511
|
-
this.root.activeTransactions.delete(child);
|
|
512
|
-
this._cleanupDeletedCache();
|
|
513
520
|
}
|
|
514
521
|
return null;
|
|
515
522
|
}
|
|
516
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
517
523
|
_diskWrite(key, value, version) {
|
|
518
524
|
const strategy = this.strategy;
|
|
519
525
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
520
526
|
if (strategy.exists(key)) {
|
|
521
527
|
const currentVal = strategy.read(key);
|
|
522
528
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
523
|
-
this.deletedCache.get(key).push({
|
|
524
|
-
value: currentVal,
|
|
525
|
-
deletedAtVersion: version
|
|
526
|
-
});
|
|
529
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
527
530
|
}
|
|
528
531
|
strategy.write(key, value);
|
|
529
532
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -539,9 +542,8 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
539
542
|
let targetVerObj = null;
|
|
540
543
|
let nextVerObj = null;
|
|
541
544
|
for (const v of versions) {
|
|
542
|
-
if (v.version <= snapshotVersion)
|
|
543
|
-
|
|
544
|
-
} else {
|
|
545
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
546
|
+
else {
|
|
545
547
|
nextVerObj = v;
|
|
546
548
|
break;
|
|
547
549
|
}
|
|
@@ -558,6 +560,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
558
560
|
}
|
|
559
561
|
if (!targetVerObj.exists) return null;
|
|
560
562
|
if (!nextVerObj) {
|
|
563
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
561
564
|
return strategy.read(key);
|
|
562
565
|
}
|
|
563
566
|
const cached = this.deletedCache.get(key);
|
|
@@ -575,14 +578,24 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
575
578
|
return strategy.exists(key);
|
|
576
579
|
}
|
|
577
580
|
let targetVerObj = null;
|
|
581
|
+
let nextVerObj = null;
|
|
578
582
|
for (const v of versions) {
|
|
579
|
-
if (v.version <= snapshotVersion)
|
|
580
|
-
|
|
581
|
-
|
|
583
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
584
|
+
else {
|
|
585
|
+
nextVerObj = v;
|
|
582
586
|
break;
|
|
583
587
|
}
|
|
584
588
|
}
|
|
585
|
-
if (!targetVerObj)
|
|
589
|
+
if (!targetVerObj) {
|
|
590
|
+
if (nextVerObj) {
|
|
591
|
+
const cached = this.deletedCache.get(key);
|
|
592
|
+
if (cached) {
|
|
593
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
594
|
+
if (match) return true;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
586
599
|
return targetVerObj.exists;
|
|
587
600
|
}
|
|
588
601
|
_diskDelete(key, snapshotVersion) {
|
|
@@ -591,12 +604,9 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
591
604
|
if (strategy.exists(key)) {
|
|
592
605
|
const currentVal = strategy.read(key);
|
|
593
606
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
594
|
-
this.deletedCache.get(key).push({
|
|
595
|
-
|
|
596
|
-
deletedAtVersion: snapshotVersion
|
|
597
|
-
});
|
|
607
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
608
|
+
strategy.delete(key);
|
|
598
609
|
}
|
|
599
|
-
strategy.delete(key);
|
|
600
610
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
601
611
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
602
612
|
}
|
|
@@ -917,84 +927,93 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
917
927
|
if (this.committed) throw new Error("Transaction already committed");
|
|
918
928
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
919
929
|
if (this.deleteBuffer.has(key)) return null;
|
|
920
|
-
|
|
930
|
+
if (this.parent) {
|
|
931
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
932
|
+
}
|
|
933
|
+
return await this._diskRead(key, this.snapshotVersion);
|
|
921
934
|
}
|
|
922
935
|
async exists(key) {
|
|
923
936
|
if (this.committed) throw new Error("Transaction already committed");
|
|
924
937
|
if (this.deleteBuffer.has(key)) return false;
|
|
925
938
|
if (this.writeBuffer.has(key)) return true;
|
|
926
|
-
|
|
939
|
+
if (this.parent) {
|
|
940
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
941
|
+
}
|
|
942
|
+
return await this._diskExists(key, this.snapshotVersion);
|
|
943
|
+
}
|
|
944
|
+
async _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
945
|
+
if (this.writeBuffer.has(key)) {
|
|
946
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
947
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
948
|
+
return true;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
if (this.deleteBuffer.has(key)) {
|
|
952
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
953
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
954
|
+
return false;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
const history = this.bufferHistory.get(key);
|
|
958
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
959
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
960
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
961
|
+
return history[i].exists;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
if (this.parent) {
|
|
966
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
967
|
+
} else {
|
|
968
|
+
return await this._diskExists(key, snapshotVersion);
|
|
969
|
+
}
|
|
927
970
|
}
|
|
928
971
|
async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
929
972
|
if (this.writeBuffer.has(key)) {
|
|
930
973
|
const keyModVersion = this.keyVersions.get(key);
|
|
931
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
974
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
932
975
|
return this.writeBuffer.get(key);
|
|
933
976
|
}
|
|
934
977
|
}
|
|
935
978
|
if (this.deleteBuffer.has(key)) {
|
|
936
979
|
const keyModVersion = this.keyVersions.get(key);
|
|
937
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
980
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
938
981
|
return null;
|
|
939
982
|
}
|
|
940
983
|
}
|
|
984
|
+
const history = this.bufferHistory.get(key);
|
|
985
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
986
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
987
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
988
|
+
return history[i].exists ? history[i].value : null;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
941
992
|
if (this.parent) {
|
|
942
993
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
943
994
|
} else {
|
|
944
|
-
return this._diskRead(key, snapshotVersion);
|
|
995
|
+
return await this._diskRead(key, snapshotVersion);
|
|
945
996
|
}
|
|
946
997
|
}
|
|
947
998
|
async commit(label) {
|
|
948
999
|
const { created, updated, deleted } = this.getResultEntries();
|
|
949
1000
|
if (this.committed) {
|
|
950
|
-
return {
|
|
951
|
-
label,
|
|
952
|
-
success: false,
|
|
953
|
-
error: "Transaction already committed",
|
|
954
|
-
conflict: void 0,
|
|
955
|
-
created,
|
|
956
|
-
updated,
|
|
957
|
-
deleted
|
|
958
|
-
};
|
|
1001
|
+
return { label, success: false, error: "Transaction already committed", conflict: void 0, created, updated, deleted };
|
|
959
1002
|
}
|
|
960
1003
|
if (this.hasCommittedAncestor()) {
|
|
961
|
-
return {
|
|
962
|
-
label,
|
|
963
|
-
success: false,
|
|
964
|
-
error: "Ancestor transaction already committed",
|
|
965
|
-
conflict: void 0,
|
|
966
|
-
created,
|
|
967
|
-
updated,
|
|
968
|
-
deleted
|
|
969
|
-
};
|
|
1004
|
+
return { label, success: false, error: "Ancestor transaction already committed", conflict: void 0, created, updated, deleted };
|
|
970
1005
|
}
|
|
971
1006
|
if (this.parent) {
|
|
972
1007
|
const failure = await this.parent._merge(this);
|
|
973
1008
|
if (failure) {
|
|
974
|
-
return {
|
|
975
|
-
label,
|
|
976
|
-
success: false,
|
|
977
|
-
error: failure.error,
|
|
978
|
-
conflict: failure.conflict,
|
|
979
|
-
created,
|
|
980
|
-
updated,
|
|
981
|
-
deleted
|
|
982
|
-
};
|
|
1009
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created, updated, deleted };
|
|
983
1010
|
}
|
|
984
1011
|
this.committed = true;
|
|
985
1012
|
} else {
|
|
986
1013
|
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
987
1014
|
const failure = await this._merge(this);
|
|
988
1015
|
if (failure) {
|
|
989
|
-
return {
|
|
990
|
-
label,
|
|
991
|
-
success: false,
|
|
992
|
-
error: failure.error,
|
|
993
|
-
conflict: failure.conflict,
|
|
994
|
-
created: [],
|
|
995
|
-
updated: [],
|
|
996
|
-
deleted: []
|
|
997
|
-
};
|
|
1016
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created: [], updated: [], deleted: [] };
|
|
998
1017
|
}
|
|
999
1018
|
this.writeBuffer.clear();
|
|
1000
1019
|
this.deleteBuffer.clear();
|
|
@@ -1002,17 +1021,12 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1002
1021
|
this.deletedValues.clear();
|
|
1003
1022
|
this.originallyExisted.clear();
|
|
1004
1023
|
this.keyVersions.clear();
|
|
1024
|
+
this.bufferHistory.clear();
|
|
1005
1025
|
this.localVersion = 0;
|
|
1006
1026
|
this.snapshotVersion = this.version;
|
|
1007
1027
|
}
|
|
1008
1028
|
}
|
|
1009
|
-
return {
|
|
1010
|
-
label,
|
|
1011
|
-
success: true,
|
|
1012
|
-
created,
|
|
1013
|
-
updated,
|
|
1014
|
-
deleted
|
|
1015
|
-
};
|
|
1029
|
+
return { label, success: true, created, updated, deleted };
|
|
1016
1030
|
}
|
|
1017
1031
|
async _merge(child) {
|
|
1018
1032
|
return this.writeLock(async () => {
|
|
@@ -1022,11 +1036,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1022
1036
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1023
1037
|
return {
|
|
1024
1038
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
1025
|
-
conflict: {
|
|
1026
|
-
key,
|
|
1027
|
-
parent: await this.read(key),
|
|
1028
|
-
child: await child.read(key)
|
|
1029
|
-
}
|
|
1039
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1030
1040
|
};
|
|
1031
1041
|
}
|
|
1032
1042
|
}
|
|
@@ -1035,41 +1045,27 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1035
1045
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1036
1046
|
return {
|
|
1037
1047
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
1038
|
-
conflict: {
|
|
1039
|
-
key,
|
|
1040
|
-
parent: await this.read(key),
|
|
1041
|
-
child: await child.read(key)
|
|
1042
|
-
}
|
|
1048
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1043
1049
|
};
|
|
1044
1050
|
}
|
|
1045
1051
|
}
|
|
1046
|
-
const
|
|
1047
|
-
for (const key of child.writeBuffer
|
|
1048
|
-
|
|
1049
|
-
this.
|
|
1050
|
-
this.
|
|
1051
|
-
if (child.createdKeys.has(key)) {
|
|
1052
|
-
this.createdKeys.add(key);
|
|
1053
|
-
}
|
|
1052
|
+
const mergeVersion = ++this.localVersion;
|
|
1053
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1054
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1055
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1056
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1054
1057
|
}
|
|
1055
1058
|
for (const key of child.deleteBuffer) {
|
|
1056
|
-
this.deleteBuffer.add(key);
|
|
1057
|
-
this.writeBuffer.delete(key);
|
|
1058
|
-
this.createdKeys.delete(key);
|
|
1059
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
1060
1059
|
const deletedValue = child.deletedValues.get(key);
|
|
1061
|
-
if (deletedValue !== void 0)
|
|
1062
|
-
|
|
1063
|
-
}
|
|
1064
|
-
if (child.originallyExisted.has(key)) {
|
|
1060
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1061
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1065
1062
|
this.originallyExisted.add(key);
|
|
1066
1063
|
}
|
|
1064
|
+
this._bufferDelete(key, mergeVersion);
|
|
1067
1065
|
}
|
|
1068
|
-
this.localVersion = newLocalVersion;
|
|
1069
1066
|
this.root.activeTransactions.delete(child);
|
|
1070
1067
|
return null;
|
|
1071
1068
|
} else {
|
|
1072
|
-
const newVersion = this.version + 1;
|
|
1073
1069
|
if (child !== this) {
|
|
1074
1070
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
1075
1071
|
for (const key of modifiedKeys) {
|
|
@@ -1079,59 +1075,54 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1079
1075
|
if (lastVer > child.snapshotVersion) {
|
|
1080
1076
|
return {
|
|
1081
1077
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
1082
|
-
conflict: {
|
|
1083
|
-
key,
|
|
1084
|
-
parent: await this.read(key),
|
|
1085
|
-
child: await child.read(key)
|
|
1086
|
-
}
|
|
1078
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1087
1079
|
};
|
|
1088
1080
|
}
|
|
1089
1081
|
}
|
|
1082
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
1083
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1084
|
+
return {
|
|
1085
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
1086
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1090
1089
|
}
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
for (const key of child.deleteBuffer) {
|
|
1100
|
-
this.deleteBuffer.add(key);
|
|
1101
|
-
this.writeBuffer.delete(key);
|
|
1102
|
-
this.createdKeys.delete(key);
|
|
1103
|
-
const deletedValue = child.deletedValues.get(key);
|
|
1104
|
-
if (deletedValue !== void 0) {
|
|
1105
|
-
this.deletedValues.set(key, deletedValue);
|
|
1090
|
+
const mergeVersion = ++this.localVersion;
|
|
1091
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1092
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1093
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1094
|
+
this.originallyExisted.add(key);
|
|
1095
|
+
}
|
|
1096
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1097
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1106
1098
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1099
|
+
for (const key of child.deleteBuffer) {
|
|
1100
|
+
const deletedValue = child.deletedValues.get(key);
|
|
1101
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1102
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1103
|
+
this.originallyExisted.add(key);
|
|
1104
|
+
}
|
|
1105
|
+
this._bufferDelete(key, mergeVersion);
|
|
1109
1106
|
}
|
|
1107
|
+
this.root.activeTransactions.delete(child);
|
|
1108
|
+
} else {
|
|
1109
|
+
const newVersion = this.version + 1;
|
|
1110
|
+
for (const [key, value] of this.writeBuffer) await this._diskWrite(key, value, newVersion);
|
|
1111
|
+
for (const key of this.deleteBuffer) await this._diskDelete(key, newVersion);
|
|
1112
|
+
this.version = newVersion;
|
|
1113
|
+
this._cleanupDeletedCache();
|
|
1110
1114
|
}
|
|
1111
|
-
for (const [key, value] of child.writeBuffer) {
|
|
1112
|
-
await this._diskWrite(key, value, newVersion);
|
|
1113
|
-
}
|
|
1114
|
-
for (const key of child.deleteBuffer) {
|
|
1115
|
-
await this._diskDelete(key, newVersion);
|
|
1116
|
-
}
|
|
1117
|
-
this.version = newVersion;
|
|
1118
|
-
this.root.activeTransactions.delete(child);
|
|
1119
|
-
this._cleanupDeletedCache();
|
|
1120
1115
|
return null;
|
|
1121
1116
|
}
|
|
1122
1117
|
});
|
|
1123
1118
|
}
|
|
1124
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
1125
1119
|
async _diskWrite(key, value, version) {
|
|
1126
1120
|
const strategy = this.strategy;
|
|
1127
1121
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1128
1122
|
if (await strategy.exists(key)) {
|
|
1129
1123
|
const currentVal = await strategy.read(key);
|
|
1130
1124
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1131
|
-
this.deletedCache.get(key).push({
|
|
1132
|
-
value: currentVal,
|
|
1133
|
-
deletedAtVersion: version
|
|
1134
|
-
});
|
|
1125
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
1135
1126
|
}
|
|
1136
1127
|
await strategy.write(key, value);
|
|
1137
1128
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -1142,14 +1133,13 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1142
1133
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1143
1134
|
const versions = this.versionIndex.get(key);
|
|
1144
1135
|
if (!versions) {
|
|
1145
|
-
return await strategy.exists(key) ? strategy.read(key) : null;
|
|
1136
|
+
return await strategy.exists(key) ? await strategy.read(key) : null;
|
|
1146
1137
|
}
|
|
1147
1138
|
let targetVerObj = null;
|
|
1148
1139
|
let nextVerObj = null;
|
|
1149
1140
|
for (const v of versions) {
|
|
1150
|
-
if (v.version <= snapshotVersion)
|
|
1151
|
-
|
|
1152
|
-
} else {
|
|
1141
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
1142
|
+
else {
|
|
1153
1143
|
nextVerObj = v;
|
|
1154
1144
|
break;
|
|
1155
1145
|
}
|
|
@@ -1166,6 +1156,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1166
1156
|
}
|
|
1167
1157
|
if (!targetVerObj.exists) return null;
|
|
1168
1158
|
if (!nextVerObj) {
|
|
1159
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
1169
1160
|
return strategy.read(key);
|
|
1170
1161
|
}
|
|
1171
1162
|
const cached = this.deletedCache.get(key);
|
|
@@ -1180,17 +1171,27 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1180
1171
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1181
1172
|
const versions = this.versionIndex.get(key);
|
|
1182
1173
|
if (!versions) {
|
|
1183
|
-
return strategy.exists(key);
|
|
1174
|
+
return await strategy.exists(key);
|
|
1184
1175
|
}
|
|
1185
1176
|
let targetVerObj = null;
|
|
1177
|
+
let nextVerObj = null;
|
|
1186
1178
|
for (const v of versions) {
|
|
1187
|
-
if (v.version <= snapshotVersion)
|
|
1188
|
-
|
|
1189
|
-
|
|
1179
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
1180
|
+
else {
|
|
1181
|
+
nextVerObj = v;
|
|
1190
1182
|
break;
|
|
1191
1183
|
}
|
|
1192
1184
|
}
|
|
1193
|
-
if (!targetVerObj)
|
|
1185
|
+
if (!targetVerObj) {
|
|
1186
|
+
if (nextVerObj) {
|
|
1187
|
+
const cached = this.deletedCache.get(key);
|
|
1188
|
+
if (cached) {
|
|
1189
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
1190
|
+
if (match) return true;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
return false;
|
|
1194
|
+
}
|
|
1194
1195
|
return targetVerObj.exists;
|
|
1195
1196
|
}
|
|
1196
1197
|
async _diskDelete(key, snapshotVersion) {
|
|
@@ -1199,12 +1200,9 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1199
1200
|
if (await strategy.exists(key)) {
|
|
1200
1201
|
const currentVal = await strategy.read(key);
|
|
1201
1202
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1202
|
-
this.deletedCache.get(key).push({
|
|
1203
|
-
|
|
1204
|
-
deletedAtVersion: snapshotVersion
|
|
1205
|
-
});
|
|
1203
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
1204
|
+
await strategy.delete(key);
|
|
1206
1205
|
}
|
|
1207
|
-
await strategy.delete(key);
|
|
1208
1206
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
1209
1207
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
1210
1208
|
}
|
|
@@ -1546,6 +1544,8 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1546
1544
|
option;
|
|
1547
1545
|
order;
|
|
1548
1546
|
rootId;
|
|
1547
|
+
isInitialized = false;
|
|
1548
|
+
isDestroyed = false;
|
|
1549
1549
|
verifierMap = {
|
|
1550
1550
|
gt: (nv, v) => this.comparator.isHigher(nv, v),
|
|
1551
1551
|
gte: (nv, v) => this.comparator.isHigher(nv, v) || this.comparator.isSame(nv, v),
|
|
@@ -1810,13 +1810,26 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1810
1810
|
getResultEntries() {
|
|
1811
1811
|
return this.mvcc.getResultEntries();
|
|
1812
1812
|
}
|
|
1813
|
+
_clearCache() {
|
|
1814
|
+
this._cachedRegexp.clear();
|
|
1815
|
+
this.nodes.clear();
|
|
1816
|
+
}
|
|
1813
1817
|
/**
|
|
1814
1818
|
* Clears all cached nodes.
|
|
1815
1819
|
* This method is useful for freeing up memory when the tree is no longer needed.
|
|
1816
1820
|
*/
|
|
1817
1821
|
clear() {
|
|
1818
|
-
this.
|
|
1819
|
-
|
|
1822
|
+
if (this.rootTx !== this) {
|
|
1823
|
+
throw new Error("Cannot call clear on a nested transaction");
|
|
1824
|
+
}
|
|
1825
|
+
this._clearInternal();
|
|
1826
|
+
}
|
|
1827
|
+
_clearInternal() {
|
|
1828
|
+
if (this.isDestroyed) {
|
|
1829
|
+
throw new Error("Transaction already destroyed");
|
|
1830
|
+
}
|
|
1831
|
+
this._clearCache();
|
|
1832
|
+
this.isDestroyed = true;
|
|
1820
1833
|
}
|
|
1821
1834
|
_binarySearchValues(values, target, usePrimary = false, upperBound = false) {
|
|
1822
1835
|
let low = 0;
|
|
@@ -2122,28 +2135,46 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2122
2135
|
}
|
|
2123
2136
|
}
|
|
2124
2137
|
init() {
|
|
2125
|
-
this.
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
const { root, order } = head;
|
|
2137
|
-
this.strategy.head = head;
|
|
2138
|
-
this.order = order;
|
|
2139
|
-
this._writeHead({
|
|
2140
|
-
root,
|
|
2141
|
-
order: this.order,
|
|
2142
|
-
data: this.strategy.head.data
|
|
2143
|
-
});
|
|
2138
|
+
if (this.rootTx !== this) {
|
|
2139
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
2140
|
+
}
|
|
2141
|
+
this._initInternal();
|
|
2142
|
+
}
|
|
2143
|
+
_initInternal() {
|
|
2144
|
+
if (this.isInitialized) {
|
|
2145
|
+
throw new Error("Transaction already initialized");
|
|
2146
|
+
}
|
|
2147
|
+
if (this.isDestroyed) {
|
|
2148
|
+
throw new Error("Transaction already destroyed");
|
|
2144
2149
|
}
|
|
2145
|
-
|
|
2146
|
-
|
|
2150
|
+
this.isInitialized = true;
|
|
2151
|
+
try {
|
|
2152
|
+
this._clearCache();
|
|
2153
|
+
const head = this._readHead();
|
|
2154
|
+
if (head === null) {
|
|
2155
|
+
this.order = this.strategy.order;
|
|
2156
|
+
const root = this._createNode(true, [], []);
|
|
2157
|
+
this._writeHead({
|
|
2158
|
+
root: root.id,
|
|
2159
|
+
order: this.order,
|
|
2160
|
+
data: this.strategy.head.data
|
|
2161
|
+
});
|
|
2162
|
+
} else {
|
|
2163
|
+
const { root, order } = head;
|
|
2164
|
+
this.strategy.head = head;
|
|
2165
|
+
this.order = order;
|
|
2166
|
+
this._writeHead({
|
|
2167
|
+
root,
|
|
2168
|
+
order: this.order,
|
|
2169
|
+
data: this.strategy.head.data
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
if (this.order < 3) {
|
|
2173
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
2174
|
+
}
|
|
2175
|
+
} catch (e) {
|
|
2176
|
+
this.isInitialized = false;
|
|
2177
|
+
throw e;
|
|
2147
2178
|
}
|
|
2148
2179
|
}
|
|
2149
2180
|
exists(key, value) {
|
|
@@ -2521,9 +2552,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2521
2552
|
commit(label) {
|
|
2522
2553
|
let result = this.mvcc.commit(label);
|
|
2523
2554
|
if (result.success) {
|
|
2524
|
-
|
|
2525
|
-
if (
|
|
2526
|
-
|
|
2555
|
+
const isRootTx = this.rootTx !== this;
|
|
2556
|
+
if (isRootTx) {
|
|
2557
|
+
result = this.rootTx.commit(label);
|
|
2558
|
+
if (result.success) {
|
|
2559
|
+
this.rootTx.rootId = this.rootId;
|
|
2560
|
+
}
|
|
2527
2561
|
}
|
|
2528
2562
|
if (result.success) {
|
|
2529
2563
|
for (const r of result.created) {
|
|
@@ -2606,7 +2640,7 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2606
2640
|
this.comparator,
|
|
2607
2641
|
this.option
|
|
2608
2642
|
);
|
|
2609
|
-
tx.
|
|
2643
|
+
tx._initInternal();
|
|
2610
2644
|
return tx;
|
|
2611
2645
|
}
|
|
2612
2646
|
insert(key, value) {
|
|
@@ -2616,7 +2650,6 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2616
2650
|
if (!result.success) {
|
|
2617
2651
|
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2618
2652
|
}
|
|
2619
|
-
this.rootId = tx.getRootId();
|
|
2620
2653
|
}
|
|
2621
2654
|
delete(key, value) {
|
|
2622
2655
|
const tx = this.createTransaction();
|
|
@@ -2625,244 +2658,512 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2625
2658
|
if (!result.success) {
|
|
2626
2659
|
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2627
2660
|
}
|
|
2628
|
-
this.rootId = tx.getRootId();
|
|
2629
2661
|
}
|
|
2630
2662
|
};
|
|
2631
2663
|
|
|
2632
|
-
//
|
|
2633
|
-
var
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
comparator,
|
|
2641
|
-
option
|
|
2642
|
-
);
|
|
2664
|
+
// node_modules/ryoiki/dist/esm/index.mjs
|
|
2665
|
+
var Ryoiki2 = class _Ryoiki2 {
|
|
2666
|
+
readings;
|
|
2667
|
+
writings;
|
|
2668
|
+
readQueue;
|
|
2669
|
+
writeQueue;
|
|
2670
|
+
static async CatchError(promise) {
|
|
2671
|
+
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
2643
2672
|
}
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2673
|
+
static IsRangeOverlap(a, b) {
|
|
2674
|
+
const [start1, end1] = a;
|
|
2675
|
+
const [start2, end2] = b;
|
|
2676
|
+
if (end1 <= start2 || end2 <= start1) {
|
|
2677
|
+
return false;
|
|
2647
2678
|
}
|
|
2648
|
-
return
|
|
2679
|
+
return true;
|
|
2680
|
+
}
|
|
2681
|
+
static ERR_ALREADY_EXISTS(lockId) {
|
|
2682
|
+
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
2683
|
+
}
|
|
2684
|
+
static ERR_NOT_EXISTS(lockId) {
|
|
2685
|
+
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
2686
|
+
}
|
|
2687
|
+
static ERR_TIMEOUT(lockId, timeout) {
|
|
2688
|
+
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
2649
2689
|
}
|
|
2650
2690
|
/**
|
|
2651
|
-
*
|
|
2691
|
+
* Constructs a new instance of the Ryoiki class.
|
|
2652
2692
|
*/
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
values,
|
|
2659
|
-
leaf,
|
|
2660
|
-
parent,
|
|
2661
|
-
next,
|
|
2662
|
-
prev
|
|
2663
|
-
};
|
|
2664
|
-
await this.mvcc.create(id, node);
|
|
2665
|
-
this.nodes.set(id, node);
|
|
2666
|
-
return node;
|
|
2693
|
+
constructor() {
|
|
2694
|
+
this.readings = /* @__PURE__ */ new Map();
|
|
2695
|
+
this.writings = /* @__PURE__ */ new Map();
|
|
2696
|
+
this.readQueue = /* @__PURE__ */ new Map();
|
|
2697
|
+
this.writeQueue = /* @__PURE__ */ new Map();
|
|
2667
2698
|
}
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2699
|
+
/**
|
|
2700
|
+
* Creates a range based on a start value and length.
|
|
2701
|
+
* @param start - The starting value of the range.
|
|
2702
|
+
* @param length - The length of the range.
|
|
2703
|
+
* @returns A range tuple [start, start + length].
|
|
2704
|
+
*/
|
|
2705
|
+
range(start, length) {
|
|
2706
|
+
return [start, start + length];
|
|
2671
2707
|
}
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
this.nodes.delete(node.id);
|
|
2708
|
+
rangeOverlapping(tasks, range) {
|
|
2709
|
+
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
2675
2710
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
const head = await this.mvcc.read("__HEAD__");
|
|
2681
|
-
return head ?? null;
|
|
2682
|
-
}
|
|
2683
|
-
async _writeHead(head) {
|
|
2684
|
-
if (!await this.mvcc.exists("__HEAD__")) {
|
|
2685
|
-
await this.mvcc.create("__HEAD__", head);
|
|
2686
|
-
} else {
|
|
2687
|
-
await this.mvcc.write("__HEAD__", head);
|
|
2688
|
-
}
|
|
2689
|
-
this.nodes.set("__HEAD__", head);
|
|
2690
|
-
this.rootId = head.root;
|
|
2711
|
+
isSameRange(a, b) {
|
|
2712
|
+
const [a1, a2] = a;
|
|
2713
|
+
const [b1, b2] = b;
|
|
2714
|
+
return a1 === b1 && a2 === b2;
|
|
2691
2715
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
2697
|
-
const keys = node.keys[i];
|
|
2698
|
-
if (keys.includes(key)) {
|
|
2699
|
-
break;
|
|
2700
|
-
}
|
|
2701
|
-
keys.push(key);
|
|
2702
|
-
await this._updateNode(node);
|
|
2703
|
-
return;
|
|
2704
|
-
} else if (this.comparator.isLower(value, nValue)) {
|
|
2705
|
-
node.values.splice(i, 0, value);
|
|
2706
|
-
node.keys.splice(i, 0, [key]);
|
|
2707
|
-
await this._updateNode(node);
|
|
2708
|
-
return;
|
|
2709
|
-
} else if (i + 1 === node.values.length) {
|
|
2710
|
-
node.values.push(value);
|
|
2711
|
-
node.keys.push([key]);
|
|
2712
|
-
await this._updateNode(node);
|
|
2713
|
-
return;
|
|
2714
|
-
}
|
|
2716
|
+
fetchUnitAndRun(queue, workspaces) {
|
|
2717
|
+
for (const [id, unit] of queue) {
|
|
2718
|
+
if (!unit.condition()) {
|
|
2719
|
+
continue;
|
|
2715
2720
|
}
|
|
2716
|
-
|
|
2717
|
-
node.values = [value];
|
|
2718
|
-
node.keys = [[key]];
|
|
2719
|
-
await this._updateNode(node);
|
|
2720
|
-
return;
|
|
2721
|
+
this._alloc(queue, workspaces, id);
|
|
2721
2722
|
}
|
|
2722
2723
|
}
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
node.parent = root.id;
|
|
2728
|
-
pointer.parent = root.id;
|
|
2729
|
-
if (pointer.leaf) {
|
|
2730
|
-
node.next = pointer.id;
|
|
2731
|
-
pointer.prev = node.id;
|
|
2732
|
-
}
|
|
2733
|
-
await this._writeHead({
|
|
2734
|
-
root: root.id,
|
|
2735
|
-
order: this.order,
|
|
2736
|
-
data: this.strategy.head.data
|
|
2737
|
-
});
|
|
2738
|
-
await this._updateNode(node);
|
|
2739
|
-
await this._updateNode(pointer);
|
|
2740
|
-
return;
|
|
2741
|
-
}
|
|
2742
|
-
const parentNode = await this.getNode(node.parent);
|
|
2743
|
-
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2744
|
-
if (nodeIndex === -1) {
|
|
2745
|
-
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
2746
|
-
}
|
|
2747
|
-
parentNode.values.splice(nodeIndex, 0, value);
|
|
2748
|
-
parentNode.keys.splice(nodeIndex + 1, 0, pointer.id);
|
|
2749
|
-
pointer.parent = parentNode.id;
|
|
2750
|
-
if (pointer.leaf) {
|
|
2751
|
-
const leftSibling = node;
|
|
2752
|
-
const oldNextId = leftSibling.next;
|
|
2753
|
-
pointer.prev = leftSibling.id;
|
|
2754
|
-
pointer.next = oldNextId;
|
|
2755
|
-
leftSibling.next = pointer.id;
|
|
2756
|
-
await this._updateNode(leftSibling);
|
|
2757
|
-
if (oldNextId) {
|
|
2758
|
-
const oldNext = await this.getNode(oldNextId);
|
|
2759
|
-
oldNext.prev = pointer.id;
|
|
2760
|
-
await this._updateNode(oldNext);
|
|
2761
|
-
}
|
|
2762
|
-
}
|
|
2763
|
-
await this._updateNode(parentNode);
|
|
2764
|
-
await this._updateNode(pointer);
|
|
2765
|
-
if (parentNode.keys.length > this.order) {
|
|
2766
|
-
const parentPointer = await this._createNode(false, [], []);
|
|
2767
|
-
parentPointer.parent = parentNode.parent;
|
|
2768
|
-
const mid = Math.ceil(this.order / 2) - 1;
|
|
2769
|
-
parentPointer.values = parentNode.values.slice(mid + 1);
|
|
2770
|
-
parentPointer.keys = parentNode.keys.slice(mid + 1);
|
|
2771
|
-
const midValue = parentNode.values[mid];
|
|
2772
|
-
parentNode.values = parentNode.values.slice(0, mid);
|
|
2773
|
-
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2774
|
-
for (const k of parentNode.keys) {
|
|
2775
|
-
const n = await this.getNode(k);
|
|
2776
|
-
n.parent = parentNode.id;
|
|
2777
|
-
await this._updateNode(n);
|
|
2778
|
-
}
|
|
2779
|
-
for (const k of parentPointer.keys) {
|
|
2780
|
-
const n = await this.getNode(k);
|
|
2781
|
-
n.parent = parentPointer.id;
|
|
2782
|
-
await this._updateNode(n);
|
|
2724
|
+
_handleOverload(args, handlers, argPatterns) {
|
|
2725
|
+
for (const [key, pattern] of Object.entries(argPatterns)) {
|
|
2726
|
+
if (this._matchArgs(args, pattern)) {
|
|
2727
|
+
return handlers[key](...args);
|
|
2783
2728
|
}
|
|
2784
|
-
await this._updateNode(parentNode);
|
|
2785
|
-
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2786
2729
|
}
|
|
2730
|
+
throw new Error("Invalid arguments");
|
|
2787
2731
|
}
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2732
|
+
_matchArgs(args, pattern) {
|
|
2733
|
+
return args.every((arg, index) => {
|
|
2734
|
+
const expectedType = pattern[index];
|
|
2735
|
+
if (expectedType === void 0) return typeof arg === "undefined";
|
|
2736
|
+
if (expectedType === Function) return typeof arg === "function";
|
|
2737
|
+
if (expectedType === Number) return typeof arg === "number";
|
|
2738
|
+
if (expectedType === Array) return Array.isArray(arg);
|
|
2739
|
+
return false;
|
|
2740
|
+
});
|
|
2795
2741
|
}
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
node = await this.getNode(node.keys[index]);
|
|
2801
|
-
}
|
|
2802
|
-
return node;
|
|
2742
|
+
_createRandomId() {
|
|
2743
|
+
const timestamp = Date.now().toString(36);
|
|
2744
|
+
const random = Math.random().toString(36).substring(2);
|
|
2745
|
+
return `${timestamp}${random}`;
|
|
2803
2746
|
}
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
node = await this.getNode(node.keys[index]);
|
|
2747
|
+
_alloc(queue, workspaces, lockId) {
|
|
2748
|
+
const unit = queue.get(lockId);
|
|
2749
|
+
if (!unit) {
|
|
2750
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2809
2751
|
}
|
|
2810
|
-
|
|
2752
|
+
workspaces.set(lockId, unit);
|
|
2753
|
+
queue.delete(lockId);
|
|
2754
|
+
unit.alloc();
|
|
2811
2755
|
}
|
|
2812
|
-
|
|
2813
|
-
const
|
|
2814
|
-
if (!
|
|
2815
|
-
|
|
2756
|
+
_free(workspaces, lockId) {
|
|
2757
|
+
const unit = workspaces.get(lockId);
|
|
2758
|
+
if (!unit) {
|
|
2759
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2816
2760
|
}
|
|
2817
|
-
|
|
2761
|
+
workspaces.delete(lockId);
|
|
2762
|
+
unit.free();
|
|
2818
2763
|
}
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2764
|
+
_lock(queue, range, timeout, task, condition) {
|
|
2765
|
+
return new Promise((resolve, reject) => {
|
|
2766
|
+
let timeoutId = null;
|
|
2767
|
+
if (timeout >= 0) {
|
|
2768
|
+
timeoutId = setTimeout(() => {
|
|
2769
|
+
reject(_Ryoiki2.ERR_TIMEOUT(id, timeout));
|
|
2770
|
+
}, timeout);
|
|
2771
|
+
}
|
|
2772
|
+
const id = this._createRandomId();
|
|
2773
|
+
const alloc = async () => {
|
|
2774
|
+
if (timeoutId !== null) {
|
|
2775
|
+
clearTimeout(timeoutId);
|
|
2776
|
+
}
|
|
2777
|
+
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
2778
|
+
if (err) reject(err);
|
|
2779
|
+
else resolve(v);
|
|
2780
|
+
};
|
|
2781
|
+
const fetch = () => {
|
|
2782
|
+
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
2783
|
+
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
2784
|
+
};
|
|
2785
|
+
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
2786
|
+
fetch();
|
|
2787
|
+
});
|
|
2788
|
+
}
|
|
2789
|
+
_checkWorking(range, workspaces) {
|
|
2790
|
+
let isLocked = false;
|
|
2791
|
+
for (const lock of workspaces.values()) {
|
|
2792
|
+
if (_Ryoiki2.IsRangeOverlap(range, lock.range)) {
|
|
2793
|
+
isLocked = true;
|
|
2828
2794
|
break;
|
|
2829
|
-
|
|
2830
|
-
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
2831
|
-
}
|
|
2832
|
-
const guessNode = insertableNode[key];
|
|
2833
|
-
if (!guessNode) {
|
|
2834
|
-
return null;
|
|
2795
|
+
}
|
|
2835
2796
|
}
|
|
2836
|
-
return
|
|
2797
|
+
return isLocked;
|
|
2837
2798
|
}
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
node = await this.getNode(keys[0]);
|
|
2846
|
-
}
|
|
2847
|
-
return node;
|
|
2799
|
+
/**
|
|
2800
|
+
* Checks if there is any active read lock within the specified range.
|
|
2801
|
+
* @param range The range to check for active read locks.
|
|
2802
|
+
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
2803
|
+
*/
|
|
2804
|
+
isReading(range) {
|
|
2805
|
+
return this._checkWorking(range, this.readings);
|
|
2848
2806
|
}
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
return
|
|
2807
|
+
/**
|
|
2808
|
+
* Checks if there is any active write lock within the specified range.
|
|
2809
|
+
* @param range The range to check for active write locks.
|
|
2810
|
+
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
2811
|
+
*/
|
|
2812
|
+
isWriting(range) {
|
|
2813
|
+
return this._checkWorking(range, this.writings);
|
|
2856
2814
|
}
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2815
|
+
/**
|
|
2816
|
+
* Checks if a read lock can be acquired within the specified range.
|
|
2817
|
+
* @param range The range to check for read lock availability.
|
|
2818
|
+
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
2819
|
+
*/
|
|
2820
|
+
canRead(range) {
|
|
2821
|
+
const writing = this.isWriting(range);
|
|
2822
|
+
return !writing;
|
|
2823
|
+
}
|
|
2824
|
+
/**
|
|
2825
|
+
* Checks if a write lock can be acquired within the specified range.
|
|
2826
|
+
* @param range The range to check for write lock availability.
|
|
2827
|
+
* @returns `true` if a write lock can be acquired, `false` otherwise.
|
|
2828
|
+
*/
|
|
2829
|
+
canWrite(range) {
|
|
2830
|
+
const reading = this.isReading(range);
|
|
2831
|
+
const writing = this.isWriting(range);
|
|
2832
|
+
return !reading && !writing;
|
|
2833
|
+
}
|
|
2834
|
+
/**
|
|
2835
|
+
* Internal implementation of the read lock. Handles both overloads.
|
|
2836
|
+
* @template T - The return type of the task.
|
|
2837
|
+
* @param arg0 - Either a range or a task callback.
|
|
2838
|
+
* If a range is provided, the task is the second argument.
|
|
2839
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2840
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2841
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2842
|
+
* If this value is not provided, no timeout will be set.
|
|
2843
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2844
|
+
*/
|
|
2845
|
+
readLock(arg0, arg1, arg2) {
|
|
2846
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2847
|
+
[arg0, arg1, arg2],
|
|
2848
|
+
{
|
|
2849
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2850
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2851
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2852
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2853
|
+
},
|
|
2854
|
+
{
|
|
2855
|
+
task: [Function],
|
|
2856
|
+
taskTimeout: [Function, Number],
|
|
2857
|
+
rangeTask: [Array, Function],
|
|
2858
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2859
|
+
}
|
|
2860
|
+
);
|
|
2861
|
+
return this._lock(
|
|
2862
|
+
this.readQueue,
|
|
2863
|
+
range,
|
|
2864
|
+
timeout,
|
|
2865
|
+
task,
|
|
2866
|
+
() => !this.rangeOverlapping(this.writings, range)
|
|
2867
|
+
);
|
|
2868
|
+
}
|
|
2869
|
+
/**
|
|
2870
|
+
* Internal implementation of the write lock. Handles both overloads.
|
|
2871
|
+
* @template T - The return type of the task.
|
|
2872
|
+
* @param arg0 - Either a range or a task callback.
|
|
2873
|
+
* If a range is provided, the task is the second argument.
|
|
2874
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2875
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2876
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2877
|
+
* If this value is not provided, no timeout will be set.
|
|
2878
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2879
|
+
*/
|
|
2880
|
+
writeLock(arg0, arg1, arg2) {
|
|
2881
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2882
|
+
[arg0, arg1, arg2],
|
|
2883
|
+
{
|
|
2884
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2885
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2886
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2887
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2888
|
+
},
|
|
2889
|
+
{
|
|
2890
|
+
task: [Function],
|
|
2891
|
+
taskTimeout: [Function, Number],
|
|
2892
|
+
rangeTask: [Array, Function],
|
|
2893
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2894
|
+
}
|
|
2895
|
+
);
|
|
2896
|
+
return this._lock(
|
|
2897
|
+
this.writeQueue,
|
|
2898
|
+
range,
|
|
2899
|
+
timeout,
|
|
2900
|
+
task,
|
|
2901
|
+
() => {
|
|
2902
|
+
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
2903
|
+
}
|
|
2904
|
+
);
|
|
2905
|
+
}
|
|
2906
|
+
/**
|
|
2907
|
+
* Releases a read lock by its lock ID.
|
|
2908
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2909
|
+
*/
|
|
2910
|
+
readUnlock(lockId) {
|
|
2911
|
+
this._free(this.readings, lockId);
|
|
2912
|
+
}
|
|
2913
|
+
/**
|
|
2914
|
+
* Releases a write lock by its lock ID.
|
|
2915
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2916
|
+
*/
|
|
2917
|
+
writeUnlock(lockId) {
|
|
2918
|
+
this._free(this.writings, lockId);
|
|
2919
|
+
}
|
|
2920
|
+
};
|
|
2921
|
+
|
|
2922
|
+
// src/transaction/BPTreeAsyncTransaction.ts
|
|
2923
|
+
var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
2924
|
+
lock;
|
|
2925
|
+
constructor(rootTx, mvccRoot, mvcc, strategy, comparator, option) {
|
|
2926
|
+
super(
|
|
2927
|
+
rootTx,
|
|
2928
|
+
mvccRoot,
|
|
2929
|
+
mvcc,
|
|
2930
|
+
strategy,
|
|
2931
|
+
comparator,
|
|
2932
|
+
option
|
|
2933
|
+
);
|
|
2934
|
+
this.lock = new Ryoiki2();
|
|
2935
|
+
}
|
|
2936
|
+
async writeLock(id, fn) {
|
|
2937
|
+
let lockId;
|
|
2938
|
+
return this.lock.writeLock([id, id + 0.1], async (_lockId) => {
|
|
2939
|
+
lockId = _lockId;
|
|
2940
|
+
return fn();
|
|
2941
|
+
}).finally(() => {
|
|
2942
|
+
this.lock.writeUnlock(lockId);
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
async getNode(id) {
|
|
2946
|
+
if (this.nodes.has(id)) {
|
|
2947
|
+
return this.nodes.get(id);
|
|
2948
|
+
}
|
|
2949
|
+
return await this.mvcc.read(id);
|
|
2950
|
+
}
|
|
2951
|
+
/**
|
|
2952
|
+
* Create a new node with a unique ID.
|
|
2953
|
+
*/
|
|
2954
|
+
async _createNode(leaf, keys, values, parent = null, next = null, prev = null) {
|
|
2955
|
+
const id = await this.strategy.id(leaf);
|
|
2956
|
+
const node = {
|
|
2957
|
+
id,
|
|
2958
|
+
keys,
|
|
2959
|
+
values,
|
|
2960
|
+
leaf,
|
|
2961
|
+
parent,
|
|
2962
|
+
next,
|
|
2963
|
+
prev
|
|
2964
|
+
};
|
|
2965
|
+
await this.mvcc.create(id, node);
|
|
2966
|
+
this.nodes.set(id, node);
|
|
2967
|
+
return node;
|
|
2968
|
+
}
|
|
2969
|
+
async _updateNode(node) {
|
|
2970
|
+
await this.mvcc.write(node.id, node);
|
|
2971
|
+
this.nodes.set(node.id, node);
|
|
2972
|
+
}
|
|
2973
|
+
async _deleteNode(node) {
|
|
2974
|
+
await this.mvcc.delete(node.id);
|
|
2975
|
+
this.nodes.delete(node.id);
|
|
2976
|
+
}
|
|
2977
|
+
async _readHead() {
|
|
2978
|
+
if (this.nodes.has("__HEAD__")) {
|
|
2979
|
+
return this.nodes.get("__HEAD__") ?? null;
|
|
2980
|
+
}
|
|
2981
|
+
const head = await this.mvcc.read("__HEAD__");
|
|
2982
|
+
return head ?? null;
|
|
2983
|
+
}
|
|
2984
|
+
async _writeHead(head) {
|
|
2985
|
+
if (!await this.mvcc.exists("__HEAD__")) {
|
|
2986
|
+
await this.mvcc.create("__HEAD__", head);
|
|
2987
|
+
} else {
|
|
2988
|
+
await this.mvcc.write("__HEAD__", head);
|
|
2989
|
+
}
|
|
2990
|
+
this.nodes.set("__HEAD__", head);
|
|
2991
|
+
this.rootId = head.root;
|
|
2992
|
+
}
|
|
2993
|
+
async _insertAtLeaf(node, key, value) {
|
|
2994
|
+
if (node.values.length) {
|
|
2995
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2996
|
+
const nValue = node.values[i];
|
|
2997
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
2998
|
+
const keys = node.keys[i];
|
|
2999
|
+
if (keys.includes(key)) {
|
|
3000
|
+
break;
|
|
3001
|
+
}
|
|
3002
|
+
keys.push(key);
|
|
3003
|
+
await this._updateNode(node);
|
|
3004
|
+
return;
|
|
3005
|
+
} else if (this.comparator.isLower(value, nValue)) {
|
|
3006
|
+
node.values.splice(i, 0, value);
|
|
3007
|
+
node.keys.splice(i, 0, [key]);
|
|
3008
|
+
await this._updateNode(node);
|
|
3009
|
+
return;
|
|
3010
|
+
} else if (i + 1 === node.values.length) {
|
|
3011
|
+
node.values.push(value);
|
|
3012
|
+
node.keys.push([key]);
|
|
3013
|
+
await this._updateNode(node);
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
} else {
|
|
3018
|
+
node.values = [value];
|
|
3019
|
+
node.keys = [[key]];
|
|
3020
|
+
await this._updateNode(node);
|
|
3021
|
+
return;
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
async _insertInParent(node, value, pointer) {
|
|
3025
|
+
if (this.rootId === node.id) {
|
|
3026
|
+
const root = await this._createNode(false, [node.id, pointer.id], [value]);
|
|
3027
|
+
this.rootId = root.id;
|
|
3028
|
+
node.parent = root.id;
|
|
3029
|
+
pointer.parent = root.id;
|
|
3030
|
+
if (pointer.leaf) {
|
|
3031
|
+
node.next = pointer.id;
|
|
3032
|
+
pointer.prev = node.id;
|
|
3033
|
+
}
|
|
3034
|
+
await this._writeHead({
|
|
3035
|
+
root: root.id,
|
|
3036
|
+
order: this.order,
|
|
3037
|
+
data: this.strategy.head.data
|
|
3038
|
+
});
|
|
3039
|
+
await this._updateNode(node);
|
|
3040
|
+
await this._updateNode(pointer);
|
|
3041
|
+
return;
|
|
3042
|
+
}
|
|
3043
|
+
const parentNode = await this.getNode(node.parent);
|
|
3044
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
3045
|
+
if (nodeIndex === -1) {
|
|
3046
|
+
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
3047
|
+
}
|
|
3048
|
+
parentNode.values.splice(nodeIndex, 0, value);
|
|
3049
|
+
parentNode.keys.splice(nodeIndex + 1, 0, pointer.id);
|
|
3050
|
+
pointer.parent = parentNode.id;
|
|
3051
|
+
if (pointer.leaf) {
|
|
3052
|
+
const leftSibling = node;
|
|
3053
|
+
const oldNextId = leftSibling.next;
|
|
3054
|
+
pointer.prev = leftSibling.id;
|
|
3055
|
+
pointer.next = oldNextId;
|
|
3056
|
+
leftSibling.next = pointer.id;
|
|
3057
|
+
await this._updateNode(leftSibling);
|
|
3058
|
+
if (oldNextId) {
|
|
3059
|
+
const oldNext = await this.getNode(oldNextId);
|
|
3060
|
+
oldNext.prev = pointer.id;
|
|
3061
|
+
await this._updateNode(oldNext);
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
await this._updateNode(parentNode);
|
|
3065
|
+
await this._updateNode(pointer);
|
|
3066
|
+
if (parentNode.keys.length > this.order) {
|
|
3067
|
+
const parentPointer = await this._createNode(false, [], []);
|
|
3068
|
+
parentPointer.parent = parentNode.parent;
|
|
3069
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
3070
|
+
parentPointer.values = parentNode.values.slice(mid + 1);
|
|
3071
|
+
parentPointer.keys = parentNode.keys.slice(mid + 1);
|
|
3072
|
+
const midValue = parentNode.values[mid];
|
|
3073
|
+
parentNode.values = parentNode.values.slice(0, mid);
|
|
3074
|
+
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
3075
|
+
for (const k of parentNode.keys) {
|
|
3076
|
+
const n = await this.getNode(k);
|
|
3077
|
+
n.parent = parentNode.id;
|
|
3078
|
+
await this._updateNode(n);
|
|
3079
|
+
}
|
|
3080
|
+
for (const k of parentPointer.keys) {
|
|
3081
|
+
const n = await this.getNode(k);
|
|
3082
|
+
n.parent = parentPointer.id;
|
|
3083
|
+
await this._updateNode(n);
|
|
3084
|
+
}
|
|
3085
|
+
await this._updateNode(parentNode);
|
|
3086
|
+
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
async insertableNode(value) {
|
|
3090
|
+
let node = await this.getNode(this.rootId);
|
|
3091
|
+
while (!node.leaf) {
|
|
3092
|
+
const { index } = this._binarySearchValues(node.values, value, false, true);
|
|
3093
|
+
node = await this.getNode(node.keys[index]);
|
|
3094
|
+
}
|
|
3095
|
+
return node;
|
|
3096
|
+
}
|
|
3097
|
+
async insertableNodeByPrimary(value) {
|
|
3098
|
+
let node = await this.getNode(this.rootId);
|
|
3099
|
+
while (!node.leaf) {
|
|
3100
|
+
const { index } = this._binarySearchValues(node.values, value, true, false);
|
|
3101
|
+
node = await this.getNode(node.keys[index]);
|
|
3102
|
+
}
|
|
3103
|
+
return node;
|
|
3104
|
+
}
|
|
3105
|
+
async insertableRightestNodeByPrimary(value) {
|
|
3106
|
+
let node = await this.getNode(this.rootId);
|
|
3107
|
+
while (!node.leaf) {
|
|
3108
|
+
const { index } = this._binarySearchValues(node.values, value, true, true);
|
|
3109
|
+
node = await this.getNode(node.keys[index]);
|
|
3110
|
+
}
|
|
3111
|
+
return node;
|
|
3112
|
+
}
|
|
3113
|
+
async insertableRightestEndNodeByPrimary(value) {
|
|
3114
|
+
const node = await this.insertableRightestNodeByPrimary(value);
|
|
3115
|
+
if (!node.next) {
|
|
3116
|
+
return null;
|
|
3117
|
+
}
|
|
3118
|
+
return await this.getNode(node.next);
|
|
3119
|
+
}
|
|
3120
|
+
async insertableEndNode(value, direction) {
|
|
3121
|
+
const insertableNode = await this.insertableNode(value);
|
|
3122
|
+
let key;
|
|
3123
|
+
switch (direction) {
|
|
3124
|
+
case -1:
|
|
3125
|
+
key = "prev";
|
|
3126
|
+
break;
|
|
3127
|
+
case 1:
|
|
3128
|
+
key = "next";
|
|
3129
|
+
break;
|
|
3130
|
+
default:
|
|
3131
|
+
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
3132
|
+
}
|
|
3133
|
+
const guessNode = insertableNode[key];
|
|
3134
|
+
if (!guessNode) {
|
|
3135
|
+
return null;
|
|
3136
|
+
}
|
|
3137
|
+
return await this.getNode(guessNode);
|
|
3138
|
+
}
|
|
3139
|
+
async leftestNode() {
|
|
3140
|
+
let node = await this.getNode(this.rootId);
|
|
3141
|
+
if (node === null) {
|
|
3142
|
+
debugger;
|
|
3143
|
+
}
|
|
3144
|
+
while (!node.leaf) {
|
|
3145
|
+
const keys = node.keys;
|
|
3146
|
+
node = await this.getNode(keys[0]);
|
|
3147
|
+
}
|
|
3148
|
+
return node;
|
|
3149
|
+
}
|
|
3150
|
+
async rightestNode() {
|
|
3151
|
+
let node = await this.getNode(this.rootId);
|
|
3152
|
+
while (!node.leaf) {
|
|
3153
|
+
const keys = node.keys;
|
|
3154
|
+
node = await this.getNode(keys[keys.length - 1]);
|
|
3155
|
+
}
|
|
3156
|
+
return node;
|
|
3157
|
+
}
|
|
3158
|
+
async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
3159
|
+
let node = startNode;
|
|
3160
|
+
let done = false;
|
|
3161
|
+
let hasMatched = false;
|
|
3162
|
+
let nextNodePromise = null;
|
|
3163
|
+
while (!done) {
|
|
3164
|
+
if (endNode && node.id === endNode.id) {
|
|
3165
|
+
done = true;
|
|
3166
|
+
break;
|
|
2866
3167
|
}
|
|
2867
3168
|
if (direction === 1) {
|
|
2868
3169
|
if (node.next && !done) {
|
|
@@ -2918,28 +3219,46 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
2918
3219
|
}
|
|
2919
3220
|
}
|
|
2920
3221
|
async init() {
|
|
2921
|
-
this.
|
|
2922
|
-
|
|
2923
|
-
if (head === null) {
|
|
2924
|
-
this.order = this.strategy.order;
|
|
2925
|
-
const root = await this._createNode(true, [], []);
|
|
2926
|
-
await this._writeHead({
|
|
2927
|
-
root: root.id,
|
|
2928
|
-
order: this.order,
|
|
2929
|
-
data: this.strategy.head.data
|
|
2930
|
-
});
|
|
2931
|
-
} else {
|
|
2932
|
-
const { root, order } = head;
|
|
2933
|
-
this.strategy.head = head;
|
|
2934
|
-
this.order = order;
|
|
2935
|
-
await this._writeHead({
|
|
2936
|
-
root,
|
|
2937
|
-
order: this.order,
|
|
2938
|
-
data: this.strategy.head.data
|
|
2939
|
-
});
|
|
3222
|
+
if (this.rootTx !== this) {
|
|
3223
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
2940
3224
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
3225
|
+
return await this._initInternal();
|
|
3226
|
+
}
|
|
3227
|
+
async _initInternal() {
|
|
3228
|
+
if (this.isInitialized) {
|
|
3229
|
+
throw new Error("Transaction already initialized");
|
|
3230
|
+
}
|
|
3231
|
+
if (this.isDestroyed) {
|
|
3232
|
+
throw new Error("Transaction already destroyed");
|
|
3233
|
+
}
|
|
3234
|
+
this.isInitialized = true;
|
|
3235
|
+
try {
|
|
3236
|
+
this._clearCache();
|
|
3237
|
+
const head = await this._readHead();
|
|
3238
|
+
if (head === null) {
|
|
3239
|
+
this.order = this.strategy.order;
|
|
3240
|
+
const root = await this._createNode(true, [], []);
|
|
3241
|
+
await this._writeHead({
|
|
3242
|
+
root: root.id,
|
|
3243
|
+
order: this.order,
|
|
3244
|
+
data: this.strategy.head.data
|
|
3245
|
+
});
|
|
3246
|
+
} else {
|
|
3247
|
+
const { root, order } = head;
|
|
3248
|
+
this.strategy.head = head;
|
|
3249
|
+
this.order = order;
|
|
3250
|
+
await this._writeHead({
|
|
3251
|
+
root,
|
|
3252
|
+
order: this.order,
|
|
3253
|
+
data: this.strategy.head.data
|
|
3254
|
+
});
|
|
3255
|
+
}
|
|
3256
|
+
if (this.order < 3) {
|
|
3257
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
3258
|
+
}
|
|
3259
|
+
} catch (e) {
|
|
3260
|
+
this.isInitialized = false;
|
|
3261
|
+
throw e;
|
|
2943
3262
|
}
|
|
2944
3263
|
}
|
|
2945
3264
|
async exists(key, value) {
|
|
@@ -3039,38 +3358,40 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3039
3358
|
}
|
|
3040
3359
|
}
|
|
3041
3360
|
async keys(condition, filterValues, order = "asc") {
|
|
3042
|
-
const set = /* @__PURE__ */ new Set();
|
|
3043
|
-
for await (const key of this.keysStream(condition, filterValues, void 0, order)) {
|
|
3044
|
-
set.add(key);
|
|
3045
|
-
}
|
|
3046
|
-
return set;
|
|
3047
|
-
}
|
|
3048
|
-
async where(condition, order = "asc") {
|
|
3049
|
-
const map = /* @__PURE__ */ new Map();
|
|
3050
|
-
for await (const [key, value] of this.whereStream(condition, void 0, order)) {
|
|
3051
|
-
map.set(key, value);
|
|
3052
|
-
}
|
|
3053
|
-
return map;
|
|
3054
|
-
}
|
|
3055
|
-
async insert(key, value) {
|
|
3056
|
-
const before = await this.insertableNode(value);
|
|
3057
|
-
await this._insertAtLeaf(before, key, value);
|
|
3058
|
-
if (before.values.length === this.order) {
|
|
3059
|
-
const after = await this._createNode(
|
|
3060
|
-
true,
|
|
3061
|
-
[],
|
|
3062
|
-
[],
|
|
3063
|
-
before.parent,
|
|
3064
|
-
null,
|
|
3065
|
-
null
|
|
3066
|
-
);
|
|
3067
|
-
const mid = Math.ceil(this.order / 2) - 1;
|
|
3068
|
-
after.values = before.values.slice(mid + 1);
|
|
3069
|
-
after.keys = before.keys.slice(mid + 1);
|
|
3070
|
-
before.values = before.values.slice(0, mid + 1);
|
|
3071
|
-
before.keys = before.keys.slice(0, mid + 1);
|
|
3072
|
-
await this._insertInParent(before, after.values[0], after);
|
|
3361
|
+
const set = /* @__PURE__ */ new Set();
|
|
3362
|
+
for await (const key of this.keysStream(condition, filterValues, void 0, order)) {
|
|
3363
|
+
set.add(key);
|
|
3364
|
+
}
|
|
3365
|
+
return set;
|
|
3366
|
+
}
|
|
3367
|
+
async where(condition, order = "asc") {
|
|
3368
|
+
const map = /* @__PURE__ */ new Map();
|
|
3369
|
+
for await (const [key, value] of this.whereStream(condition, void 0, order)) {
|
|
3370
|
+
map.set(key, value);
|
|
3073
3371
|
}
|
|
3372
|
+
return map;
|
|
3373
|
+
}
|
|
3374
|
+
async insert(key, value) {
|
|
3375
|
+
return this.writeLock(0, async () => {
|
|
3376
|
+
const before = await this.insertableNode(value);
|
|
3377
|
+
await this._insertAtLeaf(before, key, value);
|
|
3378
|
+
if (before.values.length === this.order) {
|
|
3379
|
+
const after = await this._createNode(
|
|
3380
|
+
true,
|
|
3381
|
+
[],
|
|
3382
|
+
[],
|
|
3383
|
+
before.parent,
|
|
3384
|
+
null,
|
|
3385
|
+
null
|
|
3386
|
+
);
|
|
3387
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
3388
|
+
after.values = before.values.slice(mid + 1);
|
|
3389
|
+
after.keys = before.keys.slice(mid + 1);
|
|
3390
|
+
before.values = before.values.slice(0, mid + 1);
|
|
3391
|
+
before.keys = before.keys.slice(0, mid + 1);
|
|
3392
|
+
await this._insertInParent(before, after.values[0], after);
|
|
3393
|
+
}
|
|
3394
|
+
});
|
|
3074
3395
|
}
|
|
3075
3396
|
async _deleteEntry(node, key) {
|
|
3076
3397
|
if (!node.leaf) {
|
|
@@ -3263,35 +3584,37 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3263
3584
|
}
|
|
3264
3585
|
}
|
|
3265
3586
|
async delete(key, value) {
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3587
|
+
return this.writeLock(0, async () => {
|
|
3588
|
+
let node = await this.insertableNodeByPrimary(value);
|
|
3589
|
+
let found = false;
|
|
3590
|
+
while (true) {
|
|
3591
|
+
let i = node.values.length;
|
|
3592
|
+
while (i--) {
|
|
3593
|
+
const nValue = node.values[i];
|
|
3594
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
3595
|
+
const keys = node.keys[i];
|
|
3596
|
+
const keyIndex = keys.indexOf(key);
|
|
3597
|
+
if (keyIndex !== -1) {
|
|
3598
|
+
keys.splice(keyIndex, 1);
|
|
3599
|
+
if (keys.length === 0) {
|
|
3600
|
+
node.keys.splice(i, 1);
|
|
3601
|
+
node.values.splice(i, 1);
|
|
3602
|
+
}
|
|
3603
|
+
await this._updateNode(node);
|
|
3604
|
+
await this._deleteEntry(node, key);
|
|
3605
|
+
found = true;
|
|
3606
|
+
break;
|
|
3280
3607
|
}
|
|
3281
|
-
await this._updateNode(node);
|
|
3282
|
-
await this._deleteEntry(node, key);
|
|
3283
|
-
found = true;
|
|
3284
|
-
break;
|
|
3285
3608
|
}
|
|
3286
3609
|
}
|
|
3610
|
+
if (found) break;
|
|
3611
|
+
if (node.next) {
|
|
3612
|
+
node = await this.getNode(node.next);
|
|
3613
|
+
continue;
|
|
3614
|
+
}
|
|
3615
|
+
break;
|
|
3287
3616
|
}
|
|
3288
|
-
|
|
3289
|
-
if (node.next) {
|
|
3290
|
-
node = await this.getNode(node.next);
|
|
3291
|
-
continue;
|
|
3292
|
-
}
|
|
3293
|
-
break;
|
|
3294
|
-
}
|
|
3617
|
+
});
|
|
3295
3618
|
}
|
|
3296
3619
|
async getHeadData() {
|
|
3297
3620
|
const head = await this._readHead();
|
|
@@ -3314,9 +3637,12 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3314
3637
|
async commit(label) {
|
|
3315
3638
|
let result = await this.mvcc.commit(label);
|
|
3316
3639
|
if (result.success) {
|
|
3317
|
-
|
|
3318
|
-
if (
|
|
3319
|
-
|
|
3640
|
+
const isRootTx = this.rootTx === this;
|
|
3641
|
+
if (!isRootTx) {
|
|
3642
|
+
result = await this.rootTx.commit(label);
|
|
3643
|
+
if (result.success) {
|
|
3644
|
+
this.rootTx.rootId = this.rootId;
|
|
3645
|
+
}
|
|
3320
3646
|
}
|
|
3321
3647
|
if (result.success) {
|
|
3322
3648
|
for (const r of result.created) {
|
|
@@ -3352,397 +3678,141 @@ var BPTreeMVCCStrategyAsync = class extends AsyncMVCCStrategy {
|
|
|
3352
3678
|
async write(key, value) {
|
|
3353
3679
|
if (key === "__HEAD__") {
|
|
3354
3680
|
await this.strategy.writeHead(value);
|
|
3355
|
-
} else {
|
|
3356
|
-
await this.strategy.write(key, value);
|
|
3357
|
-
}
|
|
3358
|
-
}
|
|
3359
|
-
async delete(key) {
|
|
3360
|
-
await this.strategy.delete(key);
|
|
3361
|
-
}
|
|
3362
|
-
async exists(key) {
|
|
3363
|
-
if (key === "__HEAD__") {
|
|
3364
|
-
return await this.strategy.readHead() !== null;
|
|
3365
|
-
}
|
|
3366
|
-
try {
|
|
3367
|
-
const node = await this.strategy.read(key);
|
|
3368
|
-
return node !== null && node !== void 0;
|
|
3369
|
-
} catch {
|
|
3370
|
-
return false;
|
|
3371
|
-
}
|
|
3372
|
-
}
|
|
3373
|
-
};
|
|
3374
|
-
|
|
3375
|
-
// src/BPTreeAsync.ts
|
|
3376
|
-
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3377
|
-
constructor(strategy, comparator, option) {
|
|
3378
|
-
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy));
|
|
3379
|
-
super(
|
|
3380
|
-
null,
|
|
3381
|
-
mvccRoot,
|
|
3382
|
-
mvccRoot,
|
|
3383
|
-
strategy,
|
|
3384
|
-
comparator,
|
|
3385
|
-
option
|
|
3386
|
-
);
|
|
3387
|
-
}
|
|
3388
|
-
/**
|
|
3389
|
-
* Creates a new asynchronous transaction.
|
|
3390
|
-
* @returns A new BPTreeAsyncTransaction.
|
|
3391
|
-
*/
|
|
3392
|
-
async createTransaction() {
|
|
3393
|
-
const nestedTx = await this.mvcc.createNested();
|
|
3394
|
-
const tx = new BPTreeAsyncTransaction(
|
|
3395
|
-
this,
|
|
3396
|
-
this.mvcc,
|
|
3397
|
-
nestedTx,
|
|
3398
|
-
this.strategy,
|
|
3399
|
-
this.comparator,
|
|
3400
|
-
this.option
|
|
3401
|
-
);
|
|
3402
|
-
await tx.init();
|
|
3403
|
-
return tx;
|
|
3404
|
-
}
|
|
3405
|
-
async insert(key, value) {
|
|
3406
|
-
const tx = await this.createTransaction();
|
|
3407
|
-
await tx.insert(key, value);
|
|
3408
|
-
const result = await tx.commit();
|
|
3409
|
-
if (!result.success) {
|
|
3410
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3411
|
-
}
|
|
3412
|
-
this.rootId = tx.getRootId();
|
|
3413
|
-
}
|
|
3414
|
-
async delete(key, value) {
|
|
3415
|
-
const tx = await this.createTransaction();
|
|
3416
|
-
await tx.delete(key, value);
|
|
3417
|
-
const result = await tx.commit();
|
|
3418
|
-
if (!result.success) {
|
|
3419
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3420
|
-
}
|
|
3421
|
-
this.rootId = tx.getRootId();
|
|
3422
|
-
}
|
|
3423
|
-
};
|
|
3424
|
-
|
|
3425
|
-
// src/base/SerializeStrategy.ts
|
|
3426
|
-
var SerializeStrategy = class {
|
|
3427
|
-
order;
|
|
3428
|
-
head;
|
|
3429
|
-
constructor(order) {
|
|
3430
|
-
this.order = order;
|
|
3431
|
-
this.head = {
|
|
3432
|
-
order,
|
|
3433
|
-
root: null,
|
|
3434
|
-
data: {}
|
|
3435
|
-
};
|
|
3436
|
-
}
|
|
3437
|
-
};
|
|
3438
|
-
|
|
3439
|
-
// src/SerializeStrategySync.ts
|
|
3440
|
-
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3441
|
-
getHeadData(key, defaultValue) {
|
|
3442
|
-
if (!Object.hasOwn(this.head.data, key)) {
|
|
3443
|
-
this.setHeadData(key, defaultValue);
|
|
3444
|
-
}
|
|
3445
|
-
return this.head.data[key];
|
|
3446
|
-
}
|
|
3447
|
-
setHeadData(key, data) {
|
|
3448
|
-
this.head.data[key] = data;
|
|
3449
|
-
this.writeHead(this.head);
|
|
3450
|
-
}
|
|
3451
|
-
autoIncrement(key, defaultValue) {
|
|
3452
|
-
const current = this.getHeadData(key, defaultValue);
|
|
3453
|
-
const next = current + 1;
|
|
3454
|
-
this.setHeadData(key, next);
|
|
3455
|
-
return current;
|
|
3456
|
-
}
|
|
3457
|
-
};
|
|
3458
|
-
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3459
|
-
node;
|
|
3460
|
-
constructor(order) {
|
|
3461
|
-
super(order);
|
|
3462
|
-
this.node = {};
|
|
3463
|
-
}
|
|
3464
|
-
id(isLeaf) {
|
|
3465
|
-
return this.autoIncrement("index", 1).toString();
|
|
3466
|
-
}
|
|
3467
|
-
read(id) {
|
|
3468
|
-
if (!Object.hasOwn(this.node, id)) {
|
|
3469
|
-
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3470
|
-
}
|
|
3471
|
-
const node = this.node[id];
|
|
3472
|
-
return JSON.parse(JSON.stringify(node));
|
|
3473
|
-
}
|
|
3474
|
-
write(id, node) {
|
|
3475
|
-
this.node[id] = node;
|
|
3476
|
-
}
|
|
3477
|
-
delete(id) {
|
|
3478
|
-
delete this.node[id];
|
|
3479
|
-
}
|
|
3480
|
-
readHead() {
|
|
3481
|
-
if (this.head.root === null) {
|
|
3482
|
-
return null;
|
|
3483
|
-
}
|
|
3484
|
-
return this.head;
|
|
3485
|
-
}
|
|
3486
|
-
writeHead(head) {
|
|
3487
|
-
this.head = head;
|
|
3488
|
-
}
|
|
3489
|
-
};
|
|
3490
|
-
|
|
3491
|
-
// node_modules/ryoiki/dist/esm/index.mjs
|
|
3492
|
-
var Ryoiki2 = class _Ryoiki2 {
|
|
3493
|
-
readings;
|
|
3494
|
-
writings;
|
|
3495
|
-
readQueue;
|
|
3496
|
-
writeQueue;
|
|
3497
|
-
static async CatchError(promise) {
|
|
3498
|
-
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
3499
|
-
}
|
|
3500
|
-
static IsRangeOverlap(a, b) {
|
|
3501
|
-
const [start1, end1] = a;
|
|
3502
|
-
const [start2, end2] = b;
|
|
3503
|
-
if (end1 <= start2 || end2 <= start1) {
|
|
3504
|
-
return false;
|
|
3505
|
-
}
|
|
3506
|
-
return true;
|
|
3507
|
-
}
|
|
3508
|
-
static ERR_ALREADY_EXISTS(lockId) {
|
|
3509
|
-
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
3510
|
-
}
|
|
3511
|
-
static ERR_NOT_EXISTS(lockId) {
|
|
3512
|
-
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
3513
|
-
}
|
|
3514
|
-
static ERR_TIMEOUT(lockId, timeout) {
|
|
3515
|
-
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
3516
|
-
}
|
|
3517
|
-
/**
|
|
3518
|
-
* Constructs a new instance of the Ryoiki class.
|
|
3519
|
-
*/
|
|
3520
|
-
constructor() {
|
|
3521
|
-
this.readings = /* @__PURE__ */ new Map();
|
|
3522
|
-
this.writings = /* @__PURE__ */ new Map();
|
|
3523
|
-
this.readQueue = /* @__PURE__ */ new Map();
|
|
3524
|
-
this.writeQueue = /* @__PURE__ */ new Map();
|
|
3525
|
-
}
|
|
3526
|
-
/**
|
|
3527
|
-
* Creates a range based on a start value and length.
|
|
3528
|
-
* @param start - The starting value of the range.
|
|
3529
|
-
* @param length - The length of the range.
|
|
3530
|
-
* @returns A range tuple [start, start + length].
|
|
3531
|
-
*/
|
|
3532
|
-
range(start, length) {
|
|
3533
|
-
return [start, start + length];
|
|
3534
|
-
}
|
|
3535
|
-
rangeOverlapping(tasks, range) {
|
|
3536
|
-
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
3537
|
-
}
|
|
3538
|
-
isSameRange(a, b) {
|
|
3539
|
-
const [a1, a2] = a;
|
|
3540
|
-
const [b1, b2] = b;
|
|
3541
|
-
return a1 === b1 && a2 === b2;
|
|
3542
|
-
}
|
|
3543
|
-
fetchUnitAndRun(queue, workspaces) {
|
|
3544
|
-
for (const [id, unit] of queue) {
|
|
3545
|
-
if (!unit.condition()) {
|
|
3546
|
-
continue;
|
|
3547
|
-
}
|
|
3548
|
-
this._alloc(queue, workspaces, id);
|
|
3549
|
-
}
|
|
3550
|
-
}
|
|
3551
|
-
_handleOverload(args, handlers, argPatterns) {
|
|
3552
|
-
for (const [key, pattern] of Object.entries(argPatterns)) {
|
|
3553
|
-
if (this._matchArgs(args, pattern)) {
|
|
3554
|
-
return handlers[key](...args);
|
|
3555
|
-
}
|
|
3556
|
-
}
|
|
3557
|
-
throw new Error("Invalid arguments");
|
|
3558
|
-
}
|
|
3559
|
-
_matchArgs(args, pattern) {
|
|
3560
|
-
return args.every((arg, index) => {
|
|
3561
|
-
const expectedType = pattern[index];
|
|
3562
|
-
if (expectedType === void 0) return typeof arg === "undefined";
|
|
3563
|
-
if (expectedType === Function) return typeof arg === "function";
|
|
3564
|
-
if (expectedType === Number) return typeof arg === "number";
|
|
3565
|
-
if (expectedType === Array) return Array.isArray(arg);
|
|
3566
|
-
return false;
|
|
3567
|
-
});
|
|
3568
|
-
}
|
|
3569
|
-
_createRandomId() {
|
|
3570
|
-
const timestamp = Date.now().toString(36);
|
|
3571
|
-
const random = Math.random().toString(36).substring(2);
|
|
3572
|
-
return `${timestamp}${random}`;
|
|
3573
|
-
}
|
|
3574
|
-
_alloc(queue, workspaces, lockId) {
|
|
3575
|
-
const unit = queue.get(lockId);
|
|
3576
|
-
if (!unit) {
|
|
3577
|
-
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
3681
|
+
} else {
|
|
3682
|
+
await this.strategy.write(key, value);
|
|
3578
3683
|
}
|
|
3579
|
-
workspaces.set(lockId, unit);
|
|
3580
|
-
queue.delete(lockId);
|
|
3581
|
-
unit.alloc();
|
|
3582
3684
|
}
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3685
|
+
async delete(key) {
|
|
3686
|
+
await this.strategy.delete(key);
|
|
3687
|
+
}
|
|
3688
|
+
async exists(key) {
|
|
3689
|
+
if (key === "__HEAD__") {
|
|
3690
|
+
return await this.strategy.readHead() !== null;
|
|
3691
|
+
}
|
|
3692
|
+
try {
|
|
3693
|
+
const node = await this.strategy.read(key);
|
|
3694
|
+
return node !== null && node !== void 0;
|
|
3695
|
+
} catch {
|
|
3696
|
+
return false;
|
|
3587
3697
|
}
|
|
3588
|
-
workspaces.delete(lockId);
|
|
3589
|
-
unit.free();
|
|
3590
3698
|
}
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3699
|
+
};
|
|
3700
|
+
|
|
3701
|
+
// src/BPTreeAsync.ts
|
|
3702
|
+
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3703
|
+
constructor(strategy, comparator, option) {
|
|
3704
|
+
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy));
|
|
3705
|
+
super(
|
|
3706
|
+
null,
|
|
3707
|
+
mvccRoot,
|
|
3708
|
+
mvccRoot,
|
|
3709
|
+
strategy,
|
|
3710
|
+
comparator,
|
|
3711
|
+
option
|
|
3712
|
+
);
|
|
3713
|
+
}
|
|
3714
|
+
/**
|
|
3715
|
+
* Creates a new asynchronous transaction.
|
|
3716
|
+
* @returns A new BPTreeAsyncTransaction.
|
|
3717
|
+
*/
|
|
3718
|
+
async createTransaction() {
|
|
3719
|
+
const nestedTx = this.mvcc.createNested();
|
|
3720
|
+
const tx = new BPTreeAsyncTransaction(
|
|
3721
|
+
this,
|
|
3722
|
+
this.mvcc,
|
|
3723
|
+
nestedTx,
|
|
3724
|
+
this.strategy,
|
|
3725
|
+
this.comparator,
|
|
3726
|
+
this.option
|
|
3727
|
+
);
|
|
3728
|
+
await tx._initInternal();
|
|
3729
|
+
return tx;
|
|
3730
|
+
}
|
|
3731
|
+
async insert(key, value) {
|
|
3732
|
+
return this.writeLock(1, async () => {
|
|
3733
|
+
const tx = await this.createTransaction();
|
|
3734
|
+
await tx.insert(key, value);
|
|
3735
|
+
const result = await tx.commit();
|
|
3736
|
+
if (!result.success) {
|
|
3737
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3598
3738
|
}
|
|
3599
|
-
const id = this._createRandomId();
|
|
3600
|
-
const alloc = async () => {
|
|
3601
|
-
if (timeoutId !== null) {
|
|
3602
|
-
clearTimeout(timeoutId);
|
|
3603
|
-
}
|
|
3604
|
-
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
3605
|
-
if (err) reject(err);
|
|
3606
|
-
else resolve(v);
|
|
3607
|
-
};
|
|
3608
|
-
const fetch = () => {
|
|
3609
|
-
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
3610
|
-
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
3611
|
-
};
|
|
3612
|
-
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
3613
|
-
fetch();
|
|
3614
3739
|
});
|
|
3615
3740
|
}
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3741
|
+
async delete(key, value) {
|
|
3742
|
+
return this.writeLock(1, async () => {
|
|
3743
|
+
const tx = await this.createTransaction();
|
|
3744
|
+
await tx.delete(key, value);
|
|
3745
|
+
const result = await tx.commit();
|
|
3746
|
+
if (!result.success) {
|
|
3747
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3622
3748
|
}
|
|
3749
|
+
});
|
|
3750
|
+
}
|
|
3751
|
+
};
|
|
3752
|
+
|
|
3753
|
+
// src/base/SerializeStrategy.ts
|
|
3754
|
+
var SerializeStrategy = class {
|
|
3755
|
+
order;
|
|
3756
|
+
head;
|
|
3757
|
+
constructor(order) {
|
|
3758
|
+
this.order = order;
|
|
3759
|
+
this.head = {
|
|
3760
|
+
order,
|
|
3761
|
+
root: null,
|
|
3762
|
+
data: {}
|
|
3763
|
+
};
|
|
3764
|
+
}
|
|
3765
|
+
};
|
|
3766
|
+
|
|
3767
|
+
// src/SerializeStrategySync.ts
|
|
3768
|
+
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3769
|
+
getHeadData(key, defaultValue) {
|
|
3770
|
+
if (!Object.hasOwn(this.head.data, key)) {
|
|
3771
|
+
this.setHeadData(key, defaultValue);
|
|
3623
3772
|
}
|
|
3624
|
-
return
|
|
3773
|
+
return this.head.data[key];
|
|
3625
3774
|
}
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
3630
|
-
*/
|
|
3631
|
-
isReading(range) {
|
|
3632
|
-
return this._checkWorking(range, this.readings);
|
|
3775
|
+
setHeadData(key, data) {
|
|
3776
|
+
this.head.data[key] = data;
|
|
3777
|
+
this.writeHead(this.head);
|
|
3633
3778
|
}
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
isWriting(range) {
|
|
3640
|
-
return this._checkWorking(range, this.writings);
|
|
3779
|
+
autoIncrement(key, defaultValue) {
|
|
3780
|
+
const current = this.getHeadData(key, defaultValue);
|
|
3781
|
+
const next = current + 1;
|
|
3782
|
+
this.setHeadData(key, next);
|
|
3783
|
+
return current;
|
|
3641
3784
|
}
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
const writing = this.isWriting(range);
|
|
3649
|
-
return !writing;
|
|
3785
|
+
};
|
|
3786
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3787
|
+
node;
|
|
3788
|
+
constructor(order) {
|
|
3789
|
+
super(order);
|
|
3790
|
+
this.node = {};
|
|
3650
3791
|
}
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
* @param range The range to check for write lock availability.
|
|
3654
|
-
* @returns `true` if a write lock can be acquired, `false` otherwise.
|
|
3655
|
-
*/
|
|
3656
|
-
canWrite(range) {
|
|
3657
|
-
const reading = this.isReading(range);
|
|
3658
|
-
const writing = this.isWriting(range);
|
|
3659
|
-
return !reading && !writing;
|
|
3792
|
+
id(isLeaf) {
|
|
3793
|
+
return this.autoIncrement("index", 1).toString();
|
|
3660
3794
|
}
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
3668
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
3669
|
-
* If this value is not provided, no timeout will be set.
|
|
3670
|
-
* @returns A promise resolving to the result of the task execution.
|
|
3671
|
-
*/
|
|
3672
|
-
readLock(arg0, arg1, arg2) {
|
|
3673
|
-
const [range, task, timeout] = this._handleOverload(
|
|
3674
|
-
[arg0, arg1, arg2],
|
|
3675
|
-
{
|
|
3676
|
-
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
3677
|
-
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
3678
|
-
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
3679
|
-
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
3680
|
-
},
|
|
3681
|
-
{
|
|
3682
|
-
task: [Function],
|
|
3683
|
-
taskTimeout: [Function, Number],
|
|
3684
|
-
rangeTask: [Array, Function],
|
|
3685
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
3686
|
-
}
|
|
3687
|
-
);
|
|
3688
|
-
return this._lock(
|
|
3689
|
-
this.readQueue,
|
|
3690
|
-
range,
|
|
3691
|
-
timeout,
|
|
3692
|
-
task,
|
|
3693
|
-
() => !this.rangeOverlapping(this.writings, range)
|
|
3694
|
-
);
|
|
3795
|
+
read(id) {
|
|
3796
|
+
if (!Object.hasOwn(this.node, id)) {
|
|
3797
|
+
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3798
|
+
}
|
|
3799
|
+
const node = this.node[id];
|
|
3800
|
+
return JSON.parse(JSON.stringify(node));
|
|
3695
3801
|
}
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
* @template T - The return type of the task.
|
|
3699
|
-
* @param arg0 - Either a range or a task callback.
|
|
3700
|
-
* If a range is provided, the task is the second argument.
|
|
3701
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
3702
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
3703
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
3704
|
-
* If this value is not provided, no timeout will be set.
|
|
3705
|
-
* @returns A promise resolving to the result of the task execution.
|
|
3706
|
-
*/
|
|
3707
|
-
writeLock(arg0, arg1, arg2) {
|
|
3708
|
-
const [range, task, timeout] = this._handleOverload(
|
|
3709
|
-
[arg0, arg1, arg2],
|
|
3710
|
-
{
|
|
3711
|
-
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
3712
|
-
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
3713
|
-
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
3714
|
-
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
3715
|
-
},
|
|
3716
|
-
{
|
|
3717
|
-
task: [Function],
|
|
3718
|
-
taskTimeout: [Function, Number],
|
|
3719
|
-
rangeTask: [Array, Function],
|
|
3720
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
3721
|
-
}
|
|
3722
|
-
);
|
|
3723
|
-
return this._lock(
|
|
3724
|
-
this.writeQueue,
|
|
3725
|
-
range,
|
|
3726
|
-
timeout,
|
|
3727
|
-
task,
|
|
3728
|
-
() => {
|
|
3729
|
-
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
3730
|
-
}
|
|
3731
|
-
);
|
|
3802
|
+
write(id, node) {
|
|
3803
|
+
this.node[id] = node;
|
|
3732
3804
|
}
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
* @param lockId - The unique identifier for the lock to release.
|
|
3736
|
-
*/
|
|
3737
|
-
readUnlock(lockId) {
|
|
3738
|
-
this._free(this.readings, lockId);
|
|
3805
|
+
delete(id) {
|
|
3806
|
+
delete this.node[id];
|
|
3739
3807
|
}
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3808
|
+
readHead() {
|
|
3809
|
+
if (this.head.root === null) {
|
|
3810
|
+
return null;
|
|
3811
|
+
}
|
|
3812
|
+
return this.head;
|
|
3813
|
+
}
|
|
3814
|
+
writeHead(head) {
|
|
3815
|
+
this.head = head;
|
|
3746
3816
|
}
|
|
3747
3817
|
};
|
|
3748
3818
|
|