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/esm/index.mjs
CHANGED
|
@@ -727,8 +727,8 @@ var BPTree = class _BPTree {
|
|
|
727
727
|
}
|
|
728
728
|
};
|
|
729
729
|
|
|
730
|
-
// src/
|
|
731
|
-
var
|
|
730
|
+
// src/base/BPTreeSyncBase.ts
|
|
731
|
+
var BPTreeSyncBase = class extends BPTree {
|
|
732
732
|
constructor(strategy, comparator, option) {
|
|
733
733
|
super(strategy, comparator, option);
|
|
734
734
|
this.nodes = this._createCachedNode();
|
|
@@ -837,7 +837,7 @@ var BPTreeSync = class extends BPTree {
|
|
|
837
837
|
}
|
|
838
838
|
}
|
|
839
839
|
}
|
|
840
|
-
if (this.rootId === node.id && node.keys.length === 1) {
|
|
840
|
+
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
841
841
|
const keys = node.keys;
|
|
842
842
|
this.bufferForNodeDelete(node);
|
|
843
843
|
const newRoot = this.getNode(keys[0]);
|
|
@@ -906,20 +906,11 @@ var BPTreeSync = class extends BPTree {
|
|
|
906
906
|
pointer.values.push(guess);
|
|
907
907
|
} else {
|
|
908
908
|
pointer.next = node.next;
|
|
909
|
-
pointer.prev = node.id;
|
|
910
909
|
if (pointer.next) {
|
|
911
|
-
const n = this.getNode(
|
|
910
|
+
const n = this.getNode(pointer.next);
|
|
912
911
|
n.prev = pointer.id;
|
|
913
912
|
this.bufferForNodeUpdate(n);
|
|
914
913
|
}
|
|
915
|
-
if (pointer.prev) {
|
|
916
|
-
const n = this.getNode(node.id);
|
|
917
|
-
n.next = pointer.id;
|
|
918
|
-
this.bufferForNodeUpdate(n);
|
|
919
|
-
}
|
|
920
|
-
if (isPredecessor) {
|
|
921
|
-
pointer.prev = null;
|
|
922
|
-
}
|
|
923
914
|
}
|
|
924
915
|
pointer.values.push(...node.values);
|
|
925
916
|
if (!pointer.leaf) {
|
|
@@ -1144,10 +1135,6 @@ var BPTreeSync = class extends BPTree {
|
|
|
1144
1135
|
}
|
|
1145
1136
|
return node;
|
|
1146
1137
|
}
|
|
1147
|
-
/**
|
|
1148
|
-
* Find the insertable node using primaryAsc comparison.
|
|
1149
|
-
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
1150
|
-
*/
|
|
1151
1138
|
insertableNodeByPrimary(value) {
|
|
1152
1139
|
let node = this.getNode(this.rootId);
|
|
1153
1140
|
while (!node.leaf) {
|
|
@@ -1228,6 +1215,33 @@ var BPTreeSync = class extends BPTree {
|
|
|
1228
1215
|
}
|
|
1229
1216
|
return node;
|
|
1230
1217
|
}
|
|
1218
|
+
exists(key, value) {
|
|
1219
|
+
const node = this.insertableNode(value);
|
|
1220
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1221
|
+
if (this.comparator.isSame(value, node.values[i])) {
|
|
1222
|
+
const keys = node.keys[i];
|
|
1223
|
+
if (keys.includes(key)) {
|
|
1224
|
+
return true;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
return false;
|
|
1229
|
+
}
|
|
1230
|
+
forceUpdate(id) {
|
|
1231
|
+
if (id) {
|
|
1232
|
+
this.nodes.delete(id);
|
|
1233
|
+
this.getNode(id);
|
|
1234
|
+
return 1;
|
|
1235
|
+
}
|
|
1236
|
+
const keys = Array.from(this.nodes.keys());
|
|
1237
|
+
for (const key of keys) {
|
|
1238
|
+
this.nodes.delete(key);
|
|
1239
|
+
}
|
|
1240
|
+
for (const key of keys) {
|
|
1241
|
+
this.getNode(key);
|
|
1242
|
+
}
|
|
1243
|
+
return keys.length;
|
|
1244
|
+
}
|
|
1231
1245
|
commitHeadBuffer() {
|
|
1232
1246
|
if (!this._strategyDirty) {
|
|
1233
1247
|
return;
|
|
@@ -1258,24 +1272,14 @@ var BPTreeSync = class extends BPTree {
|
|
|
1258
1272
|
}
|
|
1259
1273
|
this._nodeDeleteBuffer.clear();
|
|
1260
1274
|
}
|
|
1261
|
-
/**
|
|
1262
|
-
* Retrieves the value associated with the given key (PK).
|
|
1263
|
-
* Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
|
|
1264
|
-
*
|
|
1265
|
-
* @param key The key to search for.
|
|
1266
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
1267
|
-
*/
|
|
1268
1275
|
get(key) {
|
|
1269
1276
|
let node = this.leftestNode();
|
|
1270
1277
|
while (true) {
|
|
1271
|
-
|
|
1272
|
-
const
|
|
1273
|
-
for (let
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
if (keys[j] === key) {
|
|
1277
|
-
return node.values[i];
|
|
1278
|
-
}
|
|
1278
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1279
|
+
const keys = node.keys[i];
|
|
1280
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
1281
|
+
if (keys[j] === key) {
|
|
1282
|
+
return node.values[i];
|
|
1279
1283
|
}
|
|
1280
1284
|
}
|
|
1281
1285
|
}
|
|
@@ -1394,21 +1398,15 @@ var BPTreeSync = class extends BPTree {
|
|
|
1394
1398
|
const nValue = node.values[i];
|
|
1395
1399
|
if (this.comparator.isSame(value, nValue)) {
|
|
1396
1400
|
const keys = node.keys[i];
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
} else if (node.id === this.rootId) {
|
|
1402
|
-
node.values.splice(i, 1);
|
|
1403
|
-
node.keys.splice(i, 1);
|
|
1404
|
-
this.bufferForNodeUpdate(node);
|
|
1405
|
-
} else {
|
|
1406
|
-
keys.splice(keys.indexOf(key), 1);
|
|
1401
|
+
const keyIndex = keys.indexOf(key);
|
|
1402
|
+
if (keyIndex !== -1) {
|
|
1403
|
+
keys.splice(keyIndex, 1);
|
|
1404
|
+
if (keys.length === 0) {
|
|
1407
1405
|
node.keys.splice(i, 1);
|
|
1408
|
-
node.values.splice(
|
|
1409
|
-
this._deleteEntry(node, key, value);
|
|
1410
|
-
this.bufferForNodeUpdate(node);
|
|
1406
|
+
node.values.splice(i, 1);
|
|
1411
1407
|
}
|
|
1408
|
+
this._deleteEntry(node, key, value);
|
|
1409
|
+
break;
|
|
1412
1410
|
}
|
|
1413
1411
|
}
|
|
1414
1412
|
}
|
|
@@ -1417,298 +1415,356 @@ var BPTreeSync = class extends BPTree {
|
|
|
1417
1415
|
this.commitNodeUpdateBuffer();
|
|
1418
1416
|
this.commitNodeDeleteBuffer();
|
|
1419
1417
|
}
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
1423
|
-
const nValue = node.values[i];
|
|
1424
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
1425
|
-
const keys = node.keys[i];
|
|
1426
|
-
return keys.includes(key);
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
return false;
|
|
1418
|
+
getHeadData() {
|
|
1419
|
+
return this.strategy.head.data;
|
|
1430
1420
|
}
|
|
1431
1421
|
setHeadData(data) {
|
|
1432
1422
|
this.strategy.head.data = data;
|
|
1433
|
-
this.
|
|
1434
|
-
this.commitHeadBuffer();
|
|
1423
|
+
this.strategy.writeHead(this.strategy.head);
|
|
1435
1424
|
}
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1425
|
+
};
|
|
1426
|
+
|
|
1427
|
+
// src/base/SerializeStrategy.ts
|
|
1428
|
+
var SerializeStrategy = class {
|
|
1429
|
+
order;
|
|
1430
|
+
head;
|
|
1431
|
+
constructor(order) {
|
|
1432
|
+
this.order = order;
|
|
1433
|
+
this.head = {
|
|
1434
|
+
order,
|
|
1435
|
+
root: null,
|
|
1436
|
+
data: {}
|
|
1437
|
+
};
|
|
1444
1438
|
}
|
|
1445
1439
|
};
|
|
1446
1440
|
|
|
1447
|
-
//
|
|
1448
|
-
var
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1441
|
+
// src/SerializeStrategySync.ts
|
|
1442
|
+
var SerializeStrategySync = class extends SerializeStrategy {
|
|
1443
|
+
getHeadData(key, defaultValue) {
|
|
1444
|
+
if (!Object.hasOwn(this.head.data, key)) {
|
|
1445
|
+
this.setHeadData(key, defaultValue);
|
|
1446
|
+
}
|
|
1447
|
+
return this.head.data[key];
|
|
1448
|
+
}
|
|
1449
|
+
setHeadData(key, data) {
|
|
1450
|
+
this.head.data[key] = data;
|
|
1451
|
+
this.writeHead(this.head);
|
|
1452
|
+
}
|
|
1453
|
+
autoIncrement(key, defaultValue) {
|
|
1454
|
+
const current = this.getHeadData(key, defaultValue);
|
|
1455
|
+
const next = current + 1;
|
|
1456
|
+
this.setHeadData(key, next);
|
|
1457
|
+
return current;
|
|
1458
|
+
}
|
|
1459
|
+
compareAndSwapHead(oldRoot, newRoot) {
|
|
1460
|
+
if (this.head.root !== oldRoot) {
|
|
1460
1461
|
return false;
|
|
1461
1462
|
}
|
|
1463
|
+
this.head.root = newRoot;
|
|
1464
|
+
this.writeHead(this.head);
|
|
1462
1465
|
return true;
|
|
1463
1466
|
}
|
|
1464
|
-
|
|
1465
|
-
|
|
1467
|
+
};
|
|
1468
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
1469
|
+
node;
|
|
1470
|
+
constructor(order) {
|
|
1471
|
+
super(order);
|
|
1472
|
+
this.node = {};
|
|
1466
1473
|
}
|
|
1467
|
-
|
|
1468
|
-
return
|
|
1474
|
+
id(isLeaf) {
|
|
1475
|
+
return this.autoIncrement("index", 1).toString();
|
|
1469
1476
|
}
|
|
1470
|
-
|
|
1471
|
-
|
|
1477
|
+
read(id) {
|
|
1478
|
+
if (!Object.hasOwn(this.node, id)) {
|
|
1479
|
+
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
1480
|
+
}
|
|
1481
|
+
const node = this.node[id];
|
|
1482
|
+
return JSON.parse(JSON.stringify(node));
|
|
1472
1483
|
}
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
*/
|
|
1476
|
-
constructor() {
|
|
1477
|
-
this.readings = /* @__PURE__ */ new Map();
|
|
1478
|
-
this.writings = /* @__PURE__ */ new Map();
|
|
1479
|
-
this.readQueue = /* @__PURE__ */ new Map();
|
|
1480
|
-
this.writeQueue = /* @__PURE__ */ new Map();
|
|
1484
|
+
write(id, node) {
|
|
1485
|
+
this.node[id] = node;
|
|
1481
1486
|
}
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
* @param start - The starting value of the range.
|
|
1485
|
-
* @param length - The length of the range.
|
|
1486
|
-
* @returns A range tuple [start, start + length].
|
|
1487
|
-
*/
|
|
1488
|
-
range(start, length) {
|
|
1489
|
-
return [start, start + length];
|
|
1487
|
+
delete(id) {
|
|
1488
|
+
delete this.node[id];
|
|
1490
1489
|
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1490
|
+
readHead() {
|
|
1491
|
+
if (this.head.root === null) {
|
|
1492
|
+
return null;
|
|
1493
|
+
}
|
|
1494
|
+
return this.head;
|
|
1493
1495
|
}
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
const [b1, b2] = b;
|
|
1497
|
-
return a1 === b1 && a2 === b2;
|
|
1496
|
+
writeHead(head) {
|
|
1497
|
+
this.head = head;
|
|
1498
1498
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1499
|
+
};
|
|
1500
|
+
|
|
1501
|
+
// src/transaction/BPTreeSyncSnapshotStrategy.ts
|
|
1502
|
+
var BPTreeSyncSnapshotStrategy = class extends SerializeStrategySync {
|
|
1503
|
+
baseStrategy;
|
|
1504
|
+
snapshotHead;
|
|
1505
|
+
constructor(baseStrategy, root) {
|
|
1506
|
+
super(baseStrategy.order);
|
|
1507
|
+
this.baseStrategy = baseStrategy;
|
|
1508
|
+
this.snapshotHead = {
|
|
1509
|
+
...baseStrategy.head,
|
|
1510
|
+
root,
|
|
1511
|
+
data: { ...baseStrategy.head.data }
|
|
1512
|
+
};
|
|
1513
|
+
this.head = this.snapshotHead;
|
|
1506
1514
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
if (this._matchArgs(args, pattern)) {
|
|
1510
|
-
return handlers[key](...args);
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
throw new Error("Invalid arguments");
|
|
1515
|
+
id(isLeaf) {
|
|
1516
|
+
return this.baseStrategy.id(isLeaf);
|
|
1514
1517
|
}
|
|
1515
|
-
|
|
1516
|
-
return
|
|
1517
|
-
const expectedType = pattern[index];
|
|
1518
|
-
if (expectedType === void 0) return typeof arg === "undefined";
|
|
1519
|
-
if (expectedType === Function) return typeof arg === "function";
|
|
1520
|
-
if (expectedType === Number) return typeof arg === "number";
|
|
1521
|
-
if (expectedType === Array) return Array.isArray(arg);
|
|
1522
|
-
return false;
|
|
1523
|
-
});
|
|
1518
|
+
read(id) {
|
|
1519
|
+
return this.baseStrategy.read(id);
|
|
1524
1520
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
const random = Math.random().toString(36).substring(2);
|
|
1528
|
-
return `${timestamp}${random}`;
|
|
1529
|
-
}
|
|
1530
|
-
_alloc(queue, workspaces, lockId) {
|
|
1531
|
-
const unit = queue.get(lockId);
|
|
1532
|
-
if (!unit) {
|
|
1533
|
-
throw _Ryoiki.ERR_NOT_EXISTS(lockId);
|
|
1534
|
-
}
|
|
1535
|
-
workspaces.set(lockId, unit);
|
|
1536
|
-
queue.delete(lockId);
|
|
1537
|
-
unit.alloc();
|
|
1538
|
-
}
|
|
1539
|
-
_free(workspaces, lockId) {
|
|
1540
|
-
const unit = workspaces.get(lockId);
|
|
1541
|
-
if (!unit) {
|
|
1542
|
-
throw _Ryoiki.ERR_NOT_EXISTS(lockId);
|
|
1543
|
-
}
|
|
1544
|
-
workspaces.delete(lockId);
|
|
1545
|
-
unit.free();
|
|
1546
|
-
}
|
|
1547
|
-
_lock(queue, range, timeout, task, condition) {
|
|
1548
|
-
return new Promise((resolve, reject) => {
|
|
1549
|
-
let timeoutId = null;
|
|
1550
|
-
if (timeout >= 0) {
|
|
1551
|
-
timeoutId = setTimeout(() => {
|
|
1552
|
-
reject(_Ryoiki.ERR_TIMEOUT(id, timeout));
|
|
1553
|
-
}, timeout);
|
|
1554
|
-
}
|
|
1555
|
-
const id = this._createRandomId();
|
|
1556
|
-
const alloc = async () => {
|
|
1557
|
-
if (timeoutId !== null) {
|
|
1558
|
-
clearTimeout(timeoutId);
|
|
1559
|
-
}
|
|
1560
|
-
const [err, v] = await _Ryoiki.CatchError(task(id));
|
|
1561
|
-
if (err) reject(err);
|
|
1562
|
-
else resolve(v);
|
|
1563
|
-
};
|
|
1564
|
-
const fetch = () => {
|
|
1565
|
-
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
1566
|
-
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
1567
|
-
};
|
|
1568
|
-
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
1569
|
-
fetch();
|
|
1570
|
-
});
|
|
1521
|
+
write(id, node) {
|
|
1522
|
+
this.baseStrategy.write(id, node);
|
|
1571
1523
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
for (const lock of workspaces.values()) {
|
|
1575
|
-
if (_Ryoiki.IsRangeOverlap(range, lock.range)) {
|
|
1576
|
-
isLocked = true;
|
|
1577
|
-
break;
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
return isLocked;
|
|
1524
|
+
delete(id) {
|
|
1525
|
+
this.baseStrategy.delete(id);
|
|
1581
1526
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
* @param range The range to check for active read locks.
|
|
1585
|
-
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
1586
|
-
*/
|
|
1587
|
-
isReading(range) {
|
|
1588
|
-
return this._checkWorking(range, this.readings);
|
|
1527
|
+
readHead() {
|
|
1528
|
+
return this.snapshotHead;
|
|
1589
1529
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
1594
|
-
*/
|
|
1595
|
-
isWriting(range) {
|
|
1596
|
-
return this._checkWorking(range, this.writings);
|
|
1530
|
+
writeHead(head) {
|
|
1531
|
+
this.snapshotHead.root = head.root;
|
|
1532
|
+
this.snapshotHead.data = { ...head.data };
|
|
1597
1533
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
* @param range The range to check for read lock availability.
|
|
1601
|
-
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
1602
|
-
*/
|
|
1603
|
-
canRead(range) {
|
|
1604
|
-
const writing = this.isWriting(range);
|
|
1605
|
-
return !writing;
|
|
1534
|
+
compareAndSwapHead(oldRoot, newRoot) {
|
|
1535
|
+
return this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
|
|
1606
1536
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1537
|
+
getHeadData(key, defaultValue) {
|
|
1538
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
1539
|
+
}
|
|
1540
|
+
setHeadData(key, data) {
|
|
1541
|
+
this.snapshotHead.data[key] = data;
|
|
1542
|
+
}
|
|
1543
|
+
autoIncrement(key, defaultValue) {
|
|
1544
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
|
|
1548
|
+
// src/transaction/BPTreeSyncTransaction.ts
|
|
1549
|
+
var BPTreeSyncTransaction = class extends BPTreeSyncBase {
|
|
1550
|
+
realBaseTree;
|
|
1551
|
+
realBaseStrategy;
|
|
1552
|
+
txNodes = /* @__PURE__ */ new Map();
|
|
1553
|
+
dirtyIds = /* @__PURE__ */ new Set();
|
|
1554
|
+
createdInTx = /* @__PURE__ */ new Set();
|
|
1555
|
+
initialRootId;
|
|
1556
|
+
transactionRootId;
|
|
1557
|
+
constructor(baseTree) {
|
|
1558
|
+
super(baseTree.strategy, baseTree.comparator, baseTree.option);
|
|
1559
|
+
this.realBaseTree = baseTree;
|
|
1560
|
+
this.realBaseStrategy = baseTree.strategy;
|
|
1561
|
+
this.initialRootId = "";
|
|
1562
|
+
this.transactionRootId = "";
|
|
1616
1563
|
}
|
|
1617
1564
|
/**
|
|
1618
|
-
*
|
|
1619
|
-
* @template T - The return type of the task.
|
|
1620
|
-
* @param arg0 - Either a range or a task callback.
|
|
1621
|
-
* If a range is provided, the task is the second argument.
|
|
1622
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
1623
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
1624
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
1625
|
-
* If this value is not provided, no timeout will be set.
|
|
1626
|
-
* @returns A promise resolving to the result of the task execution.
|
|
1565
|
+
* Initializes the transaction by capturing the current state of the tree.
|
|
1627
1566
|
*/
|
|
1628
|
-
|
|
1629
|
-
const
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1567
|
+
initTransaction() {
|
|
1568
|
+
const head = this.realBaseStrategy.readHead();
|
|
1569
|
+
this.initialRootId = head?.root ?? this.realBaseTree.rootId;
|
|
1570
|
+
this.transactionRootId = this.initialRootId;
|
|
1571
|
+
this.rootId = this.transactionRootId;
|
|
1572
|
+
const snapshotStrategy = new BPTreeSyncSnapshotStrategy(this.realBaseStrategy, this.initialRootId);
|
|
1573
|
+
this.strategy = snapshotStrategy;
|
|
1574
|
+
this.txNodes.clear();
|
|
1575
|
+
this.dirtyIds.clear();
|
|
1576
|
+
this.createdInTx.clear();
|
|
1577
|
+
}
|
|
1578
|
+
getNode(id) {
|
|
1579
|
+
if (this.txNodes.has(id)) {
|
|
1580
|
+
return this.txNodes.get(id);
|
|
1581
|
+
}
|
|
1582
|
+
const baseNode = this.realBaseStrategy.read(id);
|
|
1583
|
+
const clone = JSON.parse(JSON.stringify(baseNode));
|
|
1584
|
+
this.txNodes.set(id, clone);
|
|
1585
|
+
return clone;
|
|
1586
|
+
}
|
|
1587
|
+
bufferForNodeUpdate(node) {
|
|
1588
|
+
this.txNodes.set(node.id, node);
|
|
1589
|
+
this.dirtyIds.add(node.id);
|
|
1590
|
+
this.markPathDirty(node);
|
|
1591
|
+
}
|
|
1592
|
+
bufferForNodeCreate(node) {
|
|
1593
|
+
this.txNodes.set(node.id, node);
|
|
1594
|
+
this.dirtyIds.add(node.id);
|
|
1595
|
+
this.createdInTx.add(node.id);
|
|
1596
|
+
this.markPathDirty(node);
|
|
1597
|
+
}
|
|
1598
|
+
bufferForNodeDelete(node) {
|
|
1599
|
+
this.txNodes.delete(node.id);
|
|
1600
|
+
this.dirtyIds.add(node.id);
|
|
1601
|
+
}
|
|
1602
|
+
markPathDirty(node) {
|
|
1603
|
+
let curr = node;
|
|
1604
|
+
while (curr.parent) {
|
|
1605
|
+
if (this.dirtyIds.has(curr.parent) && this.txNodes.has(curr.parent)) {
|
|
1606
|
+
break;
|
|
1642
1607
|
}
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1608
|
+
const parent = this.getNode(curr.parent);
|
|
1609
|
+
this.dirtyIds.add(parent.id);
|
|
1610
|
+
curr = parent;
|
|
1611
|
+
}
|
|
1612
|
+
if (!curr.parent) {
|
|
1613
|
+
this.transactionRootId = curr.id;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
_createNode(isLeaf, keys, values, leaf = false, parent = null, next = null, prev = null) {
|
|
1617
|
+
const id = this.realBaseStrategy.id(isLeaf);
|
|
1618
|
+
const node = {
|
|
1619
|
+
id,
|
|
1620
|
+
keys,
|
|
1621
|
+
values,
|
|
1622
|
+
leaf: isLeaf,
|
|
1623
|
+
parent,
|
|
1624
|
+
next,
|
|
1625
|
+
prev
|
|
1626
|
+
};
|
|
1627
|
+
this.bufferForNodeCreate(node);
|
|
1628
|
+
return node;
|
|
1651
1629
|
}
|
|
1652
1630
|
/**
|
|
1653
|
-
*
|
|
1654
|
-
*
|
|
1655
|
-
*
|
|
1656
|
-
*
|
|
1657
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
1658
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
1659
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
1660
|
-
* If this value is not provided, no timeout will be set.
|
|
1661
|
-
* @returns A promise resolving to the result of the task execution.
|
|
1631
|
+
* Attempts to commit the transaction.
|
|
1632
|
+
* Uses Optimistic Locking (Compare-And-Swap) on the root node ID to detect conflicts.
|
|
1633
|
+
*
|
|
1634
|
+
* @returns The transaction result.
|
|
1662
1635
|
*/
|
|
1663
|
-
|
|
1664
|
-
const
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
rangeTask: [Array, Function],
|
|
1676
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
1636
|
+
commit() {
|
|
1637
|
+
const idMapping = /* @__PURE__ */ new Map();
|
|
1638
|
+
const finalNodes = [];
|
|
1639
|
+
for (const oldId of this.dirtyIds) {
|
|
1640
|
+
if (this.createdInTx.has(oldId)) {
|
|
1641
|
+
idMapping.set(oldId, oldId);
|
|
1642
|
+
} else {
|
|
1643
|
+
const node = this.txNodes.get(oldId);
|
|
1644
|
+
if (node) {
|
|
1645
|
+
const newId = this.realBaseStrategy.id(node.leaf);
|
|
1646
|
+
idMapping.set(oldId, newId);
|
|
1647
|
+
}
|
|
1677
1648
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1649
|
+
}
|
|
1650
|
+
const newCreatedIds = [];
|
|
1651
|
+
for (const oldId of this.dirtyIds) {
|
|
1652
|
+
const node = this.txNodes.get(oldId);
|
|
1653
|
+
if (!node) continue;
|
|
1654
|
+
const newId = idMapping.get(oldId);
|
|
1655
|
+
node.id = newId;
|
|
1656
|
+
if (node.parent && idMapping.has(node.parent)) {
|
|
1657
|
+
node.parent = idMapping.get(node.parent);
|
|
1658
|
+
}
|
|
1659
|
+
if (!node.leaf) {
|
|
1660
|
+
const internal = node;
|
|
1661
|
+
for (let i = 0; i < internal.keys.length; i++) {
|
|
1662
|
+
const childId = internal.keys[i];
|
|
1663
|
+
if (idMapping.has(childId)) {
|
|
1664
|
+
internal.keys[i] = idMapping.get(childId);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1686
1667
|
}
|
|
1687
|
-
|
|
1668
|
+
if (node.leaf) {
|
|
1669
|
+
const leaf = node;
|
|
1670
|
+
if (leaf.next && idMapping.has(leaf.next)) {
|
|
1671
|
+
leaf.next = idMapping.get(leaf.next);
|
|
1672
|
+
}
|
|
1673
|
+
if (leaf.prev && idMapping.has(leaf.prev)) {
|
|
1674
|
+
leaf.prev = idMapping.get(leaf.prev);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
finalNodes.push(node);
|
|
1678
|
+
newCreatedIds.push(newId);
|
|
1679
|
+
}
|
|
1680
|
+
let newRootId = this.transactionRootId;
|
|
1681
|
+
if (idMapping.has(this.transactionRootId)) {
|
|
1682
|
+
newRootId = idMapping.get(this.transactionRootId);
|
|
1683
|
+
}
|
|
1684
|
+
for (const node of finalNodes) {
|
|
1685
|
+
this.realBaseStrategy.write(node.id, node);
|
|
1686
|
+
}
|
|
1687
|
+
const success = this.realBaseStrategy.compareAndSwapHead(this.initialRootId, newRootId);
|
|
1688
|
+
if (success) {
|
|
1689
|
+
const distinctObsolete = /* @__PURE__ */ new Set();
|
|
1690
|
+
for (const oldId of this.dirtyIds) {
|
|
1691
|
+
if (!this.createdInTx.has(oldId) && this.txNodes.has(oldId)) {
|
|
1692
|
+
distinctObsolete.add(oldId);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
return {
|
|
1696
|
+
success: true,
|
|
1697
|
+
createdIds: newCreatedIds,
|
|
1698
|
+
obsoleteIds: Array.from(distinctObsolete)
|
|
1699
|
+
};
|
|
1700
|
+
} else {
|
|
1701
|
+
this.rollback();
|
|
1702
|
+
return {
|
|
1703
|
+
success: false,
|
|
1704
|
+
createdIds: newCreatedIds,
|
|
1705
|
+
obsoleteIds: []
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1688
1708
|
}
|
|
1689
1709
|
/**
|
|
1690
|
-
*
|
|
1691
|
-
*
|
|
1710
|
+
* Rolls back the transaction by clearing all buffered changes.
|
|
1711
|
+
* Internal use only.
|
|
1692
1712
|
*/
|
|
1693
|
-
|
|
1694
|
-
this.
|
|
1713
|
+
rollback() {
|
|
1714
|
+
this.txNodes.clear();
|
|
1715
|
+
this.dirtyIds.clear();
|
|
1716
|
+
this.createdInTx.clear();
|
|
1717
|
+
}
|
|
1718
|
+
// Override to do nothing, as transaction handles its own commits
|
|
1719
|
+
commitHeadBuffer() {
|
|
1720
|
+
}
|
|
1721
|
+
commitNodeCreateBuffer() {
|
|
1722
|
+
}
|
|
1723
|
+
commitNodeUpdateBuffer() {
|
|
1724
|
+
}
|
|
1725
|
+
commitNodeDeleteBuffer() {
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1728
|
+
|
|
1729
|
+
// src/BPTreeSync.ts
|
|
1730
|
+
var BPTreeSync = class extends BPTreeSyncBase {
|
|
1731
|
+
constructor(strategy, comparator, option) {
|
|
1732
|
+
super(strategy, comparator, option);
|
|
1695
1733
|
}
|
|
1696
1734
|
/**
|
|
1697
|
-
*
|
|
1698
|
-
* @
|
|
1735
|
+
* Creates a new synchronous transaction.
|
|
1736
|
+
* @returns A new BPTreeSyncTransaction.
|
|
1699
1737
|
*/
|
|
1700
|
-
|
|
1701
|
-
|
|
1738
|
+
createTransaction() {
|
|
1739
|
+
const tx = new BPTreeSyncTransaction(this);
|
|
1740
|
+
tx.initTransaction();
|
|
1741
|
+
return tx;
|
|
1742
|
+
}
|
|
1743
|
+
insert(key, value) {
|
|
1744
|
+
const tx = this.createTransaction();
|
|
1745
|
+
tx.insert(key, value);
|
|
1746
|
+
const { success } = tx.commit();
|
|
1747
|
+
this.init();
|
|
1748
|
+
if (!success) {
|
|
1749
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
delete(key, value) {
|
|
1753
|
+
const tx = this.createTransaction();
|
|
1754
|
+
tx.delete(key, value);
|
|
1755
|
+
const { success } = tx.commit();
|
|
1756
|
+
this.init();
|
|
1757
|
+
if (!success) {
|
|
1758
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
1759
|
+
}
|
|
1702
1760
|
}
|
|
1703
1761
|
};
|
|
1704
1762
|
|
|
1705
|
-
// src/
|
|
1706
|
-
var
|
|
1707
|
-
lock;
|
|
1763
|
+
// src/base/BPTreeAsyncBase.ts
|
|
1764
|
+
var BPTreeAsyncBase = class extends BPTree {
|
|
1708
1765
|
constructor(strategy, comparator, option) {
|
|
1709
1766
|
super(strategy, comparator, option);
|
|
1710
1767
|
this.nodes = this._createCachedNode();
|
|
1711
|
-
this.lock = new Ryoiki();
|
|
1712
1768
|
}
|
|
1713
1769
|
_createCachedNode() {
|
|
1714
1770
|
return new CacheEntanglementAsync(async (key) => {
|
|
@@ -1717,24 +1773,6 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1717
1773
|
capacity: this.option.capacity ?? 1e3
|
|
1718
1774
|
});
|
|
1719
1775
|
}
|
|
1720
|
-
async readLock(callback) {
|
|
1721
|
-
let lockId;
|
|
1722
|
-
return await this.lock.readLock(async (_lockId) => {
|
|
1723
|
-
lockId = _lockId;
|
|
1724
|
-
return await callback();
|
|
1725
|
-
}).finally(() => {
|
|
1726
|
-
this.lock.readUnlock(lockId);
|
|
1727
|
-
});
|
|
1728
|
-
}
|
|
1729
|
-
async writeLock(callback) {
|
|
1730
|
-
let lockId;
|
|
1731
|
-
return await this.lock.writeLock(async (_lockId) => {
|
|
1732
|
-
lockId = _lockId;
|
|
1733
|
-
return await callback();
|
|
1734
|
-
}).finally(() => {
|
|
1735
|
-
this.lock.writeUnlock(lockId);
|
|
1736
|
-
});
|
|
1737
|
-
}
|
|
1738
1776
|
async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
1739
1777
|
let node = startNode;
|
|
1740
1778
|
let done = false;
|
|
@@ -1832,7 +1870,7 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1832
1870
|
}
|
|
1833
1871
|
}
|
|
1834
1872
|
}
|
|
1835
|
-
if (this.rootId === node.id && node.keys.length === 1) {
|
|
1873
|
+
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
1836
1874
|
const keys = node.keys;
|
|
1837
1875
|
this.bufferForNodeDelete(node);
|
|
1838
1876
|
const newRoot = await this.getNode(keys[0]);
|
|
@@ -1901,28 +1939,19 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1901
1939
|
pointer.values.push(guess);
|
|
1902
1940
|
} else {
|
|
1903
1941
|
pointer.next = node.next;
|
|
1904
|
-
pointer.prev = node.id;
|
|
1905
1942
|
if (pointer.next) {
|
|
1906
|
-
const n = await this.getNode(
|
|
1943
|
+
const n = await this.getNode(pointer.next);
|
|
1907
1944
|
n.prev = pointer.id;
|
|
1908
1945
|
this.bufferForNodeUpdate(n);
|
|
1909
1946
|
}
|
|
1910
|
-
if (pointer.prev) {
|
|
1911
|
-
const n = await this.getNode(node.id);
|
|
1912
|
-
n.next = pointer.id;
|
|
1913
|
-
this.bufferForNodeUpdate(n);
|
|
1914
|
-
}
|
|
1915
|
-
if (isPredecessor) {
|
|
1916
|
-
pointer.prev = null;
|
|
1917
|
-
}
|
|
1918
1947
|
}
|
|
1919
1948
|
pointer.values.push(...node.values);
|
|
1920
1949
|
if (!pointer.leaf) {
|
|
1921
1950
|
const keys = pointer.keys;
|
|
1922
1951
|
for (const key2 of keys) {
|
|
1923
|
-
const
|
|
1924
|
-
|
|
1925
|
-
this.bufferForNodeUpdate(
|
|
1952
|
+
const n = await this.getNode(key2);
|
|
1953
|
+
n.parent = pointer.id;
|
|
1954
|
+
this.bufferForNodeUpdate(n);
|
|
1926
1955
|
}
|
|
1927
1956
|
}
|
|
1928
1957
|
await this._deleteEntry(await this.getNode(node.parent), node.id, guess);
|
|
@@ -1999,21 +2028,24 @@ var BPTreeAsync = class extends BPTree {
|
|
|
1999
2028
|
this.bufferForNodeUpdate(pointer);
|
|
2000
2029
|
}
|
|
2001
2030
|
if (!pointer.leaf) {
|
|
2002
|
-
|
|
2031
|
+
const keys = pointer.keys;
|
|
2032
|
+
for (const key2 of keys) {
|
|
2003
2033
|
const n = await this.getNode(key2);
|
|
2004
2034
|
n.parent = pointer.id;
|
|
2005
2035
|
this.bufferForNodeUpdate(n);
|
|
2006
2036
|
}
|
|
2007
2037
|
}
|
|
2008
2038
|
if (!node.leaf) {
|
|
2009
|
-
|
|
2039
|
+
const keys = node.keys;
|
|
2040
|
+
for (const key2 of keys) {
|
|
2010
2041
|
const n = await this.getNode(key2);
|
|
2011
2042
|
n.parent = node.id;
|
|
2012
2043
|
this.bufferForNodeUpdate(n);
|
|
2013
2044
|
}
|
|
2014
2045
|
}
|
|
2015
2046
|
if (!parentNode.leaf) {
|
|
2016
|
-
|
|
2047
|
+
const keys = parentNode.keys;
|
|
2048
|
+
for (const key2 of keys) {
|
|
2017
2049
|
const n = await this.getNode(key2);
|
|
2018
2050
|
n.parent = parentNode.id;
|
|
2019
2051
|
this.bufferForNodeUpdate(n);
|
|
@@ -2077,14 +2109,14 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2077
2109
|
parentNode.values = parentNode.values.slice(0, mid);
|
|
2078
2110
|
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2079
2111
|
for (const k of parentNode.keys) {
|
|
2080
|
-
const
|
|
2081
|
-
|
|
2082
|
-
this.bufferForNodeUpdate(
|
|
2112
|
+
const n = await this.getNode(k);
|
|
2113
|
+
n.parent = parentNode.id;
|
|
2114
|
+
this.bufferForNodeUpdate(n);
|
|
2083
2115
|
}
|
|
2084
2116
|
for (const k of parentPointer.keys) {
|
|
2085
|
-
const
|
|
2086
|
-
|
|
2087
|
-
this.bufferForNodeUpdate(
|
|
2117
|
+
const n = await this.getNode(k);
|
|
2118
|
+
n.parent = parentPointer.id;
|
|
2119
|
+
this.bufferForNodeUpdate(n);
|
|
2088
2120
|
}
|
|
2089
2121
|
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2090
2122
|
this.bufferForNodeUpdate(parentNode);
|
|
@@ -2126,23 +2158,19 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2126
2158
|
const nValue = node.values[i];
|
|
2127
2159
|
const k = node.keys;
|
|
2128
2160
|
if (this.comparator.isSame(value, nValue)) {
|
|
2129
|
-
node = await this.getNode(
|
|
2161
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2130
2162
|
break;
|
|
2131
2163
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
2132
|
-
node = await this.getNode(
|
|
2164
|
+
node = await this.getNode(node.keys[i]);
|
|
2133
2165
|
break;
|
|
2134
2166
|
} else if (i + 1 === node.values.length) {
|
|
2135
|
-
node = await this.getNode(
|
|
2167
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2136
2168
|
break;
|
|
2137
2169
|
}
|
|
2138
2170
|
}
|
|
2139
2171
|
}
|
|
2140
2172
|
return node;
|
|
2141
2173
|
}
|
|
2142
|
-
/**
|
|
2143
|
-
* Find the insertable node using primaryAsc comparison.
|
|
2144
|
-
* This allows finding nodes by primary value only, ignoring unique identifiers.
|
|
2145
|
-
*/
|
|
2146
2174
|
async insertableNodeByPrimary(value) {
|
|
2147
2175
|
let node = await this.getNode(this.rootId);
|
|
2148
2176
|
while (!node.leaf) {
|
|
@@ -2150,13 +2178,13 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2150
2178
|
const nValue = node.values[i];
|
|
2151
2179
|
const k = node.keys;
|
|
2152
2180
|
if (this.comparator.isPrimarySame(value, nValue)) {
|
|
2153
|
-
node = await this.getNode(
|
|
2181
|
+
node = await this.getNode(node.keys[i]);
|
|
2154
2182
|
break;
|
|
2155
2183
|
} else if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
2156
|
-
node = await this.getNode(
|
|
2184
|
+
node = await this.getNode(node.keys[i]);
|
|
2157
2185
|
break;
|
|
2158
2186
|
} else if (i + 1 === node.values.length) {
|
|
2159
|
-
node = await this.getNode(
|
|
2187
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2160
2188
|
break;
|
|
2161
2189
|
}
|
|
2162
2190
|
}
|
|
@@ -2170,11 +2198,11 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2170
2198
|
const nValue = node.values[i];
|
|
2171
2199
|
const k = node.keys;
|
|
2172
2200
|
if (this.comparator.isPrimaryLower(value, nValue)) {
|
|
2173
|
-
node = await this.getNode(
|
|
2201
|
+
node = await this.getNode(node.keys[i]);
|
|
2174
2202
|
break;
|
|
2175
2203
|
}
|
|
2176
2204
|
if (i + 1 === node.values.length) {
|
|
2177
|
-
node = await this.getNode(
|
|
2205
|
+
node = await this.getNode(node.keys[i + 1]);
|
|
2178
2206
|
break;
|
|
2179
2207
|
}
|
|
2180
2208
|
}
|
|
@@ -2211,7 +2239,7 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2211
2239
|
let node = await this.getNode(this.rootId);
|
|
2212
2240
|
while (!node.leaf) {
|
|
2213
2241
|
const keys = node.keys;
|
|
2214
|
-
node = await this.getNode(keys[0]);
|
|
2242
|
+
node = await this.getNode(node.keys[0]);
|
|
2215
2243
|
}
|
|
2216
2244
|
return node;
|
|
2217
2245
|
}
|
|
@@ -2219,10 +2247,37 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2219
2247
|
let node = await this.getNode(this.rootId);
|
|
2220
2248
|
while (!node.leaf) {
|
|
2221
2249
|
const keys = node.keys;
|
|
2222
|
-
node = await this.getNode(keys[keys.length - 1]);
|
|
2250
|
+
node = await this.getNode(node.keys[node.keys.length - 1]);
|
|
2223
2251
|
}
|
|
2224
2252
|
return node;
|
|
2225
2253
|
}
|
|
2254
|
+
async exists(key, value) {
|
|
2255
|
+
const node = await this.insertableNode(value);
|
|
2256
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2257
|
+
if (this.comparator.isSame(value, node.values[i])) {
|
|
2258
|
+
const keys = node.keys[i];
|
|
2259
|
+
if (keys.includes(key)) {
|
|
2260
|
+
return true;
|
|
2261
|
+
}
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
return false;
|
|
2265
|
+
}
|
|
2266
|
+
async forceUpdate(id) {
|
|
2267
|
+
if (id) {
|
|
2268
|
+
this.nodes.delete(id);
|
|
2269
|
+
await this.getNode(id);
|
|
2270
|
+
return 1;
|
|
2271
|
+
}
|
|
2272
|
+
const keys = Array.from(this.nodes.keys());
|
|
2273
|
+
for (const key of keys) {
|
|
2274
|
+
this.nodes.delete(key);
|
|
2275
|
+
}
|
|
2276
|
+
for (const key of keys) {
|
|
2277
|
+
await this.getNode(key);
|
|
2278
|
+
}
|
|
2279
|
+
return keys.length;
|
|
2280
|
+
}
|
|
2226
2281
|
async commitHeadBuffer() {
|
|
2227
2282
|
if (!this._strategyDirty) {
|
|
2228
2283
|
return;
|
|
@@ -2253,25 +2308,15 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2253
2308
|
}
|
|
2254
2309
|
this._nodeDeleteBuffer.clear();
|
|
2255
2310
|
}
|
|
2256
|
-
/**
|
|
2257
|
-
* Retrieves the value associated with the given key (PK).
|
|
2258
|
-
* Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
|
|
2259
|
-
*
|
|
2260
|
-
* @param key The key to search for.
|
|
2261
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
2262
|
-
*/
|
|
2263
2311
|
async get(key) {
|
|
2264
|
-
return this.readLock(async () => {
|
|
2312
|
+
return await this.readLock(async () => {
|
|
2265
2313
|
let node = await this.leftestNode();
|
|
2266
2314
|
while (true) {
|
|
2267
|
-
|
|
2268
|
-
const
|
|
2269
|
-
for (let
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
if (keys[j] === key) {
|
|
2273
|
-
return node.values[i];
|
|
2274
|
-
}
|
|
2315
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2316
|
+
const keys = node.keys[i];
|
|
2317
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
2318
|
+
if (keys[j] === key) {
|
|
2319
|
+
return node.values[i];
|
|
2275
2320
|
}
|
|
2276
2321
|
}
|
|
2277
2322
|
}
|
|
@@ -2346,25 +2391,21 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2346
2391
|
}
|
|
2347
2392
|
}
|
|
2348
2393
|
async keys(condition, filterValues) {
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
return set;
|
|
2355
|
-
});
|
|
2394
|
+
const set = /* @__PURE__ */ new Set();
|
|
2395
|
+
for await (const key of this.keysStream(condition, filterValues)) {
|
|
2396
|
+
set.add(key);
|
|
2397
|
+
}
|
|
2398
|
+
return set;
|
|
2356
2399
|
}
|
|
2357
2400
|
async where(condition) {
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
return map;
|
|
2364
|
-
});
|
|
2401
|
+
const map = /* @__PURE__ */ new Map();
|
|
2402
|
+
for await (const [key, value] of this.whereStream(condition)) {
|
|
2403
|
+
map.set(key, value);
|
|
2404
|
+
}
|
|
2405
|
+
return map;
|
|
2365
2406
|
}
|
|
2366
2407
|
async insert(key, value) {
|
|
2367
|
-
|
|
2408
|
+
await this.writeLock(async () => {
|
|
2368
2409
|
const before = await this.insertableNode(value);
|
|
2369
2410
|
this._insertAtLeaf(before, key, value);
|
|
2370
2411
|
if (before.values.length === this.order) {
|
|
@@ -2391,188 +2432,376 @@ var BPTreeAsync = class extends BPTree {
|
|
|
2391
2432
|
});
|
|
2392
2433
|
}
|
|
2393
2434
|
async delete(key, value) {
|
|
2394
|
-
|
|
2435
|
+
await this.writeLock(async () => {
|
|
2395
2436
|
const node = await this.insertableNode(value);
|
|
2396
2437
|
let i = node.values.length;
|
|
2397
2438
|
while (i--) {
|
|
2398
2439
|
const nValue = node.values[i];
|
|
2399
2440
|
if (this.comparator.isSame(value, nValue)) {
|
|
2400
2441
|
const keys = node.keys[i];
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
} else if (node.id === this.rootId) {
|
|
2406
|
-
node.values.splice(i, 1);
|
|
2407
|
-
node.keys.splice(i, 1);
|
|
2408
|
-
this.bufferForNodeUpdate(node);
|
|
2409
|
-
} else {
|
|
2410
|
-
keys.splice(keys.indexOf(key), 1);
|
|
2442
|
+
const keyIndex = keys.indexOf(key);
|
|
2443
|
+
if (keyIndex !== -1) {
|
|
2444
|
+
keys.splice(keyIndex, 1);
|
|
2445
|
+
if (keys.length === 0) {
|
|
2411
2446
|
node.keys.splice(i, 1);
|
|
2412
|
-
node.values.splice(
|
|
2413
|
-
await this._deleteEntry(node, key, value);
|
|
2414
|
-
this.bufferForNodeUpdate(node);
|
|
2447
|
+
node.values.splice(i, 1);
|
|
2415
2448
|
}
|
|
2449
|
+
await this._deleteEntry(node, key, value);
|
|
2450
|
+
break;
|
|
2416
2451
|
}
|
|
2417
2452
|
}
|
|
2418
2453
|
}
|
|
2419
2454
|
await this.commitHeadBuffer();
|
|
2420
|
-
await this.commitNodeCreateBuffer();
|
|
2421
2455
|
await this.commitNodeUpdateBuffer();
|
|
2422
2456
|
await this.commitNodeDeleteBuffer();
|
|
2423
2457
|
});
|
|
2424
2458
|
}
|
|
2425
|
-
|
|
2426
|
-
return this.
|
|
2427
|
-
const node = await this.insertableNode(value);
|
|
2428
|
-
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
2429
|
-
const nValue = node.values[i];
|
|
2430
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
2431
|
-
const keys = node.keys[i];
|
|
2432
|
-
return keys.includes(key);
|
|
2433
|
-
}
|
|
2434
|
-
}
|
|
2435
|
-
return false;
|
|
2436
|
-
});
|
|
2459
|
+
getHeadData() {
|
|
2460
|
+
return this.strategy.head.data;
|
|
2437
2461
|
}
|
|
2438
2462
|
async setHeadData(data) {
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
this._strategyDirty = true;
|
|
2442
|
-
await this.commitHeadBuffer();
|
|
2443
|
-
});
|
|
2444
|
-
}
|
|
2445
|
-
async forceUpdate() {
|
|
2446
|
-
return this.readLock(async () => {
|
|
2447
|
-
const keys = [...this.nodes.keys()];
|
|
2448
|
-
this.nodes.clear();
|
|
2449
|
-
await this.init();
|
|
2450
|
-
for (const key of keys) {
|
|
2451
|
-
await this.getNode(key);
|
|
2452
|
-
}
|
|
2453
|
-
return keys.length;
|
|
2454
|
-
});
|
|
2455
|
-
}
|
|
2456
|
-
};
|
|
2457
|
-
|
|
2458
|
-
// src/base/SerializeStrategy.ts
|
|
2459
|
-
var SerializeStrategy = class {
|
|
2460
|
-
order;
|
|
2461
|
-
head;
|
|
2462
|
-
constructor(order) {
|
|
2463
|
-
this.order = order;
|
|
2464
|
-
this.head = {
|
|
2465
|
-
order,
|
|
2466
|
-
root: null,
|
|
2467
|
-
data: {}
|
|
2468
|
-
};
|
|
2463
|
+
this.strategy.head.data = data;
|
|
2464
|
+
await this.strategy.writeHead(this.strategy.head);
|
|
2469
2465
|
}
|
|
2470
2466
|
};
|
|
2471
2467
|
|
|
2472
|
-
// src/
|
|
2473
|
-
var
|
|
2474
|
-
getHeadData(key, defaultValue) {
|
|
2468
|
+
// src/SerializeStrategyAsync.ts
|
|
2469
|
+
var SerializeStrategyAsync = class extends SerializeStrategy {
|
|
2470
|
+
async getHeadData(key, defaultValue) {
|
|
2475
2471
|
if (!Object.hasOwn(this.head.data, key)) {
|
|
2476
|
-
this.setHeadData(key, defaultValue);
|
|
2472
|
+
await this.setHeadData(key, defaultValue);
|
|
2477
2473
|
}
|
|
2478
2474
|
return this.head.data[key];
|
|
2479
2475
|
}
|
|
2480
|
-
setHeadData(key, data) {
|
|
2476
|
+
async setHeadData(key, data) {
|
|
2481
2477
|
this.head.data[key] = data;
|
|
2482
|
-
this.writeHead(this.head);
|
|
2478
|
+
await this.writeHead(this.head);
|
|
2483
2479
|
}
|
|
2484
|
-
autoIncrement(key, defaultValue) {
|
|
2485
|
-
const current = this.getHeadData(key, defaultValue);
|
|
2480
|
+
async autoIncrement(key, defaultValue) {
|
|
2481
|
+
const current = await this.getHeadData(key, defaultValue);
|
|
2486
2482
|
const next = current + 1;
|
|
2487
|
-
this.setHeadData(key, next);
|
|
2483
|
+
await this.setHeadData(key, next);
|
|
2488
2484
|
return current;
|
|
2489
2485
|
}
|
|
2486
|
+
async compareAndSwapHead(oldRoot, newRoot) {
|
|
2487
|
+
if (this.head.root !== oldRoot) {
|
|
2488
|
+
return false;
|
|
2489
|
+
}
|
|
2490
|
+
this.head.root = newRoot;
|
|
2491
|
+
await this.writeHead(this.head);
|
|
2492
|
+
return true;
|
|
2493
|
+
}
|
|
2490
2494
|
};
|
|
2491
|
-
var
|
|
2495
|
+
var InMemoryStoreStrategyAsync = class extends SerializeStrategyAsync {
|
|
2492
2496
|
node;
|
|
2493
2497
|
constructor(order) {
|
|
2494
2498
|
super(order);
|
|
2495
2499
|
this.node = {};
|
|
2496
2500
|
}
|
|
2497
|
-
id(isLeaf) {
|
|
2498
|
-
return this.autoIncrement("index", 1).toString();
|
|
2501
|
+
async id(isLeaf) {
|
|
2502
|
+
return (await this.autoIncrement("index", 1)).toString();
|
|
2499
2503
|
}
|
|
2500
|
-
read(id) {
|
|
2504
|
+
async read(id) {
|
|
2501
2505
|
if (!Object.hasOwn(this.node, id)) {
|
|
2502
2506
|
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
2503
2507
|
}
|
|
2504
|
-
|
|
2508
|
+
const node = this.node[id];
|
|
2509
|
+
return JSON.parse(JSON.stringify(node));
|
|
2505
2510
|
}
|
|
2506
|
-
write(id, node) {
|
|
2511
|
+
async write(id, node) {
|
|
2507
2512
|
this.node[id] = node;
|
|
2508
2513
|
}
|
|
2509
|
-
delete(id) {
|
|
2514
|
+
async delete(id) {
|
|
2510
2515
|
delete this.node[id];
|
|
2511
2516
|
}
|
|
2512
|
-
readHead() {
|
|
2517
|
+
async readHead() {
|
|
2513
2518
|
if (this.head.root === null) {
|
|
2514
2519
|
return null;
|
|
2515
2520
|
}
|
|
2516
2521
|
return this.head;
|
|
2517
2522
|
}
|
|
2518
|
-
writeHead(head) {
|
|
2523
|
+
async writeHead(head) {
|
|
2519
2524
|
this.head = head;
|
|
2520
2525
|
}
|
|
2521
2526
|
};
|
|
2522
2527
|
|
|
2523
|
-
// src/
|
|
2524
|
-
var
|
|
2528
|
+
// src/transaction/BPTreeAsyncSnapshotStrategy.ts
|
|
2529
|
+
var BPTreeAsyncSnapshotStrategy = class extends SerializeStrategyAsync {
|
|
2530
|
+
baseStrategy;
|
|
2531
|
+
snapshotHead;
|
|
2532
|
+
constructor(baseStrategy, root) {
|
|
2533
|
+
super(baseStrategy.order);
|
|
2534
|
+
this.baseStrategy = baseStrategy;
|
|
2535
|
+
this.snapshotHead = {
|
|
2536
|
+
...baseStrategy.head,
|
|
2537
|
+
root,
|
|
2538
|
+
data: { ...baseStrategy.head.data }
|
|
2539
|
+
};
|
|
2540
|
+
this.head = this.snapshotHead;
|
|
2541
|
+
}
|
|
2542
|
+
async id(isLeaf) {
|
|
2543
|
+
return await this.baseStrategy.id(isLeaf);
|
|
2544
|
+
}
|
|
2545
|
+
async read(id) {
|
|
2546
|
+
return await this.baseStrategy.read(id);
|
|
2547
|
+
}
|
|
2548
|
+
async write(id, node) {
|
|
2549
|
+
await this.baseStrategy.write(id, node);
|
|
2550
|
+
}
|
|
2551
|
+
async delete(id) {
|
|
2552
|
+
await this.baseStrategy.delete(id);
|
|
2553
|
+
}
|
|
2554
|
+
async readHead() {
|
|
2555
|
+
return this.snapshotHead;
|
|
2556
|
+
}
|
|
2557
|
+
async writeHead(head) {
|
|
2558
|
+
this.snapshotHead.root = head.root;
|
|
2559
|
+
this.snapshotHead.data = { ...head.data };
|
|
2560
|
+
}
|
|
2561
|
+
async compareAndSwapHead(oldRoot, newRoot) {
|
|
2562
|
+
return await this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
|
|
2563
|
+
}
|
|
2525
2564
|
async getHeadData(key, defaultValue) {
|
|
2526
|
-
|
|
2527
|
-
await this.setHeadData(key, defaultValue);
|
|
2528
|
-
}
|
|
2529
|
-
return this.head.data[key];
|
|
2565
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
2530
2566
|
}
|
|
2531
2567
|
async setHeadData(key, data) {
|
|
2532
|
-
this.
|
|
2533
|
-
await this.writeHead(this.head);
|
|
2568
|
+
this.snapshotHead.data[key] = data;
|
|
2534
2569
|
}
|
|
2535
2570
|
async autoIncrement(key, defaultValue) {
|
|
2536
|
-
|
|
2537
|
-
const next = current + 1;
|
|
2538
|
-
await this.setHeadData(key, next);
|
|
2539
|
-
return current;
|
|
2571
|
+
return this.snapshotHead.data[key] ?? defaultValue;
|
|
2540
2572
|
}
|
|
2541
2573
|
};
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2574
|
+
|
|
2575
|
+
// src/transaction/BPTreeAsyncTransaction.ts
|
|
2576
|
+
var BPTreeAsyncTransaction = class extends BPTreeAsyncBase {
|
|
2577
|
+
realBaseTree;
|
|
2578
|
+
realBaseStrategy;
|
|
2579
|
+
txNodes = /* @__PURE__ */ new Map();
|
|
2580
|
+
dirtyIds = /* @__PURE__ */ new Set();
|
|
2581
|
+
createdInTx = /* @__PURE__ */ new Set();
|
|
2582
|
+
initialRootId;
|
|
2583
|
+
transactionRootId;
|
|
2584
|
+
constructor(baseTree) {
|
|
2585
|
+
super(baseTree.strategy, baseTree.comparator, baseTree.option);
|
|
2586
|
+
this.realBaseTree = baseTree;
|
|
2587
|
+
this.realBaseStrategy = baseTree.strategy;
|
|
2588
|
+
this.initialRootId = "";
|
|
2589
|
+
this.transactionRootId = "";
|
|
2547
2590
|
}
|
|
2548
|
-
|
|
2549
|
-
|
|
2591
|
+
/**
|
|
2592
|
+
* Initializes the transaction by capturing the current state of the tree.
|
|
2593
|
+
*/
|
|
2594
|
+
async initTransaction() {
|
|
2595
|
+
const head = await this.realBaseStrategy.readHead();
|
|
2596
|
+
this.initialRootId = head?.root ?? this.realBaseTree.rootId;
|
|
2597
|
+
this.transactionRootId = this.initialRootId;
|
|
2598
|
+
this.rootId = this.transactionRootId;
|
|
2599
|
+
const snapshotStrategy = new BPTreeAsyncSnapshotStrategy(this.realBaseStrategy, this.initialRootId);
|
|
2600
|
+
this.strategy = snapshotStrategy;
|
|
2601
|
+
this.txNodes.clear();
|
|
2602
|
+
this.dirtyIds.clear();
|
|
2603
|
+
this.createdInTx.clear();
|
|
2550
2604
|
}
|
|
2551
|
-
async
|
|
2552
|
-
if (
|
|
2553
|
-
|
|
2605
|
+
async getNode(id) {
|
|
2606
|
+
if (this.txNodes.has(id)) {
|
|
2607
|
+
return this.txNodes.get(id);
|
|
2608
|
+
}
|
|
2609
|
+
const baseNode = await this.realBaseStrategy.read(id);
|
|
2610
|
+
const clone = JSON.parse(JSON.stringify(baseNode));
|
|
2611
|
+
this.txNodes.set(id, clone);
|
|
2612
|
+
return clone;
|
|
2613
|
+
}
|
|
2614
|
+
async bufferForNodeUpdate(node) {
|
|
2615
|
+
this.txNodes.set(node.id, node);
|
|
2616
|
+
this.dirtyIds.add(node.id);
|
|
2617
|
+
await this.markPathDirty(node);
|
|
2618
|
+
}
|
|
2619
|
+
async bufferForNodeCreate(node) {
|
|
2620
|
+
this.txNodes.set(node.id, node);
|
|
2621
|
+
this.dirtyIds.add(node.id);
|
|
2622
|
+
this.createdInTx.add(node.id);
|
|
2623
|
+
await this.markPathDirty(node);
|
|
2624
|
+
}
|
|
2625
|
+
async bufferForNodeDelete(node) {
|
|
2626
|
+
this.txNodes.delete(node.id);
|
|
2627
|
+
this.dirtyIds.add(node.id);
|
|
2628
|
+
}
|
|
2629
|
+
async markPathDirty(node) {
|
|
2630
|
+
let curr = node;
|
|
2631
|
+
while (curr.parent) {
|
|
2632
|
+
if (this.dirtyIds.has(curr.parent) && this.txNodes.has(curr.parent)) {
|
|
2633
|
+
break;
|
|
2634
|
+
}
|
|
2635
|
+
const parent = await this.getNode(curr.parent);
|
|
2636
|
+
this.dirtyIds.add(parent.id);
|
|
2637
|
+
curr = parent;
|
|
2638
|
+
}
|
|
2639
|
+
if (!curr.parent) {
|
|
2640
|
+
this.transactionRootId = curr.id;
|
|
2554
2641
|
}
|
|
2555
|
-
return this.node[id];
|
|
2556
2642
|
}
|
|
2557
|
-
async
|
|
2558
|
-
|
|
2643
|
+
async _createNode(isLeaf, keys, values, leaf = false, parent = null, next = null, prev = null) {
|
|
2644
|
+
const id = await this.realBaseStrategy.id(isLeaf);
|
|
2645
|
+
const node = {
|
|
2646
|
+
id,
|
|
2647
|
+
keys,
|
|
2648
|
+
values,
|
|
2649
|
+
leaf: isLeaf,
|
|
2650
|
+
parent,
|
|
2651
|
+
next,
|
|
2652
|
+
prev
|
|
2653
|
+
};
|
|
2654
|
+
await this.bufferForNodeCreate(node);
|
|
2655
|
+
return node;
|
|
2559
2656
|
}
|
|
2560
|
-
|
|
2561
|
-
|
|
2657
|
+
/**
|
|
2658
|
+
* Attempts to commit the transaction.
|
|
2659
|
+
* Uses Optimistic Locking (Compare-And-Swap) on the root node ID to detect conflicts.
|
|
2660
|
+
*
|
|
2661
|
+
* @returns A promise that resolves to the transaction result.
|
|
2662
|
+
*/
|
|
2663
|
+
async commit() {
|
|
2664
|
+
const idMapping = /* @__PURE__ */ new Map();
|
|
2665
|
+
const finalNodes = [];
|
|
2666
|
+
for (const oldId of this.dirtyIds) {
|
|
2667
|
+
if (this.createdInTx.has(oldId)) {
|
|
2668
|
+
idMapping.set(oldId, oldId);
|
|
2669
|
+
} else {
|
|
2670
|
+
const node = this.txNodes.get(oldId);
|
|
2671
|
+
if (node) {
|
|
2672
|
+
const newId = await this.realBaseStrategy.id(node.leaf);
|
|
2673
|
+
idMapping.set(oldId, newId);
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
const newCreatedIds = [];
|
|
2678
|
+
for (const oldId of this.dirtyIds) {
|
|
2679
|
+
const node = this.txNodes.get(oldId);
|
|
2680
|
+
if (!node) continue;
|
|
2681
|
+
const newId = idMapping.get(oldId);
|
|
2682
|
+
node.id = newId;
|
|
2683
|
+
if (node.parent && idMapping.has(node.parent)) {
|
|
2684
|
+
node.parent = idMapping.get(node.parent);
|
|
2685
|
+
}
|
|
2686
|
+
if (!node.leaf) {
|
|
2687
|
+
const internal = node;
|
|
2688
|
+
for (let i = 0; i < internal.keys.length; i++) {
|
|
2689
|
+
const childId = internal.keys[i];
|
|
2690
|
+
if (idMapping.has(childId)) {
|
|
2691
|
+
internal.keys[i] = idMapping.get(childId);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
if (node.leaf) {
|
|
2696
|
+
const leaf = node;
|
|
2697
|
+
if (leaf.next && idMapping.has(leaf.next)) {
|
|
2698
|
+
leaf.next = idMapping.get(leaf.next);
|
|
2699
|
+
}
|
|
2700
|
+
if (leaf.prev && idMapping.has(leaf.prev)) {
|
|
2701
|
+
leaf.prev = idMapping.get(leaf.prev);
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
finalNodes.push(node);
|
|
2705
|
+
newCreatedIds.push(newId);
|
|
2706
|
+
}
|
|
2707
|
+
let newRootId = this.transactionRootId;
|
|
2708
|
+
if (idMapping.has(this.transactionRootId)) {
|
|
2709
|
+
newRootId = idMapping.get(this.transactionRootId);
|
|
2710
|
+
}
|
|
2711
|
+
for (const node of finalNodes) {
|
|
2712
|
+
await this.realBaseStrategy.write(node.id, node);
|
|
2713
|
+
}
|
|
2714
|
+
const success = await this.realBaseStrategy.compareAndSwapHead(this.initialRootId, newRootId);
|
|
2715
|
+
if (success) {
|
|
2716
|
+
const distinctObsolete = /* @__PURE__ */ new Set();
|
|
2717
|
+
for (const oldId of this.dirtyIds) {
|
|
2718
|
+
if (!this.createdInTx.has(oldId) && this.txNodes.has(oldId)) {
|
|
2719
|
+
distinctObsolete.add(oldId);
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
return {
|
|
2723
|
+
success: true,
|
|
2724
|
+
createdIds: newCreatedIds,
|
|
2725
|
+
obsoleteIds: Array.from(distinctObsolete)
|
|
2726
|
+
};
|
|
2727
|
+
} else {
|
|
2728
|
+
await this.rollback();
|
|
2729
|
+
return {
|
|
2730
|
+
success: false,
|
|
2731
|
+
createdIds: newCreatedIds,
|
|
2732
|
+
obsoleteIds: []
|
|
2733
|
+
};
|
|
2734
|
+
}
|
|
2562
2735
|
}
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2736
|
+
/**
|
|
2737
|
+
* Rolls back the transaction by clearing all buffered changes.
|
|
2738
|
+
* Internal use only.
|
|
2739
|
+
*/
|
|
2740
|
+
async rollback() {
|
|
2741
|
+
this.txNodes.clear();
|
|
2742
|
+
this.dirtyIds.clear();
|
|
2743
|
+
this.createdInTx.clear();
|
|
2744
|
+
}
|
|
2745
|
+
async readLock(fn) {
|
|
2746
|
+
return await fn();
|
|
2747
|
+
}
|
|
2748
|
+
async writeLock(fn) {
|
|
2749
|
+
return await fn();
|
|
2750
|
+
}
|
|
2751
|
+
async commitHeadBuffer() {
|
|
2752
|
+
}
|
|
2753
|
+
async commitNodeCreateBuffer() {
|
|
2754
|
+
}
|
|
2755
|
+
async commitNodeUpdateBuffer() {
|
|
2756
|
+
}
|
|
2757
|
+
async commitNodeDeleteBuffer() {
|
|
2758
|
+
}
|
|
2759
|
+
};
|
|
2760
|
+
|
|
2761
|
+
// src/BPTreeAsync.ts
|
|
2762
|
+
var BPTreeAsync = class extends BPTreeAsyncBase {
|
|
2763
|
+
constructor(strategy, comparator, option) {
|
|
2764
|
+
super(strategy, comparator, option);
|
|
2765
|
+
}
|
|
2766
|
+
/**
|
|
2767
|
+
* Creates a new asynchronous transaction.
|
|
2768
|
+
* @returns A promise that resolves to a new BPTreeAsyncTransaction.
|
|
2769
|
+
*/
|
|
2770
|
+
async createTransaction() {
|
|
2771
|
+
const tx = new BPTreeAsyncTransaction(this);
|
|
2772
|
+
await tx.initTransaction();
|
|
2773
|
+
return tx;
|
|
2774
|
+
}
|
|
2775
|
+
async insert(key, value) {
|
|
2776
|
+
const tx = await this.createTransaction();
|
|
2777
|
+
await tx.insert(key, value);
|
|
2778
|
+
const { success } = await tx.commit();
|
|
2779
|
+
await this.init();
|
|
2780
|
+
if (!success) {
|
|
2781
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
2566
2782
|
}
|
|
2567
|
-
return this.head;
|
|
2568
2783
|
}
|
|
2569
|
-
async
|
|
2570
|
-
|
|
2784
|
+
async delete(key, value) {
|
|
2785
|
+
const tx = await this.createTransaction();
|
|
2786
|
+
await tx.delete(key, value);
|
|
2787
|
+
const { success } = await tx.commit();
|
|
2788
|
+
await this.init();
|
|
2789
|
+
if (!success) {
|
|
2790
|
+
throw new Error("Transaction failed: Commit failed due to conflict");
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
async readLock(fn) {
|
|
2794
|
+
return await fn();
|
|
2795
|
+
}
|
|
2796
|
+
async writeLock(fn) {
|
|
2797
|
+
return await fn();
|
|
2571
2798
|
}
|
|
2572
2799
|
};
|
|
2573
2800
|
export {
|
|
2574
2801
|
BPTreeAsync,
|
|
2802
|
+
BPTreeAsyncTransaction,
|
|
2575
2803
|
BPTreeSync,
|
|
2804
|
+
BPTreeSyncTransaction,
|
|
2576
2805
|
InMemoryStoreStrategyAsync,
|
|
2577
2806
|
InMemoryStoreStrategySync,
|
|
2578
2807
|
NumericComparator,
|