dataply 0.0.21 → 0.0.22-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +1375 -1308
- package/package.json +3 -3
package/dist/cjs/index.js
CHANGED
|
@@ -137,6 +137,7 @@ var MVCCTransaction = class {
|
|
|
137
137
|
// delete 시 삭제 전 값 저장
|
|
138
138
|
originallyExisted;
|
|
139
139
|
// 트랜잭션 시작 시점에 디스크에 존재했던 키 (deleted 결과 필터링용)
|
|
140
|
+
bufferHistory = /* @__PURE__ */ new Map();
|
|
140
141
|
// Nested Transaction Properties
|
|
141
142
|
parent;
|
|
142
143
|
localVersion;
|
|
@@ -191,26 +192,45 @@ var MVCCTransaction = class {
|
|
|
191
192
|
return false;
|
|
192
193
|
}
|
|
193
194
|
// --- Internal buffer manipulation helpers ---
|
|
194
|
-
|
|
195
|
-
this.
|
|
195
|
+
_recordHistory(key) {
|
|
196
|
+
const existsInWriteBuffer = this.writeBuffer.has(key);
|
|
197
|
+
const existsInDeleteBuffer = this.deleteBuffer.has(key);
|
|
198
|
+
const currentVer = this.keyVersions.get(key);
|
|
199
|
+
if (currentVer !== void 0) {
|
|
200
|
+
if (!this.bufferHistory.has(key)) this.bufferHistory.set(key, []);
|
|
201
|
+
this.bufferHistory.get(key).push({
|
|
202
|
+
value: existsInWriteBuffer ? this.writeBuffer.get(key) : this.deletedValues.get(key) ?? null,
|
|
203
|
+
exists: existsInWriteBuffer || !existsInDeleteBuffer,
|
|
204
|
+
version: currentVer
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
_bufferCreate(key, value, version) {
|
|
209
|
+
if (version === void 0) this.localVersion++;
|
|
210
|
+
const targetVersion = version ?? this.localVersion;
|
|
211
|
+
this._recordHistory(key);
|
|
196
212
|
this.writeBuffer.set(key, value);
|
|
197
213
|
this.createdKeys.add(key);
|
|
198
214
|
this.deleteBuffer.delete(key);
|
|
199
215
|
this.originallyExisted.delete(key);
|
|
200
|
-
this.keyVersions.set(key,
|
|
216
|
+
this.keyVersions.set(key, targetVersion);
|
|
201
217
|
}
|
|
202
|
-
_bufferWrite(key, value) {
|
|
203
|
-
this.localVersion++;
|
|
218
|
+
_bufferWrite(key, value, version) {
|
|
219
|
+
if (version === void 0) this.localVersion++;
|
|
220
|
+
const targetVersion = version ?? this.localVersion;
|
|
221
|
+
this._recordHistory(key);
|
|
204
222
|
this.writeBuffer.set(key, value);
|
|
205
223
|
this.deleteBuffer.delete(key);
|
|
206
|
-
this.keyVersions.set(key,
|
|
224
|
+
this.keyVersions.set(key, targetVersion);
|
|
207
225
|
}
|
|
208
|
-
_bufferDelete(key) {
|
|
209
|
-
this.localVersion++;
|
|
226
|
+
_bufferDelete(key, version) {
|
|
227
|
+
if (version === void 0) this.localVersion++;
|
|
228
|
+
const targetVersion = version ?? this.localVersion;
|
|
229
|
+
this._recordHistory(key);
|
|
210
230
|
this.deleteBuffer.add(key);
|
|
211
231
|
this.writeBuffer.delete(key);
|
|
212
232
|
this.createdKeys.delete(key);
|
|
213
|
-
this.keyVersions.set(key,
|
|
233
|
+
this.keyVersions.set(key, targetVersion);
|
|
214
234
|
}
|
|
215
235
|
/**
|
|
216
236
|
* Returns the entries that will be created, updated, and deleted by this transaction.
|
|
@@ -287,7 +307,9 @@ var MVCCTransaction = class {
|
|
|
287
307
|
break;
|
|
288
308
|
}
|
|
289
309
|
}
|
|
290
|
-
if (latestInSnapshotIdx
|
|
310
|
+
if (latestInSnapshotIdx === versions.length - 1) {
|
|
311
|
+
this.versionIndex.delete(key);
|
|
312
|
+
} else if (latestInSnapshotIdx > 0) {
|
|
291
313
|
versions.splice(0, latestInSnapshotIdx);
|
|
292
314
|
}
|
|
293
315
|
}
|
|
@@ -344,27 +366,68 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
344
366
|
if (this.committed) throw new Error("Transaction already committed");
|
|
345
367
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
346
368
|
if (this.deleteBuffer.has(key)) return null;
|
|
347
|
-
|
|
369
|
+
if (this.parent) {
|
|
370
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
371
|
+
}
|
|
372
|
+
return this._diskRead(key, this.snapshotVersion);
|
|
348
373
|
}
|
|
349
374
|
exists(key) {
|
|
350
375
|
if (this.committed) throw new Error("Transaction already committed");
|
|
351
376
|
if (this.deleteBuffer.has(key)) return false;
|
|
352
377
|
if (this.writeBuffer.has(key)) return true;
|
|
353
|
-
|
|
378
|
+
if (this.parent) {
|
|
379
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
380
|
+
}
|
|
381
|
+
return this._diskExists(key, this.snapshotVersion);
|
|
382
|
+
}
|
|
383
|
+
_existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
384
|
+
if (this.writeBuffer.has(key)) {
|
|
385
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
386
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (this.deleteBuffer.has(key)) {
|
|
391
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
392
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
const history = this.bufferHistory.get(key);
|
|
397
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
398
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
399
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
400
|
+
return history[i].exists;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (this.parent) {
|
|
405
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
406
|
+
} else {
|
|
407
|
+
return this._diskExists(key, snapshotVersion);
|
|
408
|
+
}
|
|
354
409
|
}
|
|
355
410
|
_readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
356
411
|
if (this.writeBuffer.has(key)) {
|
|
357
412
|
const keyModVersion = this.keyVersions.get(key);
|
|
358
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
413
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
359
414
|
return this.writeBuffer.get(key);
|
|
360
415
|
}
|
|
361
416
|
}
|
|
362
417
|
if (this.deleteBuffer.has(key)) {
|
|
363
418
|
const keyModVersion = this.keyVersions.get(key);
|
|
364
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
419
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
365
420
|
return null;
|
|
366
421
|
}
|
|
367
422
|
}
|
|
423
|
+
const history = this.bufferHistory.get(key);
|
|
424
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
425
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
426
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
427
|
+
return history[i].exists ? history[i].value : null;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
368
431
|
if (this.parent) {
|
|
369
432
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
370
433
|
} else {
|
|
@@ -374,54 +437,22 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
374
437
|
commit(label) {
|
|
375
438
|
const { created, updated, deleted } = this.getResultEntries();
|
|
376
439
|
if (this.committed) {
|
|
377
|
-
return {
|
|
378
|
-
label,
|
|
379
|
-
success: false,
|
|
380
|
-
error: "Transaction already committed",
|
|
381
|
-
conflict: void 0,
|
|
382
|
-
created,
|
|
383
|
-
updated,
|
|
384
|
-
deleted
|
|
385
|
-
};
|
|
440
|
+
return { label, success: false, error: "Transaction already committed", conflict: void 0, created, updated, deleted };
|
|
386
441
|
}
|
|
387
442
|
if (this.hasCommittedAncestor()) {
|
|
388
|
-
return {
|
|
389
|
-
label,
|
|
390
|
-
success: false,
|
|
391
|
-
error: "Ancestor transaction already committed",
|
|
392
|
-
conflict: void 0,
|
|
393
|
-
created,
|
|
394
|
-
updated,
|
|
395
|
-
deleted
|
|
396
|
-
};
|
|
443
|
+
return { label, success: false, error: "Ancestor transaction already committed", conflict: void 0, created, updated, deleted };
|
|
397
444
|
}
|
|
398
445
|
if (this.parent) {
|
|
399
446
|
const failure = this.parent._merge(this);
|
|
400
447
|
if (failure) {
|
|
401
|
-
return {
|
|
402
|
-
label,
|
|
403
|
-
success: false,
|
|
404
|
-
error: failure.error,
|
|
405
|
-
conflict: failure.conflict,
|
|
406
|
-
created,
|
|
407
|
-
updated,
|
|
408
|
-
deleted
|
|
409
|
-
};
|
|
448
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created, updated, deleted };
|
|
410
449
|
}
|
|
411
450
|
this.committed = true;
|
|
412
451
|
} else {
|
|
413
452
|
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
414
453
|
const failure = this._merge(this);
|
|
415
454
|
if (failure) {
|
|
416
|
-
return {
|
|
417
|
-
label,
|
|
418
|
-
success: false,
|
|
419
|
-
error: failure.error,
|
|
420
|
-
conflict: failure.conflict,
|
|
421
|
-
created: [],
|
|
422
|
-
updated: [],
|
|
423
|
-
deleted: []
|
|
424
|
-
};
|
|
455
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created: [], updated: [], deleted: [] };
|
|
425
456
|
}
|
|
426
457
|
this.writeBuffer.clear();
|
|
427
458
|
this.deleteBuffer.clear();
|
|
@@ -429,17 +460,12 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
429
460
|
this.deletedValues.clear();
|
|
430
461
|
this.originallyExisted.clear();
|
|
431
462
|
this.keyVersions.clear();
|
|
463
|
+
this.bufferHistory.clear();
|
|
432
464
|
this.localVersion = 0;
|
|
433
465
|
this.snapshotVersion = this.version;
|
|
434
466
|
}
|
|
435
467
|
}
|
|
436
|
-
return {
|
|
437
|
-
label,
|
|
438
|
-
success: true,
|
|
439
|
-
created,
|
|
440
|
-
updated,
|
|
441
|
-
deleted
|
|
442
|
-
};
|
|
468
|
+
return { label, success: true, created, updated, deleted };
|
|
443
469
|
}
|
|
444
470
|
_merge(child) {
|
|
445
471
|
if (this.parent) {
|
|
@@ -448,11 +474,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
448
474
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
449
475
|
return {
|
|
450
476
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
451
|
-
conflict: {
|
|
452
|
-
key,
|
|
453
|
-
parent: this.read(key),
|
|
454
|
-
child: child.read(key)
|
|
455
|
-
}
|
|
477
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
456
478
|
};
|
|
457
479
|
}
|
|
458
480
|
}
|
|
@@ -461,40 +483,26 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
461
483
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
462
484
|
return {
|
|
463
485
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
464
|
-
conflict: {
|
|
465
|
-
key,
|
|
466
|
-
parent: this.read(key),
|
|
467
|
-
child: child.read(key)
|
|
468
|
-
}
|
|
486
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
469
487
|
};
|
|
470
488
|
}
|
|
471
489
|
}
|
|
472
|
-
const
|
|
473
|
-
for (const key of child.writeBuffer
|
|
474
|
-
|
|
475
|
-
this.
|
|
476
|
-
this.
|
|
477
|
-
if (child.createdKeys.has(key)) {
|
|
478
|
-
this.createdKeys.add(key);
|
|
479
|
-
}
|
|
490
|
+
const mergeVersion = ++this.localVersion;
|
|
491
|
+
for (const [key, value] of child.writeBuffer) {
|
|
492
|
+
const wasCreated = child.createdKeys.has(key);
|
|
493
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
494
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
480
495
|
}
|
|
481
496
|
for (const key of child.deleteBuffer) {
|
|
482
|
-
this.deleteBuffer.add(key);
|
|
483
|
-
this.writeBuffer.delete(key);
|
|
484
|
-
this.createdKeys.delete(key);
|
|
485
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
486
497
|
const deletedValue = child.deletedValues.get(key);
|
|
487
|
-
if (deletedValue !== void 0)
|
|
488
|
-
|
|
489
|
-
}
|
|
490
|
-
if (child.originallyExisted.has(key)) {
|
|
498
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
499
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
491
500
|
this.originallyExisted.add(key);
|
|
492
501
|
}
|
|
502
|
+
this._bufferDelete(key, mergeVersion);
|
|
493
503
|
}
|
|
494
|
-
this.localVersion = newLocalVersion;
|
|
495
504
|
this.root.activeTransactions.delete(child);
|
|
496
505
|
} else {
|
|
497
|
-
const newVersion = this.version + 1;
|
|
498
506
|
if (child !== this) {
|
|
499
507
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
500
508
|
for (const key of modifiedKeys) {
|
|
@@ -504,58 +512,53 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
504
512
|
if (lastVer > child.snapshotVersion) {
|
|
505
513
|
return {
|
|
506
514
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
507
|
-
conflict: {
|
|
508
|
-
key,
|
|
509
|
-
parent: this.read(key),
|
|
510
|
-
child: child.read(key)
|
|
511
|
-
}
|
|
515
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
512
516
|
};
|
|
513
517
|
}
|
|
514
518
|
}
|
|
519
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
520
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
521
|
+
return {
|
|
522
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
523
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
524
|
+
};
|
|
525
|
+
}
|
|
515
526
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
for (const key of child.deleteBuffer) {
|
|
525
|
-
this.deleteBuffer.add(key);
|
|
526
|
-
this.writeBuffer.delete(key);
|
|
527
|
-
this.createdKeys.delete(key);
|
|
528
|
-
const deletedValue = child.deletedValues.get(key);
|
|
529
|
-
if (deletedValue !== void 0) {
|
|
530
|
-
this.deletedValues.set(key, deletedValue);
|
|
527
|
+
const mergeVersion = ++this.localVersion;
|
|
528
|
+
for (const [key, value] of child.writeBuffer) {
|
|
529
|
+
const wasCreated = child.createdKeys.has(key);
|
|
530
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
531
|
+
this.originallyExisted.add(key);
|
|
532
|
+
}
|
|
533
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
534
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
531
535
|
}
|
|
532
|
-
|
|
533
|
-
|
|
536
|
+
for (const key of child.deleteBuffer) {
|
|
537
|
+
const deletedValue = child.deletedValues.get(key);
|
|
538
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
539
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
540
|
+
this.originallyExisted.add(key);
|
|
541
|
+
}
|
|
542
|
+
this._bufferDelete(key, mergeVersion);
|
|
534
543
|
}
|
|
544
|
+
this.root.activeTransactions.delete(child);
|
|
545
|
+
} else {
|
|
546
|
+
const newVersion = this.version + 1;
|
|
547
|
+
for (const [key, value] of this.writeBuffer) this._diskWrite(key, value, newVersion);
|
|
548
|
+
for (const key of this.deleteBuffer) this._diskDelete(key, newVersion);
|
|
549
|
+
this.version = newVersion;
|
|
550
|
+
this._cleanupDeletedCache();
|
|
535
551
|
}
|
|
536
|
-
for (const [key, value] of child.writeBuffer) {
|
|
537
|
-
this._diskWrite(key, value, newVersion);
|
|
538
|
-
}
|
|
539
|
-
for (const key of child.deleteBuffer) {
|
|
540
|
-
this._diskDelete(key, newVersion);
|
|
541
|
-
}
|
|
542
|
-
this.version = newVersion;
|
|
543
|
-
this.root.activeTransactions.delete(child);
|
|
544
|
-
this._cleanupDeletedCache();
|
|
545
552
|
}
|
|
546
553
|
return null;
|
|
547
554
|
}
|
|
548
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
549
555
|
_diskWrite(key, value, version) {
|
|
550
556
|
const strategy = this.strategy;
|
|
551
557
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
552
558
|
if (strategy.exists(key)) {
|
|
553
559
|
const currentVal = strategy.read(key);
|
|
554
560
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
555
|
-
this.deletedCache.get(key).push({
|
|
556
|
-
value: currentVal,
|
|
557
|
-
deletedAtVersion: version
|
|
558
|
-
});
|
|
561
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
559
562
|
}
|
|
560
563
|
strategy.write(key, value);
|
|
561
564
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -571,9 +574,8 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
571
574
|
let targetVerObj = null;
|
|
572
575
|
let nextVerObj = null;
|
|
573
576
|
for (const v of versions) {
|
|
574
|
-
if (v.version <= snapshotVersion)
|
|
575
|
-
|
|
576
|
-
} else {
|
|
577
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
578
|
+
else {
|
|
577
579
|
nextVerObj = v;
|
|
578
580
|
break;
|
|
579
581
|
}
|
|
@@ -590,6 +592,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
590
592
|
}
|
|
591
593
|
if (!targetVerObj.exists) return null;
|
|
592
594
|
if (!nextVerObj) {
|
|
595
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
593
596
|
return strategy.read(key);
|
|
594
597
|
}
|
|
595
598
|
const cached = this.deletedCache.get(key);
|
|
@@ -607,14 +610,24 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
607
610
|
return strategy.exists(key);
|
|
608
611
|
}
|
|
609
612
|
let targetVerObj = null;
|
|
613
|
+
let nextVerObj = null;
|
|
610
614
|
for (const v of versions) {
|
|
611
|
-
if (v.version <= snapshotVersion)
|
|
612
|
-
|
|
613
|
-
|
|
615
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
616
|
+
else {
|
|
617
|
+
nextVerObj = v;
|
|
614
618
|
break;
|
|
615
619
|
}
|
|
616
620
|
}
|
|
617
|
-
if (!targetVerObj)
|
|
621
|
+
if (!targetVerObj) {
|
|
622
|
+
if (nextVerObj) {
|
|
623
|
+
const cached = this.deletedCache.get(key);
|
|
624
|
+
if (cached) {
|
|
625
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
626
|
+
if (match) return true;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
618
631
|
return targetVerObj.exists;
|
|
619
632
|
}
|
|
620
633
|
_diskDelete(key, snapshotVersion) {
|
|
@@ -623,12 +636,9 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
623
636
|
if (strategy.exists(key)) {
|
|
624
637
|
const currentVal = strategy.read(key);
|
|
625
638
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
626
|
-
this.deletedCache.get(key).push({
|
|
627
|
-
|
|
628
|
-
deletedAtVersion: snapshotVersion
|
|
629
|
-
});
|
|
639
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
640
|
+
strategy.delete(key);
|
|
630
641
|
}
|
|
631
|
-
strategy.delete(key);
|
|
632
642
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
633
643
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
634
644
|
}
|
|
@@ -949,84 +959,93 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
949
959
|
if (this.committed) throw new Error("Transaction already committed");
|
|
950
960
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
951
961
|
if (this.deleteBuffer.has(key)) return null;
|
|
952
|
-
|
|
962
|
+
if (this.parent) {
|
|
963
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
964
|
+
}
|
|
965
|
+
return await this._diskRead(key, this.snapshotVersion);
|
|
953
966
|
}
|
|
954
967
|
async exists(key) {
|
|
955
968
|
if (this.committed) throw new Error("Transaction already committed");
|
|
956
969
|
if (this.deleteBuffer.has(key)) return false;
|
|
957
970
|
if (this.writeBuffer.has(key)) return true;
|
|
958
|
-
|
|
971
|
+
if (this.parent) {
|
|
972
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
973
|
+
}
|
|
974
|
+
return await this._diskExists(key, this.snapshotVersion);
|
|
975
|
+
}
|
|
976
|
+
async _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
977
|
+
if (this.writeBuffer.has(key)) {
|
|
978
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
979
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
980
|
+
return true;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (this.deleteBuffer.has(key)) {
|
|
984
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
985
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
986
|
+
return false;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
const history = this.bufferHistory.get(key);
|
|
990
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
991
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
992
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
993
|
+
return history[i].exists;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (this.parent) {
|
|
998
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
999
|
+
} else {
|
|
1000
|
+
return await this._diskExists(key, snapshotVersion);
|
|
1001
|
+
}
|
|
959
1002
|
}
|
|
960
1003
|
async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
961
1004
|
if (this.writeBuffer.has(key)) {
|
|
962
1005
|
const keyModVersion = this.keyVersions.get(key);
|
|
963
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
1006
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
964
1007
|
return this.writeBuffer.get(key);
|
|
965
1008
|
}
|
|
966
1009
|
}
|
|
967
1010
|
if (this.deleteBuffer.has(key)) {
|
|
968
1011
|
const keyModVersion = this.keyVersions.get(key);
|
|
969
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
1012
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
970
1013
|
return null;
|
|
971
1014
|
}
|
|
972
1015
|
}
|
|
1016
|
+
const history = this.bufferHistory.get(key);
|
|
1017
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
1018
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
1019
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
1020
|
+
return history[i].exists ? history[i].value : null;
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
973
1024
|
if (this.parent) {
|
|
974
1025
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
975
1026
|
} else {
|
|
976
|
-
return this._diskRead(key, snapshotVersion);
|
|
1027
|
+
return await this._diskRead(key, snapshotVersion);
|
|
977
1028
|
}
|
|
978
1029
|
}
|
|
979
1030
|
async commit(label) {
|
|
980
1031
|
const { created, updated, deleted } = this.getResultEntries();
|
|
981
1032
|
if (this.committed) {
|
|
982
|
-
return {
|
|
983
|
-
label,
|
|
984
|
-
success: false,
|
|
985
|
-
error: "Transaction already committed",
|
|
986
|
-
conflict: void 0,
|
|
987
|
-
created,
|
|
988
|
-
updated,
|
|
989
|
-
deleted
|
|
990
|
-
};
|
|
1033
|
+
return { label, success: false, error: "Transaction already committed", conflict: void 0, created, updated, deleted };
|
|
991
1034
|
}
|
|
992
1035
|
if (this.hasCommittedAncestor()) {
|
|
993
|
-
return {
|
|
994
|
-
label,
|
|
995
|
-
success: false,
|
|
996
|
-
error: "Ancestor transaction already committed",
|
|
997
|
-
conflict: void 0,
|
|
998
|
-
created,
|
|
999
|
-
updated,
|
|
1000
|
-
deleted
|
|
1001
|
-
};
|
|
1036
|
+
return { label, success: false, error: "Ancestor transaction already committed", conflict: void 0, created, updated, deleted };
|
|
1002
1037
|
}
|
|
1003
1038
|
if (this.parent) {
|
|
1004
1039
|
const failure = await this.parent._merge(this);
|
|
1005
1040
|
if (failure) {
|
|
1006
|
-
return {
|
|
1007
|
-
label,
|
|
1008
|
-
success: false,
|
|
1009
|
-
error: failure.error,
|
|
1010
|
-
conflict: failure.conflict,
|
|
1011
|
-
created,
|
|
1012
|
-
updated,
|
|
1013
|
-
deleted
|
|
1014
|
-
};
|
|
1041
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created, updated, deleted };
|
|
1015
1042
|
}
|
|
1016
1043
|
this.committed = true;
|
|
1017
1044
|
} else {
|
|
1018
1045
|
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
1019
1046
|
const failure = await this._merge(this);
|
|
1020
1047
|
if (failure) {
|
|
1021
|
-
return {
|
|
1022
|
-
label,
|
|
1023
|
-
success: false,
|
|
1024
|
-
error: failure.error,
|
|
1025
|
-
conflict: failure.conflict,
|
|
1026
|
-
created: [],
|
|
1027
|
-
updated: [],
|
|
1028
|
-
deleted: []
|
|
1029
|
-
};
|
|
1048
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created: [], updated: [], deleted: [] };
|
|
1030
1049
|
}
|
|
1031
1050
|
this.writeBuffer.clear();
|
|
1032
1051
|
this.deleteBuffer.clear();
|
|
@@ -1034,17 +1053,12 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1034
1053
|
this.deletedValues.clear();
|
|
1035
1054
|
this.originallyExisted.clear();
|
|
1036
1055
|
this.keyVersions.clear();
|
|
1056
|
+
this.bufferHistory.clear();
|
|
1037
1057
|
this.localVersion = 0;
|
|
1038
1058
|
this.snapshotVersion = this.version;
|
|
1039
1059
|
}
|
|
1040
1060
|
}
|
|
1041
|
-
return {
|
|
1042
|
-
label,
|
|
1043
|
-
success: true,
|
|
1044
|
-
created,
|
|
1045
|
-
updated,
|
|
1046
|
-
deleted
|
|
1047
|
-
};
|
|
1061
|
+
return { label, success: true, created, updated, deleted };
|
|
1048
1062
|
}
|
|
1049
1063
|
async _merge(child) {
|
|
1050
1064
|
return this.writeLock(async () => {
|
|
@@ -1054,11 +1068,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1054
1068
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1055
1069
|
return {
|
|
1056
1070
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
1057
|
-
conflict: {
|
|
1058
|
-
key,
|
|
1059
|
-
parent: await this.read(key),
|
|
1060
|
-
child: await child.read(key)
|
|
1061
|
-
}
|
|
1071
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1062
1072
|
};
|
|
1063
1073
|
}
|
|
1064
1074
|
}
|
|
@@ -1067,41 +1077,27 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1067
1077
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1068
1078
|
return {
|
|
1069
1079
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
1070
|
-
conflict: {
|
|
1071
|
-
key,
|
|
1072
|
-
parent: await this.read(key),
|
|
1073
|
-
child: await child.read(key)
|
|
1074
|
-
}
|
|
1080
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1075
1081
|
};
|
|
1076
1082
|
}
|
|
1077
1083
|
}
|
|
1078
|
-
const
|
|
1079
|
-
for (const key of child.writeBuffer
|
|
1080
|
-
|
|
1081
|
-
this.
|
|
1082
|
-
this.
|
|
1083
|
-
if (child.createdKeys.has(key)) {
|
|
1084
|
-
this.createdKeys.add(key);
|
|
1085
|
-
}
|
|
1084
|
+
const mergeVersion = ++this.localVersion;
|
|
1085
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1086
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1087
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1088
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1086
1089
|
}
|
|
1087
1090
|
for (const key of child.deleteBuffer) {
|
|
1088
|
-
this.deleteBuffer.add(key);
|
|
1089
|
-
this.writeBuffer.delete(key);
|
|
1090
|
-
this.createdKeys.delete(key);
|
|
1091
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
1092
1091
|
const deletedValue = child.deletedValues.get(key);
|
|
1093
|
-
if (deletedValue !== void 0)
|
|
1094
|
-
|
|
1095
|
-
}
|
|
1096
|
-
if (child.originallyExisted.has(key)) {
|
|
1092
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1093
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1097
1094
|
this.originallyExisted.add(key);
|
|
1098
1095
|
}
|
|
1096
|
+
this._bufferDelete(key, mergeVersion);
|
|
1099
1097
|
}
|
|
1100
|
-
this.localVersion = newLocalVersion;
|
|
1101
1098
|
this.root.activeTransactions.delete(child);
|
|
1102
1099
|
return null;
|
|
1103
1100
|
} else {
|
|
1104
|
-
const newVersion = this.version + 1;
|
|
1105
1101
|
if (child !== this) {
|
|
1106
1102
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
1107
1103
|
for (const key of modifiedKeys) {
|
|
@@ -1111,59 +1107,54 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1111
1107
|
if (lastVer > child.snapshotVersion) {
|
|
1112
1108
|
return {
|
|
1113
1109
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
1114
|
-
conflict: {
|
|
1115
|
-
key,
|
|
1116
|
-
parent: await this.read(key),
|
|
1117
|
-
child: await child.read(key)
|
|
1118
|
-
}
|
|
1110
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1119
1111
|
};
|
|
1120
1112
|
}
|
|
1121
1113
|
}
|
|
1114
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
1115
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1116
|
+
return {
|
|
1117
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
1118
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1122
1121
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
for (const key of child.deleteBuffer) {
|
|
1132
|
-
this.deleteBuffer.add(key);
|
|
1133
|
-
this.writeBuffer.delete(key);
|
|
1134
|
-
this.createdKeys.delete(key);
|
|
1135
|
-
const deletedValue = child.deletedValues.get(key);
|
|
1136
|
-
if (deletedValue !== void 0) {
|
|
1137
|
-
this.deletedValues.set(key, deletedValue);
|
|
1122
|
+
const mergeVersion = ++this.localVersion;
|
|
1123
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1124
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1125
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1126
|
+
this.originallyExisted.add(key);
|
|
1127
|
+
}
|
|
1128
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1129
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1138
1130
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1131
|
+
for (const key of child.deleteBuffer) {
|
|
1132
|
+
const deletedValue = child.deletedValues.get(key);
|
|
1133
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1134
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1135
|
+
this.originallyExisted.add(key);
|
|
1136
|
+
}
|
|
1137
|
+
this._bufferDelete(key, mergeVersion);
|
|
1141
1138
|
}
|
|
1139
|
+
this.root.activeTransactions.delete(child);
|
|
1140
|
+
} else {
|
|
1141
|
+
const newVersion = this.version + 1;
|
|
1142
|
+
for (const [key, value] of this.writeBuffer) await this._diskWrite(key, value, newVersion);
|
|
1143
|
+
for (const key of this.deleteBuffer) await this._diskDelete(key, newVersion);
|
|
1144
|
+
this.version = newVersion;
|
|
1145
|
+
this._cleanupDeletedCache();
|
|
1142
1146
|
}
|
|
1143
|
-
for (const [key, value] of child.writeBuffer) {
|
|
1144
|
-
await this._diskWrite(key, value, newVersion);
|
|
1145
|
-
}
|
|
1146
|
-
for (const key of child.deleteBuffer) {
|
|
1147
|
-
await this._diskDelete(key, newVersion);
|
|
1148
|
-
}
|
|
1149
|
-
this.version = newVersion;
|
|
1150
|
-
this.root.activeTransactions.delete(child);
|
|
1151
|
-
this._cleanupDeletedCache();
|
|
1152
1147
|
return null;
|
|
1153
1148
|
}
|
|
1154
1149
|
});
|
|
1155
1150
|
}
|
|
1156
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
1157
1151
|
async _diskWrite(key, value, version) {
|
|
1158
1152
|
const strategy = this.strategy;
|
|
1159
1153
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1160
1154
|
if (await strategy.exists(key)) {
|
|
1161
1155
|
const currentVal = await strategy.read(key);
|
|
1162
1156
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1163
|
-
this.deletedCache.get(key).push({
|
|
1164
|
-
value: currentVal,
|
|
1165
|
-
deletedAtVersion: version
|
|
1166
|
-
});
|
|
1157
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
1167
1158
|
}
|
|
1168
1159
|
await strategy.write(key, value);
|
|
1169
1160
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -1174,14 +1165,13 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1174
1165
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1175
1166
|
const versions = this.versionIndex.get(key);
|
|
1176
1167
|
if (!versions) {
|
|
1177
|
-
return await strategy.exists(key) ? strategy.read(key) : null;
|
|
1168
|
+
return await strategy.exists(key) ? await strategy.read(key) : null;
|
|
1178
1169
|
}
|
|
1179
1170
|
let targetVerObj = null;
|
|
1180
1171
|
let nextVerObj = null;
|
|
1181
1172
|
for (const v of versions) {
|
|
1182
|
-
if (v.version <= snapshotVersion)
|
|
1183
|
-
|
|
1184
|
-
} else {
|
|
1173
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
1174
|
+
else {
|
|
1185
1175
|
nextVerObj = v;
|
|
1186
1176
|
break;
|
|
1187
1177
|
}
|
|
@@ -1198,6 +1188,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1198
1188
|
}
|
|
1199
1189
|
if (!targetVerObj.exists) return null;
|
|
1200
1190
|
if (!nextVerObj) {
|
|
1191
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
1201
1192
|
return strategy.read(key);
|
|
1202
1193
|
}
|
|
1203
1194
|
const cached = this.deletedCache.get(key);
|
|
@@ -1212,17 +1203,27 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1212
1203
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1213
1204
|
const versions = this.versionIndex.get(key);
|
|
1214
1205
|
if (!versions) {
|
|
1215
|
-
return strategy.exists(key);
|
|
1206
|
+
return await strategy.exists(key);
|
|
1216
1207
|
}
|
|
1217
1208
|
let targetVerObj = null;
|
|
1209
|
+
let nextVerObj = null;
|
|
1218
1210
|
for (const v of versions) {
|
|
1219
|
-
if (v.version <= snapshotVersion)
|
|
1220
|
-
|
|
1221
|
-
|
|
1211
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
1212
|
+
else {
|
|
1213
|
+
nextVerObj = v;
|
|
1222
1214
|
break;
|
|
1223
1215
|
}
|
|
1224
1216
|
}
|
|
1225
|
-
if (!targetVerObj)
|
|
1217
|
+
if (!targetVerObj) {
|
|
1218
|
+
if (nextVerObj) {
|
|
1219
|
+
const cached = this.deletedCache.get(key);
|
|
1220
|
+
if (cached) {
|
|
1221
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
1222
|
+
if (match) return true;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
return false;
|
|
1226
|
+
}
|
|
1226
1227
|
return targetVerObj.exists;
|
|
1227
1228
|
}
|
|
1228
1229
|
async _diskDelete(key, snapshotVersion) {
|
|
@@ -1231,12 +1232,9 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1231
1232
|
if (await strategy.exists(key)) {
|
|
1232
1233
|
const currentVal = await strategy.read(key);
|
|
1233
1234
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1234
|
-
this.deletedCache.get(key).push({
|
|
1235
|
-
|
|
1236
|
-
deletedAtVersion: snapshotVersion
|
|
1237
|
-
});
|
|
1235
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
1236
|
+
await strategy.delete(key);
|
|
1238
1237
|
}
|
|
1239
|
-
await strategy.delete(key);
|
|
1240
1238
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
1241
1239
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
1242
1240
|
}
|
|
@@ -1574,6 +1572,8 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1574
1572
|
option;
|
|
1575
1573
|
order;
|
|
1576
1574
|
rootId;
|
|
1575
|
+
isInitialized = false;
|
|
1576
|
+
isDestroyed = false;
|
|
1577
1577
|
verifierMap = {
|
|
1578
1578
|
gt: (nv, v) => this.comparator.isHigher(nv, v),
|
|
1579
1579
|
gte: (nv, v) => this.comparator.isHigher(nv, v) || this.comparator.isSame(nv, v),
|
|
@@ -1838,13 +1838,26 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1838
1838
|
getResultEntries() {
|
|
1839
1839
|
return this.mvcc.getResultEntries();
|
|
1840
1840
|
}
|
|
1841
|
+
_clearCache() {
|
|
1842
|
+
this._cachedRegexp.clear();
|
|
1843
|
+
this.nodes.clear();
|
|
1844
|
+
}
|
|
1841
1845
|
/**
|
|
1842
1846
|
* Clears all cached nodes.
|
|
1843
1847
|
* This method is useful for freeing up memory when the tree is no longer needed.
|
|
1844
1848
|
*/
|
|
1845
1849
|
clear() {
|
|
1846
|
-
this.
|
|
1847
|
-
|
|
1850
|
+
if (this.rootTx !== this) {
|
|
1851
|
+
throw new Error("Cannot call clear on a nested transaction");
|
|
1852
|
+
}
|
|
1853
|
+
this._clearInternal();
|
|
1854
|
+
}
|
|
1855
|
+
_clearInternal() {
|
|
1856
|
+
if (this.isDestroyed) {
|
|
1857
|
+
throw new Error("Transaction already destroyed");
|
|
1858
|
+
}
|
|
1859
|
+
this._clearCache();
|
|
1860
|
+
this.isDestroyed = true;
|
|
1848
1861
|
}
|
|
1849
1862
|
_binarySearchValues(values, target, usePrimary = false, upperBound = false) {
|
|
1850
1863
|
let low = 0;
|
|
@@ -2148,28 +2161,46 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2148
2161
|
}
|
|
2149
2162
|
}
|
|
2150
2163
|
init() {
|
|
2151
|
-
this.
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
const { root, order } = head;
|
|
2163
|
-
this.strategy.head = head;
|
|
2164
|
-
this.order = order;
|
|
2165
|
-
this._writeHead({
|
|
2166
|
-
root,
|
|
2167
|
-
order: this.order,
|
|
2168
|
-
data: this.strategy.head.data
|
|
2169
|
-
});
|
|
2164
|
+
if (this.rootTx !== this) {
|
|
2165
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
2166
|
+
}
|
|
2167
|
+
this._initInternal();
|
|
2168
|
+
}
|
|
2169
|
+
_initInternal() {
|
|
2170
|
+
if (this.isInitialized) {
|
|
2171
|
+
throw new Error("Transaction already initialized");
|
|
2172
|
+
}
|
|
2173
|
+
if (this.isDestroyed) {
|
|
2174
|
+
throw new Error("Transaction already destroyed");
|
|
2170
2175
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2176
|
+
this.isInitialized = true;
|
|
2177
|
+
try {
|
|
2178
|
+
this._clearCache();
|
|
2179
|
+
const head = this._readHead();
|
|
2180
|
+
if (head === null) {
|
|
2181
|
+
this.order = this.strategy.order;
|
|
2182
|
+
const root = this._createNode(true, [], []);
|
|
2183
|
+
this._writeHead({
|
|
2184
|
+
root: root.id,
|
|
2185
|
+
order: this.order,
|
|
2186
|
+
data: this.strategy.head.data
|
|
2187
|
+
});
|
|
2188
|
+
} else {
|
|
2189
|
+
const { root, order } = head;
|
|
2190
|
+
this.strategy.head = head;
|
|
2191
|
+
this.order = order;
|
|
2192
|
+
this._writeHead({
|
|
2193
|
+
root,
|
|
2194
|
+
order: this.order,
|
|
2195
|
+
data: this.strategy.head.data
|
|
2196
|
+
});
|
|
2197
|
+
}
|
|
2198
|
+
if (this.order < 3) {
|
|
2199
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
2200
|
+
}
|
|
2201
|
+
} catch (e) {
|
|
2202
|
+
this.isInitialized = false;
|
|
2203
|
+
throw e;
|
|
2173
2204
|
}
|
|
2174
2205
|
}
|
|
2175
2206
|
exists(key, value) {
|
|
@@ -2547,9 +2578,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2547
2578
|
commit(label) {
|
|
2548
2579
|
let result = this.mvcc.commit(label);
|
|
2549
2580
|
if (result.success) {
|
|
2550
|
-
|
|
2551
|
-
if (
|
|
2552
|
-
|
|
2581
|
+
const isRootTx = this.rootTx !== this;
|
|
2582
|
+
if (isRootTx) {
|
|
2583
|
+
result = this.rootTx.commit(label);
|
|
2584
|
+
if (result.success) {
|
|
2585
|
+
this.rootTx.rootId = this.rootId;
|
|
2586
|
+
}
|
|
2553
2587
|
}
|
|
2554
2588
|
if (result.success) {
|
|
2555
2589
|
for (const r of result.created) {
|
|
@@ -2628,7 +2662,7 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2628
2662
|
this.comparator,
|
|
2629
2663
|
this.option
|
|
2630
2664
|
);
|
|
2631
|
-
tx.
|
|
2665
|
+
tx._initInternal();
|
|
2632
2666
|
return tx;
|
|
2633
2667
|
}
|
|
2634
2668
|
insert(key, value) {
|
|
@@ -2638,7 +2672,6 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2638
2672
|
if (!result.success) {
|
|
2639
2673
|
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2640
2674
|
}
|
|
2641
|
-
this.rootId = tx.getRootId();
|
|
2642
2675
|
}
|
|
2643
2676
|
delete(key, value) {
|
|
2644
2677
|
const tx = this.createTransaction();
|
|
@@ -2647,319 +2680,603 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2647
2680
|
if (!result.success) {
|
|
2648
2681
|
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2649
2682
|
}
|
|
2650
|
-
this.rootId = tx.getRootId();
|
|
2651
2683
|
}
|
|
2652
2684
|
};
|
|
2653
|
-
var
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
comparator,
|
|
2661
|
-
option
|
|
2662
|
-
);
|
|
2685
|
+
var Ryoiki2 = class _Ryoiki2 {
|
|
2686
|
+
readings;
|
|
2687
|
+
writings;
|
|
2688
|
+
readQueue;
|
|
2689
|
+
writeQueue;
|
|
2690
|
+
static async CatchError(promise) {
|
|
2691
|
+
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
2663
2692
|
}
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2693
|
+
static IsRangeOverlap(a, b) {
|
|
2694
|
+
const [start1, end1] = a;
|
|
2695
|
+
const [start2, end2] = b;
|
|
2696
|
+
if (end1 <= start2 || end2 <= start1) {
|
|
2697
|
+
return false;
|
|
2667
2698
|
}
|
|
2668
|
-
return
|
|
2699
|
+
return true;
|
|
2700
|
+
}
|
|
2701
|
+
static ERR_ALREADY_EXISTS(lockId) {
|
|
2702
|
+
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
2703
|
+
}
|
|
2704
|
+
static ERR_NOT_EXISTS(lockId) {
|
|
2705
|
+
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
2706
|
+
}
|
|
2707
|
+
static ERR_TIMEOUT(lockId, timeout) {
|
|
2708
|
+
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
2669
2709
|
}
|
|
2670
2710
|
/**
|
|
2671
|
-
*
|
|
2711
|
+
* Constructs a new instance of the Ryoiki class.
|
|
2672
2712
|
*/
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
values,
|
|
2679
|
-
leaf,
|
|
2680
|
-
parent,
|
|
2681
|
-
next,
|
|
2682
|
-
prev
|
|
2683
|
-
};
|
|
2684
|
-
await this.mvcc.create(id, node);
|
|
2685
|
-
this.nodes.set(id, node);
|
|
2686
|
-
return node;
|
|
2713
|
+
constructor() {
|
|
2714
|
+
this.readings = /* @__PURE__ */ new Map();
|
|
2715
|
+
this.writings = /* @__PURE__ */ new Map();
|
|
2716
|
+
this.readQueue = /* @__PURE__ */ new Map();
|
|
2717
|
+
this.writeQueue = /* @__PURE__ */ new Map();
|
|
2687
2718
|
}
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2719
|
+
/**
|
|
2720
|
+
* Creates a range based on a start value and length.
|
|
2721
|
+
* @param start - The starting value of the range.
|
|
2722
|
+
* @param length - The length of the range.
|
|
2723
|
+
* @returns A range tuple [start, start + length].
|
|
2724
|
+
*/
|
|
2725
|
+
range(start, length) {
|
|
2726
|
+
return [start, start + length];
|
|
2691
2727
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
this.nodes.delete(node.id);
|
|
2728
|
+
rangeOverlapping(tasks, range) {
|
|
2729
|
+
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
2695
2730
|
}
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
const head = await this.mvcc.read("__HEAD__");
|
|
2701
|
-
return head ?? null;
|
|
2702
|
-
}
|
|
2703
|
-
async _writeHead(head) {
|
|
2704
|
-
if (!await this.mvcc.exists("__HEAD__")) {
|
|
2705
|
-
await this.mvcc.create("__HEAD__", head);
|
|
2706
|
-
} else {
|
|
2707
|
-
await this.mvcc.write("__HEAD__", head);
|
|
2708
|
-
}
|
|
2709
|
-
this.nodes.set("__HEAD__", head);
|
|
2710
|
-
this.rootId = head.root;
|
|
2731
|
+
isSameRange(a, b) {
|
|
2732
|
+
const [a1, a2] = a;
|
|
2733
|
+
const [b1, b2] = b;
|
|
2734
|
+
return a1 === b1 && a2 === b2;
|
|
2711
2735
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
2717
|
-
const keys = node.keys[i];
|
|
2718
|
-
if (keys.includes(key)) {
|
|
2719
|
-
break;
|
|
2720
|
-
}
|
|
2721
|
-
keys.push(key);
|
|
2722
|
-
await this._updateNode(node);
|
|
2723
|
-
return;
|
|
2724
|
-
} else if (this.comparator.isLower(value, nValue)) {
|
|
2725
|
-
node.values.splice(i, 0, value);
|
|
2726
|
-
node.keys.splice(i, 0, [key]);
|
|
2727
|
-
await this._updateNode(node);
|
|
2728
|
-
return;
|
|
2729
|
-
} else if (i + 1 === node.values.length) {
|
|
2730
|
-
node.values.push(value);
|
|
2731
|
-
node.keys.push([key]);
|
|
2732
|
-
await this._updateNode(node);
|
|
2733
|
-
return;
|
|
2734
|
-
}
|
|
2736
|
+
fetchUnitAndRun(queue, workspaces) {
|
|
2737
|
+
for (const [id, unit] of queue) {
|
|
2738
|
+
if (!unit.condition()) {
|
|
2739
|
+
continue;
|
|
2735
2740
|
}
|
|
2736
|
-
|
|
2737
|
-
node.values = [value];
|
|
2738
|
-
node.keys = [[key]];
|
|
2739
|
-
await this._updateNode(node);
|
|
2740
|
-
return;
|
|
2741
|
+
this._alloc(queue, workspaces, id);
|
|
2741
2742
|
}
|
|
2742
2743
|
}
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
node.parent = root.id;
|
|
2748
|
-
pointer.parent = root.id;
|
|
2749
|
-
if (pointer.leaf) {
|
|
2750
|
-
node.next = pointer.id;
|
|
2751
|
-
pointer.prev = node.id;
|
|
2752
|
-
}
|
|
2753
|
-
await this._writeHead({
|
|
2754
|
-
root: root.id,
|
|
2755
|
-
order: this.order,
|
|
2756
|
-
data: this.strategy.head.data
|
|
2757
|
-
});
|
|
2758
|
-
await this._updateNode(node);
|
|
2759
|
-
await this._updateNode(pointer);
|
|
2760
|
-
return;
|
|
2761
|
-
}
|
|
2762
|
-
const parentNode = await this.getNode(node.parent);
|
|
2763
|
-
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2764
|
-
if (nodeIndex === -1) {
|
|
2765
|
-
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
2766
|
-
}
|
|
2767
|
-
parentNode.values.splice(nodeIndex, 0, value);
|
|
2768
|
-
parentNode.keys.splice(nodeIndex + 1, 0, pointer.id);
|
|
2769
|
-
pointer.parent = parentNode.id;
|
|
2770
|
-
if (pointer.leaf) {
|
|
2771
|
-
const leftSibling = node;
|
|
2772
|
-
const oldNextId = leftSibling.next;
|
|
2773
|
-
pointer.prev = leftSibling.id;
|
|
2774
|
-
pointer.next = oldNextId;
|
|
2775
|
-
leftSibling.next = pointer.id;
|
|
2776
|
-
await this._updateNode(leftSibling);
|
|
2777
|
-
if (oldNextId) {
|
|
2778
|
-
const oldNext = await this.getNode(oldNextId);
|
|
2779
|
-
oldNext.prev = pointer.id;
|
|
2780
|
-
await this._updateNode(oldNext);
|
|
2781
|
-
}
|
|
2782
|
-
}
|
|
2783
|
-
await this._updateNode(parentNode);
|
|
2784
|
-
await this._updateNode(pointer);
|
|
2785
|
-
if (parentNode.keys.length > this.order) {
|
|
2786
|
-
const parentPointer = await this._createNode(false, [], []);
|
|
2787
|
-
parentPointer.parent = parentNode.parent;
|
|
2788
|
-
const mid = Math.ceil(this.order / 2) - 1;
|
|
2789
|
-
parentPointer.values = parentNode.values.slice(mid + 1);
|
|
2790
|
-
parentPointer.keys = parentNode.keys.slice(mid + 1);
|
|
2791
|
-
const midValue = parentNode.values[mid];
|
|
2792
|
-
parentNode.values = parentNode.values.slice(0, mid);
|
|
2793
|
-
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2794
|
-
for (const k of parentNode.keys) {
|
|
2795
|
-
const n = await this.getNode(k);
|
|
2796
|
-
n.parent = parentNode.id;
|
|
2797
|
-
await this._updateNode(n);
|
|
2798
|
-
}
|
|
2799
|
-
for (const k of parentPointer.keys) {
|
|
2800
|
-
const n = await this.getNode(k);
|
|
2801
|
-
n.parent = parentPointer.id;
|
|
2802
|
-
await this._updateNode(n);
|
|
2744
|
+
_handleOverload(args, handlers, argPatterns) {
|
|
2745
|
+
for (const [key, pattern] of Object.entries(argPatterns)) {
|
|
2746
|
+
if (this._matchArgs(args, pattern)) {
|
|
2747
|
+
return handlers[key](...args);
|
|
2803
2748
|
}
|
|
2804
|
-
await this._updateNode(parentNode);
|
|
2805
|
-
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2806
2749
|
}
|
|
2750
|
+
throw new Error("Invalid arguments");
|
|
2807
2751
|
}
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2752
|
+
_matchArgs(args, pattern) {
|
|
2753
|
+
return args.every((arg, index) => {
|
|
2754
|
+
const expectedType = pattern[index];
|
|
2755
|
+
if (expectedType === void 0) return typeof arg === "undefined";
|
|
2756
|
+
if (expectedType === Function) return typeof arg === "function";
|
|
2757
|
+
if (expectedType === Number) return typeof arg === "number";
|
|
2758
|
+
if (expectedType === Array) return Array.isArray(arg);
|
|
2759
|
+
return false;
|
|
2760
|
+
});
|
|
2815
2761
|
}
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
node = await this.getNode(node.keys[index]);
|
|
2821
|
-
}
|
|
2822
|
-
return node;
|
|
2762
|
+
_createRandomId() {
|
|
2763
|
+
const timestamp = Date.now().toString(36);
|
|
2764
|
+
const random = Math.random().toString(36).substring(2);
|
|
2765
|
+
return `${timestamp}${random}`;
|
|
2823
2766
|
}
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
node = await this.getNode(node.keys[index]);
|
|
2767
|
+
_alloc(queue, workspaces, lockId) {
|
|
2768
|
+
const unit = queue.get(lockId);
|
|
2769
|
+
if (!unit) {
|
|
2770
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2829
2771
|
}
|
|
2830
|
-
|
|
2772
|
+
workspaces.set(lockId, unit);
|
|
2773
|
+
queue.delete(lockId);
|
|
2774
|
+
unit.alloc();
|
|
2831
2775
|
}
|
|
2832
|
-
|
|
2833
|
-
const
|
|
2834
|
-
if (!
|
|
2835
|
-
|
|
2776
|
+
_free(workspaces, lockId) {
|
|
2777
|
+
const unit = workspaces.get(lockId);
|
|
2778
|
+
if (!unit) {
|
|
2779
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2836
2780
|
}
|
|
2837
|
-
|
|
2781
|
+
workspaces.delete(lockId);
|
|
2782
|
+
unit.free();
|
|
2838
2783
|
}
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2784
|
+
_lock(queue, range, timeout, task, condition) {
|
|
2785
|
+
return new Promise((resolve, reject) => {
|
|
2786
|
+
let timeoutId = null;
|
|
2787
|
+
if (timeout >= 0) {
|
|
2788
|
+
timeoutId = setTimeout(() => {
|
|
2789
|
+
reject(_Ryoiki2.ERR_TIMEOUT(id, timeout));
|
|
2790
|
+
}, timeout);
|
|
2791
|
+
}
|
|
2792
|
+
const id = this._createRandomId();
|
|
2793
|
+
const alloc = async () => {
|
|
2794
|
+
if (timeoutId !== null) {
|
|
2795
|
+
clearTimeout(timeoutId);
|
|
2796
|
+
}
|
|
2797
|
+
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
2798
|
+
if (err) reject(err);
|
|
2799
|
+
else resolve(v);
|
|
2800
|
+
};
|
|
2801
|
+
const fetch = () => {
|
|
2802
|
+
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
2803
|
+
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
2804
|
+
};
|
|
2805
|
+
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
2806
|
+
fetch();
|
|
2807
|
+
});
|
|
2808
|
+
}
|
|
2809
|
+
_checkWorking(range, workspaces) {
|
|
2810
|
+
let isLocked = false;
|
|
2811
|
+
for (const lock of workspaces.values()) {
|
|
2812
|
+
if (_Ryoiki2.IsRangeOverlap(range, lock.range)) {
|
|
2813
|
+
isLocked = true;
|
|
2848
2814
|
break;
|
|
2849
|
-
|
|
2850
|
-
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
2851
|
-
}
|
|
2852
|
-
const guessNode = insertableNode[key];
|
|
2853
|
-
if (!guessNode) {
|
|
2854
|
-
return null;
|
|
2815
|
+
}
|
|
2855
2816
|
}
|
|
2856
|
-
return
|
|
2817
|
+
return isLocked;
|
|
2857
2818
|
}
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
node = await this.getNode(keys[0]);
|
|
2866
|
-
}
|
|
2867
|
-
return node;
|
|
2819
|
+
/**
|
|
2820
|
+
* Checks if there is any active read lock within the specified range.
|
|
2821
|
+
* @param range The range to check for active read locks.
|
|
2822
|
+
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
2823
|
+
*/
|
|
2824
|
+
isReading(range) {
|
|
2825
|
+
return this._checkWorking(range, this.readings);
|
|
2868
2826
|
}
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
return
|
|
2827
|
+
/**
|
|
2828
|
+
* Checks if there is any active write lock within the specified range.
|
|
2829
|
+
* @param range The range to check for active write locks.
|
|
2830
|
+
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
2831
|
+
*/
|
|
2832
|
+
isWriting(range) {
|
|
2833
|
+
return this._checkWorking(range, this.writings);
|
|
2876
2834
|
}
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2835
|
+
/**
|
|
2836
|
+
* Checks if a read lock can be acquired within the specified range.
|
|
2837
|
+
* @param range The range to check for read lock availability.
|
|
2838
|
+
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
2839
|
+
*/
|
|
2840
|
+
canRead(range) {
|
|
2841
|
+
const writing = this.isWriting(range);
|
|
2842
|
+
return !writing;
|
|
2843
|
+
}
|
|
2844
|
+
/**
|
|
2845
|
+
* Checks if a write lock can be acquired within the specified range.
|
|
2846
|
+
* @param range The range to check for write lock availability.
|
|
2847
|
+
* @returns `true` if a write lock can be acquired, `false` otherwise.
|
|
2848
|
+
*/
|
|
2849
|
+
canWrite(range) {
|
|
2850
|
+
const reading = this.isReading(range);
|
|
2851
|
+
const writing = this.isWriting(range);
|
|
2852
|
+
return !reading && !writing;
|
|
2853
|
+
}
|
|
2854
|
+
/**
|
|
2855
|
+
* Internal implementation of the read lock. Handles both overloads.
|
|
2856
|
+
* @template T - The return type of the task.
|
|
2857
|
+
* @param arg0 - Either a range or a task callback.
|
|
2858
|
+
* If a range is provided, the task is the second argument.
|
|
2859
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2860
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2861
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2862
|
+
* If this value is not provided, no timeout will be set.
|
|
2863
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2864
|
+
*/
|
|
2865
|
+
readLock(arg0, arg1, arg2) {
|
|
2866
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2867
|
+
[arg0, arg1, arg2],
|
|
2868
|
+
{
|
|
2869
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2870
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2871
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2872
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2873
|
+
},
|
|
2874
|
+
{
|
|
2875
|
+
task: [Function],
|
|
2876
|
+
taskTimeout: [Function, Number],
|
|
2877
|
+
rangeTask: [Array, Function],
|
|
2878
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2886
2879
|
}
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2880
|
+
);
|
|
2881
|
+
return this._lock(
|
|
2882
|
+
this.readQueue,
|
|
2883
|
+
range,
|
|
2884
|
+
timeout,
|
|
2885
|
+
task,
|
|
2886
|
+
() => !this.rangeOverlapping(this.writings, range)
|
|
2887
|
+
);
|
|
2888
|
+
}
|
|
2889
|
+
/**
|
|
2890
|
+
* Internal implementation of the write lock. Handles both overloads.
|
|
2891
|
+
* @template T - The return type of the task.
|
|
2892
|
+
* @param arg0 - Either a range or a task callback.
|
|
2893
|
+
* If a range is provided, the task is the second argument.
|
|
2894
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2895
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2896
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2897
|
+
* If this value is not provided, no timeout will be set.
|
|
2898
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2899
|
+
*/
|
|
2900
|
+
writeLock(arg0, arg1, arg2) {
|
|
2901
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2902
|
+
[arg0, arg1, arg2],
|
|
2903
|
+
{
|
|
2904
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2905
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2906
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2907
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2908
|
+
},
|
|
2909
|
+
{
|
|
2910
|
+
task: [Function],
|
|
2911
|
+
taskTimeout: [Function, Number],
|
|
2912
|
+
rangeTask: [Array, Function],
|
|
2913
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2895
2914
|
}
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
+
);
|
|
2916
|
+
return this._lock(
|
|
2917
|
+
this.writeQueue,
|
|
2918
|
+
range,
|
|
2919
|
+
timeout,
|
|
2920
|
+
task,
|
|
2921
|
+
() => {
|
|
2922
|
+
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
2923
|
+
}
|
|
2924
|
+
);
|
|
2925
|
+
}
|
|
2926
|
+
/**
|
|
2927
|
+
* Releases a read lock by its lock ID.
|
|
2928
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2929
|
+
*/
|
|
2930
|
+
readUnlock(lockId) {
|
|
2931
|
+
this._free(this.readings, lockId);
|
|
2932
|
+
}
|
|
2933
|
+
/**
|
|
2934
|
+
* Releases a write lock by its lock ID.
|
|
2935
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2936
|
+
*/
|
|
2937
|
+
writeUnlock(lockId) {
|
|
2938
|
+
this._free(this.writings, lockId);
|
|
2939
|
+
}
|
|
2940
|
+
};
|
|
2941
|
+
var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
2942
|
+
lock;
|
|
2943
|
+
constructor(rootTx, mvccRoot, mvcc, strategy, comparator, option) {
|
|
2944
|
+
super(
|
|
2945
|
+
rootTx,
|
|
2946
|
+
mvccRoot,
|
|
2947
|
+
mvcc,
|
|
2948
|
+
strategy,
|
|
2949
|
+
comparator,
|
|
2950
|
+
option
|
|
2951
|
+
);
|
|
2952
|
+
this.lock = new Ryoiki2();
|
|
2953
|
+
}
|
|
2954
|
+
async writeLock(id, fn) {
|
|
2955
|
+
let lockId;
|
|
2956
|
+
return this.lock.writeLock([id, id + 0.1], async (_lockId) => {
|
|
2957
|
+
lockId = _lockId;
|
|
2958
|
+
return fn();
|
|
2959
|
+
}).finally(() => {
|
|
2960
|
+
this.lock.writeUnlock(lockId);
|
|
2961
|
+
});
|
|
2962
|
+
}
|
|
2963
|
+
async getNode(id) {
|
|
2964
|
+
if (this.nodes.has(id)) {
|
|
2965
|
+
return this.nodes.get(id);
|
|
2966
|
+
}
|
|
2967
|
+
return await this.mvcc.read(id);
|
|
2968
|
+
}
|
|
2969
|
+
/**
|
|
2970
|
+
* Create a new node with a unique ID.
|
|
2971
|
+
*/
|
|
2972
|
+
async _createNode(leaf, keys, values, parent = null, next = null, prev = null) {
|
|
2973
|
+
const id = await this.strategy.id(leaf);
|
|
2974
|
+
const node = {
|
|
2975
|
+
id,
|
|
2976
|
+
keys,
|
|
2977
|
+
values,
|
|
2978
|
+
leaf,
|
|
2979
|
+
parent,
|
|
2980
|
+
next,
|
|
2981
|
+
prev
|
|
2982
|
+
};
|
|
2983
|
+
await this.mvcc.create(id, node);
|
|
2984
|
+
this.nodes.set(id, node);
|
|
2985
|
+
return node;
|
|
2986
|
+
}
|
|
2987
|
+
async _updateNode(node) {
|
|
2988
|
+
await this.mvcc.write(node.id, node);
|
|
2989
|
+
this.nodes.set(node.id, node);
|
|
2990
|
+
}
|
|
2991
|
+
async _deleteNode(node) {
|
|
2992
|
+
await this.mvcc.delete(node.id);
|
|
2993
|
+
this.nodes.delete(node.id);
|
|
2994
|
+
}
|
|
2995
|
+
async _readHead() {
|
|
2996
|
+
if (this.nodes.has("__HEAD__")) {
|
|
2997
|
+
return this.nodes.get("__HEAD__") ?? null;
|
|
2998
|
+
}
|
|
2999
|
+
const head = await this.mvcc.read("__HEAD__");
|
|
3000
|
+
return head ?? null;
|
|
3001
|
+
}
|
|
3002
|
+
async _writeHead(head) {
|
|
3003
|
+
if (!await this.mvcc.exists("__HEAD__")) {
|
|
3004
|
+
await this.mvcc.create("__HEAD__", head);
|
|
3005
|
+
} else {
|
|
3006
|
+
await this.mvcc.write("__HEAD__", head);
|
|
3007
|
+
}
|
|
3008
|
+
this.nodes.set("__HEAD__", head);
|
|
3009
|
+
this.rootId = head.root;
|
|
3010
|
+
}
|
|
3011
|
+
async _insertAtLeaf(node, key, value) {
|
|
3012
|
+
if (node.values.length) {
|
|
3013
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
3014
|
+
const nValue = node.values[i];
|
|
3015
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
2915
3016
|
const keys = node.keys[i];
|
|
2916
|
-
if (
|
|
2917
|
-
hasMatched = true;
|
|
2918
|
-
let j = keys.length;
|
|
2919
|
-
while (j--) {
|
|
2920
|
-
yield [keys[j], nValue];
|
|
2921
|
-
}
|
|
2922
|
-
} else if (earlyTerminate && hasMatched) {
|
|
2923
|
-
done = true;
|
|
3017
|
+
if (keys.includes(key)) {
|
|
2924
3018
|
break;
|
|
2925
3019
|
}
|
|
3020
|
+
keys.push(key);
|
|
3021
|
+
await this._updateNode(node);
|
|
3022
|
+
return;
|
|
3023
|
+
} else if (this.comparator.isLower(value, nValue)) {
|
|
3024
|
+
node.values.splice(i, 0, value);
|
|
3025
|
+
node.keys.splice(i, 0, [key]);
|
|
3026
|
+
await this._updateNode(node);
|
|
3027
|
+
return;
|
|
3028
|
+
} else if (i + 1 === node.values.length) {
|
|
3029
|
+
node.values.push(value);
|
|
3030
|
+
node.keys.push([key]);
|
|
3031
|
+
await this._updateNode(node);
|
|
3032
|
+
return;
|
|
2926
3033
|
}
|
|
2927
3034
|
}
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
node = await nextNodePromise;
|
|
2934
|
-
nextNodePromise = null;
|
|
2935
|
-
} else {
|
|
2936
|
-
done = true;
|
|
2937
|
-
}
|
|
3035
|
+
} else {
|
|
3036
|
+
node.values = [value];
|
|
3037
|
+
node.keys = [[key]];
|
|
3038
|
+
await this._updateNode(node);
|
|
3039
|
+
return;
|
|
2938
3040
|
}
|
|
2939
3041
|
}
|
|
2940
|
-
async
|
|
2941
|
-
this.
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
3042
|
+
async _insertInParent(node, value, pointer) {
|
|
3043
|
+
if (this.rootId === node.id) {
|
|
3044
|
+
const root = await this._createNode(false, [node.id, pointer.id], [value]);
|
|
3045
|
+
this.rootId = root.id;
|
|
3046
|
+
node.parent = root.id;
|
|
3047
|
+
pointer.parent = root.id;
|
|
3048
|
+
if (pointer.leaf) {
|
|
3049
|
+
node.next = pointer.id;
|
|
3050
|
+
pointer.prev = node.id;
|
|
3051
|
+
}
|
|
2946
3052
|
await this._writeHead({
|
|
2947
3053
|
root: root.id,
|
|
2948
3054
|
order: this.order,
|
|
2949
3055
|
data: this.strategy.head.data
|
|
2950
3056
|
});
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
3057
|
+
await this._updateNode(node);
|
|
3058
|
+
await this._updateNode(pointer);
|
|
3059
|
+
return;
|
|
3060
|
+
}
|
|
3061
|
+
const parentNode = await this.getNode(node.parent);
|
|
3062
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
3063
|
+
if (nodeIndex === -1) {
|
|
3064
|
+
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
3065
|
+
}
|
|
3066
|
+
parentNode.values.splice(nodeIndex, 0, value);
|
|
3067
|
+
parentNode.keys.splice(nodeIndex + 1, 0, pointer.id);
|
|
3068
|
+
pointer.parent = parentNode.id;
|
|
3069
|
+
if (pointer.leaf) {
|
|
3070
|
+
const leftSibling = node;
|
|
3071
|
+
const oldNextId = leftSibling.next;
|
|
3072
|
+
pointer.prev = leftSibling.id;
|
|
3073
|
+
pointer.next = oldNextId;
|
|
3074
|
+
leftSibling.next = pointer.id;
|
|
3075
|
+
await this._updateNode(leftSibling);
|
|
3076
|
+
if (oldNextId) {
|
|
3077
|
+
const oldNext = await this.getNode(oldNextId);
|
|
3078
|
+
oldNext.prev = pointer.id;
|
|
3079
|
+
await this._updateNode(oldNext);
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
await this._updateNode(parentNode);
|
|
3083
|
+
await this._updateNode(pointer);
|
|
3084
|
+
if (parentNode.keys.length > this.order) {
|
|
3085
|
+
const parentPointer = await this._createNode(false, [], []);
|
|
3086
|
+
parentPointer.parent = parentNode.parent;
|
|
3087
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
3088
|
+
parentPointer.values = parentNode.values.slice(mid + 1);
|
|
3089
|
+
parentPointer.keys = parentNode.keys.slice(mid + 1);
|
|
3090
|
+
const midValue = parentNode.values[mid];
|
|
3091
|
+
parentNode.values = parentNode.values.slice(0, mid);
|
|
3092
|
+
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
3093
|
+
for (const k of parentNode.keys) {
|
|
3094
|
+
const n = await this.getNode(k);
|
|
3095
|
+
n.parent = parentNode.id;
|
|
3096
|
+
await this._updateNode(n);
|
|
3097
|
+
}
|
|
3098
|
+
for (const k of parentPointer.keys) {
|
|
3099
|
+
const n = await this.getNode(k);
|
|
3100
|
+
n.parent = parentPointer.id;
|
|
3101
|
+
await this._updateNode(n);
|
|
3102
|
+
}
|
|
3103
|
+
await this._updateNode(parentNode);
|
|
3104
|
+
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3107
|
+
async insertableNode(value) {
|
|
3108
|
+
let node = await this.getNode(this.rootId);
|
|
3109
|
+
while (!node.leaf) {
|
|
3110
|
+
const { index } = this._binarySearchValues(node.values, value, false, true);
|
|
3111
|
+
node = await this.getNode(node.keys[index]);
|
|
3112
|
+
}
|
|
3113
|
+
return node;
|
|
3114
|
+
}
|
|
3115
|
+
async insertableNodeByPrimary(value) {
|
|
3116
|
+
let node = await this.getNode(this.rootId);
|
|
3117
|
+
while (!node.leaf) {
|
|
3118
|
+
const { index } = this._binarySearchValues(node.values, value, true, false);
|
|
3119
|
+
node = await this.getNode(node.keys[index]);
|
|
3120
|
+
}
|
|
3121
|
+
return node;
|
|
3122
|
+
}
|
|
3123
|
+
async insertableRightestNodeByPrimary(value) {
|
|
3124
|
+
let node = await this.getNode(this.rootId);
|
|
3125
|
+
while (!node.leaf) {
|
|
3126
|
+
const { index } = this._binarySearchValues(node.values, value, true, true);
|
|
3127
|
+
node = await this.getNode(node.keys[index]);
|
|
3128
|
+
}
|
|
3129
|
+
return node;
|
|
3130
|
+
}
|
|
3131
|
+
async insertableRightestEndNodeByPrimary(value) {
|
|
3132
|
+
const node = await this.insertableRightestNodeByPrimary(value);
|
|
3133
|
+
if (!node.next) {
|
|
3134
|
+
return null;
|
|
3135
|
+
}
|
|
3136
|
+
return await this.getNode(node.next);
|
|
3137
|
+
}
|
|
3138
|
+
async insertableEndNode(value, direction) {
|
|
3139
|
+
const insertableNode = await this.insertableNode(value);
|
|
3140
|
+
let key;
|
|
3141
|
+
switch (direction) {
|
|
3142
|
+
case -1:
|
|
3143
|
+
key = "prev";
|
|
3144
|
+
break;
|
|
3145
|
+
case 1:
|
|
3146
|
+
key = "next";
|
|
3147
|
+
break;
|
|
3148
|
+
default:
|
|
3149
|
+
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
3150
|
+
}
|
|
3151
|
+
const guessNode = insertableNode[key];
|
|
3152
|
+
if (!guessNode) {
|
|
3153
|
+
return null;
|
|
3154
|
+
}
|
|
3155
|
+
return await this.getNode(guessNode);
|
|
3156
|
+
}
|
|
3157
|
+
async leftestNode() {
|
|
3158
|
+
let node = await this.getNode(this.rootId);
|
|
3159
|
+
if (node === null) {
|
|
3160
|
+
debugger;
|
|
3161
|
+
}
|
|
3162
|
+
while (!node.leaf) {
|
|
3163
|
+
const keys = node.keys;
|
|
3164
|
+
node = await this.getNode(keys[0]);
|
|
3165
|
+
}
|
|
3166
|
+
return node;
|
|
3167
|
+
}
|
|
3168
|
+
async rightestNode() {
|
|
3169
|
+
let node = await this.getNode(this.rootId);
|
|
3170
|
+
while (!node.leaf) {
|
|
3171
|
+
const keys = node.keys;
|
|
3172
|
+
node = await this.getNode(keys[keys.length - 1]);
|
|
3173
|
+
}
|
|
3174
|
+
return node;
|
|
3175
|
+
}
|
|
3176
|
+
async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
3177
|
+
let node = startNode;
|
|
3178
|
+
let done = false;
|
|
3179
|
+
let hasMatched = false;
|
|
3180
|
+
let nextNodePromise = null;
|
|
3181
|
+
while (!done) {
|
|
3182
|
+
if (endNode && node.id === endNode.id) {
|
|
3183
|
+
done = true;
|
|
3184
|
+
break;
|
|
3185
|
+
}
|
|
3186
|
+
if (direction === 1) {
|
|
3187
|
+
if (node.next && !done) {
|
|
3188
|
+
nextNodePromise = this.getNode(node.next);
|
|
3189
|
+
}
|
|
3190
|
+
} else {
|
|
3191
|
+
if (node.prev && !done) {
|
|
3192
|
+
nextNodePromise = this.getNode(node.prev);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
const len = node.values.length;
|
|
3196
|
+
if (direction === 1) {
|
|
3197
|
+
for (let i = 0; i < len; i++) {
|
|
3198
|
+
const nValue = node.values[i];
|
|
3199
|
+
const keys = node.keys[i];
|
|
3200
|
+
if (comparator(nValue, value)) {
|
|
3201
|
+
hasMatched = true;
|
|
3202
|
+
for (let j = 0; j < keys.length; j++) {
|
|
3203
|
+
yield [keys[j], nValue];
|
|
3204
|
+
}
|
|
3205
|
+
} else if (earlyTerminate && hasMatched) {
|
|
3206
|
+
done = true;
|
|
3207
|
+
break;
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
} else {
|
|
3211
|
+
let i = len;
|
|
3212
|
+
while (i--) {
|
|
3213
|
+
const nValue = node.values[i];
|
|
3214
|
+
const keys = node.keys[i];
|
|
3215
|
+
if (comparator(nValue, value)) {
|
|
3216
|
+
hasMatched = true;
|
|
3217
|
+
let j = keys.length;
|
|
3218
|
+
while (j--) {
|
|
3219
|
+
yield [keys[j], nValue];
|
|
3220
|
+
}
|
|
3221
|
+
} else if (earlyTerminate && hasMatched) {
|
|
3222
|
+
done = true;
|
|
3223
|
+
break;
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
}
|
|
3227
|
+
if (done) {
|
|
3228
|
+
if (nextNodePromise) await nextNodePromise;
|
|
3229
|
+
break;
|
|
3230
|
+
}
|
|
3231
|
+
if (nextNodePromise) {
|
|
3232
|
+
node = await nextNodePromise;
|
|
3233
|
+
nextNodePromise = null;
|
|
3234
|
+
} else {
|
|
3235
|
+
done = true;
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
async init() {
|
|
3240
|
+
if (this.rootTx !== this) {
|
|
3241
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
3242
|
+
}
|
|
3243
|
+
return await this._initInternal();
|
|
3244
|
+
}
|
|
3245
|
+
async _initInternal() {
|
|
3246
|
+
if (this.isInitialized) {
|
|
3247
|
+
throw new Error("Transaction already initialized");
|
|
3248
|
+
}
|
|
3249
|
+
if (this.isDestroyed) {
|
|
3250
|
+
throw new Error("Transaction already destroyed");
|
|
2960
3251
|
}
|
|
2961
|
-
|
|
2962
|
-
|
|
3252
|
+
this.isInitialized = true;
|
|
3253
|
+
try {
|
|
3254
|
+
this._clearCache();
|
|
3255
|
+
const head = await this._readHead();
|
|
3256
|
+
if (head === null) {
|
|
3257
|
+
this.order = this.strategy.order;
|
|
3258
|
+
const root = await this._createNode(true, [], []);
|
|
3259
|
+
await this._writeHead({
|
|
3260
|
+
root: root.id,
|
|
3261
|
+
order: this.order,
|
|
3262
|
+
data: this.strategy.head.data
|
|
3263
|
+
});
|
|
3264
|
+
} else {
|
|
3265
|
+
const { root, order } = head;
|
|
3266
|
+
this.strategy.head = head;
|
|
3267
|
+
this.order = order;
|
|
3268
|
+
await this._writeHead({
|
|
3269
|
+
root,
|
|
3270
|
+
order: this.order,
|
|
3271
|
+
data: this.strategy.head.data
|
|
3272
|
+
});
|
|
3273
|
+
}
|
|
3274
|
+
if (this.order < 3) {
|
|
3275
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
3276
|
+
}
|
|
3277
|
+
} catch (e) {
|
|
3278
|
+
this.isInitialized = false;
|
|
3279
|
+
throw e;
|
|
2963
3280
|
}
|
|
2964
3281
|
}
|
|
2965
3282
|
async exists(key, value) {
|
|
@@ -3073,24 +3390,26 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3073
3390
|
return map;
|
|
3074
3391
|
}
|
|
3075
3392
|
async insert(key, value) {
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3393
|
+
return this.writeLock(0, async () => {
|
|
3394
|
+
const before = await this.insertableNode(value);
|
|
3395
|
+
await this._insertAtLeaf(before, key, value);
|
|
3396
|
+
if (before.values.length === this.order) {
|
|
3397
|
+
const after = await this._createNode(
|
|
3398
|
+
true,
|
|
3399
|
+
[],
|
|
3400
|
+
[],
|
|
3401
|
+
before.parent,
|
|
3402
|
+
null,
|
|
3403
|
+
null
|
|
3404
|
+
);
|
|
3405
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
3406
|
+
after.values = before.values.slice(mid + 1);
|
|
3407
|
+
after.keys = before.keys.slice(mid + 1);
|
|
3408
|
+
before.values = before.values.slice(0, mid + 1);
|
|
3409
|
+
before.keys = before.keys.slice(0, mid + 1);
|
|
3410
|
+
await this._insertInParent(before, after.values[0], after);
|
|
3411
|
+
}
|
|
3412
|
+
});
|
|
3094
3413
|
}
|
|
3095
3414
|
async _deleteEntry(node, key) {
|
|
3096
3415
|
if (!node.leaf) {
|
|
@@ -3255,504 +3574,255 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3255
3574
|
}
|
|
3256
3575
|
await this._updateNode(node);
|
|
3257
3576
|
await this._updateNode(pointer);
|
|
3258
|
-
}
|
|
3259
|
-
if (!pointer.leaf) {
|
|
3260
|
-
for (const key2 of pointer.keys) {
|
|
3261
|
-
const n = await this.getNode(key2);
|
|
3262
|
-
n.parent = pointer.id;
|
|
3263
|
-
await this._updateNode(n);
|
|
3264
|
-
}
|
|
3265
|
-
}
|
|
3266
|
-
if (!node.leaf) {
|
|
3267
|
-
for (const key2 of node.keys) {
|
|
3268
|
-
const n = await this.getNode(key2);
|
|
3269
|
-
n.parent = node.id;
|
|
3270
|
-
await this._updateNode(n);
|
|
3271
|
-
}
|
|
3272
|
-
}
|
|
3273
|
-
if (!parentNode.leaf) {
|
|
3274
|
-
for (const key2 of parentNode.keys) {
|
|
3275
|
-
const n = await this.getNode(key2);
|
|
3276
|
-
n.parent = parentNode.id;
|
|
3277
|
-
await this._updateNode(n);
|
|
3278
|
-
}
|
|
3279
|
-
}
|
|
3280
|
-
}
|
|
3281
|
-
} else {
|
|
3282
|
-
await this._updateNode(node);
|
|
3283
|
-
}
|
|
3284
|
-
}
|
|
3285
|
-
async delete(key, value) {
|
|
3286
|
-
let node = await this.insertableNodeByPrimary(value);
|
|
3287
|
-
let found = false;
|
|
3288
|
-
while (true) {
|
|
3289
|
-
let i = node.values.length;
|
|
3290
|
-
while (i--) {
|
|
3291
|
-
const nValue = node.values[i];
|
|
3292
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
3293
|
-
const keys = node.keys[i];
|
|
3294
|
-
const keyIndex = keys.indexOf(key);
|
|
3295
|
-
if (keyIndex !== -1) {
|
|
3296
|
-
keys.splice(keyIndex, 1);
|
|
3297
|
-
if (keys.length === 0) {
|
|
3298
|
-
node.keys.splice(i, 1);
|
|
3299
|
-
node.values.splice(i, 1);
|
|
3300
|
-
}
|
|
3301
|
-
await this._updateNode(node);
|
|
3302
|
-
await this._deleteEntry(node, key);
|
|
3303
|
-
found = true;
|
|
3304
|
-
break;
|
|
3305
|
-
}
|
|
3306
|
-
}
|
|
3307
|
-
}
|
|
3308
|
-
if (found) break;
|
|
3309
|
-
if (node.next) {
|
|
3310
|
-
node = await this.getNode(node.next);
|
|
3311
|
-
continue;
|
|
3312
|
-
}
|
|
3313
|
-
break;
|
|
3314
|
-
}
|
|
3315
|
-
}
|
|
3316
|
-
async getHeadData() {
|
|
3317
|
-
const head = await this._readHead();
|
|
3318
|
-
if (head === null) {
|
|
3319
|
-
throw new Error("Head not found");
|
|
3320
|
-
}
|
|
3321
|
-
return head.data;
|
|
3322
|
-
}
|
|
3323
|
-
async setHeadData(data) {
|
|
3324
|
-
const head = await this._readHead();
|
|
3325
|
-
if (head === null) {
|
|
3326
|
-
throw new Error("Head not found");
|
|
3327
|
-
}
|
|
3328
|
-
await this._writeHead({
|
|
3329
|
-
root: head.root,
|
|
3330
|
-
order: head.order,
|
|
3331
|
-
data
|
|
3332
|
-
});
|
|
3333
|
-
}
|
|
3334
|
-
async commit(label) {
|
|
3335
|
-
let result = await this.mvcc.commit(label);
|
|
3336
|
-
if (result.success) {
|
|
3337
|
-
result = await this.mvccRoot.commit(label);
|
|
3338
|
-
if (result.success && this.rootTx !== this) {
|
|
3339
|
-
this.rootTx.rootId = this.rootId;
|
|
3340
|
-
}
|
|
3341
|
-
if (result.success) {
|
|
3342
|
-
for (const r of result.created) {
|
|
3343
|
-
this.nodes.set(r.key, r.data);
|
|
3344
|
-
}
|
|
3345
|
-
for (const r of result.updated) {
|
|
3346
|
-
this.nodes.set(r.key, r.data);
|
|
3347
|
-
}
|
|
3348
|
-
for (const r of result.deleted) {
|
|
3349
|
-
this.nodes.delete(r.key);
|
|
3350
|
-
}
|
|
3351
|
-
}
|
|
3352
|
-
}
|
|
3353
|
-
return result;
|
|
3354
|
-
}
|
|
3355
|
-
rollback() {
|
|
3356
|
-
return this.mvcc.rollback();
|
|
3357
|
-
}
|
|
3358
|
-
};
|
|
3359
|
-
var BPTreeMVCCStrategyAsync = class extends AsyncMVCCStrategy {
|
|
3360
|
-
constructor(strategy) {
|
|
3361
|
-
super();
|
|
3362
|
-
this.strategy = strategy;
|
|
3363
|
-
}
|
|
3364
|
-
async read(key) {
|
|
3365
|
-
if (key === "__HEAD__") {
|
|
3366
|
-
return await this.strategy.readHead();
|
|
3367
|
-
}
|
|
3368
|
-
return await this.strategy.read(key);
|
|
3369
|
-
}
|
|
3370
|
-
async write(key, value) {
|
|
3371
|
-
if (key === "__HEAD__") {
|
|
3372
|
-
await this.strategy.writeHead(value);
|
|
3373
|
-
} else {
|
|
3374
|
-
await this.strategy.write(key, value);
|
|
3375
|
-
}
|
|
3376
|
-
}
|
|
3377
|
-
async delete(key) {
|
|
3378
|
-
await this.strategy.delete(key);
|
|
3379
|
-
}
|
|
3380
|
-
async exists(key) {
|
|
3381
|
-
if (key === "__HEAD__") {
|
|
3382
|
-
return await this.strategy.readHead() !== null;
|
|
3383
|
-
}
|
|
3384
|
-
try {
|
|
3385
|
-
const node = await this.strategy.read(key);
|
|
3386
|
-
return node !== null && node !== void 0;
|
|
3387
|
-
} catch {
|
|
3388
|
-
return false;
|
|
3389
|
-
}
|
|
3390
|
-
}
|
|
3391
|
-
};
|
|
3392
|
-
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3393
|
-
constructor(strategy, comparator, option) {
|
|
3394
|
-
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy));
|
|
3395
|
-
super(
|
|
3396
|
-
null,
|
|
3397
|
-
mvccRoot,
|
|
3398
|
-
mvccRoot,
|
|
3399
|
-
strategy,
|
|
3400
|
-
comparator,
|
|
3401
|
-
option
|
|
3402
|
-
);
|
|
3403
|
-
}
|
|
3404
|
-
/**
|
|
3405
|
-
* Creates a new asynchronous transaction.
|
|
3406
|
-
* @returns A new BPTreeAsyncTransaction.
|
|
3407
|
-
*/
|
|
3408
|
-
async createTransaction() {
|
|
3409
|
-
const nestedTx = await this.mvcc.createNested();
|
|
3410
|
-
const tx = new BPTreeAsyncTransaction(
|
|
3411
|
-
this,
|
|
3412
|
-
this.mvcc,
|
|
3413
|
-
nestedTx,
|
|
3414
|
-
this.strategy,
|
|
3415
|
-
this.comparator,
|
|
3416
|
-
this.option
|
|
3417
|
-
);
|
|
3418
|
-
await tx.init();
|
|
3419
|
-
return tx;
|
|
3420
|
-
}
|
|
3421
|
-
async insert(key, value) {
|
|
3422
|
-
const tx = await this.createTransaction();
|
|
3423
|
-
await tx.insert(key, value);
|
|
3424
|
-
const result = await tx.commit();
|
|
3425
|
-
if (!result.success) {
|
|
3426
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3427
|
-
}
|
|
3428
|
-
this.rootId = tx.getRootId();
|
|
3429
|
-
}
|
|
3430
|
-
async delete(key, value) {
|
|
3431
|
-
const tx = await this.createTransaction();
|
|
3432
|
-
await tx.delete(key, value);
|
|
3433
|
-
const result = await tx.commit();
|
|
3434
|
-
if (!result.success) {
|
|
3435
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3436
|
-
}
|
|
3437
|
-
this.rootId = tx.getRootId();
|
|
3438
|
-
}
|
|
3439
|
-
};
|
|
3440
|
-
var SerializeStrategy = class {
|
|
3441
|
-
order;
|
|
3442
|
-
head;
|
|
3443
|
-
constructor(order) {
|
|
3444
|
-
this.order = order;
|
|
3445
|
-
this.head = {
|
|
3446
|
-
order,
|
|
3447
|
-
root: null,
|
|
3448
|
-
data: {}
|
|
3449
|
-
};
|
|
3450
|
-
}
|
|
3451
|
-
};
|
|
3452
|
-
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3453
|
-
getHeadData(key, defaultValue) {
|
|
3454
|
-
if (!Object.hasOwn(this.head.data, key)) {
|
|
3455
|
-
this.setHeadData(key, defaultValue);
|
|
3456
|
-
}
|
|
3457
|
-
return this.head.data[key];
|
|
3458
|
-
}
|
|
3459
|
-
setHeadData(key, data) {
|
|
3460
|
-
this.head.data[key] = data;
|
|
3461
|
-
this.writeHead(this.head);
|
|
3462
|
-
}
|
|
3463
|
-
autoIncrement(key, defaultValue) {
|
|
3464
|
-
const current = this.getHeadData(key, defaultValue);
|
|
3465
|
-
const next = current + 1;
|
|
3466
|
-
this.setHeadData(key, next);
|
|
3467
|
-
return current;
|
|
3468
|
-
}
|
|
3469
|
-
};
|
|
3470
|
-
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3471
|
-
node;
|
|
3472
|
-
constructor(order) {
|
|
3473
|
-
super(order);
|
|
3474
|
-
this.node = {};
|
|
3475
|
-
}
|
|
3476
|
-
id(isLeaf) {
|
|
3477
|
-
return this.autoIncrement("index", 1).toString();
|
|
3478
|
-
}
|
|
3479
|
-
read(id) {
|
|
3480
|
-
if (!Object.hasOwn(this.node, id)) {
|
|
3481
|
-
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3482
|
-
}
|
|
3483
|
-
const node = this.node[id];
|
|
3484
|
-
return JSON.parse(JSON.stringify(node));
|
|
3485
|
-
}
|
|
3486
|
-
write(id, node) {
|
|
3487
|
-
this.node[id] = node;
|
|
3488
|
-
}
|
|
3489
|
-
delete(id) {
|
|
3490
|
-
delete this.node[id];
|
|
3491
|
-
}
|
|
3492
|
-
readHead() {
|
|
3493
|
-
if (this.head.root === null) {
|
|
3494
|
-
return null;
|
|
3495
|
-
}
|
|
3496
|
-
return this.head;
|
|
3497
|
-
}
|
|
3498
|
-
writeHead(head) {
|
|
3499
|
-
this.head = head;
|
|
3500
|
-
}
|
|
3501
|
-
};
|
|
3502
|
-
var Ryoiki2 = class _Ryoiki2 {
|
|
3503
|
-
readings;
|
|
3504
|
-
writings;
|
|
3505
|
-
readQueue;
|
|
3506
|
-
writeQueue;
|
|
3507
|
-
static async CatchError(promise) {
|
|
3508
|
-
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
3509
|
-
}
|
|
3510
|
-
static IsRangeOverlap(a, b) {
|
|
3511
|
-
const [start1, end1] = a;
|
|
3512
|
-
const [start2, end2] = b;
|
|
3513
|
-
if (end1 <= start2 || end2 <= start1) {
|
|
3514
|
-
return false;
|
|
3515
|
-
}
|
|
3516
|
-
return true;
|
|
3517
|
-
}
|
|
3518
|
-
static ERR_ALREADY_EXISTS(lockId) {
|
|
3519
|
-
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
3520
|
-
}
|
|
3521
|
-
static ERR_NOT_EXISTS(lockId) {
|
|
3522
|
-
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
3523
|
-
}
|
|
3524
|
-
static ERR_TIMEOUT(lockId, timeout) {
|
|
3525
|
-
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
3526
|
-
}
|
|
3527
|
-
/**
|
|
3528
|
-
* Constructs a new instance of the Ryoiki class.
|
|
3529
|
-
*/
|
|
3530
|
-
constructor() {
|
|
3531
|
-
this.readings = /* @__PURE__ */ new Map();
|
|
3532
|
-
this.writings = /* @__PURE__ */ new Map();
|
|
3533
|
-
this.readQueue = /* @__PURE__ */ new Map();
|
|
3534
|
-
this.writeQueue = /* @__PURE__ */ new Map();
|
|
3535
|
-
}
|
|
3536
|
-
/**
|
|
3537
|
-
* Creates a range based on a start value and length.
|
|
3538
|
-
* @param start - The starting value of the range.
|
|
3539
|
-
* @param length - The length of the range.
|
|
3540
|
-
* @returns A range tuple [start, start + length].
|
|
3541
|
-
*/
|
|
3542
|
-
range(start, length) {
|
|
3543
|
-
return [start, start + length];
|
|
3544
|
-
}
|
|
3545
|
-
rangeOverlapping(tasks, range) {
|
|
3546
|
-
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
3547
|
-
}
|
|
3548
|
-
isSameRange(a, b) {
|
|
3549
|
-
const [a1, a2] = a;
|
|
3550
|
-
const [b1, b2] = b;
|
|
3551
|
-
return a1 === b1 && a2 === b2;
|
|
3552
|
-
}
|
|
3553
|
-
fetchUnitAndRun(queue, workspaces) {
|
|
3554
|
-
for (const [id, unit] of queue) {
|
|
3555
|
-
if (!unit.condition()) {
|
|
3556
|
-
continue;
|
|
3577
|
+
}
|
|
3578
|
+
if (!pointer.leaf) {
|
|
3579
|
+
for (const key2 of pointer.keys) {
|
|
3580
|
+
const n = await this.getNode(key2);
|
|
3581
|
+
n.parent = pointer.id;
|
|
3582
|
+
await this._updateNode(n);
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
if (!node.leaf) {
|
|
3586
|
+
for (const key2 of node.keys) {
|
|
3587
|
+
const n = await this.getNode(key2);
|
|
3588
|
+
n.parent = node.id;
|
|
3589
|
+
await this._updateNode(n);
|
|
3590
|
+
}
|
|
3591
|
+
}
|
|
3592
|
+
if (!parentNode.leaf) {
|
|
3593
|
+
for (const key2 of parentNode.keys) {
|
|
3594
|
+
const n = await this.getNode(key2);
|
|
3595
|
+
n.parent = parentNode.id;
|
|
3596
|
+
await this._updateNode(n);
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3557
3599
|
}
|
|
3558
|
-
|
|
3600
|
+
} else {
|
|
3601
|
+
await this._updateNode(node);
|
|
3559
3602
|
}
|
|
3560
3603
|
}
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3604
|
+
async delete(key, value) {
|
|
3605
|
+
return this.writeLock(0, async () => {
|
|
3606
|
+
let node = await this.insertableNodeByPrimary(value);
|
|
3607
|
+
let found = false;
|
|
3608
|
+
while (true) {
|
|
3609
|
+
let i = node.values.length;
|
|
3610
|
+
while (i--) {
|
|
3611
|
+
const nValue = node.values[i];
|
|
3612
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
3613
|
+
const keys = node.keys[i];
|
|
3614
|
+
const keyIndex = keys.indexOf(key);
|
|
3615
|
+
if (keyIndex !== -1) {
|
|
3616
|
+
keys.splice(keyIndex, 1);
|
|
3617
|
+
if (keys.length === 0) {
|
|
3618
|
+
node.keys.splice(i, 1);
|
|
3619
|
+
node.values.splice(i, 1);
|
|
3620
|
+
}
|
|
3621
|
+
await this._updateNode(node);
|
|
3622
|
+
await this._deleteEntry(node, key);
|
|
3623
|
+
found = true;
|
|
3624
|
+
break;
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
if (found) break;
|
|
3629
|
+
if (node.next) {
|
|
3630
|
+
node = await this.getNode(node.next);
|
|
3631
|
+
continue;
|
|
3632
|
+
}
|
|
3633
|
+
break;
|
|
3565
3634
|
}
|
|
3566
|
-
}
|
|
3567
|
-
throw new Error("Invalid arguments");
|
|
3568
|
-
}
|
|
3569
|
-
_matchArgs(args, pattern) {
|
|
3570
|
-
return args.every((arg, index) => {
|
|
3571
|
-
const expectedType = pattern[index];
|
|
3572
|
-
if (expectedType === void 0) return typeof arg === "undefined";
|
|
3573
|
-
if (expectedType === Function) return typeof arg === "function";
|
|
3574
|
-
if (expectedType === Number) return typeof arg === "number";
|
|
3575
|
-
if (expectedType === Array) return Array.isArray(arg);
|
|
3576
|
-
return false;
|
|
3577
3635
|
});
|
|
3578
3636
|
}
|
|
3579
|
-
|
|
3580
|
-
const
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
}
|
|
3584
|
-
_alloc(queue, workspaces, lockId) {
|
|
3585
|
-
const unit = queue.get(lockId);
|
|
3586
|
-
if (!unit) {
|
|
3587
|
-
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
3637
|
+
async getHeadData() {
|
|
3638
|
+
const head = await this._readHead();
|
|
3639
|
+
if (head === null) {
|
|
3640
|
+
throw new Error("Head not found");
|
|
3588
3641
|
}
|
|
3589
|
-
|
|
3590
|
-
queue.delete(lockId);
|
|
3591
|
-
unit.alloc();
|
|
3642
|
+
return head.data;
|
|
3592
3643
|
}
|
|
3593
|
-
|
|
3594
|
-
const
|
|
3595
|
-
if (
|
|
3596
|
-
throw
|
|
3644
|
+
async setHeadData(data) {
|
|
3645
|
+
const head = await this._readHead();
|
|
3646
|
+
if (head === null) {
|
|
3647
|
+
throw new Error("Head not found");
|
|
3597
3648
|
}
|
|
3598
|
-
|
|
3599
|
-
|
|
3649
|
+
await this._writeHead({
|
|
3650
|
+
root: head.root,
|
|
3651
|
+
order: head.order,
|
|
3652
|
+
data
|
|
3653
|
+
});
|
|
3600
3654
|
}
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3655
|
+
async commit(label) {
|
|
3656
|
+
let result = await this.mvcc.commit(label);
|
|
3657
|
+
if (result.success) {
|
|
3658
|
+
const isRootTx = this.rootTx === this;
|
|
3659
|
+
if (!isRootTx) {
|
|
3660
|
+
result = await this.rootTx.commit(label);
|
|
3661
|
+
if (result.success) {
|
|
3662
|
+
this.rootTx.rootId = this.rootId;
|
|
3663
|
+
}
|
|
3608
3664
|
}
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3665
|
+
if (result.success) {
|
|
3666
|
+
for (const r of result.created) {
|
|
3667
|
+
this.nodes.set(r.key, r.data);
|
|
3668
|
+
}
|
|
3669
|
+
for (const r of result.updated) {
|
|
3670
|
+
this.nodes.set(r.key, r.data);
|
|
3671
|
+
}
|
|
3672
|
+
for (const r of result.deleted) {
|
|
3673
|
+
this.nodes.delete(r.key);
|
|
3613
3674
|
}
|
|
3614
|
-
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
3615
|
-
if (err) reject(err);
|
|
3616
|
-
else resolve(v);
|
|
3617
|
-
};
|
|
3618
|
-
const fetch = () => {
|
|
3619
|
-
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
3620
|
-
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
3621
|
-
};
|
|
3622
|
-
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
3623
|
-
fetch();
|
|
3624
|
-
});
|
|
3625
|
-
}
|
|
3626
|
-
_checkWorking(range, workspaces) {
|
|
3627
|
-
let isLocked = false;
|
|
3628
|
-
for (const lock of workspaces.values()) {
|
|
3629
|
-
if (_Ryoiki2.IsRangeOverlap(range, lock.range)) {
|
|
3630
|
-
isLocked = true;
|
|
3631
|
-
break;
|
|
3632
3675
|
}
|
|
3633
3676
|
}
|
|
3634
|
-
return
|
|
3677
|
+
return result;
|
|
3635
3678
|
}
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
* @param range The range to check for active read locks.
|
|
3639
|
-
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
3640
|
-
*/
|
|
3641
|
-
isReading(range) {
|
|
3642
|
-
return this._checkWorking(range, this.readings);
|
|
3679
|
+
rollback() {
|
|
3680
|
+
return this.mvcc.rollback();
|
|
3643
3681
|
}
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
isWriting(range) {
|
|
3650
|
-
return this._checkWorking(range, this.writings);
|
|
3682
|
+
};
|
|
3683
|
+
var BPTreeMVCCStrategyAsync = class extends AsyncMVCCStrategy {
|
|
3684
|
+
constructor(strategy) {
|
|
3685
|
+
super();
|
|
3686
|
+
this.strategy = strategy;
|
|
3651
3687
|
}
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
canRead(range) {
|
|
3658
|
-
const writing = this.isWriting(range);
|
|
3659
|
-
return !writing;
|
|
3688
|
+
async read(key) {
|
|
3689
|
+
if (key === "__HEAD__") {
|
|
3690
|
+
return await this.strategy.readHead();
|
|
3691
|
+
}
|
|
3692
|
+
return await this.strategy.read(key);
|
|
3660
3693
|
}
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
const reading = this.isReading(range);
|
|
3668
|
-
const writing = this.isWriting(range);
|
|
3669
|
-
return !reading && !writing;
|
|
3694
|
+
async write(key, value) {
|
|
3695
|
+
if (key === "__HEAD__") {
|
|
3696
|
+
await this.strategy.writeHead(value);
|
|
3697
|
+
} else {
|
|
3698
|
+
await this.strategy.write(key, value);
|
|
3699
|
+
}
|
|
3670
3700
|
}
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
* @template T - The return type of the task.
|
|
3674
|
-
* @param arg0 - Either a range or a task callback.
|
|
3675
|
-
* If a range is provided, the task is the second argument.
|
|
3676
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
3677
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
3678
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
3679
|
-
* If this value is not provided, no timeout will be set.
|
|
3680
|
-
* @returns A promise resolving to the result of the task execution.
|
|
3681
|
-
*/
|
|
3682
|
-
readLock(arg0, arg1, arg2) {
|
|
3683
|
-
const [range, task, timeout] = this._handleOverload(
|
|
3684
|
-
[arg0, arg1, arg2],
|
|
3685
|
-
{
|
|
3686
|
-
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
3687
|
-
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
3688
|
-
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
3689
|
-
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
3690
|
-
},
|
|
3691
|
-
{
|
|
3692
|
-
task: [Function],
|
|
3693
|
-
taskTimeout: [Function, Number],
|
|
3694
|
-
rangeTask: [Array, Function],
|
|
3695
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
3696
|
-
}
|
|
3697
|
-
);
|
|
3698
|
-
return this._lock(
|
|
3699
|
-
this.readQueue,
|
|
3700
|
-
range,
|
|
3701
|
-
timeout,
|
|
3702
|
-
task,
|
|
3703
|
-
() => !this.rangeOverlapping(this.writings, range)
|
|
3704
|
-
);
|
|
3701
|
+
async delete(key) {
|
|
3702
|
+
await this.strategy.delete(key);
|
|
3705
3703
|
}
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
taskTimeout: [Function, Number],
|
|
3729
|
-
rangeTask: [Array, Function],
|
|
3730
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
3731
|
-
}
|
|
3732
|
-
);
|
|
3733
|
-
return this._lock(
|
|
3734
|
-
this.writeQueue,
|
|
3735
|
-
range,
|
|
3736
|
-
timeout,
|
|
3737
|
-
task,
|
|
3738
|
-
() => {
|
|
3739
|
-
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
3740
|
-
}
|
|
3704
|
+
async exists(key) {
|
|
3705
|
+
if (key === "__HEAD__") {
|
|
3706
|
+
return await this.strategy.readHead() !== null;
|
|
3707
|
+
}
|
|
3708
|
+
try {
|
|
3709
|
+
const node = await this.strategy.read(key);
|
|
3710
|
+
return node !== null && node !== void 0;
|
|
3711
|
+
} catch {
|
|
3712
|
+
return false;
|
|
3713
|
+
}
|
|
3714
|
+
}
|
|
3715
|
+
};
|
|
3716
|
+
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3717
|
+
constructor(strategy, comparator, option) {
|
|
3718
|
+
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy));
|
|
3719
|
+
super(
|
|
3720
|
+
null,
|
|
3721
|
+
mvccRoot,
|
|
3722
|
+
mvccRoot,
|
|
3723
|
+
strategy,
|
|
3724
|
+
comparator,
|
|
3725
|
+
option
|
|
3741
3726
|
);
|
|
3742
3727
|
}
|
|
3743
3728
|
/**
|
|
3744
|
-
*
|
|
3745
|
-
* @
|
|
3729
|
+
* Creates a new asynchronous transaction.
|
|
3730
|
+
* @returns A new BPTreeAsyncTransaction.
|
|
3746
3731
|
*/
|
|
3747
|
-
|
|
3748
|
-
this.
|
|
3732
|
+
async createTransaction() {
|
|
3733
|
+
const nestedTx = this.mvcc.createNested();
|
|
3734
|
+
const tx = new BPTreeAsyncTransaction(
|
|
3735
|
+
this,
|
|
3736
|
+
this.mvcc,
|
|
3737
|
+
nestedTx,
|
|
3738
|
+
this.strategy,
|
|
3739
|
+
this.comparator,
|
|
3740
|
+
this.option
|
|
3741
|
+
);
|
|
3742
|
+
await tx._initInternal();
|
|
3743
|
+
return tx;
|
|
3749
3744
|
}
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3745
|
+
async insert(key, value) {
|
|
3746
|
+
return this.writeLock(1, async () => {
|
|
3747
|
+
const tx = await this.createTransaction();
|
|
3748
|
+
await tx.insert(key, value);
|
|
3749
|
+
const result = await tx.commit();
|
|
3750
|
+
if (!result.success) {
|
|
3751
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3752
|
+
}
|
|
3753
|
+
});
|
|
3754
|
+
}
|
|
3755
|
+
async delete(key, value) {
|
|
3756
|
+
return this.writeLock(1, async () => {
|
|
3757
|
+
const tx = await this.createTransaction();
|
|
3758
|
+
await tx.delete(key, value);
|
|
3759
|
+
const result = await tx.commit();
|
|
3760
|
+
if (!result.success) {
|
|
3761
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3762
|
+
}
|
|
3763
|
+
});
|
|
3764
|
+
}
|
|
3765
|
+
};
|
|
3766
|
+
var SerializeStrategy = class {
|
|
3767
|
+
order;
|
|
3768
|
+
head;
|
|
3769
|
+
constructor(order) {
|
|
3770
|
+
this.order = order;
|
|
3771
|
+
this.head = {
|
|
3772
|
+
order,
|
|
3773
|
+
root: null,
|
|
3774
|
+
data: {}
|
|
3775
|
+
};
|
|
3776
|
+
}
|
|
3777
|
+
};
|
|
3778
|
+
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3779
|
+
getHeadData(key, defaultValue) {
|
|
3780
|
+
if (!Object.hasOwn(this.head.data, key)) {
|
|
3781
|
+
this.setHeadData(key, defaultValue);
|
|
3782
|
+
}
|
|
3783
|
+
return this.head.data[key];
|
|
3784
|
+
}
|
|
3785
|
+
setHeadData(key, data) {
|
|
3786
|
+
this.head.data[key] = data;
|
|
3787
|
+
this.writeHead(this.head);
|
|
3788
|
+
}
|
|
3789
|
+
autoIncrement(key, defaultValue) {
|
|
3790
|
+
const current = this.getHeadData(key, defaultValue);
|
|
3791
|
+
const next = current + 1;
|
|
3792
|
+
this.setHeadData(key, next);
|
|
3793
|
+
return current;
|
|
3794
|
+
}
|
|
3795
|
+
};
|
|
3796
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3797
|
+
node;
|
|
3798
|
+
constructor(order) {
|
|
3799
|
+
super(order);
|
|
3800
|
+
this.node = {};
|
|
3801
|
+
}
|
|
3802
|
+
id(isLeaf) {
|
|
3803
|
+
return this.autoIncrement("index", 1).toString();
|
|
3804
|
+
}
|
|
3805
|
+
read(id) {
|
|
3806
|
+
if (!Object.hasOwn(this.node, id)) {
|
|
3807
|
+
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3808
|
+
}
|
|
3809
|
+
const node = this.node[id];
|
|
3810
|
+
return JSON.parse(JSON.stringify(node));
|
|
3811
|
+
}
|
|
3812
|
+
write(id, node) {
|
|
3813
|
+
this.node[id] = node;
|
|
3814
|
+
}
|
|
3815
|
+
delete(id) {
|
|
3816
|
+
delete this.node[id];
|
|
3817
|
+
}
|
|
3818
|
+
readHead() {
|
|
3819
|
+
if (this.head.root === null) {
|
|
3820
|
+
return null;
|
|
3821
|
+
}
|
|
3822
|
+
return this.head;
|
|
3823
|
+
}
|
|
3824
|
+
writeHead(head) {
|
|
3825
|
+
this.head = head;
|
|
3756
3826
|
}
|
|
3757
3827
|
};
|
|
3758
3828
|
var SerializeStrategyAsync = class extends SerializeStrategy {
|
|
@@ -4543,6 +4613,7 @@ var MVCCTransaction2 = class {
|
|
|
4543
4613
|
// delete 시 삭제 전 값 저장
|
|
4544
4614
|
originallyExisted;
|
|
4545
4615
|
// 트랜잭션 시작 시점에 디스크에 존재했던 키 (deleted 결과 필터링용)
|
|
4616
|
+
bufferHistory = /* @__PURE__ */ new Map();
|
|
4546
4617
|
// Nested Transaction Properties
|
|
4547
4618
|
parent;
|
|
4548
4619
|
localVersion;
|
|
@@ -4597,26 +4668,45 @@ var MVCCTransaction2 = class {
|
|
|
4597
4668
|
return false;
|
|
4598
4669
|
}
|
|
4599
4670
|
// --- Internal buffer manipulation helpers ---
|
|
4600
|
-
|
|
4601
|
-
this.
|
|
4671
|
+
_recordHistory(key) {
|
|
4672
|
+
const existsInWriteBuffer = this.writeBuffer.has(key);
|
|
4673
|
+
const existsInDeleteBuffer = this.deleteBuffer.has(key);
|
|
4674
|
+
const currentVer = this.keyVersions.get(key);
|
|
4675
|
+
if (currentVer !== void 0) {
|
|
4676
|
+
if (!this.bufferHistory.has(key)) this.bufferHistory.set(key, []);
|
|
4677
|
+
this.bufferHistory.get(key).push({
|
|
4678
|
+
value: existsInWriteBuffer ? this.writeBuffer.get(key) : this.deletedValues.get(key) ?? null,
|
|
4679
|
+
exists: existsInWriteBuffer || !existsInDeleteBuffer,
|
|
4680
|
+
version: currentVer
|
|
4681
|
+
});
|
|
4682
|
+
}
|
|
4683
|
+
}
|
|
4684
|
+
_bufferCreate(key, value, version) {
|
|
4685
|
+
if (version === void 0) this.localVersion++;
|
|
4686
|
+
const targetVersion = version ?? this.localVersion;
|
|
4687
|
+
this._recordHistory(key);
|
|
4602
4688
|
this.writeBuffer.set(key, value);
|
|
4603
4689
|
this.createdKeys.add(key);
|
|
4604
4690
|
this.deleteBuffer.delete(key);
|
|
4605
4691
|
this.originallyExisted.delete(key);
|
|
4606
|
-
this.keyVersions.set(key,
|
|
4692
|
+
this.keyVersions.set(key, targetVersion);
|
|
4607
4693
|
}
|
|
4608
|
-
_bufferWrite(key, value) {
|
|
4609
|
-
this.localVersion++;
|
|
4694
|
+
_bufferWrite(key, value, version) {
|
|
4695
|
+
if (version === void 0) this.localVersion++;
|
|
4696
|
+
const targetVersion = version ?? this.localVersion;
|
|
4697
|
+
this._recordHistory(key);
|
|
4610
4698
|
this.writeBuffer.set(key, value);
|
|
4611
4699
|
this.deleteBuffer.delete(key);
|
|
4612
|
-
this.keyVersions.set(key,
|
|
4700
|
+
this.keyVersions.set(key, targetVersion);
|
|
4613
4701
|
}
|
|
4614
|
-
_bufferDelete(key) {
|
|
4615
|
-
this.localVersion++;
|
|
4702
|
+
_bufferDelete(key, version) {
|
|
4703
|
+
if (version === void 0) this.localVersion++;
|
|
4704
|
+
const targetVersion = version ?? this.localVersion;
|
|
4705
|
+
this._recordHistory(key);
|
|
4616
4706
|
this.deleteBuffer.add(key);
|
|
4617
4707
|
this.writeBuffer.delete(key);
|
|
4618
4708
|
this.createdKeys.delete(key);
|
|
4619
|
-
this.keyVersions.set(key,
|
|
4709
|
+
this.keyVersions.set(key, targetVersion);
|
|
4620
4710
|
}
|
|
4621
4711
|
/**
|
|
4622
4712
|
* Returns the entries that will be created, updated, and deleted by this transaction.
|
|
@@ -4693,7 +4783,9 @@ var MVCCTransaction2 = class {
|
|
|
4693
4783
|
break;
|
|
4694
4784
|
}
|
|
4695
4785
|
}
|
|
4696
|
-
if (latestInSnapshotIdx
|
|
4786
|
+
if (latestInSnapshotIdx === versions.length - 1) {
|
|
4787
|
+
this.versionIndex.delete(key);
|
|
4788
|
+
} else if (latestInSnapshotIdx > 0) {
|
|
4697
4789
|
versions.splice(0, latestInSnapshotIdx);
|
|
4698
4790
|
}
|
|
4699
4791
|
}
|
|
@@ -4750,27 +4842,68 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4750
4842
|
if (this.committed) throw new Error("Transaction already committed");
|
|
4751
4843
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
4752
4844
|
if (this.deleteBuffer.has(key)) return null;
|
|
4753
|
-
|
|
4845
|
+
if (this.parent) {
|
|
4846
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
4847
|
+
}
|
|
4848
|
+
return this._diskRead(key, this.snapshotVersion);
|
|
4754
4849
|
}
|
|
4755
4850
|
exists(key) {
|
|
4756
4851
|
if (this.committed) throw new Error("Transaction already committed");
|
|
4757
4852
|
if (this.deleteBuffer.has(key)) return false;
|
|
4758
4853
|
if (this.writeBuffer.has(key)) return true;
|
|
4759
|
-
|
|
4854
|
+
if (this.parent) {
|
|
4855
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
4856
|
+
}
|
|
4857
|
+
return this._diskExists(key, this.snapshotVersion);
|
|
4858
|
+
}
|
|
4859
|
+
_existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
4860
|
+
if (this.writeBuffer.has(key)) {
|
|
4861
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
4862
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
4863
|
+
return true;
|
|
4864
|
+
}
|
|
4865
|
+
}
|
|
4866
|
+
if (this.deleteBuffer.has(key)) {
|
|
4867
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
4868
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
4869
|
+
return false;
|
|
4870
|
+
}
|
|
4871
|
+
}
|
|
4872
|
+
const history = this.bufferHistory.get(key);
|
|
4873
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
4874
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
4875
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
4876
|
+
return history[i].exists;
|
|
4877
|
+
}
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4880
|
+
if (this.parent) {
|
|
4881
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
4882
|
+
} else {
|
|
4883
|
+
return this._diskExists(key, snapshotVersion);
|
|
4884
|
+
}
|
|
4760
4885
|
}
|
|
4761
4886
|
_readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
4762
4887
|
if (this.writeBuffer.has(key)) {
|
|
4763
4888
|
const keyModVersion = this.keyVersions.get(key);
|
|
4764
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
4889
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
4765
4890
|
return this.writeBuffer.get(key);
|
|
4766
4891
|
}
|
|
4767
4892
|
}
|
|
4768
4893
|
if (this.deleteBuffer.has(key)) {
|
|
4769
4894
|
const keyModVersion = this.keyVersions.get(key);
|
|
4770
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
4895
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
4771
4896
|
return null;
|
|
4772
4897
|
}
|
|
4773
4898
|
}
|
|
4899
|
+
const history = this.bufferHistory.get(key);
|
|
4900
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
4901
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
4902
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
4903
|
+
return history[i].exists ? history[i].value : null;
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
}
|
|
4774
4907
|
if (this.parent) {
|
|
4775
4908
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
4776
4909
|
} else {
|
|
@@ -4780,54 +4913,22 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4780
4913
|
commit(label) {
|
|
4781
4914
|
const { created, updated, deleted } = this.getResultEntries();
|
|
4782
4915
|
if (this.committed) {
|
|
4783
|
-
return {
|
|
4784
|
-
label,
|
|
4785
|
-
success: false,
|
|
4786
|
-
error: "Transaction already committed",
|
|
4787
|
-
conflict: void 0,
|
|
4788
|
-
created,
|
|
4789
|
-
updated,
|
|
4790
|
-
deleted
|
|
4791
|
-
};
|
|
4916
|
+
return { label, success: false, error: "Transaction already committed", conflict: void 0, created, updated, deleted };
|
|
4792
4917
|
}
|
|
4793
4918
|
if (this.hasCommittedAncestor()) {
|
|
4794
|
-
return {
|
|
4795
|
-
label,
|
|
4796
|
-
success: false,
|
|
4797
|
-
error: "Ancestor transaction already committed",
|
|
4798
|
-
conflict: void 0,
|
|
4799
|
-
created,
|
|
4800
|
-
updated,
|
|
4801
|
-
deleted
|
|
4802
|
-
};
|
|
4919
|
+
return { label, success: false, error: "Ancestor transaction already committed", conflict: void 0, created, updated, deleted };
|
|
4803
4920
|
}
|
|
4804
4921
|
if (this.parent) {
|
|
4805
4922
|
const failure = this.parent._merge(this);
|
|
4806
4923
|
if (failure) {
|
|
4807
|
-
return {
|
|
4808
|
-
label,
|
|
4809
|
-
success: false,
|
|
4810
|
-
error: failure.error,
|
|
4811
|
-
conflict: failure.conflict,
|
|
4812
|
-
created,
|
|
4813
|
-
updated,
|
|
4814
|
-
deleted
|
|
4815
|
-
};
|
|
4924
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created, updated, deleted };
|
|
4816
4925
|
}
|
|
4817
4926
|
this.committed = true;
|
|
4818
4927
|
} else {
|
|
4819
4928
|
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
4820
4929
|
const failure = this._merge(this);
|
|
4821
4930
|
if (failure) {
|
|
4822
|
-
return {
|
|
4823
|
-
label,
|
|
4824
|
-
success: false,
|
|
4825
|
-
error: failure.error,
|
|
4826
|
-
conflict: failure.conflict,
|
|
4827
|
-
created: [],
|
|
4828
|
-
updated: [],
|
|
4829
|
-
deleted: []
|
|
4830
|
-
};
|
|
4931
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created: [], updated: [], deleted: [] };
|
|
4831
4932
|
}
|
|
4832
4933
|
this.writeBuffer.clear();
|
|
4833
4934
|
this.deleteBuffer.clear();
|
|
@@ -4835,17 +4936,12 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4835
4936
|
this.deletedValues.clear();
|
|
4836
4937
|
this.originallyExisted.clear();
|
|
4837
4938
|
this.keyVersions.clear();
|
|
4939
|
+
this.bufferHistory.clear();
|
|
4838
4940
|
this.localVersion = 0;
|
|
4839
4941
|
this.snapshotVersion = this.version;
|
|
4840
4942
|
}
|
|
4841
4943
|
}
|
|
4842
|
-
return {
|
|
4843
|
-
label,
|
|
4844
|
-
success: true,
|
|
4845
|
-
created,
|
|
4846
|
-
updated,
|
|
4847
|
-
deleted
|
|
4848
|
-
};
|
|
4944
|
+
return { label, success: true, created, updated, deleted };
|
|
4849
4945
|
}
|
|
4850
4946
|
_merge(child) {
|
|
4851
4947
|
if (this.parent) {
|
|
@@ -4854,11 +4950,7 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4854
4950
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
4855
4951
|
return {
|
|
4856
4952
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
4857
|
-
conflict: {
|
|
4858
|
-
key,
|
|
4859
|
-
parent: this.read(key),
|
|
4860
|
-
child: child.read(key)
|
|
4861
|
-
}
|
|
4953
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
4862
4954
|
};
|
|
4863
4955
|
}
|
|
4864
4956
|
}
|
|
@@ -4867,40 +4959,26 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4867
4959
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
4868
4960
|
return {
|
|
4869
4961
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
4870
|
-
conflict: {
|
|
4871
|
-
key,
|
|
4872
|
-
parent: this.read(key),
|
|
4873
|
-
child: child.read(key)
|
|
4874
|
-
}
|
|
4962
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
4875
4963
|
};
|
|
4876
4964
|
}
|
|
4877
4965
|
}
|
|
4878
|
-
const
|
|
4879
|
-
for (const key of child.writeBuffer
|
|
4880
|
-
|
|
4881
|
-
this.
|
|
4882
|
-
this.
|
|
4883
|
-
if (child.createdKeys.has(key)) {
|
|
4884
|
-
this.createdKeys.add(key);
|
|
4885
|
-
}
|
|
4966
|
+
const mergeVersion = ++this.localVersion;
|
|
4967
|
+
for (const [key, value] of child.writeBuffer) {
|
|
4968
|
+
const wasCreated = child.createdKeys.has(key);
|
|
4969
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
4970
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
4886
4971
|
}
|
|
4887
4972
|
for (const key of child.deleteBuffer) {
|
|
4888
|
-
this.deleteBuffer.add(key);
|
|
4889
|
-
this.writeBuffer.delete(key);
|
|
4890
|
-
this.createdKeys.delete(key);
|
|
4891
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
4892
4973
|
const deletedValue = child.deletedValues.get(key);
|
|
4893
|
-
if (deletedValue !== void 0)
|
|
4894
|
-
|
|
4895
|
-
}
|
|
4896
|
-
if (child.originallyExisted.has(key)) {
|
|
4974
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
4975
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
4897
4976
|
this.originallyExisted.add(key);
|
|
4898
4977
|
}
|
|
4978
|
+
this._bufferDelete(key, mergeVersion);
|
|
4899
4979
|
}
|
|
4900
|
-
this.localVersion = newLocalVersion;
|
|
4901
4980
|
this.root.activeTransactions.delete(child);
|
|
4902
4981
|
} else {
|
|
4903
|
-
const newVersion = this.version + 1;
|
|
4904
4982
|
if (child !== this) {
|
|
4905
4983
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
4906
4984
|
for (const key of modifiedKeys) {
|
|
@@ -4910,58 +4988,53 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4910
4988
|
if (lastVer > child.snapshotVersion) {
|
|
4911
4989
|
return {
|
|
4912
4990
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
4913
|
-
conflict: {
|
|
4914
|
-
key,
|
|
4915
|
-
parent: this.read(key),
|
|
4916
|
-
child: child.read(key)
|
|
4917
|
-
}
|
|
4991
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
4918
4992
|
};
|
|
4919
4993
|
}
|
|
4920
4994
|
}
|
|
4995
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
4996
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
4997
|
+
return {
|
|
4998
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
4999
|
+
conflict: { key, parent: this.read(key), child: child.read(key) }
|
|
5000
|
+
};
|
|
5001
|
+
}
|
|
4921
5002
|
}
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
for (const key of child.deleteBuffer) {
|
|
4931
|
-
this.deleteBuffer.add(key);
|
|
4932
|
-
this.writeBuffer.delete(key);
|
|
4933
|
-
this.createdKeys.delete(key);
|
|
4934
|
-
const deletedValue = child.deletedValues.get(key);
|
|
4935
|
-
if (deletedValue !== void 0) {
|
|
4936
|
-
this.deletedValues.set(key, deletedValue);
|
|
5003
|
+
const mergeVersion = ++this.localVersion;
|
|
5004
|
+
for (const [key, value] of child.writeBuffer) {
|
|
5005
|
+
const wasCreated = child.createdKeys.has(key);
|
|
5006
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
5007
|
+
this.originallyExisted.add(key);
|
|
5008
|
+
}
|
|
5009
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
5010
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
4937
5011
|
}
|
|
4938
|
-
|
|
4939
|
-
|
|
5012
|
+
for (const key of child.deleteBuffer) {
|
|
5013
|
+
const deletedValue = child.deletedValues.get(key);
|
|
5014
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
5015
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
5016
|
+
this.originallyExisted.add(key);
|
|
5017
|
+
}
|
|
5018
|
+
this._bufferDelete(key, mergeVersion);
|
|
4940
5019
|
}
|
|
5020
|
+
this.root.activeTransactions.delete(child);
|
|
5021
|
+
} else {
|
|
5022
|
+
const newVersion = this.version + 1;
|
|
5023
|
+
for (const [key, value] of this.writeBuffer) this._diskWrite(key, value, newVersion);
|
|
5024
|
+
for (const key of this.deleteBuffer) this._diskDelete(key, newVersion);
|
|
5025
|
+
this.version = newVersion;
|
|
5026
|
+
this._cleanupDeletedCache();
|
|
4941
5027
|
}
|
|
4942
|
-
for (const [key, value] of child.writeBuffer) {
|
|
4943
|
-
this._diskWrite(key, value, newVersion);
|
|
4944
|
-
}
|
|
4945
|
-
for (const key of child.deleteBuffer) {
|
|
4946
|
-
this._diskDelete(key, newVersion);
|
|
4947
|
-
}
|
|
4948
|
-
this.version = newVersion;
|
|
4949
|
-
this.root.activeTransactions.delete(child);
|
|
4950
|
-
this._cleanupDeletedCache();
|
|
4951
5028
|
}
|
|
4952
5029
|
return null;
|
|
4953
5030
|
}
|
|
4954
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
4955
5031
|
_diskWrite(key, value, version) {
|
|
4956
5032
|
const strategy = this.strategy;
|
|
4957
5033
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
4958
5034
|
if (strategy.exists(key)) {
|
|
4959
5035
|
const currentVal = strategy.read(key);
|
|
4960
5036
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
4961
|
-
this.deletedCache.get(key).push({
|
|
4962
|
-
value: currentVal,
|
|
4963
|
-
deletedAtVersion: version
|
|
4964
|
-
});
|
|
5037
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
4965
5038
|
}
|
|
4966
5039
|
strategy.write(key, value);
|
|
4967
5040
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -4977,9 +5050,8 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4977
5050
|
let targetVerObj = null;
|
|
4978
5051
|
let nextVerObj = null;
|
|
4979
5052
|
for (const v of versions) {
|
|
4980
|
-
if (v.version <= snapshotVersion)
|
|
4981
|
-
|
|
4982
|
-
} else {
|
|
5053
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
5054
|
+
else {
|
|
4983
5055
|
nextVerObj = v;
|
|
4984
5056
|
break;
|
|
4985
5057
|
}
|
|
@@ -4996,6 +5068,7 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4996
5068
|
}
|
|
4997
5069
|
if (!targetVerObj.exists) return null;
|
|
4998
5070
|
if (!nextVerObj) {
|
|
5071
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
4999
5072
|
return strategy.read(key);
|
|
5000
5073
|
}
|
|
5001
5074
|
const cached = this.deletedCache.get(key);
|
|
@@ -5013,14 +5086,24 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
5013
5086
|
return strategy.exists(key);
|
|
5014
5087
|
}
|
|
5015
5088
|
let targetVerObj = null;
|
|
5089
|
+
let nextVerObj = null;
|
|
5016
5090
|
for (const v of versions) {
|
|
5017
|
-
if (v.version <= snapshotVersion)
|
|
5018
|
-
|
|
5019
|
-
|
|
5091
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
5092
|
+
else {
|
|
5093
|
+
nextVerObj = v;
|
|
5020
5094
|
break;
|
|
5021
5095
|
}
|
|
5022
5096
|
}
|
|
5023
|
-
if (!targetVerObj)
|
|
5097
|
+
if (!targetVerObj) {
|
|
5098
|
+
if (nextVerObj) {
|
|
5099
|
+
const cached = this.deletedCache.get(key);
|
|
5100
|
+
if (cached) {
|
|
5101
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
5102
|
+
if (match) return true;
|
|
5103
|
+
}
|
|
5104
|
+
}
|
|
5105
|
+
return false;
|
|
5106
|
+
}
|
|
5024
5107
|
return targetVerObj.exists;
|
|
5025
5108
|
}
|
|
5026
5109
|
_diskDelete(key, snapshotVersion) {
|
|
@@ -5029,12 +5112,9 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
5029
5112
|
if (strategy.exists(key)) {
|
|
5030
5113
|
const currentVal = strategy.read(key);
|
|
5031
5114
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
5032
|
-
this.deletedCache.get(key).push({
|
|
5033
|
-
|
|
5034
|
-
deletedAtVersion: snapshotVersion
|
|
5035
|
-
});
|
|
5115
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
5116
|
+
strategy.delete(key);
|
|
5036
5117
|
}
|
|
5037
|
-
strategy.delete(key);
|
|
5038
5118
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
5039
5119
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
5040
5120
|
}
|
|
@@ -5355,84 +5435,93 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5355
5435
|
if (this.committed) throw new Error("Transaction already committed");
|
|
5356
5436
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
5357
5437
|
if (this.deleteBuffer.has(key)) return null;
|
|
5358
|
-
|
|
5438
|
+
if (this.parent) {
|
|
5439
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
5440
|
+
}
|
|
5441
|
+
return await this._diskRead(key, this.snapshotVersion);
|
|
5359
5442
|
}
|
|
5360
5443
|
async exists(key) {
|
|
5361
5444
|
if (this.committed) throw new Error("Transaction already committed");
|
|
5362
5445
|
if (this.deleteBuffer.has(key)) return false;
|
|
5363
5446
|
if (this.writeBuffer.has(key)) return true;
|
|
5364
|
-
|
|
5447
|
+
if (this.parent) {
|
|
5448
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
5449
|
+
}
|
|
5450
|
+
return await this._diskExists(key, this.snapshotVersion);
|
|
5451
|
+
}
|
|
5452
|
+
async _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
5453
|
+
if (this.writeBuffer.has(key)) {
|
|
5454
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
5455
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
5456
|
+
return true;
|
|
5457
|
+
}
|
|
5458
|
+
}
|
|
5459
|
+
if (this.deleteBuffer.has(key)) {
|
|
5460
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
5461
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
5462
|
+
return false;
|
|
5463
|
+
}
|
|
5464
|
+
}
|
|
5465
|
+
const history = this.bufferHistory.get(key);
|
|
5466
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
5467
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
5468
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
5469
|
+
return history[i].exists;
|
|
5470
|
+
}
|
|
5471
|
+
}
|
|
5472
|
+
}
|
|
5473
|
+
if (this.parent) {
|
|
5474
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
5475
|
+
} else {
|
|
5476
|
+
return await this._diskExists(key, snapshotVersion);
|
|
5477
|
+
}
|
|
5365
5478
|
}
|
|
5366
5479
|
async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
5367
5480
|
if (this.writeBuffer.has(key)) {
|
|
5368
5481
|
const keyModVersion = this.keyVersions.get(key);
|
|
5369
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
5482
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
5370
5483
|
return this.writeBuffer.get(key);
|
|
5371
5484
|
}
|
|
5372
5485
|
}
|
|
5373
5486
|
if (this.deleteBuffer.has(key)) {
|
|
5374
5487
|
const keyModVersion = this.keyVersions.get(key);
|
|
5375
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
5488
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
5376
5489
|
return null;
|
|
5377
5490
|
}
|
|
5378
5491
|
}
|
|
5492
|
+
const history = this.bufferHistory.get(key);
|
|
5493
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
5494
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
5495
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
5496
|
+
return history[i].exists ? history[i].value : null;
|
|
5497
|
+
}
|
|
5498
|
+
}
|
|
5499
|
+
}
|
|
5379
5500
|
if (this.parent) {
|
|
5380
5501
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
5381
5502
|
} else {
|
|
5382
|
-
return this._diskRead(key, snapshotVersion);
|
|
5503
|
+
return await this._diskRead(key, snapshotVersion);
|
|
5383
5504
|
}
|
|
5384
5505
|
}
|
|
5385
5506
|
async commit(label) {
|
|
5386
5507
|
const { created, updated, deleted } = this.getResultEntries();
|
|
5387
5508
|
if (this.committed) {
|
|
5388
|
-
return {
|
|
5389
|
-
label,
|
|
5390
|
-
success: false,
|
|
5391
|
-
error: "Transaction already committed",
|
|
5392
|
-
conflict: void 0,
|
|
5393
|
-
created,
|
|
5394
|
-
updated,
|
|
5395
|
-
deleted
|
|
5396
|
-
};
|
|
5509
|
+
return { label, success: false, error: "Transaction already committed", conflict: void 0, created, updated, deleted };
|
|
5397
5510
|
}
|
|
5398
5511
|
if (this.hasCommittedAncestor()) {
|
|
5399
|
-
return {
|
|
5400
|
-
label,
|
|
5401
|
-
success: false,
|
|
5402
|
-
error: "Ancestor transaction already committed",
|
|
5403
|
-
conflict: void 0,
|
|
5404
|
-
created,
|
|
5405
|
-
updated,
|
|
5406
|
-
deleted
|
|
5407
|
-
};
|
|
5512
|
+
return { label, success: false, error: "Ancestor transaction already committed", conflict: void 0, created, updated, deleted };
|
|
5408
5513
|
}
|
|
5409
5514
|
if (this.parent) {
|
|
5410
5515
|
const failure = await this.parent._merge(this);
|
|
5411
5516
|
if (failure) {
|
|
5412
|
-
return {
|
|
5413
|
-
label,
|
|
5414
|
-
success: false,
|
|
5415
|
-
error: failure.error,
|
|
5416
|
-
conflict: failure.conflict,
|
|
5417
|
-
created,
|
|
5418
|
-
updated,
|
|
5419
|
-
deleted
|
|
5420
|
-
};
|
|
5517
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created, updated, deleted };
|
|
5421
5518
|
}
|
|
5422
5519
|
this.committed = true;
|
|
5423
5520
|
} else {
|
|
5424
5521
|
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
5425
5522
|
const failure = await this._merge(this);
|
|
5426
5523
|
if (failure) {
|
|
5427
|
-
return {
|
|
5428
|
-
label,
|
|
5429
|
-
success: false,
|
|
5430
|
-
error: failure.error,
|
|
5431
|
-
conflict: failure.conflict,
|
|
5432
|
-
created: [],
|
|
5433
|
-
updated: [],
|
|
5434
|
-
deleted: []
|
|
5435
|
-
};
|
|
5524
|
+
return { label, success: false, error: failure.error, conflict: failure.conflict, created: [], updated: [], deleted: [] };
|
|
5436
5525
|
}
|
|
5437
5526
|
this.writeBuffer.clear();
|
|
5438
5527
|
this.deleteBuffer.clear();
|
|
@@ -5440,17 +5529,12 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5440
5529
|
this.deletedValues.clear();
|
|
5441
5530
|
this.originallyExisted.clear();
|
|
5442
5531
|
this.keyVersions.clear();
|
|
5532
|
+
this.bufferHistory.clear();
|
|
5443
5533
|
this.localVersion = 0;
|
|
5444
5534
|
this.snapshotVersion = this.version;
|
|
5445
5535
|
}
|
|
5446
5536
|
}
|
|
5447
|
-
return {
|
|
5448
|
-
label,
|
|
5449
|
-
success: true,
|
|
5450
|
-
created,
|
|
5451
|
-
updated,
|
|
5452
|
-
deleted
|
|
5453
|
-
};
|
|
5537
|
+
return { label, success: true, created, updated, deleted };
|
|
5454
5538
|
}
|
|
5455
5539
|
async _merge(child) {
|
|
5456
5540
|
return this.writeLock(async () => {
|
|
@@ -5460,11 +5544,7 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5460
5544
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
5461
5545
|
return {
|
|
5462
5546
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
5463
|
-
conflict: {
|
|
5464
|
-
key,
|
|
5465
|
-
parent: await this.read(key),
|
|
5466
|
-
child: await child.read(key)
|
|
5467
|
-
}
|
|
5547
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
5468
5548
|
};
|
|
5469
5549
|
}
|
|
5470
5550
|
}
|
|
@@ -5473,41 +5553,27 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5473
5553
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
5474
5554
|
return {
|
|
5475
5555
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
5476
|
-
conflict: {
|
|
5477
|
-
key,
|
|
5478
|
-
parent: await this.read(key),
|
|
5479
|
-
child: await child.read(key)
|
|
5480
|
-
}
|
|
5556
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
5481
5557
|
};
|
|
5482
5558
|
}
|
|
5483
5559
|
}
|
|
5484
|
-
const
|
|
5485
|
-
for (const key of child.writeBuffer
|
|
5486
|
-
|
|
5487
|
-
this.
|
|
5488
|
-
this.
|
|
5489
|
-
if (child.createdKeys.has(key)) {
|
|
5490
|
-
this.createdKeys.add(key);
|
|
5491
|
-
}
|
|
5560
|
+
const mergeVersion = ++this.localVersion;
|
|
5561
|
+
for (const [key, value] of child.writeBuffer) {
|
|
5562
|
+
const wasCreated = child.createdKeys.has(key);
|
|
5563
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
5564
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
5492
5565
|
}
|
|
5493
5566
|
for (const key of child.deleteBuffer) {
|
|
5494
|
-
this.deleteBuffer.add(key);
|
|
5495
|
-
this.writeBuffer.delete(key);
|
|
5496
|
-
this.createdKeys.delete(key);
|
|
5497
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
5498
5567
|
const deletedValue = child.deletedValues.get(key);
|
|
5499
|
-
if (deletedValue !== void 0)
|
|
5500
|
-
|
|
5501
|
-
}
|
|
5502
|
-
if (child.originallyExisted.has(key)) {
|
|
5568
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
5569
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
5503
5570
|
this.originallyExisted.add(key);
|
|
5504
5571
|
}
|
|
5572
|
+
this._bufferDelete(key, mergeVersion);
|
|
5505
5573
|
}
|
|
5506
|
-
this.localVersion = newLocalVersion;
|
|
5507
5574
|
this.root.activeTransactions.delete(child);
|
|
5508
5575
|
return null;
|
|
5509
5576
|
} else {
|
|
5510
|
-
const newVersion = this.version + 1;
|
|
5511
5577
|
if (child !== this) {
|
|
5512
5578
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
5513
5579
|
for (const key of modifiedKeys) {
|
|
@@ -5517,59 +5583,54 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5517
5583
|
if (lastVer > child.snapshotVersion) {
|
|
5518
5584
|
return {
|
|
5519
5585
|
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
5520
|
-
conflict: {
|
|
5521
|
-
key,
|
|
5522
|
-
parent: await this.read(key),
|
|
5523
|
-
child: await child.read(key)
|
|
5524
|
-
}
|
|
5586
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
5525
5587
|
};
|
|
5526
5588
|
}
|
|
5527
5589
|
}
|
|
5590
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
5591
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
5592
|
+
return {
|
|
5593
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
5594
|
+
conflict: { key, parent: await this.read(key), child: await child.read(key) }
|
|
5595
|
+
};
|
|
5596
|
+
}
|
|
5528
5597
|
}
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
for (const key of child.deleteBuffer) {
|
|
5538
|
-
this.deleteBuffer.add(key);
|
|
5539
|
-
this.writeBuffer.delete(key);
|
|
5540
|
-
this.createdKeys.delete(key);
|
|
5541
|
-
const deletedValue = child.deletedValues.get(key);
|
|
5542
|
-
if (deletedValue !== void 0) {
|
|
5543
|
-
this.deletedValues.set(key, deletedValue);
|
|
5598
|
+
const mergeVersion = ++this.localVersion;
|
|
5599
|
+
for (const [key, value] of child.writeBuffer) {
|
|
5600
|
+
const wasCreated = child.createdKeys.has(key);
|
|
5601
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
5602
|
+
this.originallyExisted.add(key);
|
|
5603
|
+
}
|
|
5604
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
5605
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
5544
5606
|
}
|
|
5545
|
-
|
|
5546
|
-
|
|
5607
|
+
for (const key of child.deleteBuffer) {
|
|
5608
|
+
const deletedValue = child.deletedValues.get(key);
|
|
5609
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
5610
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
5611
|
+
this.originallyExisted.add(key);
|
|
5612
|
+
}
|
|
5613
|
+
this._bufferDelete(key, mergeVersion);
|
|
5547
5614
|
}
|
|
5615
|
+
this.root.activeTransactions.delete(child);
|
|
5616
|
+
} else {
|
|
5617
|
+
const newVersion = this.version + 1;
|
|
5618
|
+
for (const [key, value] of this.writeBuffer) await this._diskWrite(key, value, newVersion);
|
|
5619
|
+
for (const key of this.deleteBuffer) await this._diskDelete(key, newVersion);
|
|
5620
|
+
this.version = newVersion;
|
|
5621
|
+
this._cleanupDeletedCache();
|
|
5548
5622
|
}
|
|
5549
|
-
for (const [key, value] of child.writeBuffer) {
|
|
5550
|
-
await this._diskWrite(key, value, newVersion);
|
|
5551
|
-
}
|
|
5552
|
-
for (const key of child.deleteBuffer) {
|
|
5553
|
-
await this._diskDelete(key, newVersion);
|
|
5554
|
-
}
|
|
5555
|
-
this.version = newVersion;
|
|
5556
|
-
this.root.activeTransactions.delete(child);
|
|
5557
|
-
this._cleanupDeletedCache();
|
|
5558
5623
|
return null;
|
|
5559
5624
|
}
|
|
5560
5625
|
});
|
|
5561
5626
|
}
|
|
5562
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
5563
5627
|
async _diskWrite(key, value, version) {
|
|
5564
5628
|
const strategy = this.strategy;
|
|
5565
5629
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
5566
5630
|
if (await strategy.exists(key)) {
|
|
5567
5631
|
const currentVal = await strategy.read(key);
|
|
5568
5632
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
5569
|
-
this.deletedCache.get(key).push({
|
|
5570
|
-
value: currentVal,
|
|
5571
|
-
deletedAtVersion: version
|
|
5572
|
-
});
|
|
5633
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
5573
5634
|
}
|
|
5574
5635
|
await strategy.write(key, value);
|
|
5575
5636
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -5580,14 +5641,13 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5580
5641
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
5581
5642
|
const versions = this.versionIndex.get(key);
|
|
5582
5643
|
if (!versions) {
|
|
5583
|
-
return await strategy.exists(key) ? strategy.read(key) : null;
|
|
5644
|
+
return await strategy.exists(key) ? await strategy.read(key) : null;
|
|
5584
5645
|
}
|
|
5585
5646
|
let targetVerObj = null;
|
|
5586
5647
|
let nextVerObj = null;
|
|
5587
5648
|
for (const v of versions) {
|
|
5588
|
-
if (v.version <= snapshotVersion)
|
|
5589
|
-
|
|
5590
|
-
} else {
|
|
5649
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
5650
|
+
else {
|
|
5591
5651
|
nextVerObj = v;
|
|
5592
5652
|
break;
|
|
5593
5653
|
}
|
|
@@ -5604,6 +5664,7 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5604
5664
|
}
|
|
5605
5665
|
if (!targetVerObj.exists) return null;
|
|
5606
5666
|
if (!nextVerObj) {
|
|
5667
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
5607
5668
|
return strategy.read(key);
|
|
5608
5669
|
}
|
|
5609
5670
|
const cached = this.deletedCache.get(key);
|
|
@@ -5618,17 +5679,27 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5618
5679
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
5619
5680
|
const versions = this.versionIndex.get(key);
|
|
5620
5681
|
if (!versions) {
|
|
5621
|
-
return strategy.exists(key);
|
|
5682
|
+
return await strategy.exists(key);
|
|
5622
5683
|
}
|
|
5623
5684
|
let targetVerObj = null;
|
|
5685
|
+
let nextVerObj = null;
|
|
5624
5686
|
for (const v of versions) {
|
|
5625
|
-
if (v.version <= snapshotVersion)
|
|
5626
|
-
|
|
5627
|
-
|
|
5687
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
5688
|
+
else {
|
|
5689
|
+
nextVerObj = v;
|
|
5628
5690
|
break;
|
|
5629
5691
|
}
|
|
5630
5692
|
}
|
|
5631
|
-
if (!targetVerObj)
|
|
5693
|
+
if (!targetVerObj) {
|
|
5694
|
+
if (nextVerObj) {
|
|
5695
|
+
const cached = this.deletedCache.get(key);
|
|
5696
|
+
if (cached) {
|
|
5697
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
5698
|
+
if (match) return true;
|
|
5699
|
+
}
|
|
5700
|
+
}
|
|
5701
|
+
return false;
|
|
5702
|
+
}
|
|
5632
5703
|
return targetVerObj.exists;
|
|
5633
5704
|
}
|
|
5634
5705
|
async _diskDelete(key, snapshotVersion) {
|
|
@@ -5637,12 +5708,9 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5637
5708
|
if (await strategy.exists(key)) {
|
|
5638
5709
|
const currentVal = await strategy.read(key);
|
|
5639
5710
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
5640
|
-
this.deletedCache.get(key).push({
|
|
5641
|
-
|
|
5642
|
-
deletedAtVersion: snapshotVersion
|
|
5643
|
-
});
|
|
5711
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
5712
|
+
await strategy.delete(key);
|
|
5644
5713
|
}
|
|
5645
|
-
await strategy.delete(key);
|
|
5646
5714
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
5647
5715
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
5648
5716
|
}
|
|
@@ -8343,7 +8411,6 @@ var RowTableEngine = class {
|
|
|
8343
8411
|
if (!btx) return;
|
|
8344
8412
|
const result = await btx.commit();
|
|
8345
8413
|
if (result.success) {
|
|
8346
|
-
await this.bptree.init();
|
|
8347
8414
|
for (const entry of result.deleted) {
|
|
8348
8415
|
await this.strategy.delete(entry.key);
|
|
8349
8416
|
}
|