serializable-bptree 6.2.2 → 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.
@@ -438,7 +438,7 @@ var BPTree = class _BPTree {
438
438
  comparator;
439
439
  option;
440
440
  order;
441
- root;
441
+ rootId;
442
442
  _strategyDirty;
443
443
  _nodeCreateBuffer;
444
444
  _nodeUpdateBuffer;
@@ -692,19 +692,19 @@ var BPTree = class _BPTree {
692
692
  }
693
693
  }
694
694
  bufferForNodeCreate(node) {
695
- if (node.id === this.root.id) {
695
+ if (node.id === this.rootId) {
696
696
  this._strategyDirty = true;
697
697
  }
698
698
  this._nodeCreateBuffer.set(node.id, node);
699
699
  }
700
700
  bufferForNodeUpdate(node) {
701
- if (node.id === this.root.id) {
701
+ if (node.id === this.rootId) {
702
702
  this._strategyDirty = true;
703
703
  }
704
704
  this._nodeUpdateBuffer.set(node.id, node);
705
705
  }
706
706
  bufferForNodeDelete(node) {
707
- if (node.id === this.root.id) {
707
+ if (node.id === this.rootId) {
708
708
  this._strategyDirty = true;
709
709
  }
710
710
  this._nodeDeleteBuffer.set(node.id, node);
@@ -727,8 +727,8 @@ var BPTree = class _BPTree {
727
727
  }
728
728
  };
729
729
 
730
- // src/BPTreeSync.ts
731
- var BPTreeSync = class extends BPTree {
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,16 +837,18 @@ var BPTreeSync = class extends BPTree {
837
837
  }
838
838
  }
839
839
  }
840
- if (this.root.id === 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
- this.bufferForNodeDelete(this.root);
843
- this.root = this.getNode(keys[0]);
844
- this.root.parent = null;
845
- this.strategy.head.root = this.root.id;
846
- this.bufferForNodeUpdate(this.root);
842
+ this.bufferForNodeDelete(node);
843
+ const newRoot = this.getNode(keys[0]);
844
+ this.rootId = newRoot.id;
845
+ newRoot.parent = null;
846
+ this.strategy.head.root = this.rootId;
847
+ this.bufferForNodeUpdate(newRoot);
847
848
  return;
848
- } else if (this.root.id === node.id) {
849
- this.bufferForNodeUpdate(this.root);
849
+ } else if (this.rootId === node.id) {
850
+ const root = this.getNode(this.rootId);
851
+ this.bufferForNodeUpdate(root);
850
852
  return;
851
853
  } else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
852
854
  if (node.parent === null) {
@@ -904,20 +906,11 @@ var BPTreeSync = class extends BPTree {
904
906
  pointer.values.push(guess);
905
907
  } else {
906
908
  pointer.next = node.next;
907
- pointer.prev = node.id;
908
909
  if (pointer.next) {
909
- const n = this.getNode(node.next);
910
+ const n = this.getNode(pointer.next);
910
911
  n.prev = pointer.id;
911
912
  this.bufferForNodeUpdate(n);
912
913
  }
913
- if (pointer.prev) {
914
- const n = this.getNode(node.id);
915
- n.next = pointer.id;
916
- this.bufferForNodeUpdate(n);
917
- }
918
- if (isPredecessor) {
919
- pointer.prev = null;
920
- }
921
914
  }
922
915
  pointer.values.push(...node.values);
923
916
  if (!pointer.leaf) {
@@ -1026,9 +1019,9 @@ var BPTreeSync = class extends BPTree {
1026
1019
  }
1027
1020
  }
1028
1021
  _insertInParent(node, value, pointer) {
1029
- if (this.root.id === node.id) {
1022
+ if (this.rootId === node.id) {
1030
1023
  const root = this._createNode(false, [node.id, pointer.id], [value]);
1031
- this.root = root;
1024
+ this.rootId = root.id;
1032
1025
  this.strategy.head.root = root.id;
1033
1026
  node.parent = root.id;
1034
1027
  pointer.parent = root.id;
@@ -1097,15 +1090,16 @@ var BPTreeSync = class extends BPTree {
1097
1090
  const head = this.strategy.readHead();
1098
1091
  if (head === null) {
1099
1092
  this.order = this.strategy.order;
1100
- this.root = this._createNode(true, [], [], true);
1101
- this.strategy.head.root = this.root.id;
1093
+ const root = this._createNode(true, [], [], true);
1094
+ this.rootId = root.id;
1095
+ this.strategy.head.root = this.rootId;
1102
1096
  this.commitHeadBuffer();
1103
1097
  this.commitNodeCreateBuffer();
1104
1098
  } else {
1105
1099
  const { root, order } = head;
1106
1100
  this.strategy.head = head;
1107
1101
  this.order = order;
1108
- this.root = this.getNode(root);
1102
+ this.rootId = root;
1109
1103
  }
1110
1104
  if (this.order < 3) {
1111
1105
  throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
@@ -1122,7 +1116,7 @@ var BPTreeSync = class extends BPTree {
1122
1116
  return cache.raw;
1123
1117
  }
1124
1118
  insertableNode(value) {
1125
- let node = this.getNode(this.root.id);
1119
+ let node = this.getNode(this.rootId);
1126
1120
  while (!node.leaf) {
1127
1121
  for (let i = 0, len = node.values.length; i < len; i++) {
1128
1122
  const nValue = node.values[i];
@@ -1141,12 +1135,8 @@ var BPTreeSync = class extends BPTree {
1141
1135
  }
1142
1136
  return node;
1143
1137
  }
1144
- /**
1145
- * Find the insertable node using primaryAsc comparison.
1146
- * This allows finding nodes by primary value only, ignoring unique identifiers.
1147
- */
1148
1138
  insertableNodeByPrimary(value) {
1149
- let node = this.getNode(this.root.id);
1139
+ let node = this.getNode(this.rootId);
1150
1140
  while (!node.leaf) {
1151
1141
  for (let i = 0, len = node.values.length; i < len; i++) {
1152
1142
  const nValue = node.values[i];
@@ -1166,7 +1156,7 @@ var BPTreeSync = class extends BPTree {
1166
1156
  return node;
1167
1157
  }
1168
1158
  insertableRightestNodeByPrimary(value) {
1169
- let node = this.getNode(this.root.id);
1159
+ let node = this.getNode(this.rootId);
1170
1160
  while (!node.leaf) {
1171
1161
  for (let i = 0, len = node.values.length; i < len; i++) {
1172
1162
  const nValue = node.values[i];
@@ -1210,7 +1200,7 @@ var BPTreeSync = class extends BPTree {
1210
1200
  return this.getNode(guessNode);
1211
1201
  }
1212
1202
  leftestNode() {
1213
- let node = this.root;
1203
+ let node = this.getNode(this.rootId);
1214
1204
  while (!node.leaf) {
1215
1205
  const keys = node.keys;
1216
1206
  node = this.getNode(keys[0]);
@@ -1218,19 +1208,49 @@ var BPTreeSync = class extends BPTree {
1218
1208
  return node;
1219
1209
  }
1220
1210
  rightestNode() {
1221
- let node = this.root;
1211
+ let node = this.getNode(this.rootId);
1222
1212
  while (!node.leaf) {
1223
1213
  const keys = node.keys;
1224
1214
  node = this.getNode(keys[keys.length - 1]);
1225
1215
  }
1226
1216
  return node;
1227
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
+ }
1228
1245
  commitHeadBuffer() {
1229
1246
  if (!this._strategyDirty) {
1230
1247
  return;
1231
1248
  }
1232
1249
  this._strategyDirty = false;
1233
1250
  this.strategy.writeHead(this.strategy.head);
1251
+ if (this.strategy.head.root) {
1252
+ this.nodes.delete(this.strategy.head.root);
1253
+ }
1234
1254
  }
1235
1255
  commitNodeCreateBuffer() {
1236
1256
  for (const node of this._nodeCreateBuffer.values()) {
@@ -1241,33 +1261,25 @@ var BPTreeSync = class extends BPTree {
1241
1261
  commitNodeUpdateBuffer() {
1242
1262
  for (const node of this._nodeUpdateBuffer.values()) {
1243
1263
  this.strategy.write(node.id, node);
1264
+ this.nodes.delete(node.id);
1244
1265
  }
1245
1266
  this._nodeUpdateBuffer.clear();
1246
1267
  }
1247
1268
  commitNodeDeleteBuffer() {
1248
1269
  for (const node of this._nodeDeleteBuffer.values()) {
1249
1270
  this.strategy.delete(node.id);
1271
+ this.nodes.delete(node.id);
1250
1272
  }
1251
1273
  this._nodeDeleteBuffer.clear();
1252
1274
  }
1253
- /**
1254
- * Retrieves the value associated with the given key (PK).
1255
- * Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
1256
- *
1257
- * @param key The key to search for.
1258
- * @returns The value associated with the key, or undefined if not found.
1259
- */
1260
1275
  get(key) {
1261
1276
  let node = this.leftestNode();
1262
1277
  while (true) {
1263
- if (node.values) {
1264
- const len = node.values.length;
1265
- for (let i = 0; i < len; i++) {
1266
- const keys = node.keys[i];
1267
- for (let j = 0; j < keys.length; j++) {
1268
- if (keys[j] === key) {
1269
- return node.values[i];
1270
- }
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];
1271
1283
  }
1272
1284
  }
1273
1285
  }
@@ -1386,21 +1398,15 @@ var BPTreeSync = class extends BPTree {
1386
1398
  const nValue = node.values[i];
1387
1399
  if (this.comparator.isSame(value, nValue)) {
1388
1400
  const keys = node.keys[i];
1389
- if (keys.includes(key)) {
1390
- if (keys.length > 1) {
1391
- keys.splice(keys.indexOf(key), 1);
1392
- this.bufferForNodeUpdate(node);
1393
- } else if (node.id === this.root.id) {
1394
- node.values.splice(i, 1);
1401
+ const keyIndex = keys.indexOf(key);
1402
+ if (keyIndex !== -1) {
1403
+ keys.splice(keyIndex, 1);
1404
+ if (keys.length === 0) {
1395
1405
  node.keys.splice(i, 1);
1396
- this.bufferForNodeUpdate(node);
1397
- } else {
1398
- keys.splice(keys.indexOf(key), 1);
1399
- node.keys.splice(i, 1);
1400
- node.values.splice(node.values.indexOf(value), 1);
1401
- this._deleteEntry(node, key, value);
1402
- this.bufferForNodeUpdate(node);
1406
+ node.values.splice(i, 1);
1403
1407
  }
1408
+ this._deleteEntry(node, key, value);
1409
+ break;
1404
1410
  }
1405
1411
  }
1406
1412
  }
@@ -1409,298 +1415,356 @@ var BPTreeSync = class extends BPTree {
1409
1415
  this.commitNodeUpdateBuffer();
1410
1416
  this.commitNodeDeleteBuffer();
1411
1417
  }
1412
- exists(key, value) {
1413
- const node = this.insertableNode(value);
1414
- for (let i = 0, len = node.values.length; i < len; i++) {
1415
- const nValue = node.values[i];
1416
- if (this.comparator.isSame(value, nValue)) {
1417
- const keys = node.keys[i];
1418
- return keys.includes(key);
1419
- }
1420
- }
1421
- return false;
1418
+ getHeadData() {
1419
+ return this.strategy.head.data;
1422
1420
  }
1423
1421
  setHeadData(data) {
1424
1422
  this.strategy.head.data = data;
1425
- this._strategyDirty = true;
1426
- this.commitHeadBuffer();
1423
+ this.strategy.writeHead(this.strategy.head);
1427
1424
  }
1428
- forceUpdate() {
1429
- const keys = [...this.nodes.keys()];
1430
- this.nodes.clear();
1431
- this.init();
1432
- for (const key of keys) {
1433
- this.getNode(key);
1434
- }
1435
- return keys.length;
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
+ };
1436
1438
  }
1437
1439
  };
1438
1440
 
1439
- // node_modules/ryoiki/dist/esm/index.mjs
1440
- var Ryoiki = class _Ryoiki {
1441
- readings;
1442
- writings;
1443
- readQueue;
1444
- writeQueue;
1445
- static async CatchError(promise) {
1446
- return await promise.then((v) => [void 0, v]).catch((err) => [err]);
1447
- }
1448
- static IsRangeOverlap(a, b) {
1449
- const [start1, end1] = a;
1450
- const [start2, end2] = b;
1451
- if (end1 <= start2 || end2 <= start1) {
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) {
1452
1461
  return false;
1453
1462
  }
1463
+ this.head.root = newRoot;
1464
+ this.writeHead(this.head);
1454
1465
  return true;
1455
1466
  }
1456
- static ERR_ALREADY_EXISTS(lockId) {
1457
- return new Error(`The '${lockId}' task already existing in queue or running.`);
1467
+ };
1468
+ var InMemoryStoreStrategySync = class extends SerializeStrategySync {
1469
+ node;
1470
+ constructor(order) {
1471
+ super(order);
1472
+ this.node = {};
1458
1473
  }
1459
- static ERR_NOT_EXISTS(lockId) {
1460
- return new Error(`The '${lockId}' task not existing in task queue.`);
1474
+ id(isLeaf) {
1475
+ return this.autoIncrement("index", 1).toString();
1461
1476
  }
1462
- static ERR_TIMEOUT(lockId, timeout) {
1463
- return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
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));
1464
1483
  }
1465
- /**
1466
- * Constructs a new instance of the Ryoiki class.
1467
- */
1468
- constructor() {
1469
- this.readings = /* @__PURE__ */ new Map();
1470
- this.writings = /* @__PURE__ */ new Map();
1471
- this.readQueue = /* @__PURE__ */ new Map();
1472
- this.writeQueue = /* @__PURE__ */ new Map();
1484
+ write(id, node) {
1485
+ this.node[id] = node;
1473
1486
  }
1474
- /**
1475
- * Creates a range based on a start value and length.
1476
- * @param start - The starting value of the range.
1477
- * @param length - The length of the range.
1478
- * @returns A range tuple [start, start + length].
1479
- */
1480
- range(start, length) {
1481
- return [start, start + length];
1487
+ delete(id) {
1488
+ delete this.node[id];
1482
1489
  }
1483
- rangeOverlapping(tasks, range) {
1484
- return Array.from(tasks.values()).some((t) => _Ryoiki.IsRangeOverlap(t.range, range));
1490
+ readHead() {
1491
+ if (this.head.root === null) {
1492
+ return null;
1493
+ }
1494
+ return this.head;
1485
1495
  }
1486
- isSameRange(a, b) {
1487
- const [a1, a2] = a;
1488
- const [b1, b2] = b;
1489
- return a1 === b1 && a2 === b2;
1496
+ writeHead(head) {
1497
+ this.head = head;
1490
1498
  }
1491
- fetchUnitAndRun(queue, workspaces) {
1492
- for (const [id, unit] of queue) {
1493
- if (!unit.condition()) {
1494
- continue;
1495
- }
1496
- this._alloc(queue, workspaces, id);
1497
- }
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;
1498
1514
  }
1499
- _handleOverload(args, handlers, argPatterns) {
1500
- for (const [key, pattern] of Object.entries(argPatterns)) {
1501
- if (this._matchArgs(args, pattern)) {
1502
- return handlers[key](...args);
1503
- }
1504
- }
1505
- throw new Error("Invalid arguments");
1515
+ id(isLeaf) {
1516
+ return this.baseStrategy.id(isLeaf);
1506
1517
  }
1507
- _matchArgs(args, pattern) {
1508
- return args.every((arg, index) => {
1509
- const expectedType = pattern[index];
1510
- if (expectedType === void 0) return typeof arg === "undefined";
1511
- if (expectedType === Function) return typeof arg === "function";
1512
- if (expectedType === Number) return typeof arg === "number";
1513
- if (expectedType === Array) return Array.isArray(arg);
1514
- return false;
1515
- });
1518
+ read(id) {
1519
+ return this.baseStrategy.read(id);
1516
1520
  }
1517
- _createRandomId() {
1518
- const timestamp = Date.now().toString(36);
1519
- const random = Math.random().toString(36).substring(2);
1520
- return `${timestamp}${random}`;
1521
- }
1522
- _alloc(queue, workspaces, lockId) {
1523
- const unit = queue.get(lockId);
1524
- if (!unit) {
1525
- throw _Ryoiki.ERR_NOT_EXISTS(lockId);
1526
- }
1527
- workspaces.set(lockId, unit);
1528
- queue.delete(lockId);
1529
- unit.alloc();
1530
- }
1531
- _free(workspaces, lockId) {
1532
- const unit = workspaces.get(lockId);
1533
- if (!unit) {
1534
- throw _Ryoiki.ERR_NOT_EXISTS(lockId);
1535
- }
1536
- workspaces.delete(lockId);
1537
- unit.free();
1538
- }
1539
- _lock(queue, range, timeout, task, condition) {
1540
- return new Promise((resolve, reject) => {
1541
- let timeoutId = null;
1542
- if (timeout >= 0) {
1543
- timeoutId = setTimeout(() => {
1544
- reject(_Ryoiki.ERR_TIMEOUT(id, timeout));
1545
- }, timeout);
1546
- }
1547
- const id = this._createRandomId();
1548
- const alloc = async () => {
1549
- if (timeoutId !== null) {
1550
- clearTimeout(timeoutId);
1551
- }
1552
- const [err, v] = await _Ryoiki.CatchError(task(id));
1553
- if (err) reject(err);
1554
- else resolve(v);
1555
- };
1556
- const fetch = () => {
1557
- this.fetchUnitAndRun(this.readQueue, this.readings);
1558
- this.fetchUnitAndRun(this.writeQueue, this.writings);
1559
- };
1560
- queue.set(id, { id, range, condition, alloc, free: fetch });
1561
- fetch();
1562
- });
1521
+ write(id, node) {
1522
+ this.baseStrategy.write(id, node);
1563
1523
  }
1564
- _checkWorking(range, workspaces) {
1565
- let isLocked = false;
1566
- for (const lock of workspaces.values()) {
1567
- if (_Ryoiki.IsRangeOverlap(range, lock.range)) {
1568
- isLocked = true;
1569
- break;
1570
- }
1571
- }
1572
- return isLocked;
1524
+ delete(id) {
1525
+ this.baseStrategy.delete(id);
1573
1526
  }
1574
- /**
1575
- * Checks if there is any active read lock within the specified range.
1576
- * @param range The range to check for active read locks.
1577
- * @returns `true` if there is an active read lock within the range, `false` otherwise.
1578
- */
1579
- isReading(range) {
1580
- return this._checkWorking(range, this.readings);
1527
+ readHead() {
1528
+ return this.snapshotHead;
1581
1529
  }
1582
- /**
1583
- * Checks if there is any active write lock within the specified range.
1584
- * @param range The range to check for active write locks.
1585
- * @returns `true` if there is an active write lock within the range, `false` otherwise.
1586
- */
1587
- isWriting(range) {
1588
- return this._checkWorking(range, this.writings);
1530
+ writeHead(head) {
1531
+ this.snapshotHead.root = head.root;
1532
+ this.snapshotHead.data = { ...head.data };
1589
1533
  }
1590
- /**
1591
- * Checks if a read lock can be acquired within the specified range.
1592
- * @param range The range to check for read lock availability.
1593
- * @returns `true` if a read lock can be acquired, `false` otherwise.
1594
- */
1595
- canRead(range) {
1596
- const writing = this.isWriting(range);
1597
- return !writing;
1534
+ compareAndSwapHead(oldRoot, newRoot) {
1535
+ return this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
1598
1536
  }
1599
- /**
1600
- * Checks if a write lock can be acquired within the specified range.
1601
- * @param range The range to check for write lock availability.
1602
- * @returns `true` if a write lock can be acquired, `false` otherwise.
1603
- */
1604
- canWrite(range) {
1605
- const reading = this.isReading(range);
1606
- const writing = this.isWriting(range);
1607
- return !reading && !writing;
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 = "";
1608
1563
  }
1609
1564
  /**
1610
- * Internal implementation of the read lock. Handles both overloads.
1611
- * @template T - The return type of the task.
1612
- * @param arg0 - Either a range or a task callback.
1613
- * If a range is provided, the task is the second argument.
1614
- * @param arg1 - The task to execute, required if a range is provided.
1615
- * @param arg2 - The timeout for acquiring the lock.
1616
- * If the lock cannot be acquired within this period, an error will be thrown.
1617
- * If this value is not provided, no timeout will be set.
1618
- * @returns A promise resolving to the result of the task execution.
1565
+ * Initializes the transaction by capturing the current state of the tree.
1619
1566
  */
1620
- readLock(arg0, arg1, arg2) {
1621
- const [range, task, timeout] = this._handleOverload(
1622
- [arg0, arg1, arg2],
1623
- {
1624
- rangeTask: (range2, task2) => [range2, task2, -1],
1625
- rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
1626
- task: (task2) => [[-Infinity, Infinity], task2, -1],
1627
- taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
1628
- },
1629
- {
1630
- task: [Function],
1631
- taskTimeout: [Function, Number],
1632
- rangeTask: [Array, Function],
1633
- rangeTaskTimeout: [Array, Function, Number]
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;
1634
1607
  }
1635
- );
1636
- return this._lock(
1637
- this.readQueue,
1638
- range,
1639
- timeout,
1640
- task,
1641
- () => !this.rangeOverlapping(this.writings, range)
1642
- );
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;
1643
1629
  }
1644
1630
  /**
1645
- * Internal implementation of the write lock. Handles both overloads.
1646
- * @template T - The return type of the task.
1647
- * @param arg0 - Either a range or a task callback.
1648
- * If a range is provided, the task is the second argument.
1649
- * @param arg1 - The task to execute, required if a range is provided.
1650
- * @param arg2 - The timeout for acquiring the lock.
1651
- * If the lock cannot be acquired within this period, an error will be thrown.
1652
- * If this value is not provided, no timeout will be set.
1653
- * @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.
1654
1635
  */
1655
- writeLock(arg0, arg1, arg2) {
1656
- const [range, task, timeout] = this._handleOverload(
1657
- [arg0, arg1, arg2],
1658
- {
1659
- rangeTask: (range2, task2) => [range2, task2, -1],
1660
- rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
1661
- task: (task2) => [[-Infinity, Infinity], task2, -1],
1662
- taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
1663
- },
1664
- {
1665
- task: [Function],
1666
- taskTimeout: [Function, Number],
1667
- rangeTask: [Array, Function],
1668
- 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
+ }
1669
1648
  }
1670
- );
1671
- return this._lock(
1672
- this.writeQueue,
1673
- range,
1674
- timeout,
1675
- task,
1676
- () => {
1677
- return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
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
+ }
1678
1667
  }
1679
- );
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
+ }
1680
1708
  }
1681
1709
  /**
1682
- * Releases a read lock by its lock ID.
1683
- * @param lockId - The unique identifier for the lock to release.
1710
+ * Rolls back the transaction by clearing all buffered changes.
1711
+ * Internal use only.
1684
1712
  */
1685
- readUnlock(lockId) {
1686
- this._free(this.readings, lockId);
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);
1687
1733
  }
1688
1734
  /**
1689
- * Releases a write lock by its lock ID.
1690
- * @param lockId - The unique identifier for the lock to release.
1735
+ * Creates a new synchronous transaction.
1736
+ * @returns A new BPTreeSyncTransaction.
1691
1737
  */
1692
- writeUnlock(lockId) {
1693
- this._free(this.writings, lockId);
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
+ }
1694
1760
  }
1695
1761
  };
1696
1762
 
1697
- // src/BPTreeAsync.ts
1698
- var BPTreeAsync = class extends BPTree {
1699
- lock;
1763
+ // src/base/BPTreeAsyncBase.ts
1764
+ var BPTreeAsyncBase = class extends BPTree {
1700
1765
  constructor(strategy, comparator, option) {
1701
1766
  super(strategy, comparator, option);
1702
1767
  this.nodes = this._createCachedNode();
1703
- this.lock = new Ryoiki();
1704
1768
  }
1705
1769
  _createCachedNode() {
1706
1770
  return new CacheEntanglementAsync(async (key) => {
@@ -1709,24 +1773,6 @@ var BPTreeAsync = class extends BPTree {
1709
1773
  capacity: this.option.capacity ?? 1e3
1710
1774
  });
1711
1775
  }
1712
- async readLock(callback) {
1713
- let lockId;
1714
- return await this.lock.readLock(async (_lockId) => {
1715
- lockId = _lockId;
1716
- return await callback();
1717
- }).finally(() => {
1718
- this.lock.readUnlock(lockId);
1719
- });
1720
- }
1721
- async writeLock(callback) {
1722
- let lockId;
1723
- return await this.lock.writeLock(async (_lockId) => {
1724
- lockId = _lockId;
1725
- return await callback();
1726
- }).finally(() => {
1727
- this.lock.writeUnlock(lockId);
1728
- });
1729
- }
1730
1776
  async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
1731
1777
  let node = startNode;
1732
1778
  let done = false;
@@ -1824,16 +1870,18 @@ var BPTreeAsync = class extends BPTree {
1824
1870
  }
1825
1871
  }
1826
1872
  }
1827
- if (this.root.id === node.id && node.keys.length === 1) {
1873
+ if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
1828
1874
  const keys = node.keys;
1829
- this.bufferForNodeDelete(this.root);
1830
- this.root = await this.getNode(keys[0]);
1831
- this.root.parent = null;
1832
- this.strategy.head.root = this.root.id;
1833
- this.bufferForNodeUpdate(this.root);
1875
+ this.bufferForNodeDelete(node);
1876
+ const newRoot = await this.getNode(keys[0]);
1877
+ this.rootId = newRoot.id;
1878
+ newRoot.parent = null;
1879
+ this.strategy.head.root = this.rootId;
1880
+ this.bufferForNodeUpdate(newRoot);
1834
1881
  return;
1835
- } else if (this.root.id === node.id) {
1836
- this.bufferForNodeUpdate(this.root);
1882
+ } else if (this.rootId === node.id) {
1883
+ const root = await this.getNode(this.rootId);
1884
+ this.bufferForNodeUpdate(root);
1837
1885
  return;
1838
1886
  } else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
1839
1887
  if (node.parent === null) {
@@ -1891,28 +1939,19 @@ var BPTreeAsync = class extends BPTree {
1891
1939
  pointer.values.push(guess);
1892
1940
  } else {
1893
1941
  pointer.next = node.next;
1894
- pointer.prev = node.id;
1895
1942
  if (pointer.next) {
1896
- const n = await this.getNode(node.next);
1943
+ const n = await this.getNode(pointer.next);
1897
1944
  n.prev = pointer.id;
1898
1945
  this.bufferForNodeUpdate(n);
1899
1946
  }
1900
- if (pointer.prev) {
1901
- const n = await this.getNode(node.id);
1902
- n.next = pointer.id;
1903
- this.bufferForNodeUpdate(n);
1904
- }
1905
- if (isPredecessor) {
1906
- pointer.prev = null;
1907
- }
1908
1947
  }
1909
1948
  pointer.values.push(...node.values);
1910
1949
  if (!pointer.leaf) {
1911
1950
  const keys = pointer.keys;
1912
1951
  for (const key2 of keys) {
1913
- const node2 = await this.getNode(key2);
1914
- node2.parent = pointer.id;
1915
- this.bufferForNodeUpdate(node2);
1952
+ const n = await this.getNode(key2);
1953
+ n.parent = pointer.id;
1954
+ this.bufferForNodeUpdate(n);
1916
1955
  }
1917
1956
  }
1918
1957
  await this._deleteEntry(await this.getNode(node.parent), node.id, guess);
@@ -1989,21 +2028,24 @@ var BPTreeAsync = class extends BPTree {
1989
2028
  this.bufferForNodeUpdate(pointer);
1990
2029
  }
1991
2030
  if (!pointer.leaf) {
1992
- for (const key2 of pointer.keys) {
2031
+ const keys = pointer.keys;
2032
+ for (const key2 of keys) {
1993
2033
  const n = await this.getNode(key2);
1994
2034
  n.parent = pointer.id;
1995
2035
  this.bufferForNodeUpdate(n);
1996
2036
  }
1997
2037
  }
1998
2038
  if (!node.leaf) {
1999
- for (const key2 of node.keys) {
2039
+ const keys = node.keys;
2040
+ for (const key2 of keys) {
2000
2041
  const n = await this.getNode(key2);
2001
2042
  n.parent = node.id;
2002
2043
  this.bufferForNodeUpdate(n);
2003
2044
  }
2004
2045
  }
2005
2046
  if (!parentNode.leaf) {
2006
- for (const key2 of parentNode.keys) {
2047
+ const keys = parentNode.keys;
2048
+ for (const key2 of keys) {
2007
2049
  const n = await this.getNode(key2);
2008
2050
  n.parent = parentNode.id;
2009
2051
  this.bufferForNodeUpdate(n);
@@ -2013,9 +2055,9 @@ var BPTreeAsync = class extends BPTree {
2013
2055
  }
2014
2056
  }
2015
2057
  async _insertInParent(node, value, pointer) {
2016
- if (this.root.id === node.id) {
2058
+ if (this.rootId === node.id) {
2017
2059
  const root = await this._createNode(false, [node.id, pointer.id], [value]);
2018
- this.root = root;
2060
+ this.rootId = root.id;
2019
2061
  this.strategy.head.root = root.id;
2020
2062
  node.parent = root.id;
2021
2063
  pointer.parent = root.id;
@@ -2067,14 +2109,14 @@ var BPTreeAsync = class extends BPTree {
2067
2109
  parentNode.values = parentNode.values.slice(0, mid);
2068
2110
  parentNode.keys = parentNode.keys.slice(0, mid + 1);
2069
2111
  for (const k of parentNode.keys) {
2070
- const node2 = await this.getNode(k);
2071
- node2.parent = parentNode.id;
2072
- this.bufferForNodeUpdate(node2);
2112
+ const n = await this.getNode(k);
2113
+ n.parent = parentNode.id;
2114
+ this.bufferForNodeUpdate(n);
2073
2115
  }
2074
2116
  for (const k of parentPointer.keys) {
2075
- const node2 = await this.getNode(k);
2076
- node2.parent = parentPointer.id;
2077
- this.bufferForNodeUpdate(node2);
2117
+ const n = await this.getNode(k);
2118
+ n.parent = parentPointer.id;
2119
+ this.bufferForNodeUpdate(n);
2078
2120
  }
2079
2121
  await this._insertInParent(parentNode, midValue, parentPointer);
2080
2122
  this.bufferForNodeUpdate(parentNode);
@@ -2084,15 +2126,16 @@ var BPTreeAsync = class extends BPTree {
2084
2126
  const head = await this.strategy.readHead();
2085
2127
  if (head === null) {
2086
2128
  this.order = this.strategy.order;
2087
- this.root = await this._createNode(true, [], [], true);
2088
- this.strategy.head.root = this.root.id;
2129
+ const root = await this._createNode(true, [], [], true);
2130
+ this.rootId = root.id;
2131
+ this.strategy.head.root = this.rootId;
2089
2132
  await this.commitHeadBuffer();
2090
2133
  await this.commitNodeCreateBuffer();
2091
2134
  } else {
2092
2135
  const { root, order } = head;
2093
2136
  this.strategy.head = head;
2094
2137
  this.order = order;
2095
- this.root = await this.getNode(root);
2138
+ this.rootId = root;
2096
2139
  }
2097
2140
  if (this.order < 3) {
2098
2141
  throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
@@ -2109,43 +2152,39 @@ var BPTreeAsync = class extends BPTree {
2109
2152
  return cache.raw;
2110
2153
  }
2111
2154
  async insertableNode(value) {
2112
- let node = await this.getNode(this.root.id);
2155
+ let node = await this.getNode(this.rootId);
2113
2156
  while (!node.leaf) {
2114
2157
  for (let i = 0, len = node.values.length; i < len; i++) {
2115
2158
  const nValue = node.values[i];
2116
2159
  const k = node.keys;
2117
2160
  if (this.comparator.isSame(value, nValue)) {
2118
- node = await this.getNode(k[i + 1]);
2161
+ node = await this.getNode(node.keys[i + 1]);
2119
2162
  break;
2120
2163
  } else if (this.comparator.isLower(value, nValue)) {
2121
- node = await this.getNode(k[i]);
2164
+ node = await this.getNode(node.keys[i]);
2122
2165
  break;
2123
2166
  } else if (i + 1 === node.values.length) {
2124
- node = await this.getNode(k[i + 1]);
2167
+ node = await this.getNode(node.keys[i + 1]);
2125
2168
  break;
2126
2169
  }
2127
2170
  }
2128
2171
  }
2129
2172
  return node;
2130
2173
  }
2131
- /**
2132
- * Find the insertable node using primaryAsc comparison.
2133
- * This allows finding nodes by primary value only, ignoring unique identifiers.
2134
- */
2135
2174
  async insertableNodeByPrimary(value) {
2136
- let node = await this.getNode(this.root.id);
2175
+ let node = await this.getNode(this.rootId);
2137
2176
  while (!node.leaf) {
2138
2177
  for (let i = 0, len = node.values.length; i < len; i++) {
2139
2178
  const nValue = node.values[i];
2140
2179
  const k = node.keys;
2141
2180
  if (this.comparator.isPrimarySame(value, nValue)) {
2142
- node = await this.getNode(k[i]);
2181
+ node = await this.getNode(node.keys[i]);
2143
2182
  break;
2144
2183
  } else if (this.comparator.isPrimaryLower(value, nValue)) {
2145
- node = await this.getNode(k[i]);
2184
+ node = await this.getNode(node.keys[i]);
2146
2185
  break;
2147
2186
  } else if (i + 1 === node.values.length) {
2148
- node = await this.getNode(k[i + 1]);
2187
+ node = await this.getNode(node.keys[i + 1]);
2149
2188
  break;
2150
2189
  }
2151
2190
  }
@@ -2153,17 +2192,17 @@ var BPTreeAsync = class extends BPTree {
2153
2192
  return node;
2154
2193
  }
2155
2194
  async insertableRightestNodeByPrimary(value) {
2156
- let node = await this.getNode(this.root.id);
2195
+ let node = await this.getNode(this.rootId);
2157
2196
  while (!node.leaf) {
2158
2197
  for (let i = 0, len = node.values.length; i < len; i++) {
2159
2198
  const nValue = node.values[i];
2160
2199
  const k = node.keys;
2161
2200
  if (this.comparator.isPrimaryLower(value, nValue)) {
2162
- node = await this.getNode(k[i]);
2201
+ node = await this.getNode(node.keys[i]);
2163
2202
  break;
2164
2203
  }
2165
2204
  if (i + 1 === node.values.length) {
2166
- node = await this.getNode(k[i + 1]);
2205
+ node = await this.getNode(node.keys[i + 1]);
2167
2206
  break;
2168
2207
  }
2169
2208
  }
@@ -2197,27 +2236,57 @@ var BPTreeAsync = class extends BPTree {
2197
2236
  return await this.getNode(guessNode);
2198
2237
  }
2199
2238
  async leftestNode() {
2200
- let node = this.root;
2239
+ let node = await this.getNode(this.rootId);
2201
2240
  while (!node.leaf) {
2202
2241
  const keys = node.keys;
2203
- node = await this.getNode(keys[0]);
2242
+ node = await this.getNode(node.keys[0]);
2204
2243
  }
2205
2244
  return node;
2206
2245
  }
2207
2246
  async rightestNode() {
2208
- let node = this.root;
2247
+ let node = await this.getNode(this.rootId);
2209
2248
  while (!node.leaf) {
2210
2249
  const keys = node.keys;
2211
- node = await this.getNode(keys[keys.length - 1]);
2250
+ node = await this.getNode(node.keys[node.keys.length - 1]);
2212
2251
  }
2213
2252
  return node;
2214
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
+ }
2215
2281
  async commitHeadBuffer() {
2216
2282
  if (!this._strategyDirty) {
2217
2283
  return;
2218
2284
  }
2219
2285
  this._strategyDirty = false;
2220
2286
  await this.strategy.writeHead(this.strategy.head);
2287
+ if (this.strategy.head.root) {
2288
+ this.nodes.delete(this.strategy.head.root);
2289
+ }
2221
2290
  }
2222
2291
  async commitNodeCreateBuffer() {
2223
2292
  for (const node of this._nodeCreateBuffer.values()) {
@@ -2228,34 +2297,26 @@ var BPTreeAsync = class extends BPTree {
2228
2297
  async commitNodeUpdateBuffer() {
2229
2298
  for (const node of this._nodeUpdateBuffer.values()) {
2230
2299
  await this.strategy.write(node.id, node);
2300
+ this.nodes.delete(node.id);
2231
2301
  }
2232
2302
  this._nodeUpdateBuffer.clear();
2233
2303
  }
2234
2304
  async commitNodeDeleteBuffer() {
2235
2305
  for (const node of this._nodeDeleteBuffer.values()) {
2236
2306
  await this.strategy.delete(node.id);
2307
+ this.nodes.delete(node.id);
2237
2308
  }
2238
2309
  this._nodeDeleteBuffer.clear();
2239
2310
  }
2240
- /**
2241
- * Retrieves the value associated with the given key (PK).
2242
- * Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
2243
- *
2244
- * @param key The key to search for.
2245
- * @returns The value associated with the key, or undefined if not found.
2246
- */
2247
2311
  async get(key) {
2248
- return this.readLock(async () => {
2312
+ return await this.readLock(async () => {
2249
2313
  let node = await this.leftestNode();
2250
2314
  while (true) {
2251
- if (node.values) {
2252
- const len = node.values.length;
2253
- for (let i = 0; i < len; i++) {
2254
- const keys = node.keys[i];
2255
- for (let j = 0; j < keys.length; j++) {
2256
- if (keys[j] === key) {
2257
- return node.values[i];
2258
- }
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];
2259
2320
  }
2260
2321
  }
2261
2322
  }
@@ -2330,25 +2391,21 @@ var BPTreeAsync = class extends BPTree {
2330
2391
  }
2331
2392
  }
2332
2393
  async keys(condition, filterValues) {
2333
- return this.readLock(async () => {
2334
- const set = /* @__PURE__ */ new Set();
2335
- for await (const key of this.keysStream(condition, filterValues)) {
2336
- set.add(key);
2337
- }
2338
- return set;
2339
- });
2394
+ const set = /* @__PURE__ */ new Set();
2395
+ for await (const key of this.keysStream(condition, filterValues)) {
2396
+ set.add(key);
2397
+ }
2398
+ return set;
2340
2399
  }
2341
2400
  async where(condition) {
2342
- return this.readLock(async () => {
2343
- const map = /* @__PURE__ */ new Map();
2344
- for await (const [key, value] of this.whereStream(condition)) {
2345
- map.set(key, value);
2346
- }
2347
- return map;
2348
- });
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;
2349
2406
  }
2350
2407
  async insert(key, value) {
2351
- return this.writeLock(async () => {
2408
+ await this.writeLock(async () => {
2352
2409
  const before = await this.insertableNode(value);
2353
2410
  this._insertAtLeaf(before, key, value);
2354
2411
  if (before.values.length === this.order) {
@@ -2375,188 +2432,376 @@ var BPTreeAsync = class extends BPTree {
2375
2432
  });
2376
2433
  }
2377
2434
  async delete(key, value) {
2378
- return this.writeLock(async () => {
2435
+ await this.writeLock(async () => {
2379
2436
  const node = await this.insertableNode(value);
2380
2437
  let i = node.values.length;
2381
2438
  while (i--) {
2382
2439
  const nValue = node.values[i];
2383
2440
  if (this.comparator.isSame(value, nValue)) {
2384
2441
  const keys = node.keys[i];
2385
- if (keys.includes(key)) {
2386
- if (keys.length > 1) {
2387
- keys.splice(keys.indexOf(key), 1);
2388
- this.bufferForNodeUpdate(node);
2389
- } else if (node.id === this.root.id) {
2390
- node.values.splice(i, 1);
2391
- node.keys.splice(i, 1);
2392
- this.bufferForNodeUpdate(node);
2393
- } else {
2394
- 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) {
2395
2446
  node.keys.splice(i, 1);
2396
- node.values.splice(node.values.indexOf(value), 1);
2397
- await this._deleteEntry(node, key, value);
2398
- this.bufferForNodeUpdate(node);
2447
+ node.values.splice(i, 1);
2399
2448
  }
2449
+ await this._deleteEntry(node, key, value);
2450
+ break;
2400
2451
  }
2401
2452
  }
2402
2453
  }
2403
2454
  await this.commitHeadBuffer();
2404
- await this.commitNodeCreateBuffer();
2405
2455
  await this.commitNodeUpdateBuffer();
2406
2456
  await this.commitNodeDeleteBuffer();
2407
2457
  });
2408
2458
  }
2409
- async exists(key, value) {
2410
- return this.readLock(async () => {
2411
- const node = await this.insertableNode(value);
2412
- for (let i = 0, len = node.values.length; i < len; i++) {
2413
- const nValue = node.values[i];
2414
- if (this.comparator.isSame(value, nValue)) {
2415
- const keys = node.keys[i];
2416
- return keys.includes(key);
2417
- }
2418
- }
2419
- return false;
2420
- });
2459
+ getHeadData() {
2460
+ return this.strategy.head.data;
2421
2461
  }
2422
2462
  async setHeadData(data) {
2423
- return this.writeLock(async () => {
2424
- this.strategy.head.data = data;
2425
- this._strategyDirty = true;
2426
- await this.commitHeadBuffer();
2427
- });
2428
- }
2429
- async forceUpdate() {
2430
- return this.readLock(async () => {
2431
- const keys = [...this.nodes.keys()];
2432
- this.nodes.clear();
2433
- await this.init();
2434
- for (const key of keys) {
2435
- await this.getNode(key);
2436
- }
2437
- return keys.length;
2438
- });
2439
- }
2440
- };
2441
-
2442
- // src/base/SerializeStrategy.ts
2443
- var SerializeStrategy = class {
2444
- order;
2445
- head;
2446
- constructor(order) {
2447
- this.order = order;
2448
- this.head = {
2449
- order,
2450
- root: null,
2451
- data: {}
2452
- };
2463
+ this.strategy.head.data = data;
2464
+ await this.strategy.writeHead(this.strategy.head);
2453
2465
  }
2454
2466
  };
2455
2467
 
2456
- // src/SerializeStrategySync.ts
2457
- var SerializeStrategySync = class extends SerializeStrategy {
2458
- getHeadData(key, defaultValue) {
2468
+ // src/SerializeStrategyAsync.ts
2469
+ var SerializeStrategyAsync = class extends SerializeStrategy {
2470
+ async getHeadData(key, defaultValue) {
2459
2471
  if (!Object.hasOwn(this.head.data, key)) {
2460
- this.setHeadData(key, defaultValue);
2472
+ await this.setHeadData(key, defaultValue);
2461
2473
  }
2462
2474
  return this.head.data[key];
2463
2475
  }
2464
- setHeadData(key, data) {
2476
+ async setHeadData(key, data) {
2465
2477
  this.head.data[key] = data;
2466
- this.writeHead(this.head);
2478
+ await this.writeHead(this.head);
2467
2479
  }
2468
- autoIncrement(key, defaultValue) {
2469
- const current = this.getHeadData(key, defaultValue);
2480
+ async autoIncrement(key, defaultValue) {
2481
+ const current = await this.getHeadData(key, defaultValue);
2470
2482
  const next = current + 1;
2471
- this.setHeadData(key, next);
2483
+ await this.setHeadData(key, next);
2472
2484
  return current;
2473
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
+ }
2474
2494
  };
2475
- var InMemoryStoreStrategySync = class extends SerializeStrategySync {
2495
+ var InMemoryStoreStrategyAsync = class extends SerializeStrategyAsync {
2476
2496
  node;
2477
2497
  constructor(order) {
2478
2498
  super(order);
2479
2499
  this.node = {};
2480
2500
  }
2481
- id(isLeaf) {
2482
- return this.autoIncrement("index", 1).toString();
2501
+ async id(isLeaf) {
2502
+ return (await this.autoIncrement("index", 1)).toString();
2483
2503
  }
2484
- read(id) {
2504
+ async read(id) {
2485
2505
  if (!Object.hasOwn(this.node, id)) {
2486
2506
  throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
2487
2507
  }
2488
- return this.node[id];
2508
+ const node = this.node[id];
2509
+ return JSON.parse(JSON.stringify(node));
2489
2510
  }
2490
- write(id, node) {
2511
+ async write(id, node) {
2491
2512
  this.node[id] = node;
2492
2513
  }
2493
- delete(id) {
2514
+ async delete(id) {
2494
2515
  delete this.node[id];
2495
2516
  }
2496
- readHead() {
2517
+ async readHead() {
2497
2518
  if (this.head.root === null) {
2498
2519
  return null;
2499
2520
  }
2500
2521
  return this.head;
2501
2522
  }
2502
- writeHead(head) {
2523
+ async writeHead(head) {
2503
2524
  this.head = head;
2504
2525
  }
2505
2526
  };
2506
2527
 
2507
- // src/SerializeStrategyAsync.ts
2508
- var SerializeStrategyAsync = class extends SerializeStrategy {
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
+ }
2509
2564
  async getHeadData(key, defaultValue) {
2510
- if (!Object.hasOwn(this.head.data, key)) {
2511
- await this.setHeadData(key, defaultValue);
2512
- }
2513
- return this.head.data[key];
2565
+ return this.snapshotHead.data[key] ?? defaultValue;
2514
2566
  }
2515
2567
  async setHeadData(key, data) {
2516
- this.head.data[key] = data;
2517
- await this.writeHead(this.head);
2568
+ this.snapshotHead.data[key] = data;
2518
2569
  }
2519
2570
  async autoIncrement(key, defaultValue) {
2520
- const current = await this.getHeadData(key, defaultValue);
2521
- const next = current + 1;
2522
- await this.setHeadData(key, next);
2523
- return current;
2571
+ return this.snapshotHead.data[key] ?? defaultValue;
2524
2572
  }
2525
2573
  };
2526
- var InMemoryStoreStrategyAsync = class extends SerializeStrategyAsync {
2527
- node;
2528
- constructor(order) {
2529
- super(order);
2530
- this.node = {};
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 = "";
2531
2590
  }
2532
- async id(isLeaf) {
2533
- return (await this.autoIncrement("index", 1)).toString();
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();
2534
2604
  }
2535
- async read(id) {
2536
- if (!Object.hasOwn(this.node, id)) {
2537
- throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
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;
2538
2641
  }
2539
- return this.node[id];
2540
2642
  }
2541
- async write(id, node) {
2542
- this.node[id] = node;
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;
2543
2656
  }
2544
- async delete(id) {
2545
- delete this.node[id];
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
+ }
2546
2735
  }
2547
- async readHead() {
2548
- if (this.head.root === null) {
2549
- return null;
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");
2550
2782
  }
2551
- return this.head;
2552
2783
  }
2553
- async writeHead(head) {
2554
- this.head = head;
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();
2555
2798
  }
2556
2799
  };
2557
2800
  export {
2558
2801
  BPTreeAsync,
2802
+ BPTreeAsyncTransaction,
2559
2803
  BPTreeSync,
2804
+ BPTreeSyncTransaction,
2560
2805
  InMemoryStoreStrategyAsync,
2561
2806
  InMemoryStoreStrategySync,
2562
2807
  NumericComparator,