serializable-bptree 6.2.3 → 7.0.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/README.md +4 -1
- package/dist/cjs/index.cjs +713 -484
- package/dist/esm/index.mjs +713 -484
- package/dist/types/BPTreeAsync.d.ts +9 -44
- package/dist/types/BPTreeSync.d.ts +7 -41
- package/dist/types/SerializeStrategyAsync.d.ts +3 -3
- package/dist/types/SerializeStrategySync.d.ts +3 -3
- package/dist/types/base/BPTree.d.ts +5 -75
- package/dist/types/base/BPTreeAsyncBase.d.ts +41 -0
- package/dist/types/base/BPTreeSyncBase.d.ts +39 -0
- package/dist/types/base/SerializeStrategy.d.ts +9 -8
- package/dist/types/index.d.ts +3 -2
- package/dist/types/transaction/BPTreeAsyncSnapshotStrategy.d.ts +17 -0
- package/dist/types/transaction/BPTreeAsyncTransaction.d.ts +44 -0
- package/dist/types/transaction/BPTreeSyncSnapshotStrategy.d.ts +17 -0
- package/dist/types/transaction/BPTreeSyncTransaction.d.ts +42 -0
- package/dist/types/types/index.d.ts +91 -0
- package/package.json +1 -1
- package/dist/types/utils/types.d.ts +0 -5
package/dist/cjs/index.cjs
CHANGED
|
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BPTreeAsync: () => BPTreeAsync,
|
|
24
|
+
BPTreeAsyncTransaction: () => BPTreeAsyncTransaction,
|
|
24
25
|
BPTreeSync: () => BPTreeSync,
|
|
26
|
+
BPTreeSyncTransaction: () => BPTreeSyncTransaction,
|
|
25
27
|
InMemoryStoreStrategyAsync: () => InMemoryStoreStrategyAsync,
|
|
26
28
|
InMemoryStoreStrategySync: () => InMemoryStoreStrategySync,
|
|
27
29
|
NumericComparator: () => NumericComparator,
|
|
@@ -761,8 +763,8 @@ var BPTree = class _BPTree {
|
|
|
761
763
|
}
|
|
762
764
|
};
|
|
763
765
|
|
|
764
|
-
// src/
|
|
765
|
-
var
|
|
766
|
+
// src/base/BPTreeSyncBase.ts
|
|
767
|
+
var BPTreeSyncBase = class extends BPTree {
|
|
766
768
|
constructor(strategy, comparator, option) {
|
|
767
769
|
super(strategy, comparator, option);
|
|
768
770
|
this.nodes = this._createCachedNode();
|
|
@@ -871,7 +873,7 @@ var BPTreeSync = class extends BPTree {
|
|
|
871
873
|
}
|
|
872
874
|
}
|
|
873
875
|
}
|
|
874
|
-
if (this.rootId === node.id && node.keys.length === 1) {
|
|
876
|
+
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
875
877
|
const keys = node.keys;
|
|
876
878
|
this.bufferForNodeDelete(node);
|
|
877
879
|
const newRoot = this.getNode(keys[0]);
|
|
@@ -940,20 +942,11 @@ var BPTreeSync = class extends BPTree {
|
|
|
940
942
|
pointer.values.push(guess);
|
|
941
943
|
} else {
|
|
942
944
|
pointer.next = node.next;
|
|
943
|
-
pointer.prev = node.id;
|
|
944
945
|
if (pointer.next) {
|
|
945
|
-
const n = this.getNode(
|
|
946
|
+
const n = this.getNode(pointer.next);
|
|
946
947
|
n.prev = pointer.id;
|
|
947
948
|
this.bufferForNodeUpdate(n);
|
|
948
949
|
}
|
|
949
|
-
if (pointer.prev) {
|
|
950
|
-
const n = this.getNode(node.id);
|
|
951
|
-
n.next = pointer.id;
|
|
952
|
-
this.bufferForNodeUpdate(n);
|
|
953
|
-
}
|
|
954
|
-
if (isPredecessor) {
|
|
955
|
-
pointer.prev = null;
|
|
956
|
-
}
|
|
957
950
|
}
|
|
958
951
|
pointer.values.push(...node.values);
|
|
959
952
|
if (!pointer.leaf) {
|
|
@@ -1178,10 +1171,6 @@ var BPTreeSync = class extends BPTree {
|
|
|
1178
1171
|
}
|
|
1179
1172
|
return node;
|
|
1180
1173
|
}
|
|
1181
|
-
/**
|
|
1182
|
-
* Find the insertable node using primaryAsc comparison.
|
|
1183
|
-
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
1184
|
-
*/
|
|
1185
1174
|
insertableNodeByPrimary(value) {
|
|
1186
1175
|
let node = this.getNode(this.rootId);
|
|
1187
1176
|
while (!node.leaf) {
|
|
@@ -1262,6 +1251,33 @@ var BPTreeSync = class extends BPTree {
|
|
|
1262
1251
|
}
|
|
1263
1252
|
return node;
|
|
1264
1253
|
}
|
|
1254
|
+
exists(key, value) {
|
|
1255
|
+
const node = this.insertableNode(value);
|
|
1256
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1257
|
+
if (this.comparator.isSame(value, node.values[i])) {
|
|
1258
|
+
const keys = node.keys[i];
|
|
1259
|
+
if (keys.includes(key)) {
|
|
1260
|
+
return true;
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
return false;
|
|
1265
|
+
}
|
|
1266
|
+
forceUpdate(id) {
|
|
1267
|
+
if (id) {
|
|
1268
|
+
this.nodes.delete(id);
|
|
1269
|
+
this.getNode(id);
|
|
1270
|
+
return 1;
|
|
1271
|
+
}
|
|
1272
|
+
const keys = Array.from(this.nodes.keys());
|
|
1273
|
+
for (const key of keys) {
|
|
1274
|
+
this.nodes.delete(key);
|
|
1275
|
+
}
|
|
1276
|
+
for (const key of keys) {
|
|
1277
|
+
this.getNode(key);
|
|
1278
|
+
}
|
|
1279
|
+
return keys.length;
|
|
1280
|
+
}
|
|
1265
1281
|
commitHeadBuffer() {
|
|
1266
1282
|
if (!this._strategyDirty) {
|
|
1267
1283
|
return;
|
|
@@ -1292,24 +1308,14 @@ var BPTreeSync = class extends BPTree {
|
|
|
1292
1308
|
}
|
|
1293
1309
|
this._nodeDeleteBuffer.clear();
|
|
1294
1310
|
}
|
|
1295
|
-
/**
|
|
1296
|
-
* Retrieves the value associated with the given key (PK).
|
|
1297
|
-
* Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
|
|
1298
|
-
*
|
|
1299
|
-
* @param key The key to search for.
|
|
1300
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
1301
|
-
*/
|
|
1302
1311
|
get(key) {
|
|
1303
1312
|
let node = this.leftestNode();
|
|
1304
1313
|
while (true) {
|
|
1305
|
-
|
|
1306
|
-
const
|
|
1307
|
-
for (let
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
if (keys[j] === key) {
|
|
1311
|
-
return node.values[i];
|
|
1312
|
-
}
|
|
1314
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1315
|
+
const keys = node.keys[i];
|
|
1316
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
1317
|
+
if (keys[j] === key) {
|
|
1318
|
+
return node.values[i];
|
|
1313
1319
|
}
|
|
1314
1320
|
}
|
|
1315
1321
|
}
|
|
@@ -1428,21 +1434,15 @@ var BPTreeSync = class extends BPTree {
|
|
|
1428
1434
|
const nValue = node.values[i];
|
|
1429
1435
|
if (this.comparator.isSame(value, nValue)) {
|
|
1430
1436
|
const keys = node.keys[i];
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
} else if (node.id === this.rootId) {
|
|
1436
|
-
node.values.splice(i, 1);
|
|
1437
|
-
node.keys.splice(i, 1);
|
|
1438
|
-
this.bufferForNodeUpdate(node);
|
|
1439
|
-
} else {
|
|
1440
|
-
keys.splice(keys.indexOf(key), 1);
|
|
1437
|
+
const keyIndex = keys.indexOf(key);
|
|
1438
|
+
if (keyIndex !== -1) {
|
|
1439
|
+
keys.splice(keyIndex, 1);
|
|
1440
|
+
if (keys.length === 0) {
|
|
1441
1441
|
node.keys.splice(i, 1);
|
|
1442
|
-
node.values.splice(
|
|
1443
|
-
this._deleteEntry(node, key, value);
|
|
1444
|
-
this.bufferForNodeUpdate(node);
|
|
1442
|
+
node.values.splice(i, 1);
|
|
1445
1443
|
}
|
|
1444
|
+
this._deleteEntry(node, key, value);
|
|
1445
|
+
break;
|
|
1446
1446
|
}
|
|
1447
1447
|
}
|
|
1448
1448
|
}
|
|
@@ -1451,298 +1451,356 @@ var BPTreeSync = class extends BPTree {
|
|
|
1451
1451
|
this.commitNodeUpdateBuffer();
|
|
1452
1452
|
this.commitNodeDeleteBuffer();
|
|
1453
1453
|
}
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1457
|
-
const nValue = node.values[i];
|
|
1458
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
1459
|
-
const keys = node.keys[i];
|
|
1460
|
-
return keys.includes(key);
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
return false;
|
|
1454
|
+
getHeadData() {
|
|
1455
|
+
return this.strategy.head.data;
|
|
1464
1456
|
}
|
|
1465
1457
|
setHeadData(data) {
|
|
1466
1458
|
this.strategy.head.data = data;
|
|
1467
|
-
this.
|
|
1468
|
-
this.commitHeadBuffer();
|
|
1459
|
+
this.strategy.writeHead(this.strategy.head);
|
|
1469
1460
|
}
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1461
|
+
};
|
|
1462
|
+
|
|
1463
|
+
// src/base/SerializeStrategy.ts
|
|
1464
|
+
var SerializeStrategy = class {
|
|
1465
|
+
order;
|
|
1466
|
+
head;
|
|
1467
|
+
constructor(order) {
|
|
1468
|
+
this.order = order;
|
|
1469
|
+
this.head = {
|
|
1470
|
+
order,
|
|
1471
|
+
root: null,
|
|
1472
|
+
data: {}
|
|
1473
|
+
};
|
|
1478
1474
|
}
|
|
1479
1475
|
};
|
|
1480
1476
|
|
|
1481
|
-
//
|
|
1482
|
-
var
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1477
|
+
// src/SerializeStrategySync.ts
|
|
1478
|
+
var SerializeStrategySync = class extends SerializeStrategy {
|
|
1479
|
+
getHeadData(key, defaultValue) {
|
|
1480
|
+
if (!Object.hasOwn(this.head.data, key)) {
|
|
1481
|
+
this.setHeadData(key, defaultValue);
|
|
1482
|
+
}
|
|
1483
|
+
return this.head.data[key];
|
|
1484
|
+
}
|
|
1485
|
+
setHeadData(key, data) {
|
|
1486
|
+
this.head.data[key] = data;
|
|
1487
|
+
this.writeHead(this.head);
|
|
1488
|
+
}
|
|
1489
|
+
autoIncrement(key, defaultValue) {
|
|
1490
|
+
const current = this.getHeadData(key, defaultValue);
|
|
1491
|
+
const next = current + 1;
|
|
1492
|
+
this.setHeadData(key, next);
|
|
1493
|
+
return current;
|
|
1494
|
+
}
|
|
1495
|
+
compareAndSwapHead(oldRoot, newRoot) {
|
|
1496
|
+
if (this.head.root !== oldRoot) {
|
|
1494
1497
|
return false;
|
|
1495
1498
|
}
|
|
1499
|
+
this.head.root = newRoot;
|
|
1500
|
+
this.writeHead(this.head);
|
|
1496
1501
|
return true;
|
|
1497
1502
|
}
|
|
1498
|
-
|
|
1499
|
-
|
|
1503
|
+
};
|
|
1504
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
1505
|
+
node;
|
|
1506
|
+
constructor(order) {
|
|
1507
|
+
super(order);
|
|
1508
|
+
this.node = {};
|
|
1500
1509
|
}
|
|
1501
|
-
|
|
1502
|
-
return
|
|
1510
|
+
id(isLeaf) {
|
|
1511
|
+
return this.autoIncrement("index", 1).toString();
|
|
1503
1512
|
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1513
|
+
read(id) {
|
|
1514
|
+
if (!Object.hasOwn(this.node, id)) {
|
|
1515
|
+
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
1516
|
+
}
|
|
1517
|
+
const node = this.node[id];
|
|
1518
|
+
return JSON.parse(JSON.stringify(node));
|
|
1506
1519
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
*/
|
|
1510
|
-
constructor() {
|
|
1511
|
-
this.readings = /* @__PURE__ */ new Map();
|
|
1512
|
-
this.writings = /* @__PURE__ */ new Map();
|
|
1513
|
-
this.readQueue = /* @__PURE__ */ new Map();
|
|
1514
|
-
this.writeQueue = /* @__PURE__ */ new Map();
|
|
1520
|
+
write(id, node) {
|
|
1521
|
+
this.node[id] = node;
|
|
1515
1522
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
* @param start - The starting value of the range.
|
|
1519
|
-
* @param length - The length of the range.
|
|
1520
|
-
* @returns A range tuple [start, start + length].
|
|
1521
|
-
*/
|
|
1522
|
-
range(start, length) {
|
|
1523
|
-
return [start, start + length];
|
|
1523
|
+
delete(id) {
|
|
1524
|
+
delete this.node[id];
|
|
1524
1525
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1526
|
+
readHead() {
|
|
1527
|
+
if (this.head.root === null) {
|
|
1528
|
+
return null;
|
|
1529
|
+
}
|
|
1530
|
+
return this.head;
|
|
1527
1531
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
const [b1, b2] = b;
|
|
1531
|
-
return a1 === b1 && a2 === b2;
|
|
1532
|
+
writeHead(head) {
|
|
1533
|
+
this.head = head;
|
|
1532
1534
|
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1537
|
+
// src/transaction/BPTreeSyncSnapshotStrategy.ts
|
|
1538
|
+
var BPTreeSyncSnapshotStrategy = class extends SerializeStrategySync {
|
|
1539
|
+
baseStrategy;
|
|
1540
|
+
snapshotHead;
|
|
1541
|
+
constructor(baseStrategy, root) {
|
|
1542
|
+
super(baseStrategy.order);
|
|
1543
|
+
this.baseStrategy = baseStrategy;
|
|
1544
|
+
this.snapshotHead = {
|
|
1545
|
+
...baseStrategy.head,
|
|
1546
|
+
root,
|
|
1547
|
+
data: { ...baseStrategy.head.data }
|
|
1548
|
+
};
|
|
1549
|
+
this.head = this.snapshotHead;
|
|
1540
1550
|
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
if (this._matchArgs(args, pattern)) {
|
|
1544
|
-
return handlers[key](...args);
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
throw new Error("Invalid arguments");
|
|
1551
|
+
id(isLeaf) {
|
|
1552
|
+
return this.baseStrategy.id(isLeaf);
|
|
1548
1553
|
}
|
|
1549
|
-
|
|
1550
|
-
return
|
|
1551
|
-
const expectedType = pattern[index];
|
|
1552
|
-
if (expectedType === void 0) return typeof arg === "undefined";
|
|
1553
|
-
if (expectedType === Function) return typeof arg === "function";
|
|
1554
|
-
if (expectedType === Number) return typeof arg === "number";
|
|
1555
|
-
if (expectedType === Array) return Array.isArray(arg);
|
|
1556
|
-
return false;
|
|
1557
|
-
});
|
|
1554
|
+
read(id) {
|
|
1555
|
+
return this.baseStrategy.read(id);
|
|
1558
1556
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
const random = Math.random().toString(36).substring(2);
|
|
1562
|
-
return `${timestamp}${random}`;
|
|
1563
|
-
}
|
|
1564
|
-
_alloc(queue, workspaces, lockId) {
|
|
1565
|
-
const unit = queue.get(lockId);
|
|
1566
|
-
if (!unit) {
|
|
1567
|
-
throw _Ryoiki.ERR_NOT_EXISTS(lockId);
|
|
1568
|
-
}
|
|
1569
|
-
workspaces.set(lockId, unit);
|
|
1570
|
-
queue.delete(lockId);
|
|
1571
|
-
unit.alloc();
|
|
1572
|
-
}
|
|
1573
|
-
_free(workspaces, lockId) {
|
|
1574
|
-
const unit = workspaces.get(lockId);
|
|
1575
|
-
if (!unit) {
|
|
1576
|
-
throw _Ryoiki.ERR_NOT_EXISTS(lockId);
|
|
1577
|
-
}
|
|
1578
|
-
workspaces.delete(lockId);
|
|
1579
|
-
unit.free();
|
|
1580
|
-
}
|
|
1581
|
-
_lock(queue, range, timeout, task, condition) {
|
|
1582
|
-
return new Promise((resolve, reject) => {
|
|
1583
|
-
let timeoutId = null;
|
|
1584
|
-
if (timeout >= 0) {
|
|
1585
|
-
timeoutId = setTimeout(() => {
|
|
1586
|
-
reject(_Ryoiki.ERR_TIMEOUT(id, timeout));
|
|
1587
|
-
}, timeout);
|
|
1588
|
-
}
|
|
1589
|
-
const id = this._createRandomId();
|
|
1590
|
-
const alloc = async () => {
|
|
1591
|
-
if (timeoutId !== null) {
|
|
1592
|
-
clearTimeout(timeoutId);
|
|
1593
|
-
}
|
|
1594
|
-
const [err, v] = await _Ryoiki.CatchError(task(id));
|
|
1595
|
-
if (err) reject(err);
|
|
1596
|
-
else resolve(v);
|
|
1597
|
-
};
|
|
1598
|
-
const fetch = () => {
|
|
1599
|
-
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
1600
|
-
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
1601
|
-
};
|
|
1602
|
-
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
1603
|
-
fetch();
|
|
1604
|
-
});
|
|
1557
|
+
write(id, node) {
|
|
1558
|
+
this.baseStrategy.write(id, node);
|
|
1605
1559
|
}
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
for (const lock of workspaces.values()) {
|
|
1609
|
-
if (_Ryoiki.IsRangeOverlap(range, lock.range)) {
|
|
1610
|
-
isLocked = true;
|
|
1611
|
-
break;
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
return isLocked;
|
|
1560
|
+
delete(id) {
|
|
1561
|
+
this.baseStrategy.delete(id);
|
|
1615
1562
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
* @param range The range to check for active read locks.
|
|
1619
|
-
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
1620
|
-
*/
|
|
1621
|
-
isReading(range) {
|
|
1622
|
-
return this._checkWorking(range, this.readings);
|
|
1563
|
+
readHead() {
|
|
1564
|
+
return this.snapshotHead;
|
|
1623
1565
|
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
1628
|
-
*/
|
|
1629
|
-
isWriting(range) {
|
|
1630
|
-
return this._checkWorking(range, this.writings);
|
|
1566
|
+
writeHead(head) {
|
|
1567
|
+
this.snapshotHead.root = head.root;
|
|
1568
|
+
this.snapshotHead.data = { ...head.data };
|
|
1631
1569
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
* @param range The range to check for read lock availability.
|
|
1635
|
-
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
1636
|
-
*/
|
|
1637
|
-
canRead(range) {
|
|
1638
|
-
const writing = this.isWriting(range);
|
|
1639
|
-
return !writing;
|
|
1570
|
+
compareAndSwapHead(oldRoot, newRoot) {
|
|
1571
|
+
return this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
|
|
1640
1572
|
}
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1573
|
+
getHeadData(key, defaultValue) {
|
|
1574
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
1575
|
+
}
|
|
1576
|
+
setHeadData(key, data) {
|
|
1577
|
+
this.snapshotHead.data[key] = data;
|
|
1578
|
+
}
|
|
1579
|
+
autoIncrement(key, defaultValue) {
|
|
1580
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
|
|
1584
|
+
// src/transaction/BPTreeSyncTransaction.ts
|
|
1585
|
+
var BPTreeSyncTransaction = class extends BPTreeSyncBase {
|
|
1586
|
+
realBaseTree;
|
|
1587
|
+
realBaseStrategy;
|
|
1588
|
+
txNodes = /* @__PURE__ */ new Map();
|
|
1589
|
+
dirtyIds = /* @__PURE__ */ new Set();
|
|
1590
|
+
createdInTx = /* @__PURE__ */ new Set();
|
|
1591
|
+
initialRootId;
|
|
1592
|
+
transactionRootId;
|
|
1593
|
+
constructor(baseTree) {
|
|
1594
|
+
super(baseTree.strategy, baseTree.comparator, baseTree.option);
|
|
1595
|
+
this.realBaseTree = baseTree;
|
|
1596
|
+
this.realBaseStrategy = baseTree.strategy;
|
|
1597
|
+
this.initialRootId = "";
|
|
1598
|
+
this.transactionRootId = "";
|
|
1650
1599
|
}
|
|
1651
1600
|
/**
|
|
1652
|
-
*
|
|
1653
|
-
* @template T - The return type of the task.
|
|
1654
|
-
* @param arg0 - Either a range or a task callback.
|
|
1655
|
-
* If a range is provided, the task is the second argument.
|
|
1656
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
1657
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
1658
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
1659
|
-
* If this value is not provided, no timeout will be set.
|
|
1660
|
-
* @returns A promise resolving to the result of the task execution.
|
|
1601
|
+
* Initializes the transaction by capturing the current state of the tree.
|
|
1661
1602
|
*/
|
|
1662
|
-
|
|
1663
|
-
const
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1603
|
+
initTransaction() {
|
|
1604
|
+
const head = this.realBaseStrategy.readHead();
|
|
1605
|
+
this.initialRootId = head?.root ?? this.realBaseTree.rootId;
|
|
1606
|
+
this.transactionRootId = this.initialRootId;
|
|
1607
|
+
this.rootId = this.transactionRootId;
|
|
1608
|
+
const snapshotStrategy = new BPTreeSyncSnapshotStrategy(this.realBaseStrategy, this.initialRootId);
|
|
1609
|
+
this.strategy = snapshotStrategy;
|
|
1610
|
+
this.txNodes.clear();
|
|
1611
|
+
this.dirtyIds.clear();
|
|
1612
|
+
this.createdInTx.clear();
|
|
1613
|
+
}
|
|
1614
|
+
getNode(id) {
|
|
1615
|
+
if (this.txNodes.has(id)) {
|
|
1616
|
+
return this.txNodes.get(id);
|
|
1617
|
+
}
|
|
1618
|
+
const baseNode = this.realBaseStrategy.read(id);
|
|
1619
|
+
const clone = JSON.parse(JSON.stringify(baseNode));
|
|
1620
|
+
this.txNodes.set(id, clone);
|
|
1621
|
+
return clone;
|
|
1622
|
+
}
|
|
1623
|
+
bufferForNodeUpdate(node) {
|
|
1624
|
+
this.txNodes.set(node.id, node);
|
|
1625
|
+
this.dirtyIds.add(node.id);
|
|
1626
|
+
this.markPathDirty(node);
|
|
1627
|
+
}
|
|
1628
|
+
bufferForNodeCreate(node) {
|
|
1629
|
+
this.txNodes.set(node.id, node);
|
|
1630
|
+
this.dirtyIds.add(node.id);
|
|
1631
|
+
this.createdInTx.add(node.id);
|
|
1632
|
+
this.markPathDirty(node);
|
|
1633
|
+
}
|
|
1634
|
+
bufferForNodeDelete(node) {
|
|
1635
|
+
this.txNodes.delete(node.id);
|
|
1636
|
+
this.dirtyIds.add(node.id);
|
|
1637
|
+
}
|
|
1638
|
+
markPathDirty(node) {
|
|
1639
|
+
let curr = node;
|
|
1640
|
+
while (curr.parent) {
|
|
1641
|
+
if (this.dirtyIds.has(curr.parent) && this.txNodes.has(curr.parent)) {
|
|
1642
|
+
break;
|
|
1676
1643
|
}
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1644
|
+
const parent = this.getNode(curr.parent);
|
|
1645
|
+
this.dirtyIds.add(parent.id);
|
|
1646
|
+
curr = parent;
|
|
1647
|
+
}
|
|
1648
|
+
if (!curr.parent) {
|
|
1649
|
+
this.transactionRootId = curr.id;
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
_createNode(isLeaf, keys, values, leaf = false, parent = null, next = null, prev = null) {
|
|
1653
|
+
const id = this.realBaseStrategy.id(isLeaf);
|
|
1654
|
+
const node = {
|
|
1655
|
+
id,
|
|
1656
|
+
keys,
|
|
1657
|
+
values,
|
|
1658
|
+
leaf: isLeaf,
|
|
1659
|
+
parent,
|
|
1660
|
+
next,
|
|
1661
|
+
prev
|
|
1662
|
+
};
|
|
1663
|
+
this.bufferForNodeCreate(node);
|
|
1664
|
+
return node;
|
|
1685
1665
|
}
|
|
1686
1666
|
/**
|
|
1687
|
-
*
|
|
1688
|
-
*
|
|
1689
|
-
*
|
|
1690
|
-
*
|
|
1691
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
1692
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
1693
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
1694
|
-
* If this value is not provided, no timeout will be set.
|
|
1695
|
-
* @returns A promise resolving to the result of the task execution.
|
|
1667
|
+
* Attempts to commit the transaction.
|
|
1668
|
+
* Uses Optimistic Locking (Compare-And-Swap) on the root node ID to detect conflicts.
|
|
1669
|
+
*
|
|
1670
|
+
* @returns The transaction result.
|
|
1696
1671
|
*/
|
|
1697
|
-
|
|
1698
|
-
const
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
rangeTask: [Array, Function],
|
|
1710
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
1672
|
+
commit() {
|
|
1673
|
+
const idMapping = /* @__PURE__ */ new Map();
|
|
1674
|
+
const finalNodes = [];
|
|
1675
|
+
for (const oldId of this.dirtyIds) {
|
|
1676
|
+
if (this.createdInTx.has(oldId)) {
|
|
1677
|
+
idMapping.set(oldId, oldId);
|
|
1678
|
+
} else {
|
|
1679
|
+
const node = this.txNodes.get(oldId);
|
|
1680
|
+
if (node) {
|
|
1681
|
+
const newId = this.realBaseStrategy.id(node.leaf);
|
|
1682
|
+
idMapping.set(oldId, newId);
|
|
1683
|
+
}
|
|
1711
1684
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1685
|
+
}
|
|
1686
|
+
const newCreatedIds = [];
|
|
1687
|
+
for (const oldId of this.dirtyIds) {
|
|
1688
|
+
const node = this.txNodes.get(oldId);
|
|
1689
|
+
if (!node) continue;
|
|
1690
|
+
const newId = idMapping.get(oldId);
|
|
1691
|
+
node.id = newId;
|
|
1692
|
+
if (node.parent && idMapping.has(node.parent)) {
|
|
1693
|
+
node.parent = idMapping.get(node.parent);
|
|
1694
|
+
}
|
|
1695
|
+
if (!node.leaf) {
|
|
1696
|
+
const internal = node;
|
|
1697
|
+
for (let i = 0; i < internal.keys.length; i++) {
|
|
1698
|
+
const childId = internal.keys[i];
|
|
1699
|
+
if (idMapping.has(childId)) {
|
|
1700
|
+
internal.keys[i] = idMapping.get(childId);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1720
1703
|
}
|
|
1721
|
-
|
|
1704
|
+
if (node.leaf) {
|
|
1705
|
+
const leaf = node;
|
|
1706
|
+
if (leaf.next && idMapping.has(leaf.next)) {
|
|
1707
|
+
leaf.next = idMapping.get(leaf.next);
|
|
1708
|
+
}
|
|
1709
|
+
if (leaf.prev && idMapping.has(leaf.prev)) {
|
|
1710
|
+
leaf.prev = idMapping.get(leaf.prev);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
finalNodes.push(node);
|
|
1714
|
+
newCreatedIds.push(newId);
|
|
1715
|
+
}
|
|
1716
|
+
let newRootId = this.transactionRootId;
|
|
1717
|
+
if (idMapping.has(this.transactionRootId)) {
|
|
1718
|
+
newRootId = idMapping.get(this.transactionRootId);
|
|
1719
|
+
}
|
|
1720
|
+
for (const node of finalNodes) {
|
|
1721
|
+
this.realBaseStrategy.write(node.id, node);
|
|
1722
|
+
}
|
|
1723
|
+
const success = this.realBaseStrategy.compareAndSwapHead(this.initialRootId, newRootId);
|
|
1724
|
+
if (success) {
|
|
1725
|
+
const distinctObsolete = /* @__PURE__ */ new Set();
|
|
1726
|
+
for (const oldId of this.dirtyIds) {
|
|
1727
|
+
if (!this.createdInTx.has(oldId) && this.txNodes.has(oldId)) {
|
|
1728
|
+
distinctObsolete.add(oldId);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
return {
|
|
1732
|
+
success: true,
|
|
1733
|
+
createdIds: newCreatedIds,
|
|
1734
|
+
obsoleteIds: Array.from(distinctObsolete)
|
|
1735
|
+
};
|
|
1736
|
+
} else {
|
|
1737
|
+
this.rollback();
|
|
1738
|
+
return {
|
|
1739
|
+
success: false,
|
|
1740
|
+
createdIds: newCreatedIds,
|
|
1741
|
+
obsoleteIds: []
|
|
1742
|
+
};
|
|
1743
|
+
}
|
|
1722
1744
|
}
|
|
1723
1745
|
/**
|
|
1724
|
-
*
|
|
1725
|
-
*
|
|
1746
|
+
* Rolls back the transaction by clearing all buffered changes.
|
|
1747
|
+
* Internal use only.
|
|
1726
1748
|
*/
|
|
1727
|
-
|
|
1728
|
-
this.
|
|
1749
|
+
rollback() {
|
|
1750
|
+
this.txNodes.clear();
|
|
1751
|
+
this.dirtyIds.clear();
|
|
1752
|
+
this.createdInTx.clear();
|
|
1753
|
+
}
|
|
1754
|
+
// Override to do nothing, as transaction handles its own commits
|
|
1755
|
+
commitHeadBuffer() {
|
|
1756
|
+
}
|
|
1757
|
+
commitNodeCreateBuffer() {
|
|
1758
|
+
}
|
|
1759
|
+
commitNodeUpdateBuffer() {
|
|
1760
|
+
}
|
|
1761
|
+
commitNodeDeleteBuffer() {
|
|
1762
|
+
}
|
|
1763
|
+
};
|
|
1764
|
+
|
|
1765
|
+
// src/BPTreeSync.ts
|
|
1766
|
+
var BPTreeSync = class extends BPTreeSyncBase {
|
|
1767
|
+
constructor(strategy, comparator, option) {
|
|
1768
|
+
super(strategy, comparator, option);
|
|
1729
1769
|
}
|
|
1730
1770
|
/**
|
|
1731
|
-
*
|
|
1732
|
-
* @
|
|
1771
|
+
* Creates a new synchronous transaction.
|
|
1772
|
+
* @returns A new BPTreeSyncTransaction.
|
|
1733
1773
|
*/
|
|
1734
|
-
|
|
1735
|
-
|
|
1774
|
+
createTransaction() {
|
|
1775
|
+
const tx = new BPTreeSyncTransaction(this);
|
|
1776
|
+
tx.initTransaction();
|
|
1777
|
+
return tx;
|
|
1778
|
+
}
|
|
1779
|
+
insert(key, value) {
|
|
1780
|
+
const tx = this.createTransaction();
|
|
1781
|
+
tx.insert(key, value);
|
|
1782
|
+
const { success } = tx.commit();
|
|
1783
|
+
this.init();
|
|
1784
|
+
if (!success) {
|
|
1785
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
delete(key, value) {
|
|
1789
|
+
const tx = this.createTransaction();
|
|
1790
|
+
tx.delete(key, value);
|
|
1791
|
+
const { success } = tx.commit();
|
|
1792
|
+
this.init();
|
|
1793
|
+
if (!success) {
|
|
1794
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
1795
|
+
}
|
|
1736
1796
|
}
|
|
1737
1797
|
};
|
|
1738
1798
|
|
|
1739
|
-
// src/
|
|
1740
|
-
var
|
|
1741
|
-
lock;
|
|
1799
|
+
// src/base/BPTreeAsyncBase.ts
|
|
1800
|
+
var BPTreeAsyncBase = class extends BPTree {
|
|
1742
1801
|
constructor(strategy, comparator, option) {
|
|
1743
1802
|
super(strategy, comparator, option);
|
|
1744
1803
|
this.nodes = this._createCachedNode();
|
|
1745
|
-
this.lock = new Ryoiki();
|
|
1746
1804
|
}
|
|
1747
1805
|
_createCachedNode() {
|
|
1748
1806
|
return new CacheEntanglementAsync(async (key) => {
|
|
@@ -1751,24 +1809,6 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1751
1809
|
capacity: this.option.capacity ?? 1e3
|
|
1752
1810
|
});
|
|
1753
1811
|
}
|
|
1754
|
-
async readLock(callback) {
|
|
1755
|
-
let lockId;
|
|
1756
|
-
return await this.lock.readLock(async (_lockId) => {
|
|
1757
|
-
lockId = _lockId;
|
|
1758
|
-
return await callback();
|
|
1759
|
-
}).finally(() => {
|
|
1760
|
-
this.lock.readUnlock(lockId);
|
|
1761
|
-
});
|
|
1762
|
-
}
|
|
1763
|
-
async writeLock(callback) {
|
|
1764
|
-
let lockId;
|
|
1765
|
-
return await this.lock.writeLock(async (_lockId) => {
|
|
1766
|
-
lockId = _lockId;
|
|
1767
|
-
return await callback();
|
|
1768
|
-
}).finally(() => {
|
|
1769
|
-
this.lock.writeUnlock(lockId);
|
|
1770
|
-
});
|
|
1771
|
-
}
|
|
1772
1812
|
async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
1773
1813
|
let node = startNode;
|
|
1774
1814
|
let done = false;
|
|
@@ -1866,7 +1906,7 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1866
1906
|
}
|
|
1867
1907
|
}
|
|
1868
1908
|
}
|
|
1869
|
-
if (this.rootId === node.id && node.keys.length === 1) {
|
|
1909
|
+
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
1870
1910
|
const keys = node.keys;
|
|
1871
1911
|
this.bufferForNodeDelete(node);
|
|
1872
1912
|
const newRoot = await this.getNode(keys[0]);
|
|
@@ -1935,28 +1975,19 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1935
1975
|
pointer.values.push(guess);
|
|
1936
1976
|
} else {
|
|
1937
1977
|
pointer.next = node.next;
|
|
1938
|
-
pointer.prev = node.id;
|
|
1939
1978
|
if (pointer.next) {
|
|
1940
|
-
const n = await this.getNode(
|
|
1979
|
+
const n = await this.getNode(pointer.next);
|
|
1941
1980
|
n.prev = pointer.id;
|
|
1942
1981
|
this.bufferForNodeUpdate(n);
|
|
1943
1982
|
}
|
|
1944
|
-
if (pointer.prev) {
|
|
1945
|
-
const n = await this.getNode(node.id);
|
|
1946
|
-
n.next = pointer.id;
|
|
1947
|
-
this.bufferForNodeUpdate(n);
|
|
1948
|
-
}
|
|
1949
|
-
if (isPredecessor) {
|
|
1950
|
-
pointer.prev = null;
|
|
1951
|
-
}
|
|
1952
1983
|
}
|
|
1953
1984
|
pointer.values.push(...node.values);
|
|
1954
1985
|
if (!pointer.leaf) {
|
|
1955
1986
|
const keys = pointer.keys;
|
|
1956
1987
|
for (const key2 of keys) {
|
|
1957
|
-
const
|
|
1958
|
-
|
|
1959
|
-
this.bufferForNodeUpdate(
|
|
1988
|
+
const n = await this.getNode(key2);
|
|
1989
|
+
n.parent = pointer.id;
|
|
1990
|
+
this.bufferForNodeUpdate(n);
|
|
1960
1991
|
}
|
|
1961
1992
|
}
|
|
1962
1993
|
await this._deleteEntry(await this.getNode(node.parent), node.id, guess);
|
|
@@ -2033,21 +2064,24 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2033
2064
|
this.bufferForNodeUpdate(pointer);
|
|
2034
2065
|
}
|
|
2035
2066
|
if (!pointer.leaf) {
|
|
2036
|
-
|
|
2067
|
+
const keys = pointer.keys;
|
|
2068
|
+
for (const key2 of keys) {
|
|
2037
2069
|
const n = await this.getNode(key2);
|
|
2038
2070
|
n.parent = pointer.id;
|
|
2039
2071
|
this.bufferForNodeUpdate(n);
|
|
2040
2072
|
}
|
|
2041
2073
|
}
|
|
2042
2074
|
if (!node.leaf) {
|
|
2043
|
-
|
|
2075
|
+
const keys = node.keys;
|
|
2076
|
+
for (const key2 of keys) {
|
|
2044
2077
|
const n = await this.getNode(key2);
|
|
2045
2078
|
n.parent = node.id;
|
|
2046
2079
|
this.bufferForNodeUpdate(n);
|
|
2047
2080
|
}
|
|
2048
2081
|
}
|
|
2049
2082
|
if (!parentNode.leaf) {
|
|
2050
|
-
|
|
2083
|
+
const keys = parentNode.keys;
|
|
2084
|
+
for (const key2 of keys) {
|
|
2051
2085
|
const n = await this.getNode(key2);
|
|
2052
2086
|
n.parent = parentNode.id;
|
|
2053
2087
|
this.bufferForNodeUpdate(n);
|
|
@@ -2111,14 +2145,14 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2111
2145
|
parentNode.values = parentNode.values.slice(0, mid);
|
|
2112
2146
|
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2113
2147
|
for (const k of parentNode.keys) {
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
this.bufferForNodeUpdate(
|
|
2148
|
+
const n = await this.getNode(k);
|
|
2149
|
+
n.parent = parentNode.id;
|
|
2150
|
+
this.bufferForNodeUpdate(n);
|
|
2117
2151
|
}
|
|
2118
2152
|
for (const k of parentPointer.keys) {
|
|
2119
|
-
const
|
|
2120
|
-
|
|
2121
|
-
this.bufferForNodeUpdate(
|
|
2153
|
+
const n = await this.getNode(k);
|
|
2154
|
+
n.parent = parentPointer.id;
|
|
2155
|
+
this.bufferForNodeUpdate(n);
|
|
2122
2156
|
}
|
|
2123
2157
|
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2124
2158
|
this.bufferForNodeUpdate(parentNode);
|
|
@@ -2160,23 +2194,19 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2160
2194
|
const nValue = node.values[i];
|
|
2161
2195
|
const k = node.keys;
|
|
2162
2196
|
if (this.comparator.isSame(value, nValue)) {
|
|
2163
|
-
node = await this.getNode(
|
|
2197
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2164
2198
|
break;
|
|
2165
2199
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
2166
|
-
node = await this.getNode(
|
|
2200
|
+
node = await this.getNode(node.keys[i]);
|
|
2167
2201
|
break;
|
|
2168
2202
|
} else if (i + 1 === node.values.length) {
|
|
2169
|
-
node = await this.getNode(
|
|
2203
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2170
2204
|
break;
|
|
2171
2205
|
}
|
|
2172
2206
|
}
|
|
2173
2207
|
}
|
|
2174
2208
|
return node;
|
|
2175
2209
|
}
|
|
2176
|
-
/**
|
|
2177
|
-
* Find the insertable node using primaryAsc comparison.
|
|
2178
|
-
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
2179
|
-
*/
|
|
2180
2210
|
async insertableNodeByPrimary(value) {
|
|
2181
2211
|
let node = await this.getNode(this.rootId);
|
|
2182
2212
|
while (!node.leaf) {
|
|
@@ -2184,13 +2214,13 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2184
2214
|
const nValue = node.values[i];
|
|
2185
2215
|
const k = node.keys;
|
|
2186
2216
|
if (this.comparator.isPrimarySame(value, nValue)) {
|
|
2187
|
-
node = await this.getNode(
|
|
2217
|
+
node = await this.getNode(node.keys[i]);
|
|
2188
2218
|
break;
|
|
2189
2219
|
} else if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
2190
|
-
node = await this.getNode(
|
|
2220
|
+
node = await this.getNode(node.keys[i]);
|
|
2191
2221
|
break;
|
|
2192
2222
|
} else if (i + 1 === node.values.length) {
|
|
2193
|
-
node = await this.getNode(
|
|
2223
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2194
2224
|
break;
|
|
2195
2225
|
}
|
|
2196
2226
|
}
|
|
@@ -2204,11 +2234,11 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2204
2234
|
const nValue = node.values[i];
|
|
2205
2235
|
const k = node.keys;
|
|
2206
2236
|
if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
2207
|
-
node = await this.getNode(
|
|
2237
|
+
node = await this.getNode(node.keys[i]);
|
|
2208
2238
|
break;
|
|
2209
2239
|
}
|
|
2210
2240
|
if (i + 1 === node.values.length) {
|
|
2211
|
-
node = await this.getNode(
|
|
2241
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2212
2242
|
break;
|
|
2213
2243
|
}
|
|
2214
2244
|
}
|
|
@@ -2245,7 +2275,7 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2245
2275
|
let node = await this.getNode(this.rootId);
|
|
2246
2276
|
while (!node.leaf) {
|
|
2247
2277
|
const keys = node.keys;
|
|
2248
|
-
node = await this.getNode(keys[0]);
|
|
2278
|
+
node = await this.getNode(node.keys[0]);
|
|
2249
2279
|
}
|
|
2250
2280
|
return node;
|
|
2251
2281
|
}
|
|
@@ -2253,10 +2283,37 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2253
2283
|
let node = await this.getNode(this.rootId);
|
|
2254
2284
|
while (!node.leaf) {
|
|
2255
2285
|
const keys = node.keys;
|
|
2256
|
-
node = await this.getNode(keys[keys.length - 1]);
|
|
2286
|
+
node = await this.getNode(node.keys[node.keys.length - 1]);
|
|
2257
2287
|
}
|
|
2258
2288
|
return node;
|
|
2259
2289
|
}
|
|
2290
|
+
async exists(key, value) {
|
|
2291
|
+
const node = await this.insertableNode(value);
|
|
2292
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2293
|
+
if (this.comparator.isSame(value, node.values[i])) {
|
|
2294
|
+
const keys = node.keys[i];
|
|
2295
|
+
if (keys.includes(key)) {
|
|
2296
|
+
return true;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
return false;
|
|
2301
|
+
}
|
|
2302
|
+
async forceUpdate(id) {
|
|
2303
|
+
if (id) {
|
|
2304
|
+
this.nodes.delete(id);
|
|
2305
|
+
await this.getNode(id);
|
|
2306
|
+
return 1;
|
|
2307
|
+
}
|
|
2308
|
+
const keys = Array.from(this.nodes.keys());
|
|
2309
|
+
for (const key of keys) {
|
|
2310
|
+
this.nodes.delete(key);
|
|
2311
|
+
}
|
|
2312
|
+
for (const key of keys) {
|
|
2313
|
+
await this.getNode(key);
|
|
2314
|
+
}
|
|
2315
|
+
return keys.length;
|
|
2316
|
+
}
|
|
2260
2317
|
async commitHeadBuffer() {
|
|
2261
2318
|
if (!this._strategyDirty) {
|
|
2262
2319
|
return;
|
|
@@ -2287,25 +2344,15 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2287
2344
|
}
|
|
2288
2345
|
this._nodeDeleteBuffer.clear();
|
|
2289
2346
|
}
|
|
2290
|
-
/**
|
|
2291
|
-
* Retrieves the value associated with the given key (PK).
|
|
2292
|
-
* Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
|
|
2293
|
-
*
|
|
2294
|
-
* @param key The key to search for.
|
|
2295
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
2296
|
-
*/
|
|
2297
2347
|
async get(key) {
|
|
2298
|
-
return this.readLock(async () => {
|
|
2348
|
+
return await this.readLock(async () => {
|
|
2299
2349
|
let node = await this.leftestNode();
|
|
2300
2350
|
while (true) {
|
|
2301
|
-
|
|
2302
|
-
const
|
|
2303
|
-
for (let
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
if (keys[j] === key) {
|
|
2307
|
-
return node.values[i];
|
|
2308
|
-
}
|
|
2351
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2352
|
+
const keys = node.keys[i];
|
|
2353
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
2354
|
+
if (keys[j] === key) {
|
|
2355
|
+
return node.values[i];
|
|
2309
2356
|
}
|
|
2310
2357
|
}
|
|
2311
2358
|
}
|
|
@@ -2380,25 +2427,21 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2380
2427
|
}
|
|
2381
2428
|
}
|
|
2382
2429
|
async keys(condition, filterValues) {
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
return set;
|
|
2389
|
-
});
|
|
2430
|
+
const set = /* @__PURE__ */ new Set();
|
|
2431
|
+
for await (const key of this.keysStream(condition, filterValues)) {
|
|
2432
|
+
set.add(key);
|
|
2433
|
+
}
|
|
2434
|
+
return set;
|
|
2390
2435
|
}
|
|
2391
2436
|
async where(condition) {
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
return map;
|
|
2398
|
-
});
|
|
2437
|
+
const map = /* @__PURE__ */ new Map();
|
|
2438
|
+
for await (const [key, value] of this.whereStream(condition)) {
|
|
2439
|
+
map.set(key, value);
|
|
2440
|
+
}
|
|
2441
|
+
return map;
|
|
2399
2442
|
}
|
|
2400
2443
|
async insert(key, value) {
|
|
2401
|
-
|
|
2444
|
+
await this.writeLock(async () => {
|
|
2402
2445
|
const before = await this.insertableNode(value);
|
|
2403
2446
|
this._insertAtLeaf(before, key, value);
|
|
2404
2447
|
if (before.values.length === this.order) {
|
|
@@ -2425,182 +2468,368 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2425
2468
|
});
|
|
2426
2469
|
}
|
|
2427
2470
|
async delete(key, value) {
|
|
2428
|
-
|
|
2471
|
+
await this.writeLock(async () => {
|
|
2429
2472
|
const node = await this.insertableNode(value);
|
|
2430
2473
|
let i = node.values.length;
|
|
2431
2474
|
while (i--) {
|
|
2432
2475
|
const nValue = node.values[i];
|
|
2433
2476
|
if (this.comparator.isSame(value, nValue)) {
|
|
2434
2477
|
const keys = node.keys[i];
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
} else if (node.id === this.rootId) {
|
|
2440
|
-
node.values.splice(i, 1);
|
|
2441
|
-
node.keys.splice(i, 1);
|
|
2442
|
-
this.bufferForNodeUpdate(node);
|
|
2443
|
-
} else {
|
|
2444
|
-
keys.splice(keys.indexOf(key), 1);
|
|
2478
|
+
const keyIndex = keys.indexOf(key);
|
|
2479
|
+
if (keyIndex !== -1) {
|
|
2480
|
+
keys.splice(keyIndex, 1);
|
|
2481
|
+
if (keys.length === 0) {
|
|
2445
2482
|
node.keys.splice(i, 1);
|
|
2446
|
-
node.values.splice(
|
|
2447
|
-
await this._deleteEntry(node, key, value);
|
|
2448
|
-
this.bufferForNodeUpdate(node);
|
|
2483
|
+
node.values.splice(i, 1);
|
|
2449
2484
|
}
|
|
2485
|
+
await this._deleteEntry(node, key, value);
|
|
2486
|
+
break;
|
|
2450
2487
|
}
|
|
2451
2488
|
}
|
|
2452
2489
|
}
|
|
2453
2490
|
await this.commitHeadBuffer();
|
|
2454
|
-
await this.commitNodeCreateBuffer();
|
|
2455
2491
|
await this.commitNodeUpdateBuffer();
|
|
2456
2492
|
await this.commitNodeDeleteBuffer();
|
|
2457
2493
|
});
|
|
2458
2494
|
}
|
|
2459
|
-
|
|
2460
|
-
return this.
|
|
2461
|
-
const node = await this.insertableNode(value);
|
|
2462
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2463
|
-
const nValue = node.values[i];
|
|
2464
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
2465
|
-
const keys = node.keys[i];
|
|
2466
|
-
return keys.includes(key);
|
|
2467
|
-
}
|
|
2468
|
-
}
|
|
2469
|
-
return false;
|
|
2470
|
-
});
|
|
2495
|
+
getHeadData() {
|
|
2496
|
+
return this.strategy.head.data;
|
|
2471
2497
|
}
|
|
2472
2498
|
async setHeadData(data) {
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
this._strategyDirty = true;
|
|
2476
|
-
await this.commitHeadBuffer();
|
|
2477
|
-
});
|
|
2478
|
-
}
|
|
2479
|
-
async forceUpdate() {
|
|
2480
|
-
return this.readLock(async () => {
|
|
2481
|
-
const keys = [...this.nodes.keys()];
|
|
2482
|
-
this.nodes.clear();
|
|
2483
|
-
await this.init();
|
|
2484
|
-
for (const key of keys) {
|
|
2485
|
-
await this.getNode(key);
|
|
2486
|
-
}
|
|
2487
|
-
return keys.length;
|
|
2488
|
-
});
|
|
2489
|
-
}
|
|
2490
|
-
};
|
|
2491
|
-
|
|
2492
|
-
// src/base/SerializeStrategy.ts
|
|
2493
|
-
var SerializeStrategy = class {
|
|
2494
|
-
order;
|
|
2495
|
-
head;
|
|
2496
|
-
constructor(order) {
|
|
2497
|
-
this.order = order;
|
|
2498
|
-
this.head = {
|
|
2499
|
-
order,
|
|
2500
|
-
root: null,
|
|
2501
|
-
data: {}
|
|
2502
|
-
};
|
|
2499
|
+
this.strategy.head.data = data;
|
|
2500
|
+
await this.strategy.writeHead(this.strategy.head);
|
|
2503
2501
|
}
|
|
2504
2502
|
};
|
|
2505
2503
|
|
|
2506
|
-
// src/
|
|
2507
|
-
var
|
|
2508
|
-
getHeadData(key, defaultValue) {
|
|
2504
|
+
// src/SerializeStrategyAsync.ts
|
|
2505
|
+
var SerializeStrategyAsync = class extends SerializeStrategy {
|
|
2506
|
+
async getHeadData(key, defaultValue) {
|
|
2509
2507
|
if (!Object.hasOwn(this.head.data, key)) {
|
|
2510
|
-
this.setHeadData(key, defaultValue);
|
|
2508
|
+
await this.setHeadData(key, defaultValue);
|
|
2511
2509
|
}
|
|
2512
2510
|
return this.head.data[key];
|
|
2513
2511
|
}
|
|
2514
|
-
setHeadData(key, data) {
|
|
2512
|
+
async setHeadData(key, data) {
|
|
2515
2513
|
this.head.data[key] = data;
|
|
2516
|
-
this.writeHead(this.head);
|
|
2514
|
+
await this.writeHead(this.head);
|
|
2517
2515
|
}
|
|
2518
|
-
autoIncrement(key, defaultValue) {
|
|
2519
|
-
const current = this.getHeadData(key, defaultValue);
|
|
2516
|
+
async autoIncrement(key, defaultValue) {
|
|
2517
|
+
const current = await this.getHeadData(key, defaultValue);
|
|
2520
2518
|
const next = current + 1;
|
|
2521
|
-
this.setHeadData(key, next);
|
|
2519
|
+
await this.setHeadData(key, next);
|
|
2522
2520
|
return current;
|
|
2523
2521
|
}
|
|
2522
|
+
async compareAndSwapHead(oldRoot, newRoot) {
|
|
2523
|
+
if (this.head.root !== oldRoot) {
|
|
2524
|
+
return false;
|
|
2525
|
+
}
|
|
2526
|
+
this.head.root = newRoot;
|
|
2527
|
+
await this.writeHead(this.head);
|
|
2528
|
+
return true;
|
|
2529
|
+
}
|
|
2524
2530
|
};
|
|
2525
|
-
var
|
|
2531
|
+
var InMemoryStoreStrategyAsync = class extends SerializeStrategyAsync {
|
|
2526
2532
|
node;
|
|
2527
2533
|
constructor(order) {
|
|
2528
2534
|
super(order);
|
|
2529
2535
|
this.node = {};
|
|
2530
2536
|
}
|
|
2531
|
-
id(isLeaf) {
|
|
2532
|
-
return this.autoIncrement("index", 1).toString();
|
|
2537
|
+
async id(isLeaf) {
|
|
2538
|
+
return (await this.autoIncrement("index", 1)).toString();
|
|
2533
2539
|
}
|
|
2534
|
-
read(id) {
|
|
2540
|
+
async read(id) {
|
|
2535
2541
|
if (!Object.hasOwn(this.node, id)) {
|
|
2536
2542
|
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
2537
2543
|
}
|
|
2538
|
-
|
|
2544
|
+
const node = this.node[id];
|
|
2545
|
+
return JSON.parse(JSON.stringify(node));
|
|
2539
2546
|
}
|
|
2540
|
-
write(id, node) {
|
|
2547
|
+
async write(id, node) {
|
|
2541
2548
|
this.node[id] = node;
|
|
2542
2549
|
}
|
|
2543
|
-
delete(id) {
|
|
2550
|
+
async delete(id) {
|
|
2544
2551
|
delete this.node[id];
|
|
2545
2552
|
}
|
|
2546
|
-
readHead() {
|
|
2553
|
+
async readHead() {
|
|
2547
2554
|
if (this.head.root === null) {
|
|
2548
2555
|
return null;
|
|
2549
2556
|
}
|
|
2550
2557
|
return this.head;
|
|
2551
2558
|
}
|
|
2552
|
-
writeHead(head) {
|
|
2559
|
+
async writeHead(head) {
|
|
2553
2560
|
this.head = head;
|
|
2554
2561
|
}
|
|
2555
2562
|
};
|
|
2556
2563
|
|
|
2557
|
-
// src/
|
|
2558
|
-
var
|
|
2564
|
+
// src/transaction/BPTreeAsyncSnapshotStrategy.ts
|
|
2565
|
+
var BPTreeAsyncSnapshotStrategy = class extends SerializeStrategyAsync {
|
|
2566
|
+
baseStrategy;
|
|
2567
|
+
snapshotHead;
|
|
2568
|
+
constructor(baseStrategy, root) {
|
|
2569
|
+
super(baseStrategy.order);
|
|
2570
|
+
this.baseStrategy = baseStrategy;
|
|
2571
|
+
this.snapshotHead = {
|
|
2572
|
+
...baseStrategy.head,
|
|
2573
|
+
root,
|
|
2574
|
+
data: { ...baseStrategy.head.data }
|
|
2575
|
+
};
|
|
2576
|
+
this.head = this.snapshotHead;
|
|
2577
|
+
}
|
|
2578
|
+
async id(isLeaf) {
|
|
2579
|
+
return await this.baseStrategy.id(isLeaf);
|
|
2580
|
+
}
|
|
2581
|
+
async read(id) {
|
|
2582
|
+
return await this.baseStrategy.read(id);
|
|
2583
|
+
}
|
|
2584
|
+
async write(id, node) {
|
|
2585
|
+
await this.baseStrategy.write(id, node);
|
|
2586
|
+
}
|
|
2587
|
+
async delete(id) {
|
|
2588
|
+
await this.baseStrategy.delete(id);
|
|
2589
|
+
}
|
|
2590
|
+
async readHead() {
|
|
2591
|
+
return this.snapshotHead;
|
|
2592
|
+
}
|
|
2593
|
+
async writeHead(head) {
|
|
2594
|
+
this.snapshotHead.root = head.root;
|
|
2595
|
+
this.snapshotHead.data = { ...head.data };
|
|
2596
|
+
}
|
|
2597
|
+
async compareAndSwapHead(oldRoot, newRoot) {
|
|
2598
|
+
return await this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
|
|
2599
|
+
}
|
|
2559
2600
|
async getHeadData(key, defaultValue) {
|
|
2560
|
-
|
|
2561
|
-
await this.setHeadData(key, defaultValue);
|
|
2562
|
-
}
|
|
2563
|
-
return this.head.data[key];
|
|
2601
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
2564
2602
|
}
|
|
2565
2603
|
async setHeadData(key, data) {
|
|
2566
|
-
this.
|
|
2567
|
-
await this.writeHead(this.head);
|
|
2604
|
+
this.snapshotHead.data[key] = data;
|
|
2568
2605
|
}
|
|
2569
2606
|
async autoIncrement(key, defaultValue) {
|
|
2570
|
-
|
|
2571
|
-
const next = current + 1;
|
|
2572
|
-
await this.setHeadData(key, next);
|
|
2573
|
-
return current;
|
|
2607
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
2574
2608
|
}
|
|
2575
2609
|
};
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2610
|
+
|
|
2611
|
+
// src/transaction/BPTreeAsyncTransaction.ts
|
|
2612
|
+
var BPTreeAsyncTransaction = class extends BPTreeAsyncBase {
|
|
2613
|
+
realBaseTree;
|
|
2614
|
+
realBaseStrategy;
|
|
2615
|
+
txNodes = /* @__PURE__ */ new Map();
|
|
2616
|
+
dirtyIds = /* @__PURE__ */ new Set();
|
|
2617
|
+
createdInTx = /* @__PURE__ */ new Set();
|
|
2618
|
+
initialRootId;
|
|
2619
|
+
transactionRootId;
|
|
2620
|
+
constructor(baseTree) {
|
|
2621
|
+
super(baseTree.strategy, baseTree.comparator, baseTree.option);
|
|
2622
|
+
this.realBaseTree = baseTree;
|
|
2623
|
+
this.realBaseStrategy = baseTree.strategy;
|
|
2624
|
+
this.initialRootId = "";
|
|
2625
|
+
this.transactionRootId = "";
|
|
2581
2626
|
}
|
|
2582
|
-
|
|
2583
|
-
|
|
2627
|
+
/**
|
|
2628
|
+
* Initializes the transaction by capturing the current state of the tree.
|
|
2629
|
+
*/
|
|
2630
|
+
async initTransaction() {
|
|
2631
|
+
const head = await this.realBaseStrategy.readHead();
|
|
2632
|
+
this.initialRootId = head?.root ?? this.realBaseTree.rootId;
|
|
2633
|
+
this.transactionRootId = this.initialRootId;
|
|
2634
|
+
this.rootId = this.transactionRootId;
|
|
2635
|
+
const snapshotStrategy = new BPTreeAsyncSnapshotStrategy(this.realBaseStrategy, this.initialRootId);
|
|
2636
|
+
this.strategy = snapshotStrategy;
|
|
2637
|
+
this.txNodes.clear();
|
|
2638
|
+
this.dirtyIds.clear();
|
|
2639
|
+
this.createdInTx.clear();
|
|
2584
2640
|
}
|
|
2585
|
-
async
|
|
2586
|
-
if (
|
|
2587
|
-
|
|
2641
|
+
async getNode(id) {
|
|
2642
|
+
if (this.txNodes.has(id)) {
|
|
2643
|
+
return this.txNodes.get(id);
|
|
2644
|
+
}
|
|
2645
|
+
const baseNode = await this.realBaseStrategy.read(id);
|
|
2646
|
+
const clone = JSON.parse(JSON.stringify(baseNode));
|
|
2647
|
+
this.txNodes.set(id, clone);
|
|
2648
|
+
return clone;
|
|
2649
|
+
}
|
|
2650
|
+
async bufferForNodeUpdate(node) {
|
|
2651
|
+
this.txNodes.set(node.id, node);
|
|
2652
|
+
this.dirtyIds.add(node.id);
|
|
2653
|
+
await this.markPathDirty(node);
|
|
2654
|
+
}
|
|
2655
|
+
async bufferForNodeCreate(node) {
|
|
2656
|
+
this.txNodes.set(node.id, node);
|
|
2657
|
+
this.dirtyIds.add(node.id);
|
|
2658
|
+
this.createdInTx.add(node.id);
|
|
2659
|
+
await this.markPathDirty(node);
|
|
2660
|
+
}
|
|
2661
|
+
async bufferForNodeDelete(node) {
|
|
2662
|
+
this.txNodes.delete(node.id);
|
|
2663
|
+
this.dirtyIds.add(node.id);
|
|
2664
|
+
}
|
|
2665
|
+
async markPathDirty(node) {
|
|
2666
|
+
let curr = node;
|
|
2667
|
+
while (curr.parent) {
|
|
2668
|
+
if (this.dirtyIds.has(curr.parent) && this.txNodes.has(curr.parent)) {
|
|
2669
|
+
break;
|
|
2670
|
+
}
|
|
2671
|
+
const parent = await this.getNode(curr.parent);
|
|
2672
|
+
this.dirtyIds.add(parent.id);
|
|
2673
|
+
curr = parent;
|
|
2674
|
+
}
|
|
2675
|
+
if (!curr.parent) {
|
|
2676
|
+
this.transactionRootId = curr.id;
|
|
2588
2677
|
}
|
|
2589
|
-
return this.node[id];
|
|
2590
2678
|
}
|
|
2591
|
-
async
|
|
2592
|
-
|
|
2679
|
+
async _createNode(isLeaf, keys, values, leaf = false, parent = null, next = null, prev = null) {
|
|
2680
|
+
const id = await this.realBaseStrategy.id(isLeaf);
|
|
2681
|
+
const node = {
|
|
2682
|
+
id,
|
|
2683
|
+
keys,
|
|
2684
|
+
values,
|
|
2685
|
+
leaf: isLeaf,
|
|
2686
|
+
parent,
|
|
2687
|
+
next,
|
|
2688
|
+
prev
|
|
2689
|
+
};
|
|
2690
|
+
await this.bufferForNodeCreate(node);
|
|
2691
|
+
return node;
|
|
2593
2692
|
}
|
|
2594
|
-
|
|
2595
|
-
|
|
2693
|
+
/**
|
|
2694
|
+
* Attempts to commit the transaction.
|
|
2695
|
+
* Uses Optimistic Locking (Compare-And-Swap) on the root node ID to detect conflicts.
|
|
2696
|
+
*
|
|
2697
|
+
* @returns A promise that resolves to the transaction result.
|
|
2698
|
+
*/
|
|
2699
|
+
async commit() {
|
|
2700
|
+
const idMapping = /* @__PURE__ */ new Map();
|
|
2701
|
+
const finalNodes = [];
|
|
2702
|
+
for (const oldId of this.dirtyIds) {
|
|
2703
|
+
if (this.createdInTx.has(oldId)) {
|
|
2704
|
+
idMapping.set(oldId, oldId);
|
|
2705
|
+
} else {
|
|
2706
|
+
const node = this.txNodes.get(oldId);
|
|
2707
|
+
if (node) {
|
|
2708
|
+
const newId = await this.realBaseStrategy.id(node.leaf);
|
|
2709
|
+
idMapping.set(oldId, newId);
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
const newCreatedIds = [];
|
|
2714
|
+
for (const oldId of this.dirtyIds) {
|
|
2715
|
+
const node = this.txNodes.get(oldId);
|
|
2716
|
+
if (!node) continue;
|
|
2717
|
+
const newId = idMapping.get(oldId);
|
|
2718
|
+
node.id = newId;
|
|
2719
|
+
if (node.parent && idMapping.has(node.parent)) {
|
|
2720
|
+
node.parent = idMapping.get(node.parent);
|
|
2721
|
+
}
|
|
2722
|
+
if (!node.leaf) {
|
|
2723
|
+
const internal = node;
|
|
2724
|
+
for (let i = 0; i < internal.keys.length; i++) {
|
|
2725
|
+
const childId = internal.keys[i];
|
|
2726
|
+
if (idMapping.has(childId)) {
|
|
2727
|
+
internal.keys[i] = idMapping.get(childId);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
if (node.leaf) {
|
|
2732
|
+
const leaf = node;
|
|
2733
|
+
if (leaf.next && idMapping.has(leaf.next)) {
|
|
2734
|
+
leaf.next = idMapping.get(leaf.next);
|
|
2735
|
+
}
|
|
2736
|
+
if (leaf.prev && idMapping.has(leaf.prev)) {
|
|
2737
|
+
leaf.prev = idMapping.get(leaf.prev);
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
finalNodes.push(node);
|
|
2741
|
+
newCreatedIds.push(newId);
|
|
2742
|
+
}
|
|
2743
|
+
let newRootId = this.transactionRootId;
|
|
2744
|
+
if (idMapping.has(this.transactionRootId)) {
|
|
2745
|
+
newRootId = idMapping.get(this.transactionRootId);
|
|
2746
|
+
}
|
|
2747
|
+
for (const node of finalNodes) {
|
|
2748
|
+
await this.realBaseStrategy.write(node.id, node);
|
|
2749
|
+
}
|
|
2750
|
+
const success = await this.realBaseStrategy.compareAndSwapHead(this.initialRootId, newRootId);
|
|
2751
|
+
if (success) {
|
|
2752
|
+
const distinctObsolete = /* @__PURE__ */ new Set();
|
|
2753
|
+
for (const oldId of this.dirtyIds) {
|
|
2754
|
+
if (!this.createdInTx.has(oldId) && this.txNodes.has(oldId)) {
|
|
2755
|
+
distinctObsolete.add(oldId);
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
return {
|
|
2759
|
+
success: true,
|
|
2760
|
+
createdIds: newCreatedIds,
|
|
2761
|
+
obsoleteIds: Array.from(distinctObsolete)
|
|
2762
|
+
};
|
|
2763
|
+
} else {
|
|
2764
|
+
await this.rollback();
|
|
2765
|
+
return {
|
|
2766
|
+
success: false,
|
|
2767
|
+
createdIds: newCreatedIds,
|
|
2768
|
+
obsoleteIds: []
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2596
2771
|
}
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2772
|
+
/**
|
|
2773
|
+
* Rolls back the transaction by clearing all buffered changes.
|
|
2774
|
+
* Internal use only.
|
|
2775
|
+
*/
|
|
2776
|
+
async rollback() {
|
|
2777
|
+
this.txNodes.clear();
|
|
2778
|
+
this.dirtyIds.clear();
|
|
2779
|
+
this.createdInTx.clear();
|
|
2780
|
+
}
|
|
2781
|
+
async readLock(fn) {
|
|
2782
|
+
return await fn();
|
|
2783
|
+
}
|
|
2784
|
+
async writeLock(fn) {
|
|
2785
|
+
return await fn();
|
|
2786
|
+
}
|
|
2787
|
+
async commitHeadBuffer() {
|
|
2788
|
+
}
|
|
2789
|
+
async commitNodeCreateBuffer() {
|
|
2790
|
+
}
|
|
2791
|
+
async commitNodeUpdateBuffer() {
|
|
2792
|
+
}
|
|
2793
|
+
async commitNodeDeleteBuffer() {
|
|
2794
|
+
}
|
|
2795
|
+
};
|
|
2796
|
+
|
|
2797
|
+
// src/BPTreeAsync.ts
|
|
2798
|
+
var BPTreeAsync = class extends BPTreeAsyncBase {
|
|
2799
|
+
constructor(strategy, comparator, option) {
|
|
2800
|
+
super(strategy, comparator, option);
|
|
2801
|
+
}
|
|
2802
|
+
/**
|
|
2803
|
+
* Creates a new asynchronous transaction.
|
|
2804
|
+
* @returns A promise that resolves to a new BPTreeAsyncTransaction.
|
|
2805
|
+
*/
|
|
2806
|
+
async createTransaction() {
|
|
2807
|
+
const tx = new BPTreeAsyncTransaction(this);
|
|
2808
|
+
await tx.initTransaction();
|
|
2809
|
+
return tx;
|
|
2810
|
+
}
|
|
2811
|
+
async insert(key, value) {
|
|
2812
|
+
const tx = await this.createTransaction();
|
|
2813
|
+
await tx.insert(key, value);
|
|
2814
|
+
const { success } = await tx.commit();
|
|
2815
|
+
await this.init();
|
|
2816
|
+
if (!success) {
|
|
2817
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
2600
2818
|
}
|
|
2601
|
-
return this.head;
|
|
2602
2819
|
}
|
|
2603
|
-
async
|
|
2604
|
-
|
|
2820
|
+
async delete(key, value) {
|
|
2821
|
+
const tx = await this.createTransaction();
|
|
2822
|
+
await tx.delete(key, value);
|
|
2823
|
+
const { success } = await tx.commit();
|
|
2824
|
+
await this.init();
|
|
2825
|
+
if (!success) {
|
|
2826
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
async readLock(fn) {
|
|
2830
|
+
return await fn();
|
|
2831
|
+
}
|
|
2832
|
+
async writeLock(fn) {
|
|
2833
|
+
return await fn();
|
|
2605
2834
|
}
|
|
2606
2835
|
};
|