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.
- package/README.md +4 -1
- package/dist/cjs/index.cjs +767 -522
- package/dist/esm/index.mjs +767 -522
- package/dist/types/BPTreeAsync.d.ts +9 -44
- package/dist/types/BPTreeSync.d.ts +7 -41
- package/dist/types/SerializeStrategyAsync.d.ts +3 -3
- package/dist/types/SerializeStrategySync.d.ts +3 -3
- package/dist/types/base/BPTree.d.ts +6 -76
- package/dist/types/base/BPTreeAsyncBase.d.ts +41 -0
- package/dist/types/base/BPTreeSyncBase.d.ts +39 -0
- package/dist/types/base/SerializeStrategy.d.ts +9 -8
- package/dist/types/index.d.ts +3 -2
- package/dist/types/transaction/BPTreeAsyncSnapshotStrategy.d.ts +17 -0
- package/dist/types/transaction/BPTreeAsyncTransaction.d.ts +44 -0
- package/dist/types/transaction/BPTreeSyncSnapshotStrategy.d.ts +17 -0
- package/dist/types/transaction/BPTreeSyncTransaction.d.ts +42 -0
- package/dist/types/types/index.d.ts +91 -0
- package/package.json +1 -1
- package/dist/types/utils/types.d.ts +0 -5
package/dist/cjs/index.cjs
CHANGED
|
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
BPTreeAsync: () => BPTreeAsync,
|
|
24
|
+
BPTreeAsyncTransaction: () => BPTreeAsyncTransaction,
|
|
24
25
|
BPTreeSync: () => BPTreeSync,
|
|
26
|
+
BPTreeSyncTransaction: () => BPTreeSyncTransaction,
|
|
25
27
|
InMemoryStoreStrategyAsync: () => InMemoryStoreStrategyAsync,
|
|
26
28
|
InMemoryStoreStrategySync: () => InMemoryStoreStrategySync,
|
|
27
29
|
NumericComparator: () => NumericComparator,
|
|
@@ -472,7 +474,7 @@ var BPTree = class _BPTree {
|
|
|
472
474
|
comparator;
|
|
473
475
|
option;
|
|
474
476
|
order;
|
|
475
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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/
|
|
765
|
-
var
|
|
766
|
+
// src/base/BPTreeSyncBase.ts
|
|
767
|
+
var BPTreeSyncBase = class extends BPTree {
|
|
766
768
|
constructor(strategy, comparator, option) {
|
|
767
769
|
super(strategy, comparator, option);
|
|
768
770
|
this.nodes = this._createCachedNode();
|
|
@@ -871,16 +873,18 @@ var BPTreeSync = class extends BPTree {
|
|
|
871
873
|
}
|
|
872
874
|
}
|
|
873
875
|
}
|
|
874
|
-
if (this.
|
|
876
|
+
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
875
877
|
const keys = node.keys;
|
|
876
|
-
this.bufferForNodeDelete(
|
|
877
|
-
|
|
878
|
-
this.
|
|
879
|
-
|
|
880
|
-
this.
|
|
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.
|
|
883
|
-
this.
|
|
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(
|
|
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.
|
|
1058
|
+
if (this.rootId === node.id) {
|
|
1064
1059
|
const root = this._createNode(false, [node.id, pointer.id], [value]);
|
|
1065
|
-
this.
|
|
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
|
-
|
|
1135
|
-
this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1298
|
-
const
|
|
1299
|
-
for (let
|
|
1300
|
-
|
|
1301
|
-
|
|
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
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1447
|
-
|
|
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.
|
|
1460
|
-
this.commitHeadBuffer();
|
|
1459
|
+
this.strategy.writeHead(this.strategy.head);
|
|
1461
1460
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
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
|
-
//
|
|
1474
|
-
var
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
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
|
-
|
|
1491
|
-
|
|
1503
|
+
};
|
|
1504
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
1505
|
+
node;
|
|
1506
|
+
constructor(order) {
|
|
1507
|
+
super(order);
|
|
1508
|
+
this.node = {};
|
|
1492
1509
|
}
|
|
1493
|
-
|
|
1494
|
-
return
|
|
1510
|
+
id(isLeaf) {
|
|
1511
|
+
return this.autoIncrement("index", 1).toString();
|
|
1495
1512
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1518
|
-
|
|
1526
|
+
readHead() {
|
|
1527
|
+
if (this.head.root === null) {
|
|
1528
|
+
return null;
|
|
1529
|
+
}
|
|
1530
|
+
return this.head;
|
|
1519
1531
|
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
const [b1, b2] = b;
|
|
1523
|
-
return a1 === b1 && a2 === b2;
|
|
1532
|
+
writeHead(head) {
|
|
1533
|
+
this.head = head;
|
|
1524
1534
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
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
|
-
|
|
1534
|
-
|
|
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
|
-
|
|
1542
|
-
return
|
|
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
|
-
|
|
1552
|
-
|
|
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
|
-
|
|
1599
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1618
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1655
|
-
const
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
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
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
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
|
-
*
|
|
1680
|
-
*
|
|
1681
|
-
*
|
|
1682
|
-
*
|
|
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
|
-
|
|
1690
|
-
const
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
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
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
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
|
-
*
|
|
1717
|
-
*
|
|
1746
|
+
* Rolls back the transaction by clearing all buffered changes.
|
|
1747
|
+
* Internal use only.
|
|
1718
1748
|
*/
|
|
1719
|
-
|
|
1720
|
-
this.
|
|
1749
|
+
rollback() {
|
|
1750
|
+
this.txNodes.clear();
|
|
1751
|
+
this.dirtyIds.clear();
|
|
1752
|
+
this.createdInTx.clear();
|
|
1753
|
+
}
|
|
1754
|
+
// Override to do nothing, as transaction handles its own commits
|
|
1755
|
+
commitHeadBuffer() {
|
|
1756
|
+
}
|
|
1757
|
+
commitNodeCreateBuffer() {
|
|
1758
|
+
}
|
|
1759
|
+
commitNodeUpdateBuffer() {
|
|
1760
|
+
}
|
|
1761
|
+
commitNodeDeleteBuffer() {
|
|
1762
|
+
}
|
|
1763
|
+
};
|
|
1764
|
+
|
|
1765
|
+
// src/BPTreeSync.ts
|
|
1766
|
+
var BPTreeSync = class extends BPTreeSyncBase {
|
|
1767
|
+
constructor(strategy, comparator, option) {
|
|
1768
|
+
super(strategy, comparator, option);
|
|
1721
1769
|
}
|
|
1722
1770
|
/**
|
|
1723
|
-
*
|
|
1724
|
-
* @
|
|
1771
|
+
* Creates a new synchronous transaction.
|
|
1772
|
+
* @returns A new BPTreeSyncTransaction.
|
|
1725
1773
|
*/
|
|
1726
|
-
|
|
1727
|
-
|
|
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/
|
|
1732
|
-
var
|
|
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.
|
|
1909
|
+
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
1862
1910
|
const keys = node.keys;
|
|
1863
|
-
this.bufferForNodeDelete(
|
|
1864
|
-
|
|
1865
|
-
this.
|
|
1866
|
-
|
|
1867
|
-
this.
|
|
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.
|
|
1870
|
-
this.
|
|
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(
|
|
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
|
|
1948
|
-
|
|
1949
|
-
this.bufferForNodeUpdate(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
2094
|
+
if (this.rootId === node.id) {
|
|
2051
2095
|
const root = await this._createNode(false, [node.id, pointer.id], [value]);
|
|
2052
|
-
this.
|
|
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
|
|
2105
|
-
|
|
2106
|
-
this.bufferForNodeUpdate(
|
|
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
|
|
2110
|
-
|
|
2111
|
-
this.bufferForNodeUpdate(
|
|
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
|
-
|
|
2122
|
-
this.
|
|
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.
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
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(
|
|
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(
|
|
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(
|
|
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.
|
|
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(
|
|
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(
|
|
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.
|
|
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.
|
|
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
|
-
|
|
2286
|
-
const
|
|
2287
|
-
for (let
|
|
2288
|
-
|
|
2289
|
-
|
|
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
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
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
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
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(
|
|
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
|
-
|
|
2444
|
-
return this.
|
|
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
|
-
|
|
2458
|
-
|
|
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/
|
|
2491
|
-
var
|
|
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
|
|
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
|
-
|
|
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/
|
|
2542
|
-
var
|
|
2564
|
+
// src/transaction/BPTreeAsyncSnapshotStrategy.ts
|
|
2565
|
+
var BPTreeAsyncSnapshotStrategy = class extends SerializeStrategyAsync {
|
|
2566
|
+
baseStrategy;
|
|
2567
|
+
snapshotHead;
|
|
2568
|
+
constructor(baseStrategy, root) {
|
|
2569
|
+
super(baseStrategy.order);
|
|
2570
|
+
this.baseStrategy = baseStrategy;
|
|
2571
|
+
this.snapshotHead = {
|
|
2572
|
+
...baseStrategy.head,
|
|
2573
|
+
root,
|
|
2574
|
+
data: { ...baseStrategy.head.data }
|
|
2575
|
+
};
|
|
2576
|
+
this.head = this.snapshotHead;
|
|
2577
|
+
}
|
|
2578
|
+
async id(isLeaf) {
|
|
2579
|
+
return await this.baseStrategy.id(isLeaf);
|
|
2580
|
+
}
|
|
2581
|
+
async read(id) {
|
|
2582
|
+
return await this.baseStrategy.read(id);
|
|
2583
|
+
}
|
|
2584
|
+
async write(id, node) {
|
|
2585
|
+
await this.baseStrategy.write(id, node);
|
|
2586
|
+
}
|
|
2587
|
+
async delete(id) {
|
|
2588
|
+
await this.baseStrategy.delete(id);
|
|
2589
|
+
}
|
|
2590
|
+
async readHead() {
|
|
2591
|
+
return this.snapshotHead;
|
|
2592
|
+
}
|
|
2593
|
+
async writeHead(head) {
|
|
2594
|
+
this.snapshotHead.root = head.root;
|
|
2595
|
+
this.snapshotHead.data = { ...head.data };
|
|
2596
|
+
}
|
|
2597
|
+
async compareAndSwapHead(oldRoot, newRoot) {
|
|
2598
|
+
return await this.baseStrategy.compareAndSwapHead(oldRoot, newRoot);
|
|
2599
|
+
}
|
|
2543
2600
|
async getHeadData(key, defaultValue) {
|
|
2544
|
-
|
|
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.
|
|
2551
|
-
await this.writeHead(this.head);
|
|
2604
|
+
this.snapshotHead.data[key] = data;
|
|
2552
2605
|
}
|
|
2553
2606
|
async autoIncrement(key, defaultValue) {
|
|
2554
|
-
|
|
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
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
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
|
-
|
|
2567
|
-
|
|
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
|
|
2570
|
-
if (
|
|
2571
|
-
|
|
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
|
|
2576
|
-
|
|
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
|
-
|
|
2579
|
-
|
|
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
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
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
|
|
2588
|
-
|
|
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
|
};
|