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.
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var src_exports = {};
22
22
  __export(src_exports, {
23
23
  BPTreeAsync: () => BPTreeAsync,
24
+ BPTreeAsyncTransaction: () => BPTreeAsyncTransaction,
24
25
  BPTreeSync: () => BPTreeSync,
26
+ BPTreeSyncTransaction: () => BPTreeSyncTransaction,
25
27
  InMemoryStoreStrategyAsync: () => InMemoryStoreStrategyAsync,
26
28
  InMemoryStoreStrategySync: () => InMemoryStoreStrategySync,
27
29
  NumericComparator: () => NumericComparator,
@@ -472,7 +474,7 @@ var BPTree = class _BPTree {
472
474
  comparator;
473
475
  option;
474
476
  order;
475
- root;
477
+ rootId;
476
478
  _strategyDirty;
477
479
  _nodeCreateBuffer;
478
480
  _nodeUpdateBuffer;
@@ -726,19 +728,19 @@ var BPTree = class _BPTree {
726
728
  }
727
729
  }
728
730
  bufferForNodeCreate(node) {
729
- if (node.id === this.root.id) {
731
+ if (node.id === this.rootId) {
730
732
  this._strategyDirty = true;
731
733
  }
732
734
  this._nodeCreateBuffer.set(node.id, node);
733
735
  }
734
736
  bufferForNodeUpdate(node) {
735
- if (node.id === this.root.id) {
737
+ if (node.id === this.rootId) {
736
738
  this._strategyDirty = true;
737
739
  }
738
740
  this._nodeUpdateBuffer.set(node.id, node);
739
741
  }
740
742
  bufferForNodeDelete(node) {
741
- if (node.id === this.root.id) {
743
+ if (node.id === this.rootId) {
742
744
  this._strategyDirty = true;
743
745
  }
744
746
  this._nodeDeleteBuffer.set(node.id, node);
@@ -761,8 +763,8 @@ var BPTree = class _BPTree {
761
763
  }
762
764
  };
763
765
 
764
- // src/BPTreeSync.ts
765
- var BPTreeSync = class extends BPTree {
766
+ // src/base/BPTreeSyncBase.ts
767
+ var BPTreeSyncBase = class extends BPTree {
766
768
  constructor(strategy, comparator, option) {
767
769
  super(strategy, comparator, option);
768
770
  this.nodes = this._createCachedNode();
@@ -871,16 +873,18 @@ var BPTreeSync = class extends BPTree {
871
873
  }
872
874
  }
873
875
  }
874
- if (this.root.id === node.id && node.keys.length === 1) {
876
+ if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
875
877
  const keys = node.keys;
876
- this.bufferForNodeDelete(this.root);
877
- this.root = this.getNode(keys[0]);
878
- this.root.parent = null;
879
- this.strategy.head.root = this.root.id;
880
- this.bufferForNodeUpdate(this.root);
878
+ this.bufferForNodeDelete(node);
879
+ const newRoot = this.getNode(keys[0]);
880
+ this.rootId = newRoot.id;
881
+ newRoot.parent = null;
882
+ this.strategy.head.root = this.rootId;
883
+ this.bufferForNodeUpdate(newRoot);
881
884
  return;
882
- } else if (this.root.id === node.id) {
883
- this.bufferForNodeUpdate(this.root);
885
+ } else if (this.rootId === node.id) {
886
+ const root = this.getNode(this.rootId);
887
+ this.bufferForNodeUpdate(root);
884
888
  return;
885
889
  } else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
886
890
  if (node.parent === null) {
@@ -938,20 +942,11 @@ var BPTreeSync = class extends BPTree {
938
942
  pointer.values.push(guess);
939
943
  } else {
940
944
  pointer.next = node.next;
941
- pointer.prev = node.id;
942
945
  if (pointer.next) {
943
- const n = this.getNode(node.next);
946
+ const n = this.getNode(pointer.next);
944
947
  n.prev = pointer.id;
945
948
  this.bufferForNodeUpdate(n);
946
949
  }
947
- if (pointer.prev) {
948
- const n = this.getNode(node.id);
949
- n.next = pointer.id;
950
- this.bufferForNodeUpdate(n);
951
- }
952
- if (isPredecessor) {
953
- pointer.prev = null;
954
- }
955
950
  }
956
951
  pointer.values.push(...node.values);
957
952
  if (!pointer.leaf) {
@@ -1060,9 +1055,9 @@ var BPTreeSync = class extends BPTree {
1060
1055
  }
1061
1056
  }
1062
1057
  _insertInParent(node, value, pointer) {
1063
- if (this.root.id === node.id) {
1058
+ if (this.rootId === node.id) {
1064
1059
  const root = this._createNode(false, [node.id, pointer.id], [value]);
1065
- this.root = root;
1060
+ this.rootId = root.id;
1066
1061
  this.strategy.head.root = root.id;
1067
1062
  node.parent = root.id;
1068
1063
  pointer.parent = root.id;
@@ -1131,15 +1126,16 @@ var BPTreeSync = class extends BPTree {
1131
1126
  const head = this.strategy.readHead();
1132
1127
  if (head === null) {
1133
1128
  this.order = this.strategy.order;
1134
- this.root = this._createNode(true, [], [], true);
1135
- this.strategy.head.root = this.root.id;
1129
+ const root = this._createNode(true, [], [], true);
1130
+ this.rootId = root.id;
1131
+ this.strategy.head.root = this.rootId;
1136
1132
  this.commitHeadBuffer();
1137
1133
  this.commitNodeCreateBuffer();
1138
1134
  } else {
1139
1135
  const { root, order } = head;
1140
1136
  this.strategy.head = head;
1141
1137
  this.order = order;
1142
- this.root = this.getNode(root);
1138
+ this.rootId = root;
1143
1139
  }
1144
1140
  if (this.order < 3) {
1145
1141
  throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
@@ -1156,7 +1152,7 @@ var BPTreeSync = class extends BPTree {
1156
1152
  return cache.raw;
1157
1153
  }
1158
1154
  insertableNode(value) {
1159
- let node = this.getNode(this.root.id);
1155
+ let node = this.getNode(this.rootId);
1160
1156
  while (!node.leaf) {
1161
1157
  for (let i = 0, len = node.values.length; i < len; i++) {
1162
1158
  const nValue = node.values[i];
@@ -1175,12 +1171,8 @@ var BPTreeSync = class extends BPTree {
1175
1171
  }
1176
1172
  return node;
1177
1173
  }
1178
- /**
1179
- * Find the insertable node using primaryAsc comparison.
1180
- * This allows finding nodes by primary value only, ignoring unique identifiers.
1181
- */
1182
1174
  insertableNodeByPrimary(value) {
1183
- let node = this.getNode(this.root.id);
1175
+ let node = this.getNode(this.rootId);
1184
1176
  while (!node.leaf) {
1185
1177
  for (let i = 0, len = node.values.length; i < len; i++) {
1186
1178
  const nValue = node.values[i];
@@ -1200,7 +1192,7 @@ var BPTreeSync = class extends BPTree {
1200
1192
  return node;
1201
1193
  }
1202
1194
  insertableRightestNodeByPrimary(value) {
1203
- let node = this.getNode(this.root.id);
1195
+ let node = this.getNode(this.rootId);
1204
1196
  while (!node.leaf) {
1205
1197
  for (let i = 0, len = node.values.length; i < len; i++) {
1206
1198
  const nValue = node.values[i];
@@ -1244,7 +1236,7 @@ var BPTreeSync = class extends BPTree {
1244
1236
  return this.getNode(guessNode);
1245
1237
  }
1246
1238
  leftestNode() {
1247
- let node = this.root;
1239
+ let node = this.getNode(this.rootId);
1248
1240
  while (!node.leaf) {
1249
1241
  const keys = node.keys;
1250
1242
  node = this.getNode(keys[0]);
@@ -1252,19 +1244,49 @@ var BPTreeSync = class extends BPTree {
1252
1244
  return node;
1253
1245
  }
1254
1246
  rightestNode() {
1255
- let node = this.root;
1247
+ let node = this.getNode(this.rootId);
1256
1248
  while (!node.leaf) {
1257
1249
  const keys = node.keys;
1258
1250
  node = this.getNode(keys[keys.length - 1]);
1259
1251
  }
1260
1252
  return node;
1261
1253
  }
1254
+ exists(key, value) {
1255
+ const node = this.insertableNode(value);
1256
+ for (let i = 0, len = node.values.length; i < len; i++) {
1257
+ if (this.comparator.isSame(value, node.values[i])) {
1258
+ const keys = node.keys[i];
1259
+ if (keys.includes(key)) {
1260
+ return true;
1261
+ }
1262
+ }
1263
+ }
1264
+ return false;
1265
+ }
1266
+ forceUpdate(id) {
1267
+ if (id) {
1268
+ this.nodes.delete(id);
1269
+ this.getNode(id);
1270
+ return 1;
1271
+ }
1272
+ const keys = Array.from(this.nodes.keys());
1273
+ for (const key of keys) {
1274
+ this.nodes.delete(key);
1275
+ }
1276
+ for (const key of keys) {
1277
+ this.getNode(key);
1278
+ }
1279
+ return keys.length;
1280
+ }
1262
1281
  commitHeadBuffer() {
1263
1282
  if (!this._strategyDirty) {
1264
1283
  return;
1265
1284
  }
1266
1285
  this._strategyDirty = false;
1267
1286
  this.strategy.writeHead(this.strategy.head);
1287
+ if (this.strategy.head.root) {
1288
+ this.nodes.delete(this.strategy.head.root);
1289
+ }
1268
1290
  }
1269
1291
  commitNodeCreateBuffer() {
1270
1292
  for (const node of this._nodeCreateBuffer.values()) {
@@ -1275,33 +1297,25 @@ var BPTreeSync = class extends BPTree {
1275
1297
  commitNodeUpdateBuffer() {
1276
1298
  for (const node of this._nodeUpdateBuffer.values()) {
1277
1299
  this.strategy.write(node.id, node);
1300
+ this.nodes.delete(node.id);
1278
1301
  }
1279
1302
  this._nodeUpdateBuffer.clear();
1280
1303
  }
1281
1304
  commitNodeDeleteBuffer() {
1282
1305
  for (const node of this._nodeDeleteBuffer.values()) {
1283
1306
  this.strategy.delete(node.id);
1307
+ this.nodes.delete(node.id);
1284
1308
  }
1285
1309
  this._nodeDeleteBuffer.clear();
1286
1310
  }
1287
- /**
1288
- * Retrieves the value associated with the given key (PK).
1289
- * Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
1290
- *
1291
- * @param key The key to search for.
1292
- * @returns The value associated with the key, or undefined if not found.
1293
- */
1294
1311
  get(key) {
1295
1312
  let node = this.leftestNode();
1296
1313
  while (true) {
1297
- if (node.values) {
1298
- const len = node.values.length;
1299
- for (let i = 0; i < len; i++) {
1300
- const keys = node.keys[i];
1301
- for (let j = 0; j < keys.length; j++) {
1302
- if (keys[j] === key) {
1303
- return node.values[i];
1304
- }
1314
+ for (let i = 0, len = node.values.length; i < len; i++) {
1315
+ const keys = node.keys[i];
1316
+ for (let j = 0, kLen = keys.length; j < kLen; j++) {
1317
+ if (keys[j] === key) {
1318
+ return node.values[i];
1305
1319
  }
1306
1320
  }
1307
1321
  }
@@ -1420,21 +1434,15 @@ var BPTreeSync = class extends BPTree {
1420
1434
  const nValue = node.values[i];
1421
1435
  if (this.comparator.isSame(value, nValue)) {
1422
1436
  const keys = node.keys[i];
1423
- if (keys.includes(key)) {
1424
- if (keys.length > 1) {
1425
- keys.splice(keys.indexOf(key), 1);
1426
- this.bufferForNodeUpdate(node);
1427
- } else if (node.id === this.root.id) {
1428
- node.values.splice(i, 1);
1437
+ const keyIndex = keys.indexOf(key);
1438
+ if (keyIndex !== -1) {
1439
+ keys.splice(keyIndex, 1);
1440
+ if (keys.length === 0) {
1429
1441
  node.keys.splice(i, 1);
1430
- this.bufferForNodeUpdate(node);
1431
- } else {
1432
- keys.splice(keys.indexOf(key), 1);
1433
- node.keys.splice(i, 1);
1434
- node.values.splice(node.values.indexOf(value), 1);
1435
- this._deleteEntry(node, key, value);
1436
- this.bufferForNodeUpdate(node);
1442
+ node.values.splice(i, 1);
1437
1443
  }
1444
+ this._deleteEntry(node, key, value);
1445
+ break;
1438
1446
  }
1439
1447
  }
1440
1448
  }
@@ -1443,298 +1451,356 @@ var BPTreeSync = class extends BPTree {
1443
1451
  this.commitNodeUpdateBuffer();
1444
1452
  this.commitNodeDeleteBuffer();
1445
1453
  }
1446
- exists(key, value) {
1447
- const node = this.insertableNode(value);
1448
- for (let i = 0, len = node.values.length; i < len; i++) {
1449
- const nValue = node.values[i];
1450
- if (this.comparator.isSame(value, nValue)) {
1451
- const keys = node.keys[i];
1452
- return keys.includes(key);
1453
- }
1454
- }
1455
- return false;
1454
+ getHeadData() {
1455
+ return this.strategy.head.data;
1456
1456
  }
1457
1457
  setHeadData(data) {
1458
1458
  this.strategy.head.data = data;
1459
- this._strategyDirty = true;
1460
- this.commitHeadBuffer();
1459
+ this.strategy.writeHead(this.strategy.head);
1461
1460
  }
1462
- forceUpdate() {
1463
- const keys = [...this.nodes.keys()];
1464
- this.nodes.clear();
1465
- this.init();
1466
- for (const key of keys) {
1467
- this.getNode(key);
1468
- }
1469
- return keys.length;
1461
+ };
1462
+
1463
+ // src/base/SerializeStrategy.ts
1464
+ var SerializeStrategy = class {
1465
+ order;
1466
+ head;
1467
+ constructor(order) {
1468
+ this.order = order;
1469
+ this.head = {
1470
+ order,
1471
+ root: null,
1472
+ data: {}
1473
+ };
1470
1474
  }
1471
1475
  };
1472
1476
 
1473
- // node_modules/ryoiki/dist/esm/index.mjs
1474
- var Ryoiki = class _Ryoiki {
1475
- readings;
1476
- writings;
1477
- readQueue;
1478
- writeQueue;
1479
- static async CatchError(promise) {
1480
- return await promise.then((v) => [void 0, v]).catch((err) => [err]);
1481
- }
1482
- static IsRangeOverlap(a, b) {
1483
- const [start1, end1] = a;
1484
- const [start2, end2] = b;
1485
- if (end1 <= start2 || end2 <= start1) {
1477
+ // src/SerializeStrategySync.ts
1478
+ var SerializeStrategySync = class extends SerializeStrategy {
1479
+ getHeadData(key, defaultValue) {
1480
+ if (!Object.hasOwn(this.head.data, key)) {
1481
+ this.setHeadData(key, defaultValue);
1482
+ }
1483
+ return this.head.data[key];
1484
+ }
1485
+ setHeadData(key, data) {
1486
+ this.head.data[key] = data;
1487
+ this.writeHead(this.head);
1488
+ }
1489
+ autoIncrement(key, defaultValue) {
1490
+ const current = this.getHeadData(key, defaultValue);
1491
+ const next = current + 1;
1492
+ this.setHeadData(key, next);
1493
+ return current;
1494
+ }
1495
+ compareAndSwapHead(oldRoot, newRoot) {
1496
+ if (this.head.root !== oldRoot) {
1486
1497
  return false;
1487
1498
  }
1499
+ this.head.root = newRoot;
1500
+ this.writeHead(this.head);
1488
1501
  return true;
1489
1502
  }
1490
- static ERR_ALREADY_EXISTS(lockId) {
1491
- return new Error(`The '${lockId}' task already existing in queue or running.`);
1503
+ };
1504
+ var InMemoryStoreStrategySync = class extends SerializeStrategySync {
1505
+ node;
1506
+ constructor(order) {
1507
+ super(order);
1508
+ this.node = {};
1492
1509
  }
1493
- static ERR_NOT_EXISTS(lockId) {
1494
- return new Error(`The '${lockId}' task not existing in task queue.`);
1510
+ id(isLeaf) {
1511
+ return this.autoIncrement("index", 1).toString();
1495
1512
  }
1496
- static ERR_TIMEOUT(lockId, timeout) {
1497
- return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
1513
+ read(id) {
1514
+ if (!Object.hasOwn(this.node, id)) {
1515
+ throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
1516
+ }
1517
+ const node = this.node[id];
1518
+ return JSON.parse(JSON.stringify(node));
1498
1519
  }
1499
- /**
1500
- * Constructs a new instance of the Ryoiki class.
1501
- */
1502
- constructor() {
1503
- this.readings = /* @__PURE__ */ new Map();
1504
- this.writings = /* @__PURE__ */ new Map();
1505
- this.readQueue = /* @__PURE__ */ new Map();
1506
- this.writeQueue = /* @__PURE__ */ new Map();
1520
+ write(id, node) {
1521
+ this.node[id] = node;
1507
1522
  }
1508
- /**
1509
- * Creates a range based on a start value and length.
1510
- * @param start - The starting value of the range.
1511
- * @param length - The length of the range.
1512
- * @returns A range tuple [start, start + length].
1513
- */
1514
- range(start, length) {
1515
- return [start, start + length];
1523
+ delete(id) {
1524
+ delete this.node[id];
1516
1525
  }
1517
- rangeOverlapping(tasks, range) {
1518
- return Array.from(tasks.values()).some((t) => _Ryoiki.IsRangeOverlap(t.range, range));
1526
+ readHead() {
1527
+ if (this.head.root === null) {
1528
+ return null;
1529
+ }
1530
+ return this.head;
1519
1531
  }
1520
- isSameRange(a, b) {
1521
- const [a1, a2] = a;
1522
- const [b1, b2] = b;
1523
- return a1 === b1 && a2 === b2;
1532
+ writeHead(head) {
1533
+ this.head = head;
1524
1534
  }
1525
- fetchUnitAndRun(queue, workspaces) {
1526
- for (const [id, unit] of queue) {
1527
- if (!unit.condition()) {
1528
- continue;
1529
- }
1530
- this._alloc(queue, workspaces, id);
1531
- }
1535
+ };
1536
+
1537
+ // src/transaction/BPTreeSyncSnapshotStrategy.ts
1538
+ var BPTreeSyncSnapshotStrategy = class extends SerializeStrategySync {
1539
+ baseStrategy;
1540
+ snapshotHead;
1541
+ constructor(baseStrategy, root) {
1542
+ super(baseStrategy.order);
1543
+ this.baseStrategy = baseStrategy;
1544
+ this.snapshotHead = {
1545
+ ...baseStrategy.head,
1546
+ root,
1547
+ data: { ...baseStrategy.head.data }
1548
+ };
1549
+ this.head = this.snapshotHead;
1532
1550
  }
1533
- _handleOverload(args, handlers, argPatterns) {
1534
- for (const [key, pattern] of Object.entries(argPatterns)) {
1535
- if (this._matchArgs(args, pattern)) {
1536
- return handlers[key](...args);
1537
- }
1538
- }
1539
- throw new Error("Invalid arguments");
1551
+ id(isLeaf) {
1552
+ return this.baseStrategy.id(isLeaf);
1540
1553
  }
1541
- _matchArgs(args, pattern) {
1542
- return args.every((arg, index) => {
1543
- const expectedType = pattern[index];
1544
- if (expectedType === void 0) return typeof arg === "undefined";
1545
- if (expectedType === Function) return typeof arg === "function";
1546
- if (expectedType === Number) return typeof arg === "number";
1547
- if (expectedType === Array) return Array.isArray(arg);
1548
- return false;
1549
- });
1554
+ read(id) {
1555
+ return this.baseStrategy.read(id);
1550
1556
  }
1551
- _createRandomId() {
1552
- const timestamp = Date.now().toString(36);
1553
- const random = Math.random().toString(36).substring(2);
1554
- return `${timestamp}${random}`;
1555
- }
1556
- _alloc(queue, workspaces, lockId) {
1557
- const unit = queue.get(lockId);
1558
- if (!unit) {
1559
- throw _Ryoiki.ERR_NOT_EXISTS(lockId);
1560
- }
1561
- workspaces.set(lockId, unit);
1562
- queue.delete(lockId);
1563
- unit.alloc();
1564
- }
1565
- _free(workspaces, lockId) {
1566
- const unit = workspaces.get(lockId);
1567
- if (!unit) {
1568
- throw _Ryoiki.ERR_NOT_EXISTS(lockId);
1569
- }
1570
- workspaces.delete(lockId);
1571
- unit.free();
1572
- }
1573
- _lock(queue, range, timeout, task, condition) {
1574
- return new Promise((resolve, reject) => {
1575
- let timeoutId = null;
1576
- if (timeout >= 0) {
1577
- timeoutId = setTimeout(() => {
1578
- reject(_Ryoiki.ERR_TIMEOUT(id, timeout));
1579
- }, timeout);
1580
- }
1581
- const id = this._createRandomId();
1582
- const alloc = async () => {
1583
- if (timeoutId !== null) {
1584
- clearTimeout(timeoutId);
1585
- }
1586
- const [err, v] = await _Ryoiki.CatchError(task(id));
1587
- if (err) reject(err);
1588
- else resolve(v);
1589
- };
1590
- const fetch = () => {
1591
- this.fetchUnitAndRun(this.readQueue, this.readings);
1592
- this.fetchUnitAndRun(this.writeQueue, this.writings);
1593
- };
1594
- queue.set(id, { id, range, condition, alloc, free: fetch });
1595
- fetch();
1596
- });
1557
+ write(id, node) {
1558
+ this.baseStrategy.write(id, node);
1597
1559
  }
1598
- _checkWorking(range, workspaces) {
1599
- let isLocked = false;
1600
- for (const lock of workspaces.values()) {
1601
- if (_Ryoiki.IsRangeOverlap(range, lock.range)) {
1602
- isLocked = true;
1603
- break;
1604
- }
1605
- }
1606
- return isLocked;
1560
+ delete(id) {
1561
+ this.baseStrategy.delete(id);
1607
1562
  }
1608
- /**
1609
- * Checks if there is any active read lock within the specified range.
1610
- * @param range The range to check for active read locks.
1611
- * @returns `true` if there is an active read lock within the range, `false` otherwise.
1612
- */
1613
- isReading(range) {
1614
- return this._checkWorking(range, this.readings);
1563
+ readHead() {
1564
+ return this.snapshotHead;
1615
1565
  }
1616
- /**
1617
- * Checks if there is any active write lock within the specified range.
1618
- * @param range The range to check for active write locks.
1619
- * @returns `true` if there is an active write lock within the range, `false` otherwise.
1620
- */
1621
- isWriting(range) {
1622
- return this._checkWorking(range, this.writings);
1566
+ writeHead(head) {
1567
+ this.snapshotHead.root = head.root;
1568
+ this.snapshotHead.data = { ...head.data };
1623
1569
  }
1624
- /**
1625
- * Checks if a read lock can be acquired within the specified range.
1626
- * @param range The range to check for read lock availability.
1627
- * @returns `true` if a read lock can be acquired, `false` otherwise.
1628
- */
1629
- canRead(range) {
1630
- const writing = this.isWriting(range);
1631
- return !writing;
1570
+ compareAndSwapHead(oldRoot, newRoot) {
1571
+ return this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
1632
1572
  }
1633
- /**
1634
- * Checks if a write lock can be acquired within the specified range.
1635
- * @param range The range to check for write lock availability.
1636
- * @returns `true` if a write lock can be acquired, `false` otherwise.
1637
- */
1638
- canWrite(range) {
1639
- const reading = this.isReading(range);
1640
- const writing = this.isWriting(range);
1641
- return !reading && !writing;
1573
+ getHeadData(key, defaultValue) {
1574
+ return this.snapshotHead.data[key] ?? defaultValue;
1575
+ }
1576
+ setHeadData(key, data) {
1577
+ this.snapshotHead.data[key] = data;
1578
+ }
1579
+ autoIncrement(key, defaultValue) {
1580
+ return this.snapshotHead.data[key] ?? defaultValue;
1581
+ }
1582
+ };
1583
+
1584
+ // src/transaction/BPTreeSyncTransaction.ts
1585
+ var BPTreeSyncTransaction = class extends BPTreeSyncBase {
1586
+ realBaseTree;
1587
+ realBaseStrategy;
1588
+ txNodes = /* @__PURE__ */ new Map();
1589
+ dirtyIds = /* @__PURE__ */ new Set();
1590
+ createdInTx = /* @__PURE__ */ new Set();
1591
+ initialRootId;
1592
+ transactionRootId;
1593
+ constructor(baseTree) {
1594
+ super(baseTree.strategy, baseTree.comparator, baseTree.option);
1595
+ this.realBaseTree = baseTree;
1596
+ this.realBaseStrategy = baseTree.strategy;
1597
+ this.initialRootId = "";
1598
+ this.transactionRootId = "";
1642
1599
  }
1643
1600
  /**
1644
- * Internal implementation of the read lock. Handles both overloads.
1645
- * @template T - The return type of the task.
1646
- * @param arg0 - Either a range or a task callback.
1647
- * If a range is provided, the task is the second argument.
1648
- * @param arg1 - The task to execute, required if a range is provided.
1649
- * @param arg2 - The timeout for acquiring the lock.
1650
- * If the lock cannot be acquired within this period, an error will be thrown.
1651
- * If this value is not provided, no timeout will be set.
1652
- * @returns A promise resolving to the result of the task execution.
1601
+ * Initializes the transaction by capturing the current state of the tree.
1653
1602
  */
1654
- readLock(arg0, arg1, arg2) {
1655
- const [range, task, timeout] = this._handleOverload(
1656
- [arg0, arg1, arg2],
1657
- {
1658
- rangeTask: (range2, task2) => [range2, task2, -1],
1659
- rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
1660
- task: (task2) => [[-Infinity, Infinity], task2, -1],
1661
- taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
1662
- },
1663
- {
1664
- task: [Function],
1665
- taskTimeout: [Function, Number],
1666
- rangeTask: [Array, Function],
1667
- rangeTaskTimeout: [Array, Function, Number]
1603
+ initTransaction() {
1604
+ const head = this.realBaseStrategy.readHead();
1605
+ this.initialRootId = head?.root ?? this.realBaseTree.rootId;
1606
+ this.transactionRootId = this.initialRootId;
1607
+ this.rootId = this.transactionRootId;
1608
+ const snapshotStrategy = new BPTreeSyncSnapshotStrategy(this.realBaseStrategy, this.initialRootId);
1609
+ this.strategy = snapshotStrategy;
1610
+ this.txNodes.clear();
1611
+ this.dirtyIds.clear();
1612
+ this.createdInTx.clear();
1613
+ }
1614
+ getNode(id) {
1615
+ if (this.txNodes.has(id)) {
1616
+ return this.txNodes.get(id);
1617
+ }
1618
+ const baseNode = this.realBaseStrategy.read(id);
1619
+ const clone = JSON.parse(JSON.stringify(baseNode));
1620
+ this.txNodes.set(id, clone);
1621
+ return clone;
1622
+ }
1623
+ bufferForNodeUpdate(node) {
1624
+ this.txNodes.set(node.id, node);
1625
+ this.dirtyIds.add(node.id);
1626
+ this.markPathDirty(node);
1627
+ }
1628
+ bufferForNodeCreate(node) {
1629
+ this.txNodes.set(node.id, node);
1630
+ this.dirtyIds.add(node.id);
1631
+ this.createdInTx.add(node.id);
1632
+ this.markPathDirty(node);
1633
+ }
1634
+ bufferForNodeDelete(node) {
1635
+ this.txNodes.delete(node.id);
1636
+ this.dirtyIds.add(node.id);
1637
+ }
1638
+ markPathDirty(node) {
1639
+ let curr = node;
1640
+ while (curr.parent) {
1641
+ if (this.dirtyIds.has(curr.parent) && this.txNodes.has(curr.parent)) {
1642
+ break;
1668
1643
  }
1669
- );
1670
- return this._lock(
1671
- this.readQueue,
1672
- range,
1673
- timeout,
1674
- task,
1675
- () => !this.rangeOverlapping(this.writings, range)
1676
- );
1644
+ const parent = this.getNode(curr.parent);
1645
+ this.dirtyIds.add(parent.id);
1646
+ curr = parent;
1647
+ }
1648
+ if (!curr.parent) {
1649
+ this.transactionRootId = curr.id;
1650
+ }
1651
+ }
1652
+ _createNode(isLeaf, keys, values, leaf = false, parent = null, next = null, prev = null) {
1653
+ const id = this.realBaseStrategy.id(isLeaf);
1654
+ const node = {
1655
+ id,
1656
+ keys,
1657
+ values,
1658
+ leaf: isLeaf,
1659
+ parent,
1660
+ next,
1661
+ prev
1662
+ };
1663
+ this.bufferForNodeCreate(node);
1664
+ return node;
1677
1665
  }
1678
1666
  /**
1679
- * Internal implementation of the write lock. Handles both overloads.
1680
- * @template T - The return type of the task.
1681
- * @param arg0 - Either a range or a task callback.
1682
- * If a range is provided, the task is the second argument.
1683
- * @param arg1 - The task to execute, required if a range is provided.
1684
- * @param arg2 - The timeout for acquiring the lock.
1685
- * If the lock cannot be acquired within this period, an error will be thrown.
1686
- * If this value is not provided, no timeout will be set.
1687
- * @returns A promise resolving to the result of the task execution.
1667
+ * Attempts to commit the transaction.
1668
+ * Uses Optimistic Locking (Compare-And-Swap) on the root node ID to detect conflicts.
1669
+ *
1670
+ * @returns The transaction result.
1688
1671
  */
1689
- writeLock(arg0, arg1, arg2) {
1690
- const [range, task, timeout] = this._handleOverload(
1691
- [arg0, arg1, arg2],
1692
- {
1693
- rangeTask: (range2, task2) => [range2, task2, -1],
1694
- rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
1695
- task: (task2) => [[-Infinity, Infinity], task2, -1],
1696
- taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
1697
- },
1698
- {
1699
- task: [Function],
1700
- taskTimeout: [Function, Number],
1701
- rangeTask: [Array, Function],
1702
- rangeTaskTimeout: [Array, Function, Number]
1672
+ commit() {
1673
+ const idMapping = /* @__PURE__ */ new Map();
1674
+ const finalNodes = [];
1675
+ for (const oldId of this.dirtyIds) {
1676
+ if (this.createdInTx.has(oldId)) {
1677
+ idMapping.set(oldId, oldId);
1678
+ } else {
1679
+ const node = this.txNodes.get(oldId);
1680
+ if (node) {
1681
+ const newId = this.realBaseStrategy.id(node.leaf);
1682
+ idMapping.set(oldId, newId);
1683
+ }
1703
1684
  }
1704
- );
1705
- return this._lock(
1706
- this.writeQueue,
1707
- range,
1708
- timeout,
1709
- task,
1710
- () => {
1711
- return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
1685
+ }
1686
+ const newCreatedIds = [];
1687
+ for (const oldId of this.dirtyIds) {
1688
+ const node = this.txNodes.get(oldId);
1689
+ if (!node) continue;
1690
+ const newId = idMapping.get(oldId);
1691
+ node.id = newId;
1692
+ if (node.parent && idMapping.has(node.parent)) {
1693
+ node.parent = idMapping.get(node.parent);
1694
+ }
1695
+ if (!node.leaf) {
1696
+ const internal = node;
1697
+ for (let i = 0; i < internal.keys.length; i++) {
1698
+ const childId = internal.keys[i];
1699
+ if (idMapping.has(childId)) {
1700
+ internal.keys[i] = idMapping.get(childId);
1701
+ }
1702
+ }
1712
1703
  }
1713
- );
1704
+ if (node.leaf) {
1705
+ const leaf = node;
1706
+ if (leaf.next && idMapping.has(leaf.next)) {
1707
+ leaf.next = idMapping.get(leaf.next);
1708
+ }
1709
+ if (leaf.prev && idMapping.has(leaf.prev)) {
1710
+ leaf.prev = idMapping.get(leaf.prev);
1711
+ }
1712
+ }
1713
+ finalNodes.push(node);
1714
+ newCreatedIds.push(newId);
1715
+ }
1716
+ let newRootId = this.transactionRootId;
1717
+ if (idMapping.has(this.transactionRootId)) {
1718
+ newRootId = idMapping.get(this.transactionRootId);
1719
+ }
1720
+ for (const node of finalNodes) {
1721
+ this.realBaseStrategy.write(node.id, node);
1722
+ }
1723
+ const success = this.realBaseStrategy.compareAndSwapHead(this.initialRootId, newRootId);
1724
+ if (success) {
1725
+ const distinctObsolete = /* @__PURE__ */ new Set();
1726
+ for (const oldId of this.dirtyIds) {
1727
+ if (!this.createdInTx.has(oldId) && this.txNodes.has(oldId)) {
1728
+ distinctObsolete.add(oldId);
1729
+ }
1730
+ }
1731
+ return {
1732
+ success: true,
1733
+ createdIds: newCreatedIds,
1734
+ obsoleteIds: Array.from(distinctObsolete)
1735
+ };
1736
+ } else {
1737
+ this.rollback();
1738
+ return {
1739
+ success: false,
1740
+ createdIds: newCreatedIds,
1741
+ obsoleteIds: []
1742
+ };
1743
+ }
1714
1744
  }
1715
1745
  /**
1716
- * Releases a read lock by its lock ID.
1717
- * @param lockId - The unique identifier for the lock to release.
1746
+ * Rolls back the transaction by clearing all buffered changes.
1747
+ * Internal use only.
1718
1748
  */
1719
- readUnlock(lockId) {
1720
- this._free(this.readings, lockId);
1749
+ rollback() {
1750
+ this.txNodes.clear();
1751
+ this.dirtyIds.clear();
1752
+ this.createdInTx.clear();
1753
+ }
1754
+ // Override to do nothing, as transaction handles its own commits
1755
+ commitHeadBuffer() {
1756
+ }
1757
+ commitNodeCreateBuffer() {
1758
+ }
1759
+ commitNodeUpdateBuffer() {
1760
+ }
1761
+ commitNodeDeleteBuffer() {
1762
+ }
1763
+ };
1764
+
1765
+ // src/BPTreeSync.ts
1766
+ var BPTreeSync = class extends BPTreeSyncBase {
1767
+ constructor(strategy, comparator, option) {
1768
+ super(strategy, comparator, option);
1721
1769
  }
1722
1770
  /**
1723
- * Releases a write lock by its lock ID.
1724
- * @param lockId - The unique identifier for the lock to release.
1771
+ * Creates a new synchronous transaction.
1772
+ * @returns A new BPTreeSyncTransaction.
1725
1773
  */
1726
- writeUnlock(lockId) {
1727
- this._free(this.writings, lockId);
1774
+ createTransaction() {
1775
+ const tx = new BPTreeSyncTransaction(this);
1776
+ tx.initTransaction();
1777
+ return tx;
1778
+ }
1779
+ insert(key, value) {
1780
+ const tx = this.createTransaction();
1781
+ tx.insert(key, value);
1782
+ const { success } = tx.commit();
1783
+ this.init();
1784
+ if (!success) {
1785
+ throw new Error("Transaction failed: Commit failed due to conflict");
1786
+ }
1787
+ }
1788
+ delete(key, value) {
1789
+ const tx = this.createTransaction();
1790
+ tx.delete(key, value);
1791
+ const { success } = tx.commit();
1792
+ this.init();
1793
+ if (!success) {
1794
+ throw new Error("Transaction failed: Commit failed due to conflict");
1795
+ }
1728
1796
  }
1729
1797
  };
1730
1798
 
1731
- // src/BPTreeAsync.ts
1732
- var BPTreeAsync = class extends BPTree {
1733
- lock;
1799
+ // src/base/BPTreeAsyncBase.ts
1800
+ var BPTreeAsyncBase = class extends BPTree {
1734
1801
  constructor(strategy, comparator, option) {
1735
1802
  super(strategy, comparator, option);
1736
1803
  this.nodes = this._createCachedNode();
1737
- this.lock = new Ryoiki();
1738
1804
  }
1739
1805
  _createCachedNode() {
1740
1806
  return new CacheEntanglementAsync(async (key) => {
@@ -1743,24 +1809,6 @@ var BPTreeAsync = class extends BPTree {
1743
1809
  capacity: this.option.capacity ?? 1e3
1744
1810
  });
1745
1811
  }
1746
- async readLock(callback) {
1747
- let lockId;
1748
- return await this.lock.readLock(async (_lockId) => {
1749
- lockId = _lockId;
1750
- return await callback();
1751
- }).finally(() => {
1752
- this.lock.readUnlock(lockId);
1753
- });
1754
- }
1755
- async writeLock(callback) {
1756
- let lockId;
1757
- return await this.lock.writeLock(async (_lockId) => {
1758
- lockId = _lockId;
1759
- return await callback();
1760
- }).finally(() => {
1761
- this.lock.writeUnlock(lockId);
1762
- });
1763
- }
1764
1812
  async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
1765
1813
  let node = startNode;
1766
1814
  let done = false;
@@ -1858,16 +1906,18 @@ var BPTreeAsync = class extends BPTree {
1858
1906
  }
1859
1907
  }
1860
1908
  }
1861
- if (this.root.id === node.id && node.keys.length === 1) {
1909
+ if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
1862
1910
  const keys = node.keys;
1863
- this.bufferForNodeDelete(this.root);
1864
- this.root = await this.getNode(keys[0]);
1865
- this.root.parent = null;
1866
- this.strategy.head.root = this.root.id;
1867
- this.bufferForNodeUpdate(this.root);
1911
+ this.bufferForNodeDelete(node);
1912
+ const newRoot = await this.getNode(keys[0]);
1913
+ this.rootId = newRoot.id;
1914
+ newRoot.parent = null;
1915
+ this.strategy.head.root = this.rootId;
1916
+ this.bufferForNodeUpdate(newRoot);
1868
1917
  return;
1869
- } else if (this.root.id === node.id) {
1870
- this.bufferForNodeUpdate(this.root);
1918
+ } else if (this.rootId === node.id) {
1919
+ const root = await this.getNode(this.rootId);
1920
+ this.bufferForNodeUpdate(root);
1871
1921
  return;
1872
1922
  } else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
1873
1923
  if (node.parent === null) {
@@ -1925,28 +1975,19 @@ var BPTreeAsync = class extends BPTree {
1925
1975
  pointer.values.push(guess);
1926
1976
  } else {
1927
1977
  pointer.next = node.next;
1928
- pointer.prev = node.id;
1929
1978
  if (pointer.next) {
1930
- const n = await this.getNode(node.next);
1979
+ const n = await this.getNode(pointer.next);
1931
1980
  n.prev = pointer.id;
1932
1981
  this.bufferForNodeUpdate(n);
1933
1982
  }
1934
- if (pointer.prev) {
1935
- const n = await this.getNode(node.id);
1936
- n.next = pointer.id;
1937
- this.bufferForNodeUpdate(n);
1938
- }
1939
- if (isPredecessor) {
1940
- pointer.prev = null;
1941
- }
1942
1983
  }
1943
1984
  pointer.values.push(...node.values);
1944
1985
  if (!pointer.leaf) {
1945
1986
  const keys = pointer.keys;
1946
1987
  for (const key2 of keys) {
1947
- const node2 = await this.getNode(key2);
1948
- node2.parent = pointer.id;
1949
- this.bufferForNodeUpdate(node2);
1988
+ const n = await this.getNode(key2);
1989
+ n.parent = pointer.id;
1990
+ this.bufferForNodeUpdate(n);
1950
1991
  }
1951
1992
  }
1952
1993
  await this._deleteEntry(await this.getNode(node.parent), node.id, guess);
@@ -2023,21 +2064,24 @@ var BPTreeAsync = class extends BPTree {
2023
2064
  this.bufferForNodeUpdate(pointer);
2024
2065
  }
2025
2066
  if (!pointer.leaf) {
2026
- for (const key2 of pointer.keys) {
2067
+ const keys = pointer.keys;
2068
+ for (const key2 of keys) {
2027
2069
  const n = await this.getNode(key2);
2028
2070
  n.parent = pointer.id;
2029
2071
  this.bufferForNodeUpdate(n);
2030
2072
  }
2031
2073
  }
2032
2074
  if (!node.leaf) {
2033
- for (const key2 of node.keys) {
2075
+ const keys = node.keys;
2076
+ for (const key2 of keys) {
2034
2077
  const n = await this.getNode(key2);
2035
2078
  n.parent = node.id;
2036
2079
  this.bufferForNodeUpdate(n);
2037
2080
  }
2038
2081
  }
2039
2082
  if (!parentNode.leaf) {
2040
- for (const key2 of parentNode.keys) {
2083
+ const keys = parentNode.keys;
2084
+ for (const key2 of keys) {
2041
2085
  const n = await this.getNode(key2);
2042
2086
  n.parent = parentNode.id;
2043
2087
  this.bufferForNodeUpdate(n);
@@ -2047,9 +2091,9 @@ var BPTreeAsync = class extends BPTree {
2047
2091
  }
2048
2092
  }
2049
2093
  async _insertInParent(node, value, pointer) {
2050
- if (this.root.id === node.id) {
2094
+ if (this.rootId === node.id) {
2051
2095
  const root = await this._createNode(false, [node.id, pointer.id], [value]);
2052
- this.root = root;
2096
+ this.rootId = root.id;
2053
2097
  this.strategy.head.root = root.id;
2054
2098
  node.parent = root.id;
2055
2099
  pointer.parent = root.id;
@@ -2101,14 +2145,14 @@ var BPTreeAsync = class extends BPTree {
2101
2145
  parentNode.values = parentNode.values.slice(0, mid);
2102
2146
  parentNode.keys = parentNode.keys.slice(0, mid + 1);
2103
2147
  for (const k of parentNode.keys) {
2104
- const node2 = await this.getNode(k);
2105
- node2.parent = parentNode.id;
2106
- this.bufferForNodeUpdate(node2);
2148
+ const n = await this.getNode(k);
2149
+ n.parent = parentNode.id;
2150
+ this.bufferForNodeUpdate(n);
2107
2151
  }
2108
2152
  for (const k of parentPointer.keys) {
2109
- const node2 = await this.getNode(k);
2110
- node2.parent = parentPointer.id;
2111
- this.bufferForNodeUpdate(node2);
2153
+ const n = await this.getNode(k);
2154
+ n.parent = parentPointer.id;
2155
+ this.bufferForNodeUpdate(n);
2112
2156
  }
2113
2157
  await this._insertInParent(parentNode, midValue, parentPointer);
2114
2158
  this.bufferForNodeUpdate(parentNode);
@@ -2118,15 +2162,16 @@ var BPTreeAsync = class extends BPTree {
2118
2162
  const head = await this.strategy.readHead();
2119
2163
  if (head === null) {
2120
2164
  this.order = this.strategy.order;
2121
- this.root = await this._createNode(true, [], [], true);
2122
- this.strategy.head.root = this.root.id;
2165
+ const root = await this._createNode(true, [], [], true);
2166
+ this.rootId = root.id;
2167
+ this.strategy.head.root = this.rootId;
2123
2168
  await this.commitHeadBuffer();
2124
2169
  await this.commitNodeCreateBuffer();
2125
2170
  } else {
2126
2171
  const { root, order } = head;
2127
2172
  this.strategy.head = head;
2128
2173
  this.order = order;
2129
- this.root = await this.getNode(root);
2174
+ this.rootId = root;
2130
2175
  }
2131
2176
  if (this.order < 3) {
2132
2177
  throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
@@ -2143,43 +2188,39 @@ var BPTreeAsync = class extends BPTree {
2143
2188
  return cache.raw;
2144
2189
  }
2145
2190
  async insertableNode(value) {
2146
- let node = await this.getNode(this.root.id);
2191
+ let node = await this.getNode(this.rootId);
2147
2192
  while (!node.leaf) {
2148
2193
  for (let i = 0, len = node.values.length; i < len; i++) {
2149
2194
  const nValue = node.values[i];
2150
2195
  const k = node.keys;
2151
2196
  if (this.comparator.isSame(value, nValue)) {
2152
- node = await this.getNode(k[i + 1]);
2197
+ node = await this.getNode(node.keys[i + 1]);
2153
2198
  break;
2154
2199
  } else if (this.comparator.isLower(value, nValue)) {
2155
- node = await this.getNode(k[i]);
2200
+ node = await this.getNode(node.keys[i]);
2156
2201
  break;
2157
2202
  } else if (i + 1 === node.values.length) {
2158
- node = await this.getNode(k[i + 1]);
2203
+ node = await this.getNode(node.keys[i + 1]);
2159
2204
  break;
2160
2205
  }
2161
2206
  }
2162
2207
  }
2163
2208
  return node;
2164
2209
  }
2165
- /**
2166
- * Find the insertable node using primaryAsc comparison.
2167
- * This allows finding nodes by primary value only, ignoring unique identifiers.
2168
- */
2169
2210
  async insertableNodeByPrimary(value) {
2170
- let node = await this.getNode(this.root.id);
2211
+ let node = await this.getNode(this.rootId);
2171
2212
  while (!node.leaf) {
2172
2213
  for (let i = 0, len = node.values.length; i < len; i++) {
2173
2214
  const nValue = node.values[i];
2174
2215
  const k = node.keys;
2175
2216
  if (this.comparator.isPrimarySame(value, nValue)) {
2176
- node = await this.getNode(k[i]);
2217
+ node = await this.getNode(node.keys[i]);
2177
2218
  break;
2178
2219
  } else if (this.comparator.isPrimaryLower(value, nValue)) {
2179
- node = await this.getNode(k[i]);
2220
+ node = await this.getNode(node.keys[i]);
2180
2221
  break;
2181
2222
  } else if (i + 1 === node.values.length) {
2182
- node = await this.getNode(k[i + 1]);
2223
+ node = await this.getNode(node.keys[i + 1]);
2183
2224
  break;
2184
2225
  }
2185
2226
  }
@@ -2187,17 +2228,17 @@ var BPTreeAsync = class extends BPTree {
2187
2228
  return node;
2188
2229
  }
2189
2230
  async insertableRightestNodeByPrimary(value) {
2190
- let node = await this.getNode(this.root.id);
2231
+ let node = await this.getNode(this.rootId);
2191
2232
  while (!node.leaf) {
2192
2233
  for (let i = 0, len = node.values.length; i < len; i++) {
2193
2234
  const nValue = node.values[i];
2194
2235
  const k = node.keys;
2195
2236
  if (this.comparator.isPrimaryLower(value, nValue)) {
2196
- node = await this.getNode(k[i]);
2237
+ node = await this.getNode(node.keys[i]);
2197
2238
  break;
2198
2239
  }
2199
2240
  if (i + 1 === node.values.length) {
2200
- node = await this.getNode(k[i + 1]);
2241
+ node = await this.getNode(node.keys[i + 1]);
2201
2242
  break;
2202
2243
  }
2203
2244
  }
@@ -2231,27 +2272,57 @@ var BPTreeAsync = class extends BPTree {
2231
2272
  return await this.getNode(guessNode);
2232
2273
  }
2233
2274
  async leftestNode() {
2234
- let node = this.root;
2275
+ let node = await this.getNode(this.rootId);
2235
2276
  while (!node.leaf) {
2236
2277
  const keys = node.keys;
2237
- node = await this.getNode(keys[0]);
2278
+ node = await this.getNode(node.keys[0]);
2238
2279
  }
2239
2280
  return node;
2240
2281
  }
2241
2282
  async rightestNode() {
2242
- let node = this.root;
2283
+ let node = await this.getNode(this.rootId);
2243
2284
  while (!node.leaf) {
2244
2285
  const keys = node.keys;
2245
- node = await this.getNode(keys[keys.length - 1]);
2286
+ node = await this.getNode(node.keys[node.keys.length - 1]);
2246
2287
  }
2247
2288
  return node;
2248
2289
  }
2290
+ async exists(key, value) {
2291
+ const node = await this.insertableNode(value);
2292
+ for (let i = 0, len = node.values.length; i < len; i++) {
2293
+ if (this.comparator.isSame(value, node.values[i])) {
2294
+ const keys = node.keys[i];
2295
+ if (keys.includes(key)) {
2296
+ return true;
2297
+ }
2298
+ }
2299
+ }
2300
+ return false;
2301
+ }
2302
+ async forceUpdate(id) {
2303
+ if (id) {
2304
+ this.nodes.delete(id);
2305
+ await this.getNode(id);
2306
+ return 1;
2307
+ }
2308
+ const keys = Array.from(this.nodes.keys());
2309
+ for (const key of keys) {
2310
+ this.nodes.delete(key);
2311
+ }
2312
+ for (const key of keys) {
2313
+ await this.getNode(key);
2314
+ }
2315
+ return keys.length;
2316
+ }
2249
2317
  async commitHeadBuffer() {
2250
2318
  if (!this._strategyDirty) {
2251
2319
  return;
2252
2320
  }
2253
2321
  this._strategyDirty = false;
2254
2322
  await this.strategy.writeHead(this.strategy.head);
2323
+ if (this.strategy.head.root) {
2324
+ this.nodes.delete(this.strategy.head.root);
2325
+ }
2255
2326
  }
2256
2327
  async commitNodeCreateBuffer() {
2257
2328
  for (const node of this._nodeCreateBuffer.values()) {
@@ -2262,34 +2333,26 @@ var BPTreeAsync = class extends BPTree {
2262
2333
  async commitNodeUpdateBuffer() {
2263
2334
  for (const node of this._nodeUpdateBuffer.values()) {
2264
2335
  await this.strategy.write(node.id, node);
2336
+ this.nodes.delete(node.id);
2265
2337
  }
2266
2338
  this._nodeUpdateBuffer.clear();
2267
2339
  }
2268
2340
  async commitNodeDeleteBuffer() {
2269
2341
  for (const node of this._nodeDeleteBuffer.values()) {
2270
2342
  await this.strategy.delete(node.id);
2343
+ this.nodes.delete(node.id);
2271
2344
  }
2272
2345
  this._nodeDeleteBuffer.clear();
2273
2346
  }
2274
- /**
2275
- * Retrieves the value associated with the given key (PK).
2276
- * Note: This method performs a full scan of leaf nodes as the tree is ordered by Value, not Key.
2277
- *
2278
- * @param key The key to search for.
2279
- * @returns The value associated with the key, or undefined if not found.
2280
- */
2281
2347
  async get(key) {
2282
- return this.readLock(async () => {
2348
+ return await this.readLock(async () => {
2283
2349
  let node = await this.leftestNode();
2284
2350
  while (true) {
2285
- if (node.values) {
2286
- const len = node.values.length;
2287
- for (let i = 0; i < len; i++) {
2288
- const keys = node.keys[i];
2289
- for (let j = 0; j < keys.length; j++) {
2290
- if (keys[j] === key) {
2291
- return node.values[i];
2292
- }
2351
+ for (let i = 0, len = node.values.length; i < len; i++) {
2352
+ const keys = node.keys[i];
2353
+ for (let j = 0, kLen = keys.length; j < kLen; j++) {
2354
+ if (keys[j] === key) {
2355
+ return node.values[i];
2293
2356
  }
2294
2357
  }
2295
2358
  }
@@ -2364,25 +2427,21 @@ var BPTreeAsync = class extends BPTree {
2364
2427
  }
2365
2428
  }
2366
2429
  async keys(condition, filterValues) {
2367
- return this.readLock(async () => {
2368
- const set = /* @__PURE__ */ new Set();
2369
- for await (const key of this.keysStream(condition, filterValues)) {
2370
- set.add(key);
2371
- }
2372
- return set;
2373
- });
2430
+ const set = /* @__PURE__ */ new Set();
2431
+ for await (const key of this.keysStream(condition, filterValues)) {
2432
+ set.add(key);
2433
+ }
2434
+ return set;
2374
2435
  }
2375
2436
  async where(condition) {
2376
- return this.readLock(async () => {
2377
- const map = /* @__PURE__ */ new Map();
2378
- for await (const [key, value] of this.whereStream(condition)) {
2379
- map.set(key, value);
2380
- }
2381
- return map;
2382
- });
2437
+ const map = /* @__PURE__ */ new Map();
2438
+ for await (const [key, value] of this.whereStream(condition)) {
2439
+ map.set(key, value);
2440
+ }
2441
+ return map;
2383
2442
  }
2384
2443
  async insert(key, value) {
2385
- return this.writeLock(async () => {
2444
+ await this.writeLock(async () => {
2386
2445
  const before = await this.insertableNode(value);
2387
2446
  this._insertAtLeaf(before, key, value);
2388
2447
  if (before.values.length === this.order) {
@@ -2409,182 +2468,368 @@ var BPTreeAsync = class extends BPTree {
2409
2468
  });
2410
2469
  }
2411
2470
  async delete(key, value) {
2412
- return this.writeLock(async () => {
2471
+ await this.writeLock(async () => {
2413
2472
  const node = await this.insertableNode(value);
2414
2473
  let i = node.values.length;
2415
2474
  while (i--) {
2416
2475
  const nValue = node.values[i];
2417
2476
  if (this.comparator.isSame(value, nValue)) {
2418
2477
  const keys = node.keys[i];
2419
- if (keys.includes(key)) {
2420
- if (keys.length > 1) {
2421
- keys.splice(keys.indexOf(key), 1);
2422
- this.bufferForNodeUpdate(node);
2423
- } else if (node.id === this.root.id) {
2424
- node.values.splice(i, 1);
2425
- node.keys.splice(i, 1);
2426
- this.bufferForNodeUpdate(node);
2427
- } else {
2428
- keys.splice(keys.indexOf(key), 1);
2478
+ const keyIndex = keys.indexOf(key);
2479
+ if (keyIndex !== -1) {
2480
+ keys.splice(keyIndex, 1);
2481
+ if (keys.length === 0) {
2429
2482
  node.keys.splice(i, 1);
2430
- node.values.splice(node.values.indexOf(value), 1);
2431
- await this._deleteEntry(node, key, value);
2432
- this.bufferForNodeUpdate(node);
2483
+ node.values.splice(i, 1);
2433
2484
  }
2485
+ await this._deleteEntry(node, key, value);
2486
+ break;
2434
2487
  }
2435
2488
  }
2436
2489
  }
2437
2490
  await this.commitHeadBuffer();
2438
- await this.commitNodeCreateBuffer();
2439
2491
  await this.commitNodeUpdateBuffer();
2440
2492
  await this.commitNodeDeleteBuffer();
2441
2493
  });
2442
2494
  }
2443
- async exists(key, value) {
2444
- return this.readLock(async () => {
2445
- const node = await this.insertableNode(value);
2446
- for (let i = 0, len = node.values.length; i < len; i++) {
2447
- const nValue = node.values[i];
2448
- if (this.comparator.isSame(value, nValue)) {
2449
- const keys = node.keys[i];
2450
- return keys.includes(key);
2451
- }
2452
- }
2453
- return false;
2454
- });
2495
+ getHeadData() {
2496
+ return this.strategy.head.data;
2455
2497
  }
2456
2498
  async setHeadData(data) {
2457
- return this.writeLock(async () => {
2458
- this.strategy.head.data = data;
2459
- this._strategyDirty = true;
2460
- await this.commitHeadBuffer();
2461
- });
2462
- }
2463
- async forceUpdate() {
2464
- return this.readLock(async () => {
2465
- const keys = [...this.nodes.keys()];
2466
- this.nodes.clear();
2467
- await this.init();
2468
- for (const key of keys) {
2469
- await this.getNode(key);
2470
- }
2471
- return keys.length;
2472
- });
2473
- }
2474
- };
2475
-
2476
- // src/base/SerializeStrategy.ts
2477
- var SerializeStrategy = class {
2478
- order;
2479
- head;
2480
- constructor(order) {
2481
- this.order = order;
2482
- this.head = {
2483
- order,
2484
- root: null,
2485
- data: {}
2486
- };
2499
+ this.strategy.head.data = data;
2500
+ await this.strategy.writeHead(this.strategy.head);
2487
2501
  }
2488
2502
  };
2489
2503
 
2490
- // src/SerializeStrategySync.ts
2491
- var SerializeStrategySync = class extends SerializeStrategy {
2492
- getHeadData(key, defaultValue) {
2504
+ // src/SerializeStrategyAsync.ts
2505
+ var SerializeStrategyAsync = class extends SerializeStrategy {
2506
+ async getHeadData(key, defaultValue) {
2493
2507
  if (!Object.hasOwn(this.head.data, key)) {
2494
- this.setHeadData(key, defaultValue);
2508
+ await this.setHeadData(key, defaultValue);
2495
2509
  }
2496
2510
  return this.head.data[key];
2497
2511
  }
2498
- setHeadData(key, data) {
2512
+ async setHeadData(key, data) {
2499
2513
  this.head.data[key] = data;
2500
- this.writeHead(this.head);
2514
+ await this.writeHead(this.head);
2501
2515
  }
2502
- autoIncrement(key, defaultValue) {
2503
- const current = this.getHeadData(key, defaultValue);
2516
+ async autoIncrement(key, defaultValue) {
2517
+ const current = await this.getHeadData(key, defaultValue);
2504
2518
  const next = current + 1;
2505
- this.setHeadData(key, next);
2519
+ await this.setHeadData(key, next);
2506
2520
  return current;
2507
2521
  }
2522
+ async compareAndSwapHead(oldRoot, newRoot) {
2523
+ if (this.head.root !== oldRoot) {
2524
+ return false;
2525
+ }
2526
+ this.head.root = newRoot;
2527
+ await this.writeHead(this.head);
2528
+ return true;
2529
+ }
2508
2530
  };
2509
- var InMemoryStoreStrategySync = class extends SerializeStrategySync {
2531
+ var InMemoryStoreStrategyAsync = class extends SerializeStrategyAsync {
2510
2532
  node;
2511
2533
  constructor(order) {
2512
2534
  super(order);
2513
2535
  this.node = {};
2514
2536
  }
2515
- id(isLeaf) {
2516
- return this.autoIncrement("index", 1).toString();
2537
+ async id(isLeaf) {
2538
+ return (await this.autoIncrement("index", 1)).toString();
2517
2539
  }
2518
- read(id) {
2540
+ async read(id) {
2519
2541
  if (!Object.hasOwn(this.node, id)) {
2520
2542
  throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
2521
2543
  }
2522
- return this.node[id];
2544
+ const node = this.node[id];
2545
+ return JSON.parse(JSON.stringify(node));
2523
2546
  }
2524
- write(id, node) {
2547
+ async write(id, node) {
2525
2548
  this.node[id] = node;
2526
2549
  }
2527
- delete(id) {
2550
+ async delete(id) {
2528
2551
  delete this.node[id];
2529
2552
  }
2530
- readHead() {
2553
+ async readHead() {
2531
2554
  if (this.head.root === null) {
2532
2555
  return null;
2533
2556
  }
2534
2557
  return this.head;
2535
2558
  }
2536
- writeHead(head) {
2559
+ async writeHead(head) {
2537
2560
  this.head = head;
2538
2561
  }
2539
2562
  };
2540
2563
 
2541
- // src/SerializeStrategyAsync.ts
2542
- var SerializeStrategyAsync = class extends SerializeStrategy {
2564
+ // src/transaction/BPTreeAsyncSnapshotStrategy.ts
2565
+ var BPTreeAsyncSnapshotStrategy = class extends SerializeStrategyAsync {
2566
+ baseStrategy;
2567
+ snapshotHead;
2568
+ constructor(baseStrategy, root) {
2569
+ super(baseStrategy.order);
2570
+ this.baseStrategy = baseStrategy;
2571
+ this.snapshotHead = {
2572
+ ...baseStrategy.head,
2573
+ root,
2574
+ data: { ...baseStrategy.head.data }
2575
+ };
2576
+ this.head = this.snapshotHead;
2577
+ }
2578
+ async id(isLeaf) {
2579
+ return await this.baseStrategy.id(isLeaf);
2580
+ }
2581
+ async read(id) {
2582
+ return await this.baseStrategy.read(id);
2583
+ }
2584
+ async write(id, node) {
2585
+ await this.baseStrategy.write(id, node);
2586
+ }
2587
+ async delete(id) {
2588
+ await this.baseStrategy.delete(id);
2589
+ }
2590
+ async readHead() {
2591
+ return this.snapshotHead;
2592
+ }
2593
+ async writeHead(head) {
2594
+ this.snapshotHead.root = head.root;
2595
+ this.snapshotHead.data = { ...head.data };
2596
+ }
2597
+ async compareAndSwapHead(oldRoot, newRoot) {
2598
+ return await this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
2599
+ }
2543
2600
  async getHeadData(key, defaultValue) {
2544
- if (!Object.hasOwn(this.head.data, key)) {
2545
- await this.setHeadData(key, defaultValue);
2546
- }
2547
- return this.head.data[key];
2601
+ return this.snapshotHead.data[key] ?? defaultValue;
2548
2602
  }
2549
2603
  async setHeadData(key, data) {
2550
- this.head.data[key] = data;
2551
- await this.writeHead(this.head);
2604
+ this.snapshotHead.data[key] = data;
2552
2605
  }
2553
2606
  async autoIncrement(key, defaultValue) {
2554
- const current = await this.getHeadData(key, defaultValue);
2555
- const next = current + 1;
2556
- await this.setHeadData(key, next);
2557
- return current;
2607
+ return this.snapshotHead.data[key] ?? defaultValue;
2558
2608
  }
2559
2609
  };
2560
- var InMemoryStoreStrategyAsync = class extends SerializeStrategyAsync {
2561
- node;
2562
- constructor(order) {
2563
- super(order);
2564
- this.node = {};
2610
+
2611
+ // src/transaction/BPTreeAsyncTransaction.ts
2612
+ var BPTreeAsyncTransaction = class extends BPTreeAsyncBase {
2613
+ realBaseTree;
2614
+ realBaseStrategy;
2615
+ txNodes = /* @__PURE__ */ new Map();
2616
+ dirtyIds = /* @__PURE__ */ new Set();
2617
+ createdInTx = /* @__PURE__ */ new Set();
2618
+ initialRootId;
2619
+ transactionRootId;
2620
+ constructor(baseTree) {
2621
+ super(baseTree.strategy, baseTree.comparator, baseTree.option);
2622
+ this.realBaseTree = baseTree;
2623
+ this.realBaseStrategy = baseTree.strategy;
2624
+ this.initialRootId = "";
2625
+ this.transactionRootId = "";
2565
2626
  }
2566
- async id(isLeaf) {
2567
- return (await this.autoIncrement("index", 1)).toString();
2627
+ /**
2628
+ * Initializes the transaction by capturing the current state of the tree.
2629
+ */
2630
+ async initTransaction() {
2631
+ const head = await this.realBaseStrategy.readHead();
2632
+ this.initialRootId = head?.root ?? this.realBaseTree.rootId;
2633
+ this.transactionRootId = this.initialRootId;
2634
+ this.rootId = this.transactionRootId;
2635
+ const snapshotStrategy = new BPTreeAsyncSnapshotStrategy(this.realBaseStrategy, this.initialRootId);
2636
+ this.strategy = snapshotStrategy;
2637
+ this.txNodes.clear();
2638
+ this.dirtyIds.clear();
2639
+ this.createdInTx.clear();
2568
2640
  }
2569
- async read(id) {
2570
- if (!Object.hasOwn(this.node, id)) {
2571
- throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
2641
+ async getNode(id) {
2642
+ if (this.txNodes.has(id)) {
2643
+ return this.txNodes.get(id);
2644
+ }
2645
+ const baseNode = await this.realBaseStrategy.read(id);
2646
+ const clone = JSON.parse(JSON.stringify(baseNode));
2647
+ this.txNodes.set(id, clone);
2648
+ return clone;
2649
+ }
2650
+ async bufferForNodeUpdate(node) {
2651
+ this.txNodes.set(node.id, node);
2652
+ this.dirtyIds.add(node.id);
2653
+ await this.markPathDirty(node);
2654
+ }
2655
+ async bufferForNodeCreate(node) {
2656
+ this.txNodes.set(node.id, node);
2657
+ this.dirtyIds.add(node.id);
2658
+ this.createdInTx.add(node.id);
2659
+ await this.markPathDirty(node);
2660
+ }
2661
+ async bufferForNodeDelete(node) {
2662
+ this.txNodes.delete(node.id);
2663
+ this.dirtyIds.add(node.id);
2664
+ }
2665
+ async markPathDirty(node) {
2666
+ let curr = node;
2667
+ while (curr.parent) {
2668
+ if (this.dirtyIds.has(curr.parent) && this.txNodes.has(curr.parent)) {
2669
+ break;
2670
+ }
2671
+ const parent = await this.getNode(curr.parent);
2672
+ this.dirtyIds.add(parent.id);
2673
+ curr = parent;
2674
+ }
2675
+ if (!curr.parent) {
2676
+ this.transactionRootId = curr.id;
2572
2677
  }
2573
- return this.node[id];
2574
2678
  }
2575
- async write(id, node) {
2576
- this.node[id] = node;
2679
+ async _createNode(isLeaf, keys, values, leaf = false, parent = null, next = null, prev = null) {
2680
+ const id = await this.realBaseStrategy.id(isLeaf);
2681
+ const node = {
2682
+ id,
2683
+ keys,
2684
+ values,
2685
+ leaf: isLeaf,
2686
+ parent,
2687
+ next,
2688
+ prev
2689
+ };
2690
+ await this.bufferForNodeCreate(node);
2691
+ return node;
2577
2692
  }
2578
- async delete(id) {
2579
- delete this.node[id];
2693
+ /**
2694
+ * Attempts to commit the transaction.
2695
+ * Uses Optimistic Locking (Compare-And-Swap) on the root node ID to detect conflicts.
2696
+ *
2697
+ * @returns A promise that resolves to the transaction result.
2698
+ */
2699
+ async commit() {
2700
+ const idMapping = /* @__PURE__ */ new Map();
2701
+ const finalNodes = [];
2702
+ for (const oldId of this.dirtyIds) {
2703
+ if (this.createdInTx.has(oldId)) {
2704
+ idMapping.set(oldId, oldId);
2705
+ } else {
2706
+ const node = this.txNodes.get(oldId);
2707
+ if (node) {
2708
+ const newId = await this.realBaseStrategy.id(node.leaf);
2709
+ idMapping.set(oldId, newId);
2710
+ }
2711
+ }
2712
+ }
2713
+ const newCreatedIds = [];
2714
+ for (const oldId of this.dirtyIds) {
2715
+ const node = this.txNodes.get(oldId);
2716
+ if (!node) continue;
2717
+ const newId = idMapping.get(oldId);
2718
+ node.id = newId;
2719
+ if (node.parent && idMapping.has(node.parent)) {
2720
+ node.parent = idMapping.get(node.parent);
2721
+ }
2722
+ if (!node.leaf) {
2723
+ const internal = node;
2724
+ for (let i = 0; i < internal.keys.length; i++) {
2725
+ const childId = internal.keys[i];
2726
+ if (idMapping.has(childId)) {
2727
+ internal.keys[i] = idMapping.get(childId);
2728
+ }
2729
+ }
2730
+ }
2731
+ if (node.leaf) {
2732
+ const leaf = node;
2733
+ if (leaf.next && idMapping.has(leaf.next)) {
2734
+ leaf.next = idMapping.get(leaf.next);
2735
+ }
2736
+ if (leaf.prev && idMapping.has(leaf.prev)) {
2737
+ leaf.prev = idMapping.get(leaf.prev);
2738
+ }
2739
+ }
2740
+ finalNodes.push(node);
2741
+ newCreatedIds.push(newId);
2742
+ }
2743
+ let newRootId = this.transactionRootId;
2744
+ if (idMapping.has(this.transactionRootId)) {
2745
+ newRootId = idMapping.get(this.transactionRootId);
2746
+ }
2747
+ for (const node of finalNodes) {
2748
+ await this.realBaseStrategy.write(node.id, node);
2749
+ }
2750
+ const success = await this.realBaseStrategy.compareAndSwapHead(this.initialRootId, newRootId);
2751
+ if (success) {
2752
+ const distinctObsolete = /* @__PURE__ */ new Set();
2753
+ for (const oldId of this.dirtyIds) {
2754
+ if (!this.createdInTx.has(oldId) && this.txNodes.has(oldId)) {
2755
+ distinctObsolete.add(oldId);
2756
+ }
2757
+ }
2758
+ return {
2759
+ success: true,
2760
+ createdIds: newCreatedIds,
2761
+ obsoleteIds: Array.from(distinctObsolete)
2762
+ };
2763
+ } else {
2764
+ await this.rollback();
2765
+ return {
2766
+ success: false,
2767
+ createdIds: newCreatedIds,
2768
+ obsoleteIds: []
2769
+ };
2770
+ }
2580
2771
  }
2581
- async readHead() {
2582
- if (this.head.root === null) {
2583
- return null;
2772
+ /**
2773
+ * Rolls back the transaction by clearing all buffered changes.
2774
+ * Internal use only.
2775
+ */
2776
+ async rollback() {
2777
+ this.txNodes.clear();
2778
+ this.dirtyIds.clear();
2779
+ this.createdInTx.clear();
2780
+ }
2781
+ async readLock(fn) {
2782
+ return await fn();
2783
+ }
2784
+ async writeLock(fn) {
2785
+ return await fn();
2786
+ }
2787
+ async commitHeadBuffer() {
2788
+ }
2789
+ async commitNodeCreateBuffer() {
2790
+ }
2791
+ async commitNodeUpdateBuffer() {
2792
+ }
2793
+ async commitNodeDeleteBuffer() {
2794
+ }
2795
+ };
2796
+
2797
+ // src/BPTreeAsync.ts
2798
+ var BPTreeAsync = class extends BPTreeAsyncBase {
2799
+ constructor(strategy, comparator, option) {
2800
+ super(strategy, comparator, option);
2801
+ }
2802
+ /**
2803
+ * Creates a new asynchronous transaction.
2804
+ * @returns A promise that resolves to a new BPTreeAsyncTransaction.
2805
+ */
2806
+ async createTransaction() {
2807
+ const tx = new BPTreeAsyncTransaction(this);
2808
+ await tx.initTransaction();
2809
+ return tx;
2810
+ }
2811
+ async insert(key, value) {
2812
+ const tx = await this.createTransaction();
2813
+ await tx.insert(key, value);
2814
+ const { success } = await tx.commit();
2815
+ await this.init();
2816
+ if (!success) {
2817
+ throw new Error("Transaction failed: Commit failed due to conflict");
2584
2818
  }
2585
- return this.head;
2586
2819
  }
2587
- async writeHead(head) {
2588
- this.head = head;
2820
+ async delete(key, value) {
2821
+ const tx = await this.createTransaction();
2822
+ await tx.delete(key, value);
2823
+ const { success } = await tx.commit();
2824
+ await this.init();
2825
+ if (!success) {
2826
+ throw new Error("Transaction failed: Commit failed due to conflict");
2827
+ }
2828
+ }
2829
+ async readLock(fn) {
2830
+ return await fn();
2831
+ }
2832
+ async writeLock(fn) {
2833
+ return await fn();
2589
2834
  }
2590
2835
  };