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