serializable-bptree 8.1.2 → 8.1.4
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 +2 -0
- package/dist/cjs/index.cjs +1404 -1466
- package/dist/esm/index.mjs +1404 -1466
- package/dist/types/base/BPTreeTransaction.d.ts +14 -14
- package/dist/types/transaction/BPTreeAsyncTransaction.d.ts +7 -4
- package/dist/types/transaction/BPTreeSyncTransaction.d.ts +3 -3
- package/package.json +4 -4
package/dist/esm/index.mjs
CHANGED
|
@@ -57,6 +57,42 @@ var StringComparator = class extends ValueComparator {
|
|
|
57
57
|
// node_modules/mvcc-api/dist/esm/index.mjs
|
|
58
58
|
var MVCCStrategy = class {
|
|
59
59
|
};
|
|
60
|
+
var LRUMap = class {
|
|
61
|
+
cache = /* @__PURE__ */ new Map();
|
|
62
|
+
capacity;
|
|
63
|
+
constructor(capacity) {
|
|
64
|
+
this.capacity = capacity;
|
|
65
|
+
}
|
|
66
|
+
get(key) {
|
|
67
|
+
if (!this.cache.has(key)) return void 0;
|
|
68
|
+
const value = this.cache.get(key);
|
|
69
|
+
this.cache.delete(key);
|
|
70
|
+
this.cache.set(key, value);
|
|
71
|
+
return value;
|
|
72
|
+
}
|
|
73
|
+
set(key, value) {
|
|
74
|
+
if (this.cache.has(key)) {
|
|
75
|
+
this.cache.delete(key);
|
|
76
|
+
} else if (this.cache.size >= this.capacity) {
|
|
77
|
+
const oldestKey = this.cache.keys().next().value;
|
|
78
|
+
if (oldestKey !== void 0) this.cache.delete(oldestKey);
|
|
79
|
+
}
|
|
80
|
+
this.cache.set(key, value);
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
has(key) {
|
|
84
|
+
return this.cache.has(key);
|
|
85
|
+
}
|
|
86
|
+
delete(key) {
|
|
87
|
+
return this.cache.delete(key);
|
|
88
|
+
}
|
|
89
|
+
clear() {
|
|
90
|
+
this.cache.clear();
|
|
91
|
+
}
|
|
92
|
+
get size() {
|
|
93
|
+
return this.cache.size;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
60
96
|
var MVCCTransaction = class {
|
|
61
97
|
committed;
|
|
62
98
|
snapshotVersion;
|
|
@@ -64,11 +100,11 @@ var MVCCTransaction = class {
|
|
|
64
100
|
writeBuffer;
|
|
65
101
|
deleteBuffer;
|
|
66
102
|
createdKeys;
|
|
67
|
-
// create()로 생성된 키 추적
|
|
68
103
|
deletedValues;
|
|
69
104
|
// delete 시 삭제 전 값 저장
|
|
70
105
|
originallyExisted;
|
|
71
106
|
// 트랜잭션 시작 시점에 디스크에 존재했던 키 (deleted 결과 필터링용)
|
|
107
|
+
bufferHistory = /* @__PURE__ */ new Map();
|
|
72
108
|
// Nested Transaction Properties
|
|
73
109
|
parent;
|
|
74
110
|
localVersion;
|
|
@@ -82,7 +118,8 @@ var MVCCTransaction = class {
|
|
|
82
118
|
versionIndex = /* @__PURE__ */ new Map();
|
|
83
119
|
deletedCache = /* @__PURE__ */ new Map();
|
|
84
120
|
activeTransactions = /* @__PURE__ */ new Set();
|
|
85
|
-
|
|
121
|
+
diskCache;
|
|
122
|
+
constructor(strategy, options, parent, snapshotVersion) {
|
|
86
123
|
this.snapshotVersion = snapshotVersion ?? 0;
|
|
87
124
|
this.writeBuffer = /* @__PURE__ */ new Map();
|
|
88
125
|
this.deleteBuffer = /* @__PURE__ */ new Set();
|
|
@@ -97,6 +134,7 @@ var MVCCTransaction = class {
|
|
|
97
134
|
this.snapshotLocalVersion = parent.localVersion;
|
|
98
135
|
this.strategy = void 0;
|
|
99
136
|
this.root = parent.root;
|
|
137
|
+
this.diskCache = parent.diskCache;
|
|
100
138
|
} else {
|
|
101
139
|
if (!strategy) throw new Error("Root Transaction must get Strategy");
|
|
102
140
|
this.strategy = strategy;
|
|
@@ -104,8 +142,13 @@ var MVCCTransaction = class {
|
|
|
104
142
|
this.localVersion = 0;
|
|
105
143
|
this.snapshotLocalVersion = 0;
|
|
106
144
|
this.root = this;
|
|
145
|
+
this.diskCache = new LRUMap(options?.cacheCapacity ?? 1e3);
|
|
107
146
|
}
|
|
108
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Checks if the transaction is a root transaction.
|
|
150
|
+
* @returns True if the transaction is a root transaction, false otherwise.
|
|
151
|
+
*/
|
|
109
152
|
isRoot() {
|
|
110
153
|
return !this.parent;
|
|
111
154
|
}
|
|
@@ -122,27 +165,61 @@ var MVCCTransaction = class {
|
|
|
122
165
|
}
|
|
123
166
|
return false;
|
|
124
167
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Checks if a key was written in this transaction.
|
|
170
|
+
* @param key The key to check.
|
|
171
|
+
* @returns True if the key was written in this transaction, false otherwise.
|
|
172
|
+
*/
|
|
173
|
+
isWrote(key) {
|
|
174
|
+
return this.writeBuffer.has(key);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Checks if a key was deleted in this transaction.
|
|
178
|
+
* @param key The key to check.
|
|
179
|
+
* @returns True if the key was deleted in this transaction, false otherwise.
|
|
180
|
+
*/
|
|
181
|
+
isDeleted(key) {
|
|
182
|
+
return this.deleteBuffer.has(key);
|
|
183
|
+
}
|
|
184
|
+
_recordHistory(key) {
|
|
185
|
+
const existsInWriteBuffer = this.writeBuffer.has(key);
|
|
186
|
+
const existsInDeleteBuffer = this.deleteBuffer.has(key);
|
|
187
|
+
const currentVer = this.keyVersions.get(key);
|
|
188
|
+
if (currentVer !== void 0) {
|
|
189
|
+
if (!this.bufferHistory.has(key)) this.bufferHistory.set(key, []);
|
|
190
|
+
this.bufferHistory.get(key).push({
|
|
191
|
+
value: existsInWriteBuffer ? this.writeBuffer.get(key) : this.deletedValues.get(key) ?? null,
|
|
192
|
+
exists: existsInWriteBuffer || !existsInDeleteBuffer,
|
|
193
|
+
version: currentVer
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
_bufferCreate(key, value, version) {
|
|
198
|
+
if (version === void 0) this.localVersion++;
|
|
199
|
+
const targetVersion = version ?? this.localVersion;
|
|
200
|
+
this._recordHistory(key);
|
|
128
201
|
this.writeBuffer.set(key, value);
|
|
129
202
|
this.createdKeys.add(key);
|
|
130
203
|
this.deleteBuffer.delete(key);
|
|
131
204
|
this.originallyExisted.delete(key);
|
|
132
|
-
this.keyVersions.set(key,
|
|
205
|
+
this.keyVersions.set(key, targetVersion);
|
|
133
206
|
}
|
|
134
|
-
_bufferWrite(key, value) {
|
|
135
|
-
this.localVersion++;
|
|
207
|
+
_bufferWrite(key, value, version) {
|
|
208
|
+
if (version === void 0) this.localVersion++;
|
|
209
|
+
const targetVersion = version ?? this.localVersion;
|
|
210
|
+
this._recordHistory(key);
|
|
136
211
|
this.writeBuffer.set(key, value);
|
|
137
212
|
this.deleteBuffer.delete(key);
|
|
138
|
-
this.keyVersions.set(key,
|
|
213
|
+
this.keyVersions.set(key, targetVersion);
|
|
139
214
|
}
|
|
140
|
-
_bufferDelete(key) {
|
|
141
|
-
this.localVersion++;
|
|
215
|
+
_bufferDelete(key, version) {
|
|
216
|
+
if (version === void 0) this.localVersion++;
|
|
217
|
+
const targetVersion = version ?? this.localVersion;
|
|
218
|
+
this._recordHistory(key);
|
|
142
219
|
this.deleteBuffer.add(key);
|
|
143
220
|
this.writeBuffer.delete(key);
|
|
144
221
|
this.createdKeys.delete(key);
|
|
145
|
-
this.keyVersions.set(key,
|
|
222
|
+
this.keyVersions.set(key, targetVersion);
|
|
146
223
|
}
|
|
147
224
|
/**
|
|
148
225
|
* Returns the entries that will be created, updated, and deleted by this transaction.
|
|
@@ -166,7 +243,11 @@ var MVCCTransaction = class {
|
|
|
166
243
|
deleted.push({ key, data });
|
|
167
244
|
}
|
|
168
245
|
}
|
|
169
|
-
return {
|
|
246
|
+
return {
|
|
247
|
+
created,
|
|
248
|
+
updated,
|
|
249
|
+
deleted
|
|
250
|
+
};
|
|
170
251
|
}
|
|
171
252
|
/**
|
|
172
253
|
* Rolls back the transaction.
|
|
@@ -184,7 +265,12 @@ var MVCCTransaction = class {
|
|
|
184
265
|
if (this.root !== this) {
|
|
185
266
|
this.root.activeTransactions.delete(this);
|
|
186
267
|
}
|
|
187
|
-
return {
|
|
268
|
+
return {
|
|
269
|
+
success: true,
|
|
270
|
+
created,
|
|
271
|
+
updated,
|
|
272
|
+
deleted
|
|
273
|
+
};
|
|
188
274
|
}
|
|
189
275
|
/**
|
|
190
276
|
* Cleans up both deletedCache and versionIndex based on minActiveVersion.
|
|
@@ -219,7 +305,9 @@ var MVCCTransaction = class {
|
|
|
219
305
|
break;
|
|
220
306
|
}
|
|
221
307
|
}
|
|
222
|
-
if (latestInSnapshotIdx
|
|
308
|
+
if (latestInSnapshotIdx === versions.length - 1) {
|
|
309
|
+
this.versionIndex.delete(key);
|
|
310
|
+
} else if (latestInSnapshotIdx > 0) {
|
|
223
311
|
versions.splice(0, latestInSnapshotIdx);
|
|
224
312
|
}
|
|
225
313
|
}
|
|
@@ -268,7 +356,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
268
356
|
createNested() {
|
|
269
357
|
if (this.committed) throw new Error("Transaction already committed");
|
|
270
358
|
const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
|
|
271
|
-
const child = new _SyncMVCCTransaction(void 0, this, childVersion);
|
|
359
|
+
const child = new _SyncMVCCTransaction(void 0, void 0, this, childVersion);
|
|
272
360
|
this.root.activeTransactions.add(child);
|
|
273
361
|
return child;
|
|
274
362
|
}
|
|
@@ -276,27 +364,68 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
276
364
|
if (this.committed) throw new Error("Transaction already committed");
|
|
277
365
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
278
366
|
if (this.deleteBuffer.has(key)) return null;
|
|
279
|
-
|
|
367
|
+
if (this.parent) {
|
|
368
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
369
|
+
}
|
|
370
|
+
return this._diskRead(key, this.snapshotVersion);
|
|
280
371
|
}
|
|
281
372
|
exists(key) {
|
|
282
373
|
if (this.committed) throw new Error("Transaction already committed");
|
|
283
374
|
if (this.deleteBuffer.has(key)) return false;
|
|
284
375
|
if (this.writeBuffer.has(key)) return true;
|
|
285
|
-
|
|
376
|
+
if (this.parent) {
|
|
377
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
378
|
+
}
|
|
379
|
+
return this._diskExists(key, this.snapshotVersion);
|
|
380
|
+
}
|
|
381
|
+
_existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
382
|
+
if (this.writeBuffer.has(key)) {
|
|
383
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
384
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
if (this.deleteBuffer.has(key)) {
|
|
389
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
390
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
const history = this.bufferHistory.get(key);
|
|
395
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
396
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
397
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
398
|
+
return history[i].exists;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (this.parent) {
|
|
403
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
404
|
+
} else {
|
|
405
|
+
return this._diskExists(key, snapshotVersion);
|
|
406
|
+
}
|
|
286
407
|
}
|
|
287
408
|
_readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
288
409
|
if (this.writeBuffer.has(key)) {
|
|
289
410
|
const keyModVersion = this.keyVersions.get(key);
|
|
290
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
411
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
291
412
|
return this.writeBuffer.get(key);
|
|
292
413
|
}
|
|
293
414
|
}
|
|
294
415
|
if (this.deleteBuffer.has(key)) {
|
|
295
416
|
const keyModVersion = this.keyVersions.get(key);
|
|
296
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
417
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
297
418
|
return null;
|
|
298
419
|
}
|
|
299
420
|
}
|
|
421
|
+
const history = this.bufferHistory.get(key);
|
|
422
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
423
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
424
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
425
|
+
return history[i].exists ? history[i].value : null;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
}
|
|
300
429
|
if (this.parent) {
|
|
301
430
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
302
431
|
} else {
|
|
@@ -361,6 +490,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
361
490
|
this.deletedValues.clear();
|
|
362
491
|
this.originallyExisted.clear();
|
|
363
492
|
this.keyVersions.clear();
|
|
493
|
+
this.bufferHistory.clear();
|
|
364
494
|
this.localVersion = 0;
|
|
365
495
|
this.snapshotVersion = this.version;
|
|
366
496
|
}
|
|
@@ -401,32 +531,22 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
401
531
|
};
|
|
402
532
|
}
|
|
403
533
|
}
|
|
404
|
-
const
|
|
405
|
-
for (const key of child.writeBuffer
|
|
406
|
-
|
|
407
|
-
this.
|
|
408
|
-
this.
|
|
409
|
-
if (child.createdKeys.has(key)) {
|
|
410
|
-
this.createdKeys.add(key);
|
|
411
|
-
}
|
|
534
|
+
const mergeVersion = ++this.localVersion;
|
|
535
|
+
for (const [key, value] of child.writeBuffer) {
|
|
536
|
+
const wasCreated = child.createdKeys.has(key);
|
|
537
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
538
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
412
539
|
}
|
|
413
540
|
for (const key of child.deleteBuffer) {
|
|
414
|
-
this.deleteBuffer.add(key);
|
|
415
|
-
this.writeBuffer.delete(key);
|
|
416
|
-
this.createdKeys.delete(key);
|
|
417
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
418
541
|
const deletedValue = child.deletedValues.get(key);
|
|
419
|
-
if (deletedValue !== void 0)
|
|
420
|
-
|
|
421
|
-
}
|
|
422
|
-
if (child.originallyExisted.has(key)) {
|
|
542
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
543
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
423
544
|
this.originallyExisted.add(key);
|
|
424
545
|
}
|
|
546
|
+
this._bufferDelete(key, mergeVersion);
|
|
425
547
|
}
|
|
426
|
-
this.localVersion = newLocalVersion;
|
|
427
548
|
this.root.activeTransactions.delete(child);
|
|
428
549
|
} else {
|
|
429
|
-
const newVersion = this.version + 1;
|
|
430
550
|
if (child !== this) {
|
|
431
551
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
432
552
|
for (const key of modifiedKeys) {
|
|
@@ -444,50 +564,57 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
444
564
|
};
|
|
445
565
|
}
|
|
446
566
|
}
|
|
567
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
568
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
569
|
+
return {
|
|
570
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
571
|
+
conflict: {
|
|
572
|
+
key,
|
|
573
|
+
parent: this.read(key),
|
|
574
|
+
child: child.read(key)
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
}
|
|
447
578
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
for (const key of child.deleteBuffer) {
|
|
457
|
-
this.deleteBuffer.add(key);
|
|
458
|
-
this.writeBuffer.delete(key);
|
|
459
|
-
this.createdKeys.delete(key);
|
|
460
|
-
const deletedValue = child.deletedValues.get(key);
|
|
461
|
-
if (deletedValue !== void 0) {
|
|
462
|
-
this.deletedValues.set(key, deletedValue);
|
|
579
|
+
const mergeVersion = ++this.localVersion;
|
|
580
|
+
for (const [key, value] of child.writeBuffer) {
|
|
581
|
+
const wasCreated = child.createdKeys.has(key);
|
|
582
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
583
|
+
this.originallyExisted.add(key);
|
|
584
|
+
}
|
|
585
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
586
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
463
587
|
}
|
|
464
|
-
|
|
465
|
-
|
|
588
|
+
for (const key of child.deleteBuffer) {
|
|
589
|
+
const deletedValue = child.deletedValues.get(key);
|
|
590
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
591
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
592
|
+
this.originallyExisted.add(key);
|
|
593
|
+
}
|
|
594
|
+
this._bufferDelete(key, mergeVersion);
|
|
466
595
|
}
|
|
596
|
+
this.root.activeTransactions.delete(child);
|
|
597
|
+
} else {
|
|
598
|
+
const newVersion = this.version + 1;
|
|
599
|
+
for (const [key, value] of this.writeBuffer) this._diskWrite(key, value, newVersion);
|
|
600
|
+
for (const key of this.deleteBuffer) this._diskDelete(key, newVersion);
|
|
601
|
+
this.version = newVersion;
|
|
602
|
+
this._cleanupDeletedCache();
|
|
467
603
|
}
|
|
468
|
-
for (const [key, value] of child.writeBuffer) {
|
|
469
|
-
this._diskWrite(key, value, newVersion);
|
|
470
|
-
}
|
|
471
|
-
for (const key of child.deleteBuffer) {
|
|
472
|
-
this._diskDelete(key, newVersion);
|
|
473
|
-
}
|
|
474
|
-
this.version = newVersion;
|
|
475
|
-
this.root.activeTransactions.delete(child);
|
|
476
|
-
this._cleanupDeletedCache();
|
|
477
604
|
}
|
|
478
605
|
return null;
|
|
479
606
|
}
|
|
480
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
481
607
|
_diskWrite(key, value, version) {
|
|
482
608
|
const strategy = this.strategy;
|
|
483
609
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
484
|
-
|
|
485
|
-
|
|
610
|
+
const rootAsAny = this.root;
|
|
611
|
+
if (this._diskExists(key, version)) {
|
|
612
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
486
613
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
487
|
-
this.deletedCache.get(key).push({
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
614
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
615
|
+
rootAsAny.diskCache.set(key, value);
|
|
616
|
+
} else {
|
|
617
|
+
rootAsAny.diskCache.set(key, value);
|
|
491
618
|
}
|
|
492
619
|
strategy.write(key, value);
|
|
493
620
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -498,14 +625,19 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
498
625
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
499
626
|
const versions = this.versionIndex.get(key);
|
|
500
627
|
if (!versions) {
|
|
501
|
-
|
|
628
|
+
const rootAsAny = this.root;
|
|
629
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
630
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
631
|
+
rootAsAny.diskCache.set(key, val);
|
|
632
|
+
return val;
|
|
633
|
+
}
|
|
634
|
+
return null;
|
|
502
635
|
}
|
|
503
636
|
let targetVerObj = null;
|
|
504
637
|
let nextVerObj = null;
|
|
505
638
|
for (const v of versions) {
|
|
506
|
-
if (v.version <= snapshotVersion)
|
|
507
|
-
|
|
508
|
-
} else {
|
|
639
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
640
|
+
else {
|
|
509
641
|
nextVerObj = v;
|
|
510
642
|
break;
|
|
511
643
|
}
|
|
@@ -522,7 +654,14 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
522
654
|
}
|
|
523
655
|
if (!targetVerObj.exists) return null;
|
|
524
656
|
if (!nextVerObj) {
|
|
525
|
-
return
|
|
657
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
658
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
659
|
+
const rootAsAny = this.root;
|
|
660
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
661
|
+
rootAsAny.diskCache.set(key, val);
|
|
662
|
+
return val;
|
|
663
|
+
}
|
|
664
|
+
return null;
|
|
526
665
|
}
|
|
527
666
|
const cached = this.deletedCache.get(key);
|
|
528
667
|
if (cached) {
|
|
@@ -536,31 +675,44 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
536
675
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
537
676
|
const versions = this.versionIndex.get(key);
|
|
538
677
|
if (!versions) {
|
|
539
|
-
|
|
678
|
+
const rootAsAny = this.root;
|
|
679
|
+
if (rootAsAny.diskCache.has(key)) return rootAsAny.diskCache.get(key) !== null;
|
|
680
|
+
const exists = strategy.exists(key);
|
|
681
|
+
if (!exists) rootAsAny.diskCache.set(key, null);
|
|
682
|
+
return exists;
|
|
540
683
|
}
|
|
541
684
|
let targetVerObj = null;
|
|
685
|
+
let nextVerObj = null;
|
|
542
686
|
for (const v of versions) {
|
|
543
|
-
if (v.version <= snapshotVersion)
|
|
544
|
-
|
|
545
|
-
|
|
687
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
688
|
+
else {
|
|
689
|
+
nextVerObj = v;
|
|
546
690
|
break;
|
|
547
691
|
}
|
|
548
692
|
}
|
|
549
|
-
if (!targetVerObj)
|
|
693
|
+
if (!targetVerObj) {
|
|
694
|
+
if (nextVerObj) {
|
|
695
|
+
const cached = this.deletedCache.get(key);
|
|
696
|
+
if (cached) {
|
|
697
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
698
|
+
if (match) return true;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
550
703
|
return targetVerObj.exists;
|
|
551
704
|
}
|
|
552
705
|
_diskDelete(key, snapshotVersion) {
|
|
553
706
|
const strategy = this.strategy;
|
|
554
707
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
555
|
-
|
|
556
|
-
|
|
708
|
+
const rootAsAny = this.root;
|
|
709
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
710
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
557
711
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
558
|
-
this.deletedCache.get(key).push({
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
});
|
|
712
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
713
|
+
strategy.delete(key);
|
|
714
|
+
rootAsAny.diskCache.delete(key);
|
|
562
715
|
}
|
|
563
|
-
strategy.delete(key);
|
|
564
716
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
565
717
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
566
718
|
}
|
|
@@ -873,7 +1025,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
873
1025
|
createNested() {
|
|
874
1026
|
if (this.committed) throw new Error("Transaction already committed");
|
|
875
1027
|
const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
|
|
876
|
-
const child = new _AsyncMVCCTransaction(void 0, this, childVersion);
|
|
1028
|
+
const child = new _AsyncMVCCTransaction(void 0, void 0, this, childVersion);
|
|
877
1029
|
this.root.activeTransactions.add(child);
|
|
878
1030
|
return child;
|
|
879
1031
|
}
|
|
@@ -881,31 +1033,72 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
881
1033
|
if (this.committed) throw new Error("Transaction already committed");
|
|
882
1034
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
883
1035
|
if (this.deleteBuffer.has(key)) return null;
|
|
884
|
-
|
|
1036
|
+
if (this.parent) {
|
|
1037
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
1038
|
+
}
|
|
1039
|
+
return await this._diskRead(key, this.snapshotVersion);
|
|
885
1040
|
}
|
|
886
1041
|
async exists(key) {
|
|
887
1042
|
if (this.committed) throw new Error("Transaction already committed");
|
|
888
1043
|
if (this.deleteBuffer.has(key)) return false;
|
|
889
1044
|
if (this.writeBuffer.has(key)) return true;
|
|
890
|
-
|
|
1045
|
+
if (this.parent) {
|
|
1046
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
1047
|
+
}
|
|
1048
|
+
return await this._diskExists(key, this.snapshotVersion);
|
|
1049
|
+
}
|
|
1050
|
+
async _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
1051
|
+
if (this.writeBuffer.has(key)) {
|
|
1052
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
1053
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
1054
|
+
return true;
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
if (this.deleteBuffer.has(key)) {
|
|
1058
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
1059
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
1060
|
+
return false;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
const history = this.bufferHistory.get(key);
|
|
1064
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
1065
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
1066
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
1067
|
+
return history[i].exists;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
if (this.parent) {
|
|
1072
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
1073
|
+
} else {
|
|
1074
|
+
return await this._diskExists(key, snapshotVersion);
|
|
1075
|
+
}
|
|
891
1076
|
}
|
|
892
1077
|
async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
893
1078
|
if (this.writeBuffer.has(key)) {
|
|
894
1079
|
const keyModVersion = this.keyVersions.get(key);
|
|
895
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
1080
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
896
1081
|
return this.writeBuffer.get(key);
|
|
897
1082
|
}
|
|
898
1083
|
}
|
|
899
1084
|
if (this.deleteBuffer.has(key)) {
|
|
900
1085
|
const keyModVersion = this.keyVersions.get(key);
|
|
901
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
1086
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
902
1087
|
return null;
|
|
903
1088
|
}
|
|
904
1089
|
}
|
|
1090
|
+
const history = this.bufferHistory.get(key);
|
|
1091
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
1092
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
1093
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
1094
|
+
return history[i].exists ? history[i].value : null;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
905
1098
|
if (this.parent) {
|
|
906
1099
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
907
1100
|
} else {
|
|
908
|
-
return this._diskRead(key, snapshotVersion);
|
|
1101
|
+
return await this._diskRead(key, snapshotVersion);
|
|
909
1102
|
}
|
|
910
1103
|
}
|
|
911
1104
|
async commit(label) {
|
|
@@ -966,6 +1159,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
966
1159
|
this.deletedValues.clear();
|
|
967
1160
|
this.originallyExisted.clear();
|
|
968
1161
|
this.keyVersions.clear();
|
|
1162
|
+
this.bufferHistory.clear();
|
|
969
1163
|
this.localVersion = 0;
|
|
970
1164
|
this.snapshotVersion = this.version;
|
|
971
1165
|
}
|
|
@@ -1007,33 +1201,23 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1007
1201
|
};
|
|
1008
1202
|
}
|
|
1009
1203
|
}
|
|
1010
|
-
const
|
|
1011
|
-
for (const key of child.writeBuffer
|
|
1012
|
-
|
|
1013
|
-
this.
|
|
1014
|
-
this.
|
|
1015
|
-
if (child.createdKeys.has(key)) {
|
|
1016
|
-
this.createdKeys.add(key);
|
|
1017
|
-
}
|
|
1204
|
+
const mergeVersion = ++this.localVersion;
|
|
1205
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1206
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1207
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1208
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1018
1209
|
}
|
|
1019
1210
|
for (const key of child.deleteBuffer) {
|
|
1020
|
-
this.deleteBuffer.add(key);
|
|
1021
|
-
this.writeBuffer.delete(key);
|
|
1022
|
-
this.createdKeys.delete(key);
|
|
1023
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
1024
1211
|
const deletedValue = child.deletedValues.get(key);
|
|
1025
|
-
if (deletedValue !== void 0)
|
|
1026
|
-
|
|
1027
|
-
}
|
|
1028
|
-
if (child.originallyExisted.has(key)) {
|
|
1212
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1213
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1029
1214
|
this.originallyExisted.add(key);
|
|
1030
1215
|
}
|
|
1216
|
+
this._bufferDelete(key, mergeVersion);
|
|
1031
1217
|
}
|
|
1032
|
-
this.localVersion = newLocalVersion;
|
|
1033
1218
|
this.root.activeTransactions.delete(child);
|
|
1034
1219
|
return null;
|
|
1035
1220
|
} else {
|
|
1036
|
-
const newVersion = this.version + 1;
|
|
1037
1221
|
if (child !== this) {
|
|
1038
1222
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
1039
1223
|
for (const key of modifiedKeys) {
|
|
@@ -1051,51 +1235,58 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1051
1235
|
};
|
|
1052
1236
|
}
|
|
1053
1237
|
}
|
|
1238
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
1239
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1240
|
+
return {
|
|
1241
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
1242
|
+
conflict: {
|
|
1243
|
+
key,
|
|
1244
|
+
parent: await this.read(key),
|
|
1245
|
+
child: await child.read(key)
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1054
1249
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
for (const key of child.deleteBuffer) {
|
|
1064
|
-
this.deleteBuffer.add(key);
|
|
1065
|
-
this.writeBuffer.delete(key);
|
|
1066
|
-
this.createdKeys.delete(key);
|
|
1067
|
-
const deletedValue = child.deletedValues.get(key);
|
|
1068
|
-
if (deletedValue !== void 0) {
|
|
1069
|
-
this.deletedValues.set(key, deletedValue);
|
|
1250
|
+
const mergeVersion = ++this.localVersion;
|
|
1251
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1252
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1253
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1254
|
+
this.originallyExisted.add(key);
|
|
1255
|
+
}
|
|
1256
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1257
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1070
1258
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1259
|
+
for (const key of child.deleteBuffer) {
|
|
1260
|
+
const deletedValue = child.deletedValues.get(key);
|
|
1261
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1262
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1263
|
+
this.originallyExisted.add(key);
|
|
1264
|
+
}
|
|
1265
|
+
this._bufferDelete(key, mergeVersion);
|
|
1073
1266
|
}
|
|
1267
|
+
this.root.activeTransactions.delete(child);
|
|
1268
|
+
} else {
|
|
1269
|
+
const newVersion = this.version + 1;
|
|
1270
|
+
for (const [key, value] of this.writeBuffer) await this._diskWrite(key, value, newVersion);
|
|
1271
|
+
for (const key of this.deleteBuffer) await this._diskDelete(key, newVersion);
|
|
1272
|
+
this.version = newVersion;
|
|
1273
|
+
this._cleanupDeletedCache();
|
|
1074
1274
|
}
|
|
1075
|
-
for (const [key, value] of child.writeBuffer) {
|
|
1076
|
-
await this._diskWrite(key, value, newVersion);
|
|
1077
|
-
}
|
|
1078
|
-
for (const key of child.deleteBuffer) {
|
|
1079
|
-
await this._diskDelete(key, newVersion);
|
|
1080
|
-
}
|
|
1081
|
-
this.version = newVersion;
|
|
1082
|
-
this.root.activeTransactions.delete(child);
|
|
1083
|
-
this._cleanupDeletedCache();
|
|
1084
1275
|
return null;
|
|
1085
1276
|
}
|
|
1086
1277
|
});
|
|
1087
1278
|
}
|
|
1088
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
1089
1279
|
async _diskWrite(key, value, version) {
|
|
1090
1280
|
const strategy = this.strategy;
|
|
1091
1281
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1092
|
-
|
|
1093
|
-
|
|
1282
|
+
const rootAsAny = this.root;
|
|
1283
|
+
if (await this._diskExists(key, version)) {
|
|
1284
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1094
1285
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1095
|
-
this.deletedCache.get(key).push({
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1286
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
1287
|
+
rootAsAny.diskCache.set(key, value);
|
|
1288
|
+
} else {
|
|
1289
|
+
rootAsAny.diskCache.set(key, value);
|
|
1099
1290
|
}
|
|
1100
1291
|
await strategy.write(key, value);
|
|
1101
1292
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -1106,14 +1297,19 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1106
1297
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1107
1298
|
const versions = this.versionIndex.get(key);
|
|
1108
1299
|
if (!versions) {
|
|
1109
|
-
|
|
1300
|
+
const rootAsAny = this.root;
|
|
1301
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
1302
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1303
|
+
rootAsAny.diskCache.set(key, val);
|
|
1304
|
+
return val;
|
|
1305
|
+
}
|
|
1306
|
+
return null;
|
|
1110
1307
|
}
|
|
1111
1308
|
let targetVerObj = null;
|
|
1112
1309
|
let nextVerObj = null;
|
|
1113
1310
|
for (const v of versions) {
|
|
1114
|
-
if (v.version <= snapshotVersion)
|
|
1115
|
-
|
|
1116
|
-
} else {
|
|
1311
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
1312
|
+
else {
|
|
1117
1313
|
nextVerObj = v;
|
|
1118
1314
|
break;
|
|
1119
1315
|
}
|
|
@@ -1130,7 +1326,14 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1130
1326
|
}
|
|
1131
1327
|
if (!targetVerObj.exists) return null;
|
|
1132
1328
|
if (!nextVerObj) {
|
|
1133
|
-
return
|
|
1329
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
1330
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
1331
|
+
const rootAsAny = this.root;
|
|
1332
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1333
|
+
rootAsAny.diskCache.set(key, val);
|
|
1334
|
+
return val;
|
|
1335
|
+
}
|
|
1336
|
+
return null;
|
|
1134
1337
|
}
|
|
1135
1338
|
const cached = this.deletedCache.get(key);
|
|
1136
1339
|
if (cached) {
|
|
@@ -1144,393 +1347,89 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1144
1347
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1145
1348
|
const versions = this.versionIndex.get(key);
|
|
1146
1349
|
if (!versions) {
|
|
1147
|
-
|
|
1350
|
+
const rootAsAny = this.root;
|
|
1351
|
+
if (rootAsAny.diskCache.has(key)) return rootAsAny.diskCache.get(key) !== null;
|
|
1352
|
+
const exists = await strategy.exists(key);
|
|
1353
|
+
if (!exists) rootAsAny.diskCache.set(key, null);
|
|
1354
|
+
return exists;
|
|
1148
1355
|
}
|
|
1149
1356
|
let targetVerObj = null;
|
|
1357
|
+
let nextVerObj = null;
|
|
1150
1358
|
for (const v of versions) {
|
|
1151
|
-
if (v.version <= snapshotVersion)
|
|
1152
|
-
|
|
1153
|
-
|
|
1359
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
1360
|
+
else {
|
|
1361
|
+
nextVerObj = v;
|
|
1154
1362
|
break;
|
|
1155
1363
|
}
|
|
1156
1364
|
}
|
|
1157
|
-
if (!targetVerObj)
|
|
1365
|
+
if (!targetVerObj) {
|
|
1366
|
+
if (nextVerObj) {
|
|
1367
|
+
const cached = this.deletedCache.get(key);
|
|
1368
|
+
if (cached) {
|
|
1369
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
1370
|
+
if (match) return true;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return false;
|
|
1374
|
+
}
|
|
1158
1375
|
return targetVerObj.exists;
|
|
1159
1376
|
}
|
|
1160
1377
|
async _diskDelete(key, snapshotVersion) {
|
|
1161
1378
|
const strategy = this.strategy;
|
|
1162
1379
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1163
|
-
|
|
1164
|
-
|
|
1380
|
+
const rootAsAny = this.root;
|
|
1381
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
1382
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1165
1383
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1166
|
-
this.deletedCache.get(key).push({
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
});
|
|
1384
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
1385
|
+
await strategy.delete(key);
|
|
1386
|
+
rootAsAny.diskCache.delete(key);
|
|
1170
1387
|
}
|
|
1171
|
-
await strategy.delete(key);
|
|
1172
1388
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
1173
1389
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
1174
1390
|
}
|
|
1175
1391
|
};
|
|
1176
1392
|
|
|
1177
|
-
//
|
|
1178
|
-
var
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
this.
|
|
1197
|
-
this.
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
if (this.head) this.head.prev = node;
|
|
1218
|
-
this.head = node;
|
|
1219
|
-
if (!this.tail) this.tail = node;
|
|
1220
|
-
}
|
|
1221
|
-
/**
|
|
1222
|
-
* Stores or updates a value by key.
|
|
1223
|
-
* If the capacity is exceeded, the least recently used item (tail) is removed.
|
|
1224
|
-
* @param key The key to store.
|
|
1225
|
-
* @param value The value to store.
|
|
1226
|
-
*/
|
|
1227
|
-
set(key, value) {
|
|
1228
|
-
const existing = this.map.get(key);
|
|
1229
|
-
if (existing) {
|
|
1230
|
-
existing.value = value;
|
|
1231
|
-
this.promote(existing);
|
|
1232
|
-
return;
|
|
1233
|
-
}
|
|
1234
|
-
const newNode = { key, value, prev: null, next: null };
|
|
1235
|
-
this.map.set(key, newNode);
|
|
1236
|
-
this.prepend(newNode);
|
|
1237
|
-
if (this.map.size > this.capacity && this.tail) {
|
|
1238
|
-
this.map.delete(this.tail.key);
|
|
1239
|
-
this.extract(this.tail);
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
/**
|
|
1243
|
-
* Retrieves a value by key.
|
|
1244
|
-
* Accessing the item moves it to the "most recently used" position.
|
|
1245
|
-
* @param key The key to look for.
|
|
1246
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
1247
|
-
*/
|
|
1248
|
-
get(key) {
|
|
1249
|
-
const node = this.map.get(key);
|
|
1250
|
-
if (!node) return void 0;
|
|
1251
|
-
this.promote(node);
|
|
1252
|
-
return node.value;
|
|
1253
|
-
}
|
|
1254
|
-
/**
|
|
1255
|
-
* Checks if a key exists in the cache without changing its access order.
|
|
1256
|
-
* @param key The key to check.
|
|
1257
|
-
* @returns True if the key exists, false otherwise.
|
|
1258
|
-
*/
|
|
1259
|
-
has(key) {
|
|
1260
|
-
return this.map.has(key);
|
|
1261
|
-
}
|
|
1262
|
-
/**
|
|
1263
|
-
* Removes a key and its associated value from the cache.
|
|
1264
|
-
* @param key The key to remove.
|
|
1265
|
-
* @returns True if the key was found and removed, false otherwise.
|
|
1266
|
-
*/
|
|
1267
|
-
delete(key) {
|
|
1268
|
-
const node = this.map.get(key);
|
|
1269
|
-
if (!node) return false;
|
|
1270
|
-
this.extract(node);
|
|
1271
|
-
this.map.delete(key);
|
|
1272
|
-
return true;
|
|
1273
|
-
}
|
|
1274
|
-
/**
|
|
1275
|
-
* Returns an iterator of keys in the order of most recently used to least recently used.
|
|
1276
|
-
* @returns An iterable iterator of keys.
|
|
1277
|
-
*/
|
|
1278
|
-
*keys() {
|
|
1279
|
-
let current = this.head;
|
|
1280
|
-
while (current) {
|
|
1281
|
-
yield current.key;
|
|
1282
|
-
current = current.next;
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
/**
|
|
1286
|
-
* Returns the current number of items in the cache.
|
|
1287
|
-
*/
|
|
1288
|
-
get size() {
|
|
1289
|
-
return this.map.size;
|
|
1290
|
-
}
|
|
1291
|
-
/**
|
|
1292
|
-
* Clears all items from the cache.
|
|
1293
|
-
*/
|
|
1294
|
-
clear() {
|
|
1295
|
-
this.map.clear();
|
|
1296
|
-
this.head = null;
|
|
1297
|
-
this.tail = null;
|
|
1298
|
-
}
|
|
1299
|
-
};
|
|
1300
|
-
var CacheEntanglement = class {
|
|
1301
|
-
creation;
|
|
1302
|
-
beforeUpdateHook;
|
|
1303
|
-
capacity;
|
|
1304
|
-
dependencies;
|
|
1305
|
-
caches;
|
|
1306
|
-
parameters;
|
|
1307
|
-
assignments;
|
|
1308
|
-
dependencyProperties;
|
|
1309
|
-
updateRequirements;
|
|
1310
|
-
constructor(creation, option) {
|
|
1311
|
-
option = option ?? {};
|
|
1312
|
-
const {
|
|
1313
|
-
dependencies,
|
|
1314
|
-
capacity,
|
|
1315
|
-
beforeUpdateHook
|
|
1316
|
-
} = option;
|
|
1317
|
-
this.creation = creation;
|
|
1318
|
-
this.beforeUpdateHook = beforeUpdateHook ?? (() => {
|
|
1319
|
-
});
|
|
1320
|
-
this.capacity = capacity ?? 100;
|
|
1321
|
-
this.assignments = [];
|
|
1322
|
-
this.caches = new LRUMap(this.capacity);
|
|
1323
|
-
this.parameters = /* @__PURE__ */ new Map();
|
|
1324
|
-
this.dependencies = dependencies ?? {};
|
|
1325
|
-
this.dependencyProperties = Object.keys(this.dependencies);
|
|
1326
|
-
this.updateRequirements = /* @__PURE__ */ new Set();
|
|
1327
|
-
for (const name in this.dependencies) {
|
|
1328
|
-
const dependency = this.dependencies[name];
|
|
1329
|
-
if (!dependency.assignments.includes(this)) {
|
|
1330
|
-
dependency.assignments.push(this);
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
}
|
|
1334
|
-
bubbleUpdateSignal(key) {
|
|
1335
|
-
this.updateRequirements.add(key);
|
|
1336
|
-
for (let i = 0, len = this.assignments.length; i < len; i++) {
|
|
1337
|
-
const t = this.assignments[i];
|
|
1338
|
-
const instance = t;
|
|
1339
|
-
for (const cacheKey of instance.caches.keys()) {
|
|
1340
|
-
if (cacheKey === key || cacheKey.startsWith(`${key}/`)) {
|
|
1341
|
-
instance.bubbleUpdateSignal(cacheKey);
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
dependencyKey(key) {
|
|
1347
|
-
const i = key.lastIndexOf("/");
|
|
1348
|
-
if (i === -1) {
|
|
1349
|
-
return key;
|
|
1350
|
-
}
|
|
1351
|
-
return key.substring(0, i);
|
|
1352
|
-
}
|
|
1353
|
-
/**
|
|
1354
|
-
* Returns all keys stored in the instance.
|
|
1355
|
-
*/
|
|
1356
|
-
keys() {
|
|
1357
|
-
return this.parameters.keys();
|
|
1358
|
-
}
|
|
1359
|
-
/**
|
|
1360
|
-
* Deletes all cache values stored in the instance.
|
|
1361
|
-
*/
|
|
1362
|
-
clear() {
|
|
1363
|
-
for (const key of this.keys()) {
|
|
1364
|
-
this.delete(key);
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
/**
|
|
1368
|
-
* Checks if there is a cache value stored in the key within the instance.
|
|
1369
|
-
* @param key The key to search.
|
|
1370
|
-
*/
|
|
1371
|
-
exists(key) {
|
|
1372
|
-
return this.parameters.has(key);
|
|
1373
|
-
}
|
|
1374
|
-
/**
|
|
1375
|
-
* Checks if there is a cache value stored in the key within the instance.
|
|
1376
|
-
* This method is an alias for `exists`.
|
|
1377
|
-
* @param key The key to search.
|
|
1378
|
-
*/
|
|
1379
|
-
has(key) {
|
|
1380
|
-
return this.exists(key);
|
|
1381
|
-
}
|
|
1382
|
-
/**
|
|
1383
|
-
* Deletes the cache value stored in the key within the instance.
|
|
1384
|
-
* @param key The key to delete.
|
|
1385
|
-
*/
|
|
1386
|
-
delete(key) {
|
|
1387
|
-
this.caches.delete(key);
|
|
1388
|
-
this.parameters.delete(key);
|
|
1389
|
-
this.updateRequirements.delete(key);
|
|
1390
|
-
for (let i = 0, len = this.assignments.length; i < len; i++) {
|
|
1391
|
-
const t = this.assignments[i];
|
|
1392
|
-
const instance = t;
|
|
1393
|
-
for (const cacheKey of instance.keys()) {
|
|
1394
|
-
if (cacheKey === key || cacheKey.startsWith(`${key}/`)) {
|
|
1395
|
-
instance.delete(cacheKey);
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
};
|
|
1401
|
-
var CacheData = class _CacheData {
|
|
1402
|
-
static StructuredClone = globalThis.structuredClone.bind(globalThis);
|
|
1403
|
-
_value;
|
|
1404
|
-
constructor(value) {
|
|
1405
|
-
this._value = value;
|
|
1406
|
-
}
|
|
1407
|
-
/**
|
|
1408
|
-
* This is cached data.
|
|
1409
|
-
* It was generated at the time of caching, so there is a risk of modification if it's an object due to shallow copying.
|
|
1410
|
-
* Therefore, if it's not a primitive type, please avoid using this value directly and use the `clone` method to use a copied version of the data.
|
|
1411
|
-
*/
|
|
1412
|
-
get raw() {
|
|
1413
|
-
return this._value;
|
|
1414
|
-
}
|
|
1415
|
-
/**
|
|
1416
|
-
* The method returns a copied value of the cached data.
|
|
1417
|
-
* You can pass a function as a parameter to copy the value. This parameter function should return the copied value.
|
|
1418
|
-
*
|
|
1419
|
-
* If no parameter is passed, it defaults to using `structuredClone` function to copy the value.
|
|
1420
|
-
* If you prefer shallow copying instead of deep copying,
|
|
1421
|
-
* you can use the default options `array-shallow-copy`, `object-shallow-copy` and `deep-copy`,
|
|
1422
|
-
* which are replaced with functions to shallow copy arrays and objects, respectively. This is a syntactic sugar.
|
|
1423
|
-
* @param strategy The function that returns the copied value.
|
|
1424
|
-
* If you want to perform a shallow copy, simply pass the strings `array-shallow-copy` or `object-shallow-copy` for easy use.
|
|
1425
|
-
* The `array-shallow-copy` strategy performs a shallow copy of an array.
|
|
1426
|
-
* The `object-shallow-copy` strategy performs a shallow copy of an object.
|
|
1427
|
-
* The `deep-copy` strategy performs a deep copy of the value using `structuredClone`.
|
|
1428
|
-
* The default is `deep-copy`.
|
|
1429
|
-
*/
|
|
1430
|
-
clone(strategy = "deep-copy") {
|
|
1431
|
-
if (strategy && typeof strategy !== "string") {
|
|
1432
|
-
return strategy(this.raw);
|
|
1433
|
-
}
|
|
1434
|
-
switch (strategy) {
|
|
1435
|
-
case "array-shallow-copy":
|
|
1436
|
-
return [].concat(this.raw);
|
|
1437
|
-
case "object-shallow-copy":
|
|
1438
|
-
return Object.assign({}, this.raw);
|
|
1439
|
-
case "deep-copy":
|
|
1440
|
-
default:
|
|
1441
|
-
return _CacheData.StructuredClone(this.raw);
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
};
|
|
1445
|
-
var CacheEntanglementSync = class extends CacheEntanglement {
|
|
1446
|
-
constructor(creation, option) {
|
|
1447
|
-
super(creation, option);
|
|
1448
|
-
}
|
|
1449
|
-
recache(key) {
|
|
1450
|
-
if (!this.parameters.has(key)) {
|
|
1451
|
-
return;
|
|
1452
|
-
}
|
|
1453
|
-
if (!this.caches.has(key) || this.updateRequirements.has(key)) {
|
|
1454
|
-
this.resolve(key, ...this.parameters.get(key));
|
|
1455
|
-
}
|
|
1456
|
-
return this.caches.get(key);
|
|
1457
|
-
}
|
|
1458
|
-
resolve(key, ...parameter) {
|
|
1459
|
-
const resolved = {};
|
|
1460
|
-
const dependencyKey = this.dependencyKey(key);
|
|
1461
|
-
this.beforeUpdateHook(key, dependencyKey, ...parameter);
|
|
1462
|
-
for (let i = 0, len = this.dependencyProperties.length; i < len; i++) {
|
|
1463
|
-
const name = this.dependencyProperties[i];
|
|
1464
|
-
const dependency = this.dependencies[name];
|
|
1465
|
-
if (!dependency.exists(key) && !dependency.exists(dependencyKey)) {
|
|
1466
|
-
throw new Error(`The key '${key}' or '${dependencyKey}' has not been assigned yet in dependency '${name.toString()}'.`, {
|
|
1467
|
-
cause: {
|
|
1468
|
-
from: this
|
|
1469
|
-
}
|
|
1470
|
-
});
|
|
1471
|
-
}
|
|
1472
|
-
const dependencyValue = dependency.recache(key) ?? dependency.recache(dependencyKey);
|
|
1473
|
-
resolved[name] = dependencyValue;
|
|
1474
|
-
}
|
|
1475
|
-
const value = new CacheData(this.creation(key, resolved, ...parameter));
|
|
1476
|
-
this.updateRequirements.delete(key);
|
|
1477
|
-
this.parameters.set(key, parameter);
|
|
1478
|
-
this.caches.set(key, value);
|
|
1479
|
-
return value;
|
|
1480
|
-
}
|
|
1481
|
-
get(key) {
|
|
1482
|
-
if (!this.parameters.has(key)) {
|
|
1483
|
-
throw new Error(`Cache value not found: ${key}`);
|
|
1484
|
-
}
|
|
1485
|
-
return this.cache(key, ...this.parameters.get(key));
|
|
1486
|
-
}
|
|
1487
|
-
cache(key, ...parameter) {
|
|
1488
|
-
if (!this.caches.has(key) || this.updateRequirements.has(key)) {
|
|
1489
|
-
this.resolve(key, ...parameter);
|
|
1490
|
-
}
|
|
1491
|
-
return this.caches.get(key);
|
|
1492
|
-
}
|
|
1493
|
-
update(key, ...parameter) {
|
|
1494
|
-
this.bubbleUpdateSignal(key);
|
|
1495
|
-
this.resolve(key, ...parameter);
|
|
1496
|
-
return this.caches.get(key);
|
|
1497
|
-
}
|
|
1498
|
-
};
|
|
1499
|
-
|
|
1500
|
-
// src/base/BPTreeTransaction.ts
|
|
1501
|
-
var BPTreeTransaction = class _BPTreeTransaction {
|
|
1502
|
-
_cachedRegexp;
|
|
1503
|
-
nodes;
|
|
1504
|
-
deletedNodeBuffer = /* @__PURE__ */ new Map();
|
|
1505
|
-
rootTx;
|
|
1506
|
-
mvccRoot;
|
|
1507
|
-
mvcc;
|
|
1508
|
-
strategy;
|
|
1509
|
-
comparator;
|
|
1510
|
-
option;
|
|
1511
|
-
order;
|
|
1512
|
-
rootId;
|
|
1513
|
-
verifierMap = {
|
|
1514
|
-
gt: (nv, v) => this.comparator.isHigher(nv, v),
|
|
1515
|
-
gte: (nv, v) => this.comparator.isHigher(nv, v) || this.comparator.isSame(nv, v),
|
|
1516
|
-
lt: (nv, v) => this.comparator.isLower(nv, v),
|
|
1517
|
-
lte: (nv, v) => this.comparator.isLower(nv, v) || this.comparator.isSame(nv, v),
|
|
1518
|
-
equal: (nv, v) => this.comparator.isSame(nv, v),
|
|
1519
|
-
notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
|
|
1520
|
-
or: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isSame(nv, v2)),
|
|
1521
|
-
primaryGt: (nv, v) => this.comparator.isPrimaryHigher(nv, v),
|
|
1522
|
-
primaryGte: (nv, v) => this.comparator.isPrimaryHigher(nv, v) || this.comparator.isPrimarySame(nv, v),
|
|
1523
|
-
primaryLt: (nv, v) => this.comparator.isPrimaryLower(nv, v),
|
|
1524
|
-
primaryLte: (nv, v) => this.comparator.isPrimaryLower(nv, v) || this.comparator.isPrimarySame(nv, v),
|
|
1525
|
-
primaryEqual: (nv, v) => this.comparator.isPrimarySame(nv, v),
|
|
1526
|
-
primaryNotEqual: (nv, v) => this.comparator.isPrimarySame(nv, v) === false,
|
|
1527
|
-
primaryOr: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isPrimarySame(nv, v2)),
|
|
1528
|
-
like: (nv, v) => {
|
|
1529
|
-
const nodeValue = this.comparator.match(nv);
|
|
1530
|
-
const value = v;
|
|
1531
|
-
const cache = this._cachedRegexp.cache(value);
|
|
1532
|
-
const regexp = cache.raw;
|
|
1533
|
-
return regexp.test(nodeValue);
|
|
1393
|
+
// src/base/BPTreeTransaction.ts
|
|
1394
|
+
var BPTreeTransaction = class _BPTreeTransaction {
|
|
1395
|
+
_cachedRegexp = /* @__PURE__ */ new Map();
|
|
1396
|
+
nodes = /* @__PURE__ */ new Map();
|
|
1397
|
+
deletedNodeBuffer = /* @__PURE__ */ new Map();
|
|
1398
|
+
rootTx;
|
|
1399
|
+
mvccRoot;
|
|
1400
|
+
mvcc;
|
|
1401
|
+
strategy;
|
|
1402
|
+
comparator;
|
|
1403
|
+
option;
|
|
1404
|
+
order;
|
|
1405
|
+
rootId;
|
|
1406
|
+
isInitialized = false;
|
|
1407
|
+
isDestroyed = false;
|
|
1408
|
+
verifierMap = {
|
|
1409
|
+
gt: (nv, v) => this.comparator.isHigher(nv, v),
|
|
1410
|
+
gte: (nv, v) => this.comparator.isHigher(nv, v) || this.comparator.isSame(nv, v),
|
|
1411
|
+
lt: (nv, v) => this.comparator.isLower(nv, v),
|
|
1412
|
+
lte: (nv, v) => this.comparator.isLower(nv, v) || this.comparator.isSame(nv, v),
|
|
1413
|
+
equal: (nv, v) => this.comparator.isSame(nv, v),
|
|
1414
|
+
notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
|
|
1415
|
+
or: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isSame(nv, v2)),
|
|
1416
|
+
primaryGt: (nv, v) => this.comparator.isPrimaryHigher(nv, v),
|
|
1417
|
+
primaryGte: (nv, v) => this.comparator.isPrimaryHigher(nv, v) || this.comparator.isPrimarySame(nv, v),
|
|
1418
|
+
primaryLt: (nv, v) => this.comparator.isPrimaryLower(nv, v),
|
|
1419
|
+
primaryLte: (nv, v) => this.comparator.isPrimaryLower(nv, v) || this.comparator.isPrimarySame(nv, v),
|
|
1420
|
+
primaryEqual: (nv, v) => this.comparator.isPrimarySame(nv, v),
|
|
1421
|
+
primaryNotEqual: (nv, v) => this.comparator.isPrimarySame(nv, v) === false,
|
|
1422
|
+
primaryOr: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isPrimarySame(nv, v2)),
|
|
1423
|
+
like: (nv, v) => {
|
|
1424
|
+
const nodeValue = this.comparator.match(nv);
|
|
1425
|
+
const value = v;
|
|
1426
|
+
if (!this._cachedRegexp.has(value)) {
|
|
1427
|
+
const pattern = value.replace(/%/g, ".*").replace(/_/g, ".");
|
|
1428
|
+
const regexp2 = new RegExp(`^${pattern}$`, "i");
|
|
1429
|
+
this._cachedRegexp.set(value, regexp2);
|
|
1430
|
+
}
|
|
1431
|
+
const regexp = this._cachedRegexp.get(value);
|
|
1432
|
+
return regexp.test(nodeValue);
|
|
1534
1433
|
}
|
|
1535
1434
|
};
|
|
1536
1435
|
verifierStartNode = {
|
|
@@ -1701,6 +1600,9 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1701
1600
|
}
|
|
1702
1601
|
return true;
|
|
1703
1602
|
}
|
|
1603
|
+
_cloneNode(node) {
|
|
1604
|
+
return JSON.parse(JSON.stringify(node));
|
|
1605
|
+
}
|
|
1704
1606
|
/**
|
|
1705
1607
|
* Selects the best driver key from a condition object.
|
|
1706
1608
|
* The driver key determines the starting point and traversal direction for queries.
|
|
@@ -1733,17 +1635,6 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1733
1635
|
this.strategy = strategy;
|
|
1734
1636
|
this.comparator = comparator;
|
|
1735
1637
|
this.option = option ?? {};
|
|
1736
|
-
this.nodes = new LRUMap(this.option.capacity ?? 1e3);
|
|
1737
|
-
this._cachedRegexp = this._createCachedRegexp();
|
|
1738
|
-
}
|
|
1739
|
-
_createCachedRegexp() {
|
|
1740
|
-
return new CacheEntanglementSync((key) => {
|
|
1741
|
-
const pattern = key.replace(/%/g, ".*").replace(/_/g, ".");
|
|
1742
|
-
const regexp = new RegExp(`^${pattern}$`, "i");
|
|
1743
|
-
return regexp;
|
|
1744
|
-
}, {
|
|
1745
|
-
capacity: this.option.capacity ?? 1e3
|
|
1746
|
-
});
|
|
1747
1638
|
}
|
|
1748
1639
|
ensureValues(v) {
|
|
1749
1640
|
if (!Array.isArray(v)) {
|
|
@@ -1774,13 +1665,25 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1774
1665
|
getResultEntries() {
|
|
1775
1666
|
return this.mvcc.getResultEntries();
|
|
1776
1667
|
}
|
|
1668
|
+
_clearCache() {
|
|
1669
|
+
this._cachedRegexp.clear();
|
|
1670
|
+
}
|
|
1777
1671
|
/**
|
|
1778
1672
|
* Clears all cached nodes.
|
|
1779
1673
|
* This method is useful for freeing up memory when the tree is no longer needed.
|
|
1780
1674
|
*/
|
|
1781
1675
|
clear() {
|
|
1782
|
-
this.
|
|
1783
|
-
|
|
1676
|
+
if (this.rootTx !== this) {
|
|
1677
|
+
throw new Error("Cannot call clear on a nested transaction");
|
|
1678
|
+
}
|
|
1679
|
+
this._clearInternal();
|
|
1680
|
+
}
|
|
1681
|
+
_clearInternal() {
|
|
1682
|
+
if (this.isDestroyed) {
|
|
1683
|
+
throw new Error("Transaction already destroyed");
|
|
1684
|
+
}
|
|
1685
|
+
this._clearCache();
|
|
1686
|
+
this.isDestroyed = true;
|
|
1784
1687
|
}
|
|
1785
1688
|
_binarySearchValues(values, target, usePrimary = false, upperBound = false) {
|
|
1786
1689
|
let low = 0;
|
|
@@ -1816,9 +1719,6 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1816
1719
|
);
|
|
1817
1720
|
}
|
|
1818
1721
|
getNode(id) {
|
|
1819
|
-
if (this.nodes.has(id)) {
|
|
1820
|
-
return this.nodes.get(id);
|
|
1821
|
-
}
|
|
1822
1722
|
return this.mvcc.read(id);
|
|
1823
1723
|
}
|
|
1824
1724
|
/**
|
|
@@ -1836,23 +1736,22 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1836
1736
|
prev
|
|
1837
1737
|
};
|
|
1838
1738
|
this.mvcc.create(id, node);
|
|
1839
|
-
this.nodes.set(id, node);
|
|
1840
1739
|
return node;
|
|
1841
1740
|
}
|
|
1842
1741
|
_updateNode(node) {
|
|
1742
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1843
1745
|
this.mvcc.write(node.id, node);
|
|
1844
|
-
this.nodes.set(node.id, node);
|
|
1845
1746
|
}
|
|
1846
1747
|
_deleteNode(node) {
|
|
1748
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
1749
|
+
return;
|
|
1750
|
+
}
|
|
1847
1751
|
this.mvcc.delete(node.id);
|
|
1848
|
-
this.nodes.delete(node.id);
|
|
1849
1752
|
}
|
|
1850
1753
|
_readHead() {
|
|
1851
|
-
|
|
1852
|
-
return this.nodes.get("__HEAD__") ?? null;
|
|
1853
|
-
}
|
|
1854
|
-
const head = this.mvcc.read("__HEAD__");
|
|
1855
|
-
return head ?? null;
|
|
1754
|
+
return this.mvcc.read("__HEAD__");
|
|
1856
1755
|
}
|
|
1857
1756
|
_writeHead(head) {
|
|
1858
1757
|
if (!this.mvcc.exists("__HEAD__")) {
|
|
@@ -1860,41 +1759,45 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1860
1759
|
} else {
|
|
1861
1760
|
this.mvcc.write("__HEAD__", head);
|
|
1862
1761
|
}
|
|
1863
|
-
this.nodes.set("__HEAD__", head);
|
|
1864
1762
|
this.rootId = head.root;
|
|
1865
1763
|
}
|
|
1866
1764
|
_insertAtLeaf(node, key, value) {
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1765
|
+
let leaf = node;
|
|
1766
|
+
leaf = this._cloneNode(leaf);
|
|
1767
|
+
if (leaf.values.length) {
|
|
1768
|
+
for (let i = 0, len = leaf.values.length; i < len; i++) {
|
|
1769
|
+
const nValue = leaf.values[i];
|
|
1870
1770
|
if (this.comparator.isSame(value, nValue)) {
|
|
1871
|
-
const keys =
|
|
1771
|
+
const keys = leaf.keys[i];
|
|
1872
1772
|
if (keys.includes(key)) {
|
|
1873
1773
|
break;
|
|
1874
1774
|
}
|
|
1875
1775
|
keys.push(key);
|
|
1876
|
-
this._updateNode(
|
|
1877
|
-
return;
|
|
1776
|
+
this._updateNode(leaf);
|
|
1777
|
+
return leaf;
|
|
1878
1778
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
this._updateNode(
|
|
1882
|
-
return;
|
|
1883
|
-
} else if (i + 1 ===
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
this._updateNode(
|
|
1887
|
-
return;
|
|
1779
|
+
leaf.values.splice(i, 0, value);
|
|
1780
|
+
leaf.keys.splice(i, 0, [key]);
|
|
1781
|
+
this._updateNode(leaf);
|
|
1782
|
+
return leaf;
|
|
1783
|
+
} else if (i + 1 === leaf.values.length) {
|
|
1784
|
+
leaf.values.push(value);
|
|
1785
|
+
leaf.keys.push([key]);
|
|
1786
|
+
this._updateNode(leaf);
|
|
1787
|
+
return leaf;
|
|
1888
1788
|
}
|
|
1889
1789
|
}
|
|
1890
1790
|
} else {
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
this._updateNode(
|
|
1894
|
-
return;
|
|
1791
|
+
leaf.values = [value];
|
|
1792
|
+
leaf.keys = [[key]];
|
|
1793
|
+
this._updateNode(leaf);
|
|
1794
|
+
return leaf;
|
|
1895
1795
|
}
|
|
1796
|
+
return leaf;
|
|
1896
1797
|
}
|
|
1897
1798
|
_insertInParent(node, value, pointer) {
|
|
1799
|
+
node = this._cloneNode(node);
|
|
1800
|
+
pointer = this._cloneNode(pointer);
|
|
1898
1801
|
if (this.rootId === node.id) {
|
|
1899
1802
|
const root = this._createNode(false, [node.id, pointer.id], [value]);
|
|
1900
1803
|
this.rootId = root.id;
|
|
@@ -1913,7 +1816,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1913
1816
|
this._updateNode(pointer);
|
|
1914
1817
|
return;
|
|
1915
1818
|
}
|
|
1916
|
-
const parentNode = this.getNode(node.parent);
|
|
1819
|
+
const parentNode = this._cloneNode(this.getNode(node.parent));
|
|
1917
1820
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
1918
1821
|
if (nodeIndex === -1) {
|
|
1919
1822
|
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
@@ -1929,7 +1832,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1929
1832
|
leftSibling.next = pointer.id;
|
|
1930
1833
|
this._updateNode(leftSibling);
|
|
1931
1834
|
if (oldNextId) {
|
|
1932
|
-
const oldNext = this.getNode(oldNextId);
|
|
1835
|
+
const oldNext = this._cloneNode(this.getNode(oldNextId));
|
|
1933
1836
|
oldNext.prev = pointer.id;
|
|
1934
1837
|
this._updateNode(oldNext);
|
|
1935
1838
|
}
|
|
@@ -1946,12 +1849,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1946
1849
|
parentNode.values = parentNode.values.slice(0, mid);
|
|
1947
1850
|
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
1948
1851
|
for (const k of parentNode.keys) {
|
|
1949
|
-
const n = this.getNode(k);
|
|
1852
|
+
const n = this._cloneNode(this.getNode(k));
|
|
1950
1853
|
n.parent = parentNode.id;
|
|
1951
1854
|
this._updateNode(n);
|
|
1952
1855
|
}
|
|
1953
1856
|
for (const k of parentPointer.keys) {
|
|
1954
|
-
const n = this.getNode(k);
|
|
1857
|
+
const n = this._cloneNode(this.getNode(k));
|
|
1955
1858
|
n.parent = parentPointer.id;
|
|
1956
1859
|
this._updateNode(n);
|
|
1957
1860
|
}
|
|
@@ -2086,28 +1989,46 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2086
1989
|
}
|
|
2087
1990
|
}
|
|
2088
1991
|
init() {
|
|
2089
|
-
this.
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
const { root, order } = head;
|
|
2101
|
-
this.strategy.head = head;
|
|
2102
|
-
this.order = order;
|
|
2103
|
-
this._writeHead({
|
|
2104
|
-
root,
|
|
2105
|
-
order: this.order,
|
|
2106
|
-
data: this.strategy.head.data
|
|
2107
|
-
});
|
|
1992
|
+
if (this.rootTx !== this) {
|
|
1993
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
1994
|
+
}
|
|
1995
|
+
this._initInternal();
|
|
1996
|
+
}
|
|
1997
|
+
_initInternal() {
|
|
1998
|
+
if (this.isInitialized) {
|
|
1999
|
+
throw new Error("Transaction already initialized");
|
|
2000
|
+
}
|
|
2001
|
+
if (this.isDestroyed) {
|
|
2002
|
+
throw new Error("Transaction already destroyed");
|
|
2108
2003
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2004
|
+
this.isInitialized = true;
|
|
2005
|
+
try {
|
|
2006
|
+
this._clearCache();
|
|
2007
|
+
const head = this._readHead();
|
|
2008
|
+
if (head === null) {
|
|
2009
|
+
this.order = this.strategy.order;
|
|
2010
|
+
const root = this._createNode(true, [], []);
|
|
2011
|
+
this._writeHead({
|
|
2012
|
+
root: root.id,
|
|
2013
|
+
order: this.order,
|
|
2014
|
+
data: this.strategy.head.data
|
|
2015
|
+
});
|
|
2016
|
+
} else {
|
|
2017
|
+
const { root, order } = head;
|
|
2018
|
+
this.strategy.head = head;
|
|
2019
|
+
this.order = order;
|
|
2020
|
+
this._writeHead({
|
|
2021
|
+
root,
|
|
2022
|
+
order: this.order,
|
|
2023
|
+
data: this.strategy.head.data
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
if (this.order < 3) {
|
|
2027
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
2028
|
+
}
|
|
2029
|
+
} catch (e) {
|
|
2030
|
+
this.isInitialized = false;
|
|
2031
|
+
throw e;
|
|
2111
2032
|
}
|
|
2112
2033
|
}
|
|
2113
2034
|
exists(key, value) {
|
|
@@ -2121,21 +2042,6 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2121
2042
|
}
|
|
2122
2043
|
return false;
|
|
2123
2044
|
}
|
|
2124
|
-
forceUpdate(id) {
|
|
2125
|
-
if (id) {
|
|
2126
|
-
this.nodes.delete(id);
|
|
2127
|
-
this.getNode(id);
|
|
2128
|
-
return 1;
|
|
2129
|
-
}
|
|
2130
|
-
const keys = Array.from(this.nodes.keys());
|
|
2131
|
-
for (const key of keys) {
|
|
2132
|
-
this.nodes.delete(key);
|
|
2133
|
-
}
|
|
2134
|
-
for (const key of keys) {
|
|
2135
|
-
this.getNode(key);
|
|
2136
|
-
}
|
|
2137
|
-
return keys.length;
|
|
2138
|
-
}
|
|
2139
2045
|
get(key) {
|
|
2140
2046
|
let node = this.leftestNode();
|
|
2141
2047
|
while (true) {
|
|
@@ -2221,10 +2127,10 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2221
2127
|
return map;
|
|
2222
2128
|
}
|
|
2223
2129
|
insert(key, value) {
|
|
2224
|
-
|
|
2225
|
-
this._insertAtLeaf(before, key, value);
|
|
2130
|
+
let before = this.insertableNode(value);
|
|
2131
|
+
before = this._insertAtLeaf(before, key, value);
|
|
2226
2132
|
if (before.values.length === this.order) {
|
|
2227
|
-
|
|
2133
|
+
let after = this._createNode(
|
|
2228
2134
|
true,
|
|
2229
2135
|
[],
|
|
2230
2136
|
[],
|
|
@@ -2233,14 +2139,18 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2233
2139
|
null
|
|
2234
2140
|
);
|
|
2235
2141
|
const mid = Math.ceil(this.order / 2) - 1;
|
|
2142
|
+
after = this._cloneNode(after);
|
|
2236
2143
|
after.values = before.values.slice(mid + 1);
|
|
2237
2144
|
after.keys = before.keys.slice(mid + 1);
|
|
2238
2145
|
before.values = before.values.slice(0, mid + 1);
|
|
2239
2146
|
before.keys = before.keys.slice(0, mid + 1);
|
|
2147
|
+
this._updateNode(before);
|
|
2148
|
+
this._updateNode(after);
|
|
2240
2149
|
this._insertInParent(before, after.values[0], after);
|
|
2241
2150
|
}
|
|
2242
2151
|
}
|
|
2243
2152
|
_deleteEntry(node, key) {
|
|
2153
|
+
node = this._cloneNode(node);
|
|
2244
2154
|
if (!node.leaf) {
|
|
2245
2155
|
let keyIndex = -1;
|
|
2246
2156
|
for (let i = 0, len = node.keys.length; i < len; i++) {
|
|
@@ -2259,7 +2169,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2259
2169
|
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
2260
2170
|
const keys = node.keys;
|
|
2261
2171
|
this._deleteNode(node);
|
|
2262
|
-
const newRoot = this.getNode(keys[0]);
|
|
2172
|
+
const newRoot = this._cloneNode(this.getNode(keys[0]));
|
|
2263
2173
|
newRoot.parent = null;
|
|
2264
2174
|
this._updateNode(newRoot);
|
|
2265
2175
|
this._writeHead({
|
|
@@ -2267,17 +2177,17 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2267
2177
|
order: this.order,
|
|
2268
2178
|
data: this.strategy.head.data
|
|
2269
2179
|
});
|
|
2270
|
-
return;
|
|
2180
|
+
return node;
|
|
2271
2181
|
} else if (this.rootId === node.id) {
|
|
2272
2182
|
this._writeHead({
|
|
2273
2183
|
root: node.id,
|
|
2274
2184
|
order: this.order,
|
|
2275
2185
|
data: this.strategy.head.data
|
|
2276
2186
|
});
|
|
2277
|
-
return;
|
|
2187
|
+
return node;
|
|
2278
2188
|
} else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
|
|
2279
2189
|
if (node.parent === null) {
|
|
2280
|
-
return;
|
|
2190
|
+
return node;
|
|
2281
2191
|
}
|
|
2282
2192
|
let isPredecessor = false;
|
|
2283
2193
|
let parentNode = this.getNode(node.parent);
|
|
@@ -2289,11 +2199,11 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2289
2199
|
const nKey = parentNode.keys[i];
|
|
2290
2200
|
if (nKey === node.id) {
|
|
2291
2201
|
if (i > 0) {
|
|
2292
|
-
prevNode = this.getNode(parentNode.keys[i - 1]);
|
|
2202
|
+
prevNode = this._cloneNode(this.getNode(parentNode.keys[i - 1]));
|
|
2293
2203
|
prevValue = parentNode.values[i - 1];
|
|
2294
2204
|
}
|
|
2295
2205
|
if (i < parentNode.keys.length - 1) {
|
|
2296
|
-
nextNode = this.getNode(parentNode.keys[i + 1]);
|
|
2206
|
+
nextNode = this._cloneNode(this.getNode(parentNode.keys[i + 1]));
|
|
2297
2207
|
postValue = parentNode.values[i];
|
|
2298
2208
|
}
|
|
2299
2209
|
}
|
|
@@ -2318,7 +2228,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2318
2228
|
}
|
|
2319
2229
|
}
|
|
2320
2230
|
if (!pointer) {
|
|
2321
|
-
return;
|
|
2231
|
+
return node;
|
|
2322
2232
|
}
|
|
2323
2233
|
if (node.values.length + pointer.values.length < this.order) {
|
|
2324
2234
|
if (!isPredecessor) {
|
|
@@ -2332,7 +2242,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2332
2242
|
} else {
|
|
2333
2243
|
pointer.next = node.next;
|
|
2334
2244
|
if (pointer.next) {
|
|
2335
|
-
const n = this.getNode(pointer.next);
|
|
2245
|
+
const n = this._cloneNode(this.getNode(pointer.next));
|
|
2336
2246
|
n.prev = pointer.id;
|
|
2337
2247
|
this._updateNode(n);
|
|
2338
2248
|
}
|
|
@@ -2341,14 +2251,14 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2341
2251
|
if (!pointer.leaf) {
|
|
2342
2252
|
const keys = pointer.keys;
|
|
2343
2253
|
for (const key2 of keys) {
|
|
2344
|
-
const node2 = this.getNode(key2);
|
|
2254
|
+
const node2 = this._cloneNode(this.getNode(key2));
|
|
2345
2255
|
node2.parent = pointer.id;
|
|
2346
2256
|
this._updateNode(node2);
|
|
2347
2257
|
}
|
|
2348
2258
|
}
|
|
2349
2259
|
this._deleteNode(node);
|
|
2350
2260
|
this._updateNode(pointer);
|
|
2351
|
-
this._deleteEntry(this.getNode(node.parent), node.id);
|
|
2261
|
+
this._deleteEntry(this._cloneNode(this.getNode(node.parent)), node.id);
|
|
2352
2262
|
} else {
|
|
2353
2263
|
if (isPredecessor) {
|
|
2354
2264
|
let pointerPm;
|
|
@@ -2358,7 +2268,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2358
2268
|
pointerKm = pointer.values.splice(-1)[0];
|
|
2359
2269
|
node.keys = [pointerPm, ...node.keys];
|
|
2360
2270
|
node.values = [guess, ...node.values];
|
|
2361
|
-
parentNode = this.getNode(node.parent);
|
|
2271
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2362
2272
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2363
2273
|
if (nodeIndex > 0) {
|
|
2364
2274
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
@@ -2369,7 +2279,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2369
2279
|
pointerKm = pointer.values.splice(-1)[0];
|
|
2370
2280
|
node.keys = [pointerPm, ...node.keys];
|
|
2371
2281
|
node.values = [pointerKm, ...node.values];
|
|
2372
|
-
parentNode = this.getNode(node.parent);
|
|
2282
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2373
2283
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2374
2284
|
if (nodeIndex > 0) {
|
|
2375
2285
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
@@ -2386,7 +2296,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2386
2296
|
pointerK0 = pointer.values.splice(0, 1)[0];
|
|
2387
2297
|
node.keys = [...node.keys, pointerP0];
|
|
2388
2298
|
node.values = [...node.values, guess];
|
|
2389
|
-
parentNode = this.getNode(node.parent);
|
|
2299
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2390
2300
|
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
2391
2301
|
if (pointerIndex > 0) {
|
|
2392
2302
|
parentNode.values[pointerIndex - 1] = pointerK0;
|
|
@@ -2397,7 +2307,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2397
2307
|
pointerK0 = pointer.values.splice(0, 1)[0];
|
|
2398
2308
|
node.keys = [...node.keys, pointerP0];
|
|
2399
2309
|
node.values = [...node.values, pointerK0];
|
|
2400
|
-
parentNode = this.getNode(node.parent);
|
|
2310
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2401
2311
|
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
2402
2312
|
if (pointerIndex > 0) {
|
|
2403
2313
|
parentNode.values[pointerIndex - 1] = pointer.values[0];
|
|
@@ -2409,21 +2319,21 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2409
2319
|
}
|
|
2410
2320
|
if (!pointer.leaf) {
|
|
2411
2321
|
for (const key2 of pointer.keys) {
|
|
2412
|
-
const n = this.getNode(key2);
|
|
2322
|
+
const n = this._cloneNode(this.getNode(key2));
|
|
2413
2323
|
n.parent = pointer.id;
|
|
2414
2324
|
this._updateNode(n);
|
|
2415
2325
|
}
|
|
2416
2326
|
}
|
|
2417
2327
|
if (!node.leaf) {
|
|
2418
2328
|
for (const key2 of node.keys) {
|
|
2419
|
-
const n = this.getNode(key2);
|
|
2329
|
+
const n = this._cloneNode(this.getNode(key2));
|
|
2420
2330
|
n.parent = node.id;
|
|
2421
2331
|
this._updateNode(n);
|
|
2422
2332
|
}
|
|
2423
2333
|
}
|
|
2424
2334
|
if (!parentNode.leaf) {
|
|
2425
2335
|
for (const key2 of parentNode.keys) {
|
|
2426
|
-
const n = this.getNode(key2);
|
|
2336
|
+
const n = this._cloneNode(this.getNode(key2));
|
|
2427
2337
|
n.parent = parentNode.id;
|
|
2428
2338
|
this._updateNode(n);
|
|
2429
2339
|
}
|
|
@@ -2432,10 +2342,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2432
2342
|
} else {
|
|
2433
2343
|
this._updateNode(node);
|
|
2434
2344
|
}
|
|
2345
|
+
return node;
|
|
2435
2346
|
}
|
|
2436
2347
|
delete(key, value) {
|
|
2437
2348
|
let node = this.insertableNodeByPrimary(value);
|
|
2438
2349
|
let found = false;
|
|
2350
|
+
node = this._cloneNode(node);
|
|
2439
2351
|
while (true) {
|
|
2440
2352
|
let i = node.values.length;
|
|
2441
2353
|
while (i--) {
|
|
@@ -2450,7 +2362,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2450
2362
|
node.values.splice(i, 1);
|
|
2451
2363
|
}
|
|
2452
2364
|
this._updateNode(node);
|
|
2453
|
-
this._deleteEntry(node, key);
|
|
2365
|
+
node = this._deleteEntry(node, key);
|
|
2454
2366
|
found = true;
|
|
2455
2367
|
break;
|
|
2456
2368
|
}
|
|
@@ -2485,20 +2397,14 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2485
2397
|
commit(label) {
|
|
2486
2398
|
let result = this.mvcc.commit(label);
|
|
2487
2399
|
if (result.success) {
|
|
2488
|
-
|
|
2489
|
-
if (
|
|
2490
|
-
|
|
2400
|
+
const isRootTx = this.rootTx !== this;
|
|
2401
|
+
if (isRootTx) {
|
|
2402
|
+
result = this.rootTx.commit(label);
|
|
2403
|
+
if (result.success) {
|
|
2404
|
+
this.rootTx.rootId = this.rootId;
|
|
2405
|
+
}
|
|
2491
2406
|
}
|
|
2492
2407
|
if (result.success) {
|
|
2493
|
-
for (const r of result.created) {
|
|
2494
|
-
this.nodes.set(r.key, r.data);
|
|
2495
|
-
}
|
|
2496
|
-
for (const r of result.updated) {
|
|
2497
|
-
this.nodes.set(r.key, r.data);
|
|
2498
|
-
}
|
|
2499
|
-
for (const r of result.deleted) {
|
|
2500
|
-
this.nodes.delete(r.key);
|
|
2501
|
-
}
|
|
2502
2408
|
}
|
|
2503
2409
|
}
|
|
2504
2410
|
return result;
|
|
@@ -2546,7 +2452,9 @@ var BPTreeMVCCStrategySync = class extends SyncMVCCStrategy {
|
|
|
2546
2452
|
// src/BPTreeSync.ts
|
|
2547
2453
|
var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
2548
2454
|
constructor(strategy, comparator, option) {
|
|
2549
|
-
const mvccRoot = new SyncMVCCTransaction(new BPTreeMVCCStrategySync(strategy)
|
|
2455
|
+
const mvccRoot = new SyncMVCCTransaction(new BPTreeMVCCStrategySync(strategy), {
|
|
2456
|
+
cacheCapacity: option?.capacity ?? void 0
|
|
2457
|
+
});
|
|
2550
2458
|
super(
|
|
2551
2459
|
null,
|
|
2552
2460
|
mvccRoot,
|
|
@@ -2570,7 +2478,7 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2570
2478
|
this.comparator,
|
|
2571
2479
|
this.option
|
|
2572
2480
|
);
|
|
2573
|
-
tx.
|
|
2481
|
+
tx._initInternal();
|
|
2574
2482
|
return tx;
|
|
2575
2483
|
}
|
|
2576
2484
|
insert(key, value) {
|
|
@@ -2580,7 +2488,6 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2580
2488
|
if (!result.success) {
|
|
2581
2489
|
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2582
2490
|
}
|
|
2583
|
-
this.rootId = tx.getRootId();
|
|
2584
2491
|
}
|
|
2585
2492
|
delete(key, value) {
|
|
2586
2493
|
const tx = this.createTransaction();
|
|
@@ -2589,394 +2496,673 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2589
2496
|
if (!result.success) {
|
|
2590
2497
|
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2591
2498
|
}
|
|
2592
|
-
this.rootId = tx.getRootId();
|
|
2593
2499
|
}
|
|
2594
2500
|
};
|
|
2595
2501
|
|
|
2596
|
-
//
|
|
2597
|
-
var
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
comparator,
|
|
2605
|
-
option
|
|
2606
|
-
);
|
|
2502
|
+
// node_modules/ryoiki/dist/esm/index.mjs
|
|
2503
|
+
var Ryoiki2 = class _Ryoiki2 {
|
|
2504
|
+
readings;
|
|
2505
|
+
writings;
|
|
2506
|
+
readQueue;
|
|
2507
|
+
writeQueue;
|
|
2508
|
+
static async CatchError(promise) {
|
|
2509
|
+
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
2607
2510
|
}
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2511
|
+
static IsRangeOverlap(a, b) {
|
|
2512
|
+
const [start1, end1] = a;
|
|
2513
|
+
const [start2, end2] = b;
|
|
2514
|
+
if (end1 <= start2 || end2 <= start1) {
|
|
2515
|
+
return false;
|
|
2611
2516
|
}
|
|
2612
|
-
return
|
|
2517
|
+
return true;
|
|
2518
|
+
}
|
|
2519
|
+
static ERR_ALREADY_EXISTS(lockId) {
|
|
2520
|
+
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
2521
|
+
}
|
|
2522
|
+
static ERR_NOT_EXISTS(lockId) {
|
|
2523
|
+
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
2524
|
+
}
|
|
2525
|
+
static ERR_TIMEOUT(lockId, timeout) {
|
|
2526
|
+
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
2613
2527
|
}
|
|
2614
2528
|
/**
|
|
2615
|
-
*
|
|
2529
|
+
* Constructs a new instance of the Ryoiki class.
|
|
2616
2530
|
*/
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
values,
|
|
2623
|
-
leaf,
|
|
2624
|
-
parent,
|
|
2625
|
-
next,
|
|
2626
|
-
prev
|
|
2627
|
-
};
|
|
2628
|
-
await this.mvcc.create(id, node);
|
|
2629
|
-
this.nodes.set(id, node);
|
|
2630
|
-
return node;
|
|
2531
|
+
constructor() {
|
|
2532
|
+
this.readings = /* @__PURE__ */ new Map();
|
|
2533
|
+
this.writings = /* @__PURE__ */ new Map();
|
|
2534
|
+
this.readQueue = /* @__PURE__ */ new Map();
|
|
2535
|
+
this.writeQueue = /* @__PURE__ */ new Map();
|
|
2631
2536
|
}
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2537
|
+
/**
|
|
2538
|
+
* Creates a range based on a start value and length.
|
|
2539
|
+
* @param start - The starting value of the range.
|
|
2540
|
+
* @param length - The length of the range.
|
|
2541
|
+
* @returns A range tuple [start, start + length].
|
|
2542
|
+
*/
|
|
2543
|
+
range(start, length) {
|
|
2544
|
+
return [start, start + length];
|
|
2635
2545
|
}
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
this.nodes.delete(node.id);
|
|
2546
|
+
rangeOverlapping(tasks, range) {
|
|
2547
|
+
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
2639
2548
|
}
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
const head = await this.mvcc.read("__HEAD__");
|
|
2645
|
-
return head ?? null;
|
|
2549
|
+
isSameRange(a, b) {
|
|
2550
|
+
const [a1, a2] = a;
|
|
2551
|
+
const [b1, b2] = b;
|
|
2552
|
+
return a1 === b1 && a2 === b2;
|
|
2646
2553
|
}
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2554
|
+
fetchUnitAndRun(queue, workspaces) {
|
|
2555
|
+
for (const [id, unit] of queue) {
|
|
2556
|
+
if (!unit.condition()) {
|
|
2557
|
+
continue;
|
|
2558
|
+
}
|
|
2559
|
+
this._alloc(queue, workspaces, id);
|
|
2652
2560
|
}
|
|
2653
|
-
this.nodes.set("__HEAD__", head);
|
|
2654
|
-
this.rootId = head.root;
|
|
2655
2561
|
}
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
2661
|
-
const keys = node.keys[i];
|
|
2662
|
-
if (keys.includes(key)) {
|
|
2663
|
-
break;
|
|
2664
|
-
}
|
|
2665
|
-
keys.push(key);
|
|
2666
|
-
await this._updateNode(node);
|
|
2667
|
-
return;
|
|
2668
|
-
} else if (this.comparator.isLower(value, nValue)) {
|
|
2669
|
-
node.values.splice(i, 0, value);
|
|
2670
|
-
node.keys.splice(i, 0, [key]);
|
|
2671
|
-
await this._updateNode(node);
|
|
2672
|
-
return;
|
|
2673
|
-
} else if (i + 1 === node.values.length) {
|
|
2674
|
-
node.values.push(value);
|
|
2675
|
-
node.keys.push([key]);
|
|
2676
|
-
await this._updateNode(node);
|
|
2677
|
-
return;
|
|
2678
|
-
}
|
|
2679
|
-
}
|
|
2680
|
-
} else {
|
|
2681
|
-
node.values = [value];
|
|
2682
|
-
node.keys = [[key]];
|
|
2683
|
-
await this._updateNode(node);
|
|
2684
|
-
return;
|
|
2685
|
-
}
|
|
2686
|
-
}
|
|
2687
|
-
async _insertInParent(node, value, pointer) {
|
|
2688
|
-
if (this.rootId === node.id) {
|
|
2689
|
-
const root = await this._createNode(false, [node.id, pointer.id], [value]);
|
|
2690
|
-
this.rootId = root.id;
|
|
2691
|
-
node.parent = root.id;
|
|
2692
|
-
pointer.parent = root.id;
|
|
2693
|
-
if (pointer.leaf) {
|
|
2694
|
-
node.next = pointer.id;
|
|
2695
|
-
pointer.prev = node.id;
|
|
2696
|
-
}
|
|
2697
|
-
await this._writeHead({
|
|
2698
|
-
root: root.id,
|
|
2699
|
-
order: this.order,
|
|
2700
|
-
data: this.strategy.head.data
|
|
2701
|
-
});
|
|
2702
|
-
await this._updateNode(node);
|
|
2703
|
-
await this._updateNode(pointer);
|
|
2704
|
-
return;
|
|
2705
|
-
}
|
|
2706
|
-
const parentNode = await this.getNode(node.parent);
|
|
2707
|
-
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2708
|
-
if (nodeIndex === -1) {
|
|
2709
|
-
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
2710
|
-
}
|
|
2711
|
-
parentNode.values.splice(nodeIndex, 0, value);
|
|
2712
|
-
parentNode.keys.splice(nodeIndex + 1, 0, pointer.id);
|
|
2713
|
-
pointer.parent = parentNode.id;
|
|
2714
|
-
if (pointer.leaf) {
|
|
2715
|
-
const leftSibling = node;
|
|
2716
|
-
const oldNextId = leftSibling.next;
|
|
2717
|
-
pointer.prev = leftSibling.id;
|
|
2718
|
-
pointer.next = oldNextId;
|
|
2719
|
-
leftSibling.next = pointer.id;
|
|
2720
|
-
await this._updateNode(leftSibling);
|
|
2721
|
-
if (oldNextId) {
|
|
2722
|
-
const oldNext = await this.getNode(oldNextId);
|
|
2723
|
-
oldNext.prev = pointer.id;
|
|
2724
|
-
await this._updateNode(oldNext);
|
|
2725
|
-
}
|
|
2726
|
-
}
|
|
2727
|
-
await this._updateNode(parentNode);
|
|
2728
|
-
await this._updateNode(pointer);
|
|
2729
|
-
if (parentNode.keys.length > this.order) {
|
|
2730
|
-
const parentPointer = await this._createNode(false, [], []);
|
|
2731
|
-
parentPointer.parent = parentNode.parent;
|
|
2732
|
-
const mid = Math.ceil(this.order / 2) - 1;
|
|
2733
|
-
parentPointer.values = parentNode.values.slice(mid + 1);
|
|
2734
|
-
parentPointer.keys = parentNode.keys.slice(mid + 1);
|
|
2735
|
-
const midValue = parentNode.values[mid];
|
|
2736
|
-
parentNode.values = parentNode.values.slice(0, mid);
|
|
2737
|
-
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2738
|
-
for (const k of parentNode.keys) {
|
|
2739
|
-
const n = await this.getNode(k);
|
|
2740
|
-
n.parent = parentNode.id;
|
|
2741
|
-
await this._updateNode(n);
|
|
2742
|
-
}
|
|
2743
|
-
for (const k of parentPointer.keys) {
|
|
2744
|
-
const n = await this.getNode(k);
|
|
2745
|
-
n.parent = parentPointer.id;
|
|
2746
|
-
await this._updateNode(n);
|
|
2562
|
+
_handleOverload(args, handlers, argPatterns) {
|
|
2563
|
+
for (const [key, pattern] of Object.entries(argPatterns)) {
|
|
2564
|
+
if (this._matchArgs(args, pattern)) {
|
|
2565
|
+
return handlers[key](...args);
|
|
2747
2566
|
}
|
|
2748
|
-
await this._updateNode(parentNode);
|
|
2749
|
-
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2750
|
-
}
|
|
2751
|
-
}
|
|
2752
|
-
async insertableNode(value) {
|
|
2753
|
-
let node = await this.getNode(this.rootId);
|
|
2754
|
-
while (!node.leaf) {
|
|
2755
|
-
const { index } = this._binarySearchValues(node.values, value, false, true);
|
|
2756
|
-
node = await this.getNode(node.keys[index]);
|
|
2757
|
-
}
|
|
2758
|
-
return node;
|
|
2759
|
-
}
|
|
2760
|
-
async insertableNodeByPrimary(value) {
|
|
2761
|
-
let node = await this.getNode(this.rootId);
|
|
2762
|
-
while (!node.leaf) {
|
|
2763
|
-
const { index } = this._binarySearchValues(node.values, value, true, false);
|
|
2764
|
-
node = await this.getNode(node.keys[index]);
|
|
2765
|
-
}
|
|
2766
|
-
return node;
|
|
2767
|
-
}
|
|
2768
|
-
async insertableRightestNodeByPrimary(value) {
|
|
2769
|
-
let node = await this.getNode(this.rootId);
|
|
2770
|
-
while (!node.leaf) {
|
|
2771
|
-
const { index } = this._binarySearchValues(node.values, value, true, true);
|
|
2772
|
-
node = await this.getNode(node.keys[index]);
|
|
2773
2567
|
}
|
|
2774
|
-
|
|
2568
|
+
throw new Error("Invalid arguments");
|
|
2775
2569
|
}
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
return
|
|
2780
|
-
|
|
2781
|
-
|
|
2570
|
+
_matchArgs(args, pattern) {
|
|
2571
|
+
return args.every((arg, index) => {
|
|
2572
|
+
const expectedType = pattern[index];
|
|
2573
|
+
if (expectedType === void 0) return typeof arg === "undefined";
|
|
2574
|
+
if (expectedType === Function) return typeof arg === "function";
|
|
2575
|
+
if (expectedType === Number) return typeof arg === "number";
|
|
2576
|
+
if (expectedType === Array) return Array.isArray(arg);
|
|
2577
|
+
return false;
|
|
2578
|
+
});
|
|
2782
2579
|
}
|
|
2783
|
-
|
|
2784
|
-
const
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
case -1:
|
|
2788
|
-
key = "prev";
|
|
2789
|
-
break;
|
|
2790
|
-
case 1:
|
|
2791
|
-
key = "next";
|
|
2792
|
-
break;
|
|
2793
|
-
default:
|
|
2794
|
-
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
2795
|
-
}
|
|
2796
|
-
const guessNode = insertableNode[key];
|
|
2797
|
-
if (!guessNode) {
|
|
2798
|
-
return null;
|
|
2799
|
-
}
|
|
2800
|
-
return await this.getNode(guessNode);
|
|
2580
|
+
_createRandomId() {
|
|
2581
|
+
const timestamp = Date.now().toString(36);
|
|
2582
|
+
const random = Math.random().toString(36).substring(2);
|
|
2583
|
+
return `${timestamp}${random}`;
|
|
2801
2584
|
}
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
if (
|
|
2805
|
-
|
|
2806
|
-
}
|
|
2807
|
-
while (!node.leaf) {
|
|
2808
|
-
const keys = node.keys;
|
|
2809
|
-
node = await this.getNode(keys[0]);
|
|
2585
|
+
_alloc(queue, workspaces, lockId) {
|
|
2586
|
+
const unit = queue.get(lockId);
|
|
2587
|
+
if (!unit) {
|
|
2588
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2810
2589
|
}
|
|
2811
|
-
|
|
2590
|
+
workspaces.set(lockId, unit);
|
|
2591
|
+
queue.delete(lockId);
|
|
2592
|
+
unit.alloc();
|
|
2812
2593
|
}
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
node = await this.getNode(keys[keys.length - 1]);
|
|
2594
|
+
_free(workspaces, lockId) {
|
|
2595
|
+
const unit = workspaces.get(lockId);
|
|
2596
|
+
if (!unit) {
|
|
2597
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2818
2598
|
}
|
|
2819
|
-
|
|
2599
|
+
workspaces.delete(lockId);
|
|
2600
|
+
unit.free();
|
|
2820
2601
|
}
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
done = true;
|
|
2829
|
-
break;
|
|
2830
|
-
}
|
|
2831
|
-
if (direction === 1) {
|
|
2832
|
-
if (node.next && !done) {
|
|
2833
|
-
nextNodePromise = this.getNode(node.next);
|
|
2834
|
-
}
|
|
2835
|
-
} else {
|
|
2836
|
-
if (node.prev && !done) {
|
|
2837
|
-
nextNodePromise = this.getNode(node.prev);
|
|
2838
|
-
}
|
|
2602
|
+
_lock(queue, range, timeout, task, condition) {
|
|
2603
|
+
return new Promise((resolve, reject) => {
|
|
2604
|
+
let timeoutId = null;
|
|
2605
|
+
if (timeout >= 0) {
|
|
2606
|
+
timeoutId = setTimeout(() => {
|
|
2607
|
+
reject(_Ryoiki2.ERR_TIMEOUT(id, timeout));
|
|
2608
|
+
}, timeout);
|
|
2839
2609
|
}
|
|
2840
|
-
const
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
const keys = node.keys[i];
|
|
2845
|
-
if (comparator(nValue, value)) {
|
|
2846
|
-
hasMatched = true;
|
|
2847
|
-
for (let j = 0; j < keys.length; j++) {
|
|
2848
|
-
yield [keys[j], nValue];
|
|
2849
|
-
}
|
|
2850
|
-
} else if (earlyTerminate && hasMatched) {
|
|
2851
|
-
done = true;
|
|
2852
|
-
break;
|
|
2853
|
-
}
|
|
2854
|
-
}
|
|
2855
|
-
} else {
|
|
2856
|
-
let i = len;
|
|
2857
|
-
while (i--) {
|
|
2858
|
-
const nValue = node.values[i];
|
|
2859
|
-
const keys = node.keys[i];
|
|
2860
|
-
if (comparator(nValue, value)) {
|
|
2861
|
-
hasMatched = true;
|
|
2862
|
-
let j = keys.length;
|
|
2863
|
-
while (j--) {
|
|
2864
|
-
yield [keys[j], nValue];
|
|
2865
|
-
}
|
|
2866
|
-
} else if (earlyTerminate && hasMatched) {
|
|
2867
|
-
done = true;
|
|
2868
|
-
break;
|
|
2869
|
-
}
|
|
2610
|
+
const id = this._createRandomId();
|
|
2611
|
+
const alloc = async () => {
|
|
2612
|
+
if (timeoutId !== null) {
|
|
2613
|
+
clearTimeout(timeoutId);
|
|
2870
2614
|
}
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2615
|
+
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
2616
|
+
if (err) reject(err);
|
|
2617
|
+
else resolve(v);
|
|
2618
|
+
};
|
|
2619
|
+
const fetch = () => {
|
|
2620
|
+
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
2621
|
+
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
2622
|
+
};
|
|
2623
|
+
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
2624
|
+
fetch();
|
|
2625
|
+
});
|
|
2626
|
+
}
|
|
2627
|
+
_checkWorking(range, workspaces) {
|
|
2628
|
+
let isLocked = false;
|
|
2629
|
+
for (const lock of workspaces.values()) {
|
|
2630
|
+
if (_Ryoiki2.IsRangeOverlap(range, lock.range)) {
|
|
2631
|
+
isLocked = true;
|
|
2874
2632
|
break;
|
|
2875
2633
|
}
|
|
2876
|
-
if (nextNodePromise) {
|
|
2877
|
-
node = await nextNodePromise;
|
|
2878
|
-
nextNodePromise = null;
|
|
2879
|
-
} else {
|
|
2880
|
-
done = true;
|
|
2881
|
-
}
|
|
2882
2634
|
}
|
|
2635
|
+
return isLocked;
|
|
2883
2636
|
}
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2637
|
+
/**
|
|
2638
|
+
* Checks if there is any active read lock within the specified range.
|
|
2639
|
+
* @param range The range to check for active read locks.
|
|
2640
|
+
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
2641
|
+
*/
|
|
2642
|
+
isReading(range) {
|
|
2643
|
+
return this._checkWorking(range, this.readings);
|
|
2644
|
+
}
|
|
2645
|
+
/**
|
|
2646
|
+
* Checks if there is any active write lock within the specified range.
|
|
2647
|
+
* @param range The range to check for active write locks.
|
|
2648
|
+
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
2649
|
+
*/
|
|
2650
|
+
isWriting(range) {
|
|
2651
|
+
return this._checkWorking(range, this.writings);
|
|
2652
|
+
}
|
|
2653
|
+
/**
|
|
2654
|
+
* Checks if a read lock can be acquired within the specified range.
|
|
2655
|
+
* @param range The range to check for read lock availability.
|
|
2656
|
+
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
2657
|
+
*/
|
|
2658
|
+
canRead(range) {
|
|
2659
|
+
const writing = this.isWriting(range);
|
|
2660
|
+
return !writing;
|
|
2661
|
+
}
|
|
2662
|
+
/**
|
|
2663
|
+
* Checks if a write lock can be acquired within the specified range.
|
|
2664
|
+
* @param range The range to check for write lock availability.
|
|
2665
|
+
* @returns `true` if a write lock can be acquired, `false` otherwise.
|
|
2666
|
+
*/
|
|
2667
|
+
canWrite(range) {
|
|
2668
|
+
const reading = this.isReading(range);
|
|
2669
|
+
const writing = this.isWriting(range);
|
|
2670
|
+
return !reading && !writing;
|
|
2671
|
+
}
|
|
2672
|
+
/**
|
|
2673
|
+
* Internal implementation of the read lock. Handles both overloads.
|
|
2674
|
+
* @template T - The return type of the task.
|
|
2675
|
+
* @param arg0 - Either a range or a task callback.
|
|
2676
|
+
* If a range is provided, the task is the second argument.
|
|
2677
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2678
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2679
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2680
|
+
* If this value is not provided, no timeout will be set.
|
|
2681
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2682
|
+
*/
|
|
2683
|
+
readLock(arg0, arg1, arg2) {
|
|
2684
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2685
|
+
[arg0, arg1, arg2],
|
|
2686
|
+
{
|
|
2687
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2688
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2689
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2690
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2691
|
+
},
|
|
2692
|
+
{
|
|
2693
|
+
task: [Function],
|
|
2694
|
+
taskTimeout: [Function, Number],
|
|
2695
|
+
rangeTask: [Array, Function],
|
|
2696
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2697
|
+
}
|
|
2698
|
+
);
|
|
2699
|
+
return this._lock(
|
|
2700
|
+
this.readQueue,
|
|
2701
|
+
range,
|
|
2702
|
+
timeout,
|
|
2703
|
+
task,
|
|
2704
|
+
() => !this.rangeOverlapping(this.writings, range)
|
|
2705
|
+
);
|
|
2706
|
+
}
|
|
2707
|
+
/**
|
|
2708
|
+
* Internal implementation of the write lock. Handles both overloads.
|
|
2709
|
+
* @template T - The return type of the task.
|
|
2710
|
+
* @param arg0 - Either a range or a task callback.
|
|
2711
|
+
* If a range is provided, the task is the second argument.
|
|
2712
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2713
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2714
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2715
|
+
* If this value is not provided, no timeout will be set.
|
|
2716
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2717
|
+
*/
|
|
2718
|
+
writeLock(arg0, arg1, arg2) {
|
|
2719
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2720
|
+
[arg0, arg1, arg2],
|
|
2721
|
+
{
|
|
2722
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2723
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2724
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2725
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2726
|
+
},
|
|
2727
|
+
{
|
|
2728
|
+
task: [Function],
|
|
2729
|
+
taskTimeout: [Function, Number],
|
|
2730
|
+
rangeTask: [Array, Function],
|
|
2731
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2732
|
+
}
|
|
2733
|
+
);
|
|
2734
|
+
return this._lock(
|
|
2735
|
+
this.writeQueue,
|
|
2736
|
+
range,
|
|
2737
|
+
timeout,
|
|
2738
|
+
task,
|
|
2739
|
+
() => {
|
|
2740
|
+
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
2741
|
+
}
|
|
2742
|
+
);
|
|
2743
|
+
}
|
|
2744
|
+
/**
|
|
2745
|
+
* Releases a read lock by its lock ID.
|
|
2746
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2747
|
+
*/
|
|
2748
|
+
readUnlock(lockId) {
|
|
2749
|
+
this._free(this.readings, lockId);
|
|
2750
|
+
}
|
|
2751
|
+
/**
|
|
2752
|
+
* Releases a write lock by its lock ID.
|
|
2753
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2754
|
+
*/
|
|
2755
|
+
writeUnlock(lockId) {
|
|
2756
|
+
this._free(this.writings, lockId);
|
|
2757
|
+
}
|
|
2758
|
+
};
|
|
2759
|
+
|
|
2760
|
+
// src/transaction/BPTreeAsyncTransaction.ts
|
|
2761
|
+
var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
2762
|
+
lock;
|
|
2763
|
+
constructor(rootTx, mvccRoot, mvcc, strategy, comparator, option) {
|
|
2764
|
+
super(
|
|
2765
|
+
rootTx,
|
|
2766
|
+
mvccRoot,
|
|
2767
|
+
mvcc,
|
|
2768
|
+
strategy,
|
|
2769
|
+
comparator,
|
|
2770
|
+
option
|
|
2771
|
+
);
|
|
2772
|
+
this.lock = new Ryoiki2();
|
|
2773
|
+
}
|
|
2774
|
+
async writeLock(id, fn) {
|
|
2775
|
+
let lockId;
|
|
2776
|
+
return this.lock.writeLock([id, id + 0.1], async (_lockId) => {
|
|
2777
|
+
lockId = _lockId;
|
|
2778
|
+
return fn();
|
|
2779
|
+
}).finally(() => {
|
|
2780
|
+
this.lock.writeUnlock(lockId);
|
|
2781
|
+
});
|
|
2782
|
+
}
|
|
2783
|
+
async getNode(id) {
|
|
2784
|
+
return await this.mvcc.read(id);
|
|
2785
|
+
}
|
|
2786
|
+
/**
|
|
2787
|
+
* Create a new node with a unique ID.
|
|
2788
|
+
*/
|
|
2789
|
+
async _createNode(leaf, keys, values, parent = null, next = null, prev = null) {
|
|
2790
|
+
const id = await this.strategy.id(leaf);
|
|
2791
|
+
const node = {
|
|
2792
|
+
id,
|
|
2793
|
+
keys,
|
|
2794
|
+
values,
|
|
2795
|
+
leaf,
|
|
2796
|
+
parent,
|
|
2797
|
+
next,
|
|
2798
|
+
prev
|
|
2799
|
+
};
|
|
2800
|
+
const head = await this._readHead();
|
|
2801
|
+
if (head) {
|
|
2899
2802
|
await this._writeHead({
|
|
2900
|
-
root,
|
|
2901
|
-
order:
|
|
2803
|
+
root: head.root,
|
|
2804
|
+
order: head.order,
|
|
2902
2805
|
data: this.strategy.head.data
|
|
2903
2806
|
});
|
|
2904
2807
|
}
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
}
|
|
2808
|
+
await this.mvcc.create(id, node);
|
|
2809
|
+
return node;
|
|
2908
2810
|
}
|
|
2909
|
-
async
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
if (found) {
|
|
2913
|
-
const keys = node.keys[index];
|
|
2914
|
-
if (keys.includes(key)) {
|
|
2915
|
-
return true;
|
|
2916
|
-
}
|
|
2811
|
+
async _updateNode(node) {
|
|
2812
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
2813
|
+
return;
|
|
2917
2814
|
}
|
|
2918
|
-
|
|
2815
|
+
await this.mvcc.write(node.id, this._cloneNode(node));
|
|
2919
2816
|
}
|
|
2920
|
-
async
|
|
2921
|
-
if (id) {
|
|
2922
|
-
|
|
2923
|
-
await this.getNode(id);
|
|
2924
|
-
return 1;
|
|
2925
|
-
}
|
|
2926
|
-
const keys = Array.from(this.nodes.keys());
|
|
2927
|
-
for (const key of keys) {
|
|
2928
|
-
this.nodes.delete(key);
|
|
2817
|
+
async _deleteNode(node) {
|
|
2818
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
2819
|
+
return;
|
|
2929
2820
|
}
|
|
2930
|
-
|
|
2931
|
-
|
|
2821
|
+
await this.mvcc.delete(node.id);
|
|
2822
|
+
}
|
|
2823
|
+
async _readHead() {
|
|
2824
|
+
return await this.mvcc.read("__HEAD__");
|
|
2825
|
+
}
|
|
2826
|
+
async _writeHead(head) {
|
|
2827
|
+
if (!await this.mvcc.exists("__HEAD__")) {
|
|
2828
|
+
await this.mvcc.create("__HEAD__", this._cloneNode(head));
|
|
2829
|
+
} else {
|
|
2830
|
+
await this.mvcc.write("__HEAD__", this._cloneNode(head));
|
|
2932
2831
|
}
|
|
2933
|
-
|
|
2832
|
+
this.rootId = head.root;
|
|
2934
2833
|
}
|
|
2935
|
-
async
|
|
2936
|
-
let
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2834
|
+
async _insertAtLeaf(node, key, value) {
|
|
2835
|
+
let leaf = node;
|
|
2836
|
+
leaf = this._cloneNode(leaf);
|
|
2837
|
+
if (leaf.values.length) {
|
|
2838
|
+
for (let i = 0, len = leaf.values.length; i < len; i++) {
|
|
2839
|
+
const nValue = leaf.values[i];
|
|
2840
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
2841
|
+
const keys = leaf.keys[i];
|
|
2842
|
+
if (keys.includes(key)) {
|
|
2843
|
+
break;
|
|
2943
2844
|
}
|
|
2845
|
+
keys.push(key);
|
|
2846
|
+
await this._updateNode(leaf);
|
|
2847
|
+
return leaf;
|
|
2848
|
+
} else if (this.comparator.isLower(value, nValue)) {
|
|
2849
|
+
leaf.values.splice(i, 0, value);
|
|
2850
|
+
leaf.keys.splice(i, 0, [key]);
|
|
2851
|
+
await this._updateNode(leaf);
|
|
2852
|
+
return leaf;
|
|
2853
|
+
} else if (i + 1 === leaf.values.length) {
|
|
2854
|
+
leaf.values.push(value);
|
|
2855
|
+
leaf.keys.push([key]);
|
|
2856
|
+
await this._updateNode(leaf);
|
|
2857
|
+
return leaf;
|
|
2944
2858
|
}
|
|
2945
2859
|
}
|
|
2946
|
-
|
|
2947
|
-
|
|
2860
|
+
} else {
|
|
2861
|
+
leaf.values = [value];
|
|
2862
|
+
leaf.keys = [[key]];
|
|
2863
|
+
await this._updateNode(leaf);
|
|
2864
|
+
return leaf;
|
|
2948
2865
|
}
|
|
2949
|
-
return
|
|
2866
|
+
return leaf;
|
|
2950
2867
|
}
|
|
2951
|
-
async
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2868
|
+
async _insertInParent(node, value, pointer) {
|
|
2869
|
+
node = this._cloneNode(node);
|
|
2870
|
+
pointer = this._cloneNode(pointer);
|
|
2871
|
+
if (this.rootId === node.id) {
|
|
2872
|
+
const root = await this._createNode(false, [node.id, pointer.id], [value]);
|
|
2873
|
+
this.rootId = root.id;
|
|
2874
|
+
node.parent = root.id;
|
|
2875
|
+
pointer.parent = root.id;
|
|
2876
|
+
if (pointer.leaf) {
|
|
2877
|
+
node.next = pointer.id;
|
|
2878
|
+
pointer.prev = node.id;
|
|
2957
2879
|
}
|
|
2958
|
-
|
|
2880
|
+
await this._writeHead({
|
|
2881
|
+
root: root.id,
|
|
2882
|
+
order: this.order,
|
|
2883
|
+
data: this.strategy.head.data
|
|
2884
|
+
});
|
|
2885
|
+
await this._updateNode(node);
|
|
2886
|
+
await this._updateNode(pointer);
|
|
2887
|
+
return;
|
|
2959
2888
|
}
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
const value = condition[driverKey];
|
|
2965
|
-
let startNode = await this.verifierStartNode[driverKey](value);
|
|
2966
|
-
let endNode = await this.verifierEndNode[driverKey](value);
|
|
2967
|
-
let direction = this.verifierDirection[driverKey];
|
|
2968
|
-
const comparator = this.verifierMap[driverKey];
|
|
2969
|
-
const earlyTerminate = this.verifierEarlyTerminate[driverKey];
|
|
2970
|
-
if (order === "desc") {
|
|
2971
|
-
startNode = endNode ?? await this.rightestNode();
|
|
2972
|
-
endNode = null;
|
|
2973
|
-
direction *= -1;
|
|
2889
|
+
const parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
2890
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2891
|
+
if (nodeIndex === -1) {
|
|
2892
|
+
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
2974
2893
|
}
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2894
|
+
parentNode.values.splice(nodeIndex, 0, value);
|
|
2895
|
+
parentNode.keys.splice(nodeIndex + 1, 0, pointer.id);
|
|
2896
|
+
pointer.parent = parentNode.id;
|
|
2897
|
+
if (pointer.leaf) {
|
|
2898
|
+
const leftSibling = node;
|
|
2899
|
+
const oldNextId = leftSibling.next;
|
|
2900
|
+
pointer.prev = leftSibling.id;
|
|
2901
|
+
pointer.next = oldNextId;
|
|
2902
|
+
leftSibling.next = pointer.id;
|
|
2903
|
+
await this._updateNode(leftSibling);
|
|
2904
|
+
if (oldNextId) {
|
|
2905
|
+
const oldNext = this._cloneNode(await this.getNode(oldNextId));
|
|
2906
|
+
oldNext.prev = pointer.id;
|
|
2907
|
+
await this._updateNode(oldNext);
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
await this._updateNode(parentNode);
|
|
2911
|
+
await this._updateNode(pointer);
|
|
2912
|
+
if (parentNode.keys.length > this.order) {
|
|
2913
|
+
const parentPointer = await this._createNode(false, [], []);
|
|
2914
|
+
parentPointer.parent = parentNode.parent;
|
|
2915
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
2916
|
+
parentPointer.values = parentNode.values.slice(mid + 1);
|
|
2917
|
+
parentPointer.keys = parentNode.keys.slice(mid + 1);
|
|
2918
|
+
const midValue = parentNode.values[mid];
|
|
2919
|
+
parentNode.values = parentNode.values.slice(0, mid);
|
|
2920
|
+
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2921
|
+
for (const k of parentNode.keys) {
|
|
2922
|
+
const n = this._cloneNode(await this.getNode(k));
|
|
2923
|
+
n.parent = parentNode.id;
|
|
2924
|
+
await this._updateNode(n);
|
|
2925
|
+
}
|
|
2926
|
+
for (const k of parentPointer.keys) {
|
|
2927
|
+
const n = this._cloneNode(await this.getNode(k));
|
|
2928
|
+
n.parent = parentPointer.id;
|
|
2929
|
+
await this._updateNode(n);
|
|
2930
|
+
}
|
|
2931
|
+
await this._updateNode(parentNode);
|
|
2932
|
+
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
async insertableNode(value) {
|
|
2936
|
+
let node = await this.getNode(this.rootId);
|
|
2937
|
+
while (!node.leaf) {
|
|
2938
|
+
const { index } = this._binarySearchValues(node.values, value, false, true);
|
|
2939
|
+
node = await this.getNode(node.keys[index]);
|
|
2940
|
+
}
|
|
2941
|
+
return node;
|
|
2942
|
+
}
|
|
2943
|
+
async insertableNodeByPrimary(value) {
|
|
2944
|
+
let node = await this.getNode(this.rootId);
|
|
2945
|
+
while (!node.leaf) {
|
|
2946
|
+
const { index } = this._binarySearchValues(node.values, value, true, false);
|
|
2947
|
+
node = await this.getNode(node.keys[index]);
|
|
2948
|
+
}
|
|
2949
|
+
return node;
|
|
2950
|
+
}
|
|
2951
|
+
async insertableRightestNodeByPrimary(value) {
|
|
2952
|
+
let node = await this.getNode(this.rootId);
|
|
2953
|
+
while (!node.leaf) {
|
|
2954
|
+
const { index } = this._binarySearchValues(node.values, value, true, true);
|
|
2955
|
+
node = await this.getNode(node.keys[index]);
|
|
2956
|
+
}
|
|
2957
|
+
return node;
|
|
2958
|
+
}
|
|
2959
|
+
async insertableRightestEndNodeByPrimary(value) {
|
|
2960
|
+
const node = await this.insertableRightestNodeByPrimary(value);
|
|
2961
|
+
if (!node.next) {
|
|
2962
|
+
return null;
|
|
2963
|
+
}
|
|
2964
|
+
return await this.getNode(node.next);
|
|
2965
|
+
}
|
|
2966
|
+
async insertableEndNode(value, direction) {
|
|
2967
|
+
const insertableNode = await this.insertableNode(value);
|
|
2968
|
+
let key;
|
|
2969
|
+
switch (direction) {
|
|
2970
|
+
case -1:
|
|
2971
|
+
key = "prev";
|
|
2972
|
+
break;
|
|
2973
|
+
case 1:
|
|
2974
|
+
key = "next";
|
|
2975
|
+
break;
|
|
2976
|
+
default:
|
|
2977
|
+
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
2978
|
+
}
|
|
2979
|
+
const guessNode = insertableNode[key];
|
|
2980
|
+
if (!guessNode) {
|
|
2981
|
+
return null;
|
|
2982
|
+
}
|
|
2983
|
+
return await this.getNode(guessNode);
|
|
2984
|
+
}
|
|
2985
|
+
async leftestNode() {
|
|
2986
|
+
let node = await this.getNode(this.rootId);
|
|
2987
|
+
if (node === null) {
|
|
2988
|
+
debugger;
|
|
2989
|
+
}
|
|
2990
|
+
while (!node.leaf) {
|
|
2991
|
+
const keys = node.keys;
|
|
2992
|
+
node = await this.getNode(keys[0]);
|
|
2993
|
+
}
|
|
2994
|
+
return node;
|
|
2995
|
+
}
|
|
2996
|
+
async rightestNode() {
|
|
2997
|
+
let node = await this.getNode(this.rootId);
|
|
2998
|
+
while (!node.leaf) {
|
|
2999
|
+
const keys = node.keys;
|
|
3000
|
+
node = await this.getNode(keys[keys.length - 1]);
|
|
3001
|
+
}
|
|
3002
|
+
return node;
|
|
3003
|
+
}
|
|
3004
|
+
async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
3005
|
+
let node = startNode;
|
|
3006
|
+
let done = false;
|
|
3007
|
+
let hasMatched = false;
|
|
3008
|
+
let nextNodePromise = null;
|
|
3009
|
+
while (!done) {
|
|
3010
|
+
if (endNode && node.id === endNode.id) {
|
|
3011
|
+
done = true;
|
|
3012
|
+
break;
|
|
3013
|
+
}
|
|
3014
|
+
if (direction === 1) {
|
|
3015
|
+
if (node.next && !done) {
|
|
3016
|
+
nextNodePromise = this.getNode(node.next);
|
|
3017
|
+
}
|
|
3018
|
+
} else {
|
|
3019
|
+
if (node.prev && !done) {
|
|
3020
|
+
nextNodePromise = this.getNode(node.prev);
|
|
3021
|
+
}
|
|
3022
|
+
}
|
|
3023
|
+
const len = node.values.length;
|
|
3024
|
+
if (direction === 1) {
|
|
3025
|
+
for (let i = 0; i < len; i++) {
|
|
3026
|
+
const nValue = node.values[i];
|
|
3027
|
+
const keys = node.keys[i];
|
|
3028
|
+
if (comparator(nValue, value)) {
|
|
3029
|
+
hasMatched = true;
|
|
3030
|
+
for (let j = 0; j < keys.length; j++) {
|
|
3031
|
+
yield [keys[j], nValue];
|
|
3032
|
+
}
|
|
3033
|
+
} else if (earlyTerminate && hasMatched) {
|
|
3034
|
+
done = true;
|
|
3035
|
+
break;
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
} else {
|
|
3039
|
+
let i = len;
|
|
3040
|
+
while (i--) {
|
|
3041
|
+
const nValue = node.values[i];
|
|
3042
|
+
const keys = node.keys[i];
|
|
3043
|
+
if (comparator(nValue, value)) {
|
|
3044
|
+
hasMatched = true;
|
|
3045
|
+
let j = keys.length;
|
|
3046
|
+
while (j--) {
|
|
3047
|
+
yield [keys[j], nValue];
|
|
3048
|
+
}
|
|
3049
|
+
} else if (earlyTerminate && hasMatched) {
|
|
3050
|
+
done = true;
|
|
3051
|
+
break;
|
|
3052
|
+
}
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
if (done) {
|
|
3056
|
+
if (nextNodePromise) await nextNodePromise;
|
|
3057
|
+
break;
|
|
3058
|
+
}
|
|
3059
|
+
if (nextNodePromise) {
|
|
3060
|
+
node = await nextNodePromise;
|
|
3061
|
+
nextNodePromise = null;
|
|
3062
|
+
} else {
|
|
3063
|
+
done = true;
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
async init() {
|
|
3068
|
+
if (this.rootTx !== this) {
|
|
3069
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
3070
|
+
}
|
|
3071
|
+
return await this._initInternal();
|
|
3072
|
+
}
|
|
3073
|
+
async _initInternal() {
|
|
3074
|
+
if (this.isInitialized) {
|
|
3075
|
+
throw new Error("Transaction already initialized");
|
|
3076
|
+
}
|
|
3077
|
+
if (this.isDestroyed) {
|
|
3078
|
+
throw new Error("Transaction already destroyed");
|
|
3079
|
+
}
|
|
3080
|
+
this.isInitialized = true;
|
|
3081
|
+
try {
|
|
3082
|
+
this._clearCache();
|
|
3083
|
+
const head = await this._readHead();
|
|
3084
|
+
if (head === null) {
|
|
3085
|
+
this.order = this.strategy.order;
|
|
3086
|
+
const root = await this._createNode(true, [], []);
|
|
3087
|
+
await this._writeHead({
|
|
3088
|
+
root: root.id,
|
|
3089
|
+
order: this.order,
|
|
3090
|
+
data: this.strategy.head.data
|
|
3091
|
+
});
|
|
3092
|
+
} else {
|
|
3093
|
+
const { root, order } = head;
|
|
3094
|
+
this.strategy.head = head;
|
|
3095
|
+
this.order = order;
|
|
3096
|
+
await this._writeHead({
|
|
3097
|
+
root,
|
|
3098
|
+
order: this.order,
|
|
3099
|
+
data: this.strategy.head.data
|
|
3100
|
+
});
|
|
3101
|
+
}
|
|
3102
|
+
if (this.order < 3) {
|
|
3103
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
3104
|
+
}
|
|
3105
|
+
} catch (e) {
|
|
3106
|
+
this.isInitialized = false;
|
|
3107
|
+
throw e;
|
|
3108
|
+
}
|
|
3109
|
+
}
|
|
3110
|
+
async exists(key, value) {
|
|
3111
|
+
const node = await this.insertableNode(value);
|
|
3112
|
+
const { index, found } = this._binarySearchValues(node.values, value);
|
|
3113
|
+
if (found) {
|
|
3114
|
+
const keys = node.keys[index];
|
|
3115
|
+
if (keys.includes(key)) {
|
|
3116
|
+
return true;
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
return false;
|
|
3120
|
+
}
|
|
3121
|
+
async get(key) {
|
|
3122
|
+
let node = await this.leftestNode();
|
|
3123
|
+
while (true) {
|
|
3124
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
3125
|
+
const keys = node.keys[i];
|
|
3126
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
3127
|
+
if (keys[j] === key) {
|
|
3128
|
+
return node.values[i];
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
if (!node.next) break;
|
|
3133
|
+
node = await this.getNode(node.next);
|
|
3134
|
+
}
|
|
3135
|
+
return void 0;
|
|
3136
|
+
}
|
|
3137
|
+
async *keysStream(condition, filterValues, limit, order = "asc") {
|
|
3138
|
+
const stream = this.whereStream(condition, limit, order);
|
|
3139
|
+
const intersection = filterValues && filterValues.size > 0 ? filterValues : null;
|
|
3140
|
+
for await (const [key] of stream) {
|
|
3141
|
+
if (intersection && !intersection.has(key)) {
|
|
3142
|
+
continue;
|
|
3143
|
+
}
|
|
3144
|
+
yield key;
|
|
3145
|
+
}
|
|
3146
|
+
}
|
|
3147
|
+
async *whereStream(condition, limit, order = "asc") {
|
|
3148
|
+
const driverKey = this.getDriverKey(condition);
|
|
3149
|
+
if (!driverKey) return;
|
|
3150
|
+
const value = condition[driverKey];
|
|
3151
|
+
let startNode = await this.verifierStartNode[driverKey](value);
|
|
3152
|
+
let endNode = await this.verifierEndNode[driverKey](value);
|
|
3153
|
+
let direction = this.verifierDirection[driverKey];
|
|
3154
|
+
const comparator = this.verifierMap[driverKey];
|
|
3155
|
+
const earlyTerminate = this.verifierEarlyTerminate[driverKey];
|
|
3156
|
+
if (order === "desc") {
|
|
3157
|
+
startNode = endNode ?? await this.rightestNode();
|
|
3158
|
+
endNode = null;
|
|
3159
|
+
direction *= -1;
|
|
3160
|
+
}
|
|
3161
|
+
const generator = this.getPairsGenerator(
|
|
3162
|
+
value,
|
|
3163
|
+
startNode,
|
|
3164
|
+
endNode,
|
|
3165
|
+
comparator,
|
|
2980
3166
|
direction,
|
|
2981
3167
|
earlyTerminate
|
|
2982
3168
|
);
|
|
@@ -3017,26 +3203,32 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3017
3203
|
return map;
|
|
3018
3204
|
}
|
|
3019
3205
|
async insert(key, value) {
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3206
|
+
return this.writeLock(0, async () => {
|
|
3207
|
+
let before = await this.insertableNode(value);
|
|
3208
|
+
before = await this._insertAtLeaf(before, key, value);
|
|
3209
|
+
if (before.values.length === this.order) {
|
|
3210
|
+
let after = await this._createNode(
|
|
3211
|
+
true,
|
|
3212
|
+
[],
|
|
3213
|
+
[],
|
|
3214
|
+
before.parent,
|
|
3215
|
+
null,
|
|
3216
|
+
null
|
|
3217
|
+
);
|
|
3218
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
3219
|
+
after = this._cloneNode(after);
|
|
3220
|
+
after.values = before.values.slice(mid + 1);
|
|
3221
|
+
after.keys = before.keys.slice(mid + 1);
|
|
3222
|
+
before.values = before.values.slice(0, mid + 1);
|
|
3223
|
+
before.keys = before.keys.slice(0, mid + 1);
|
|
3224
|
+
await this._updateNode(before);
|
|
3225
|
+
await this._updateNode(after);
|
|
3226
|
+
await this._insertInParent(before, after.values[0], after);
|
|
3227
|
+
}
|
|
3228
|
+
});
|
|
3038
3229
|
}
|
|
3039
3230
|
async _deleteEntry(node, key) {
|
|
3231
|
+
node = this._cloneNode(node);
|
|
3040
3232
|
if (!node.leaf) {
|
|
3041
3233
|
let keyIndex = -1;
|
|
3042
3234
|
for (let i = 0, len = node.keys.length; i < len; i++) {
|
|
@@ -3055,7 +3247,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3055
3247
|
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
3056
3248
|
const keys = node.keys;
|
|
3057
3249
|
this._deleteNode(node);
|
|
3058
|
-
const newRoot = await this.getNode(keys[0]);
|
|
3250
|
+
const newRoot = this._cloneNode(await this.getNode(keys[0]));
|
|
3059
3251
|
newRoot.parent = null;
|
|
3060
3252
|
await this._updateNode(newRoot);
|
|
3061
3253
|
await this._writeHead({
|
|
@@ -3063,14 +3255,17 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3063
3255
|
order: this.order,
|
|
3064
3256
|
data: this.strategy.head.data
|
|
3065
3257
|
});
|
|
3066
|
-
return;
|
|
3258
|
+
return node;
|
|
3067
3259
|
} else if (this.rootId === node.id) {
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3260
|
+
await this._writeHead({
|
|
3261
|
+
root: node.id,
|
|
3262
|
+
order: this.order,
|
|
3263
|
+
data: this.strategy.head.data
|
|
3264
|
+
});
|
|
3265
|
+
return node;
|
|
3071
3266
|
} else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
|
|
3072
3267
|
if (node.parent === null) {
|
|
3073
|
-
return;
|
|
3268
|
+
return node;
|
|
3074
3269
|
}
|
|
3075
3270
|
let isPredecessor = false;
|
|
3076
3271
|
let parentNode = await this.getNode(node.parent);
|
|
@@ -3082,11 +3277,11 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3082
3277
|
const nKey = parentNode.keys[i];
|
|
3083
3278
|
if (nKey === node.id) {
|
|
3084
3279
|
if (i > 0) {
|
|
3085
|
-
prevNode = await this.getNode(parentNode.keys[i - 1]);
|
|
3280
|
+
prevNode = this._cloneNode(await this.getNode(parentNode.keys[i - 1]));
|
|
3086
3281
|
prevValue = parentNode.values[i - 1];
|
|
3087
3282
|
}
|
|
3088
3283
|
if (i < parentNode.keys.length - 1) {
|
|
3089
|
-
nextNode = await this.getNode(parentNode.keys[i + 1]);
|
|
3284
|
+
nextNode = this._cloneNode(await this.getNode(parentNode.keys[i + 1]));
|
|
3090
3285
|
postValue = parentNode.values[i];
|
|
3091
3286
|
}
|
|
3092
3287
|
}
|
|
@@ -3111,7 +3306,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3111
3306
|
}
|
|
3112
3307
|
}
|
|
3113
3308
|
if (!pointer) {
|
|
3114
|
-
return;
|
|
3309
|
+
return node;
|
|
3115
3310
|
}
|
|
3116
3311
|
if (node.values.length + pointer.values.length < this.order) {
|
|
3117
3312
|
if (!isPredecessor) {
|
|
@@ -3125,7 +3320,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3125
3320
|
} else {
|
|
3126
3321
|
pointer.next = node.next;
|
|
3127
3322
|
if (pointer.next) {
|
|
3128
|
-
const n = await this.getNode(pointer.next);
|
|
3323
|
+
const n = this._cloneNode(await this.getNode(pointer.next));
|
|
3129
3324
|
n.prev = pointer.id;
|
|
3130
3325
|
await this._updateNode(n);
|
|
3131
3326
|
}
|
|
@@ -3134,14 +3329,14 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3134
3329
|
if (!pointer.leaf) {
|
|
3135
3330
|
const keys = pointer.keys;
|
|
3136
3331
|
for (const key2 of keys) {
|
|
3137
|
-
const node2 = await this.getNode(key2);
|
|
3332
|
+
const node2 = this._cloneNode(await this.getNode(key2));
|
|
3138
3333
|
node2.parent = pointer.id;
|
|
3139
3334
|
await this._updateNode(node2);
|
|
3140
3335
|
}
|
|
3141
3336
|
}
|
|
3142
3337
|
this._deleteNode(node);
|
|
3143
3338
|
await this._updateNode(pointer);
|
|
3144
|
-
await this._deleteEntry(await this.getNode(node.parent), node.id);
|
|
3339
|
+
await this._deleteEntry(this._cloneNode(await this.getNode(node.parent)), node.id);
|
|
3145
3340
|
} else {
|
|
3146
3341
|
if (isPredecessor) {
|
|
3147
3342
|
let pointerPm;
|
|
@@ -3151,7 +3346,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3151
3346
|
pointerKm = pointer.values.splice(-1)[0];
|
|
3152
3347
|
node.keys = [pointerPm, ...node.keys];
|
|
3153
3348
|
node.values = [guess, ...node.values];
|
|
3154
|
-
parentNode = await this.getNode(node.parent);
|
|
3349
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3155
3350
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
3156
3351
|
if (nodeIndex > 0) {
|
|
3157
3352
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
@@ -3162,7 +3357,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3162
3357
|
pointerKm = pointer.values.splice(-1)[0];
|
|
3163
3358
|
node.keys = [pointerPm, ...node.keys];
|
|
3164
3359
|
node.values = [pointerKm, ...node.values];
|
|
3165
|
-
parentNode = await this.getNode(node.parent);
|
|
3360
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3166
3361
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
3167
3362
|
if (nodeIndex > 0) {
|
|
3168
3363
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
@@ -3179,7 +3374,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3179
3374
|
pointerK0 = pointer.values.splice(0, 1)[0];
|
|
3180
3375
|
node.keys = [...node.keys, pointerP0];
|
|
3181
3376
|
node.values = [...node.values, guess];
|
|
3182
|
-
parentNode = await this.getNode(node.parent);
|
|
3377
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3183
3378
|
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
3184
3379
|
if (pointerIndex > 0) {
|
|
3185
3380
|
parentNode.values[pointerIndex - 1] = pointerK0;
|
|
@@ -3190,7 +3385,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3190
3385
|
pointerK0 = pointer.values.splice(0, 1)[0];
|
|
3191
3386
|
node.keys = [...node.keys, pointerP0];
|
|
3192
3387
|
node.values = [...node.values, pointerK0];
|
|
3193
|
-
parentNode = await this.getNode(node.parent);
|
|
3388
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3194
3389
|
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
3195
3390
|
if (pointerIndex > 0) {
|
|
3196
3391
|
parentNode.values[pointerIndex - 1] = pointer.values[0];
|
|
@@ -3202,21 +3397,21 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3202
3397
|
}
|
|
3203
3398
|
if (!pointer.leaf) {
|
|
3204
3399
|
for (const key2 of pointer.keys) {
|
|
3205
|
-
const n = await this.getNode(key2);
|
|
3400
|
+
const n = this._cloneNode(await this.getNode(key2));
|
|
3206
3401
|
n.parent = pointer.id;
|
|
3207
3402
|
await this._updateNode(n);
|
|
3208
3403
|
}
|
|
3209
3404
|
}
|
|
3210
3405
|
if (!node.leaf) {
|
|
3211
3406
|
for (const key2 of node.keys) {
|
|
3212
|
-
const n = await this.getNode(key2);
|
|
3407
|
+
const n = this._cloneNode(await this.getNode(key2));
|
|
3213
3408
|
n.parent = node.id;
|
|
3214
3409
|
await this._updateNode(n);
|
|
3215
3410
|
}
|
|
3216
3411
|
}
|
|
3217
3412
|
if (!parentNode.leaf) {
|
|
3218
3413
|
for (const key2 of parentNode.keys) {
|
|
3219
|
-
const n = await this.getNode(key2);
|
|
3414
|
+
const n = this._cloneNode(await this.getNode(key2));
|
|
3220
3415
|
n.parent = parentNode.id;
|
|
3221
3416
|
await this._updateNode(n);
|
|
3222
3417
|
}
|
|
@@ -3225,37 +3420,42 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3225
3420
|
} else {
|
|
3226
3421
|
await this._updateNode(node);
|
|
3227
3422
|
}
|
|
3423
|
+
await this._updateNode(node);
|
|
3424
|
+
return node;
|
|
3228
3425
|
}
|
|
3229
3426
|
async delete(key, value) {
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
while (
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
const
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
keys.
|
|
3241
|
-
if (
|
|
3242
|
-
|
|
3243
|
-
|
|
3427
|
+
return this.writeLock(0, async () => {
|
|
3428
|
+
let node = await this.insertableNodeByPrimary(value);
|
|
3429
|
+
let found = false;
|
|
3430
|
+
node = this._cloneNode(node);
|
|
3431
|
+
while (true) {
|
|
3432
|
+
let i = node.values.length;
|
|
3433
|
+
while (i--) {
|
|
3434
|
+
const nValue = node.values[i];
|
|
3435
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
3436
|
+
const keys = node.keys[i];
|
|
3437
|
+
const keyIndex = keys.indexOf(key);
|
|
3438
|
+
if (keyIndex !== -1) {
|
|
3439
|
+
keys.splice(keyIndex, 1);
|
|
3440
|
+
if (keys.length === 0) {
|
|
3441
|
+
node.keys.splice(i, 1);
|
|
3442
|
+
node.values.splice(i, 1);
|
|
3443
|
+
}
|
|
3444
|
+
await this._updateNode(node);
|
|
3445
|
+
node = await this._deleteEntry(node, key);
|
|
3446
|
+
found = true;
|
|
3447
|
+
break;
|
|
3244
3448
|
}
|
|
3245
|
-
await this._updateNode(node);
|
|
3246
|
-
await this._deleteEntry(node, key);
|
|
3247
|
-
found = true;
|
|
3248
|
-
break;
|
|
3249
3449
|
}
|
|
3250
3450
|
}
|
|
3451
|
+
if (found) break;
|
|
3452
|
+
if (node.next) {
|
|
3453
|
+
node = await this.getNode(node.next);
|
|
3454
|
+
continue;
|
|
3455
|
+
}
|
|
3456
|
+
break;
|
|
3251
3457
|
}
|
|
3252
|
-
|
|
3253
|
-
if (node.next) {
|
|
3254
|
-
node = await this.getNode(node.next);
|
|
3255
|
-
continue;
|
|
3256
|
-
}
|
|
3257
|
-
break;
|
|
3258
|
-
}
|
|
3458
|
+
});
|
|
3259
3459
|
}
|
|
3260
3460
|
async getHeadData() {
|
|
3261
3461
|
const head = await this._readHead();
|
|
@@ -3266,447 +3466,185 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3266
3466
|
}
|
|
3267
3467
|
async setHeadData(data) {
|
|
3268
3468
|
const head = await this._readHead();
|
|
3269
|
-
if (head === null) {
|
|
3270
|
-
throw new Error("Head not found");
|
|
3271
|
-
}
|
|
3272
|
-
await this._writeHead({
|
|
3273
|
-
root: head.root,
|
|
3274
|
-
order: head.order,
|
|
3275
|
-
data
|
|
3276
|
-
});
|
|
3277
|
-
}
|
|
3278
|
-
async commit(label) {
|
|
3279
|
-
let result = await this.mvcc.commit(label);
|
|
3280
|
-
if (result.success) {
|
|
3281
|
-
result = await this.mvccRoot.commit(label);
|
|
3282
|
-
if (result.success && this.rootTx !== this) {
|
|
3283
|
-
this.rootTx.rootId = this.rootId;
|
|
3284
|
-
}
|
|
3285
|
-
if (result.success) {
|
|
3286
|
-
for (const r of result.created) {
|
|
3287
|
-
this.nodes.set(r.key, r.data);
|
|
3288
|
-
}
|
|
3289
|
-
for (const r of result.updated) {
|
|
3290
|
-
this.nodes.set(r.key, r.data);
|
|
3291
|
-
}
|
|
3292
|
-
for (const r of result.deleted) {
|
|
3293
|
-
this.nodes.delete(r.key);
|
|
3294
|
-
}
|
|
3295
|
-
}
|
|
3296
|
-
}
|
|
3297
|
-
return result;
|
|
3298
|
-
}
|
|
3299
|
-
rollback() {
|
|
3300
|
-
return this.mvcc.rollback();
|
|
3301
|
-
}
|
|
3302
|
-
};
|
|
3303
|
-
|
|
3304
|
-
// src/transaction/BPTreeMVCCStrategyAsync.ts
|
|
3305
|
-
var BPTreeMVCCStrategyAsync = class extends AsyncMVCCStrategy {
|
|
3306
|
-
constructor(strategy) {
|
|
3307
|
-
super();
|
|
3308
|
-
this.strategy = strategy;
|
|
3309
|
-
}
|
|
3310
|
-
async read(key) {
|
|
3311
|
-
if (key === "__HEAD__") {
|
|
3312
|
-
return await this.strategy.readHead();
|
|
3313
|
-
}
|
|
3314
|
-
return await this.strategy.read(key);
|
|
3315
|
-
}
|
|
3316
|
-
async write(key, value) {
|
|
3317
|
-
if (key === "__HEAD__") {
|
|
3318
|
-
await this.strategy.writeHead(value);
|
|
3319
|
-
} else {
|
|
3320
|
-
await this.strategy.write(key, value);
|
|
3321
|
-
}
|
|
3322
|
-
}
|
|
3323
|
-
async delete(key) {
|
|
3324
|
-
await this.strategy.delete(key);
|
|
3325
|
-
}
|
|
3326
|
-
async exists(key) {
|
|
3327
|
-
if (key === "__HEAD__") {
|
|
3328
|
-
return await this.strategy.readHead() !== null;
|
|
3329
|
-
}
|
|
3330
|
-
try {
|
|
3331
|
-
const node = await this.strategy.read(key);
|
|
3332
|
-
return node !== null && node !== void 0;
|
|
3333
|
-
} catch {
|
|
3334
|
-
return false;
|
|
3335
|
-
}
|
|
3336
|
-
}
|
|
3337
|
-
};
|
|
3338
|
-
|
|
3339
|
-
// src/BPTreeAsync.ts
|
|
3340
|
-
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3341
|
-
constructor(strategy, comparator, option) {
|
|
3342
|
-
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy));
|
|
3343
|
-
super(
|
|
3344
|
-
null,
|
|
3345
|
-
mvccRoot,
|
|
3346
|
-
mvccRoot,
|
|
3347
|
-
strategy,
|
|
3348
|
-
comparator,
|
|
3349
|
-
option
|
|
3350
|
-
);
|
|
3351
|
-
}
|
|
3352
|
-
/**
|
|
3353
|
-
* Creates a new asynchronous transaction.
|
|
3354
|
-
* @returns A new BPTreeAsyncTransaction.
|
|
3355
|
-
*/
|
|
3356
|
-
async createTransaction() {
|
|
3357
|
-
const nestedTx = await this.mvcc.createNested();
|
|
3358
|
-
const tx = new BPTreeAsyncTransaction(
|
|
3359
|
-
this,
|
|
3360
|
-
this.mvcc,
|
|
3361
|
-
nestedTx,
|
|
3362
|
-
this.strategy,
|
|
3363
|
-
this.comparator,
|
|
3364
|
-
this.option
|
|
3365
|
-
);
|
|
3366
|
-
await tx.init();
|
|
3367
|
-
return tx;
|
|
3368
|
-
}
|
|
3369
|
-
async insert(key, value) {
|
|
3370
|
-
const tx = await this.createTransaction();
|
|
3371
|
-
await tx.insert(key, value);
|
|
3372
|
-
const result = await tx.commit();
|
|
3373
|
-
if (!result.success) {
|
|
3374
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3375
|
-
}
|
|
3376
|
-
this.rootId = tx.getRootId();
|
|
3377
|
-
}
|
|
3378
|
-
async delete(key, value) {
|
|
3379
|
-
const tx = await this.createTransaction();
|
|
3380
|
-
await tx.delete(key, value);
|
|
3381
|
-
const result = await tx.commit();
|
|
3382
|
-
if (!result.success) {
|
|
3383
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3384
|
-
}
|
|
3385
|
-
this.rootId = tx.getRootId();
|
|
3386
|
-
}
|
|
3387
|
-
};
|
|
3388
|
-
|
|
3389
|
-
// src/base/SerializeStrategy.ts
|
|
3390
|
-
var SerializeStrategy = class {
|
|
3391
|
-
order;
|
|
3392
|
-
head;
|
|
3393
|
-
constructor(order) {
|
|
3394
|
-
this.order = order;
|
|
3395
|
-
this.head = {
|
|
3396
|
-
order,
|
|
3397
|
-
root: null,
|
|
3398
|
-
data: {}
|
|
3399
|
-
};
|
|
3400
|
-
}
|
|
3401
|
-
};
|
|
3402
|
-
|
|
3403
|
-
// src/SerializeStrategySync.ts
|
|
3404
|
-
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3405
|
-
getHeadData(key, defaultValue) {
|
|
3406
|
-
if (!Object.hasOwn(this.head.data, key)) {
|
|
3407
|
-
this.setHeadData(key, defaultValue);
|
|
3408
|
-
}
|
|
3409
|
-
return this.head.data[key];
|
|
3410
|
-
}
|
|
3411
|
-
setHeadData(key, data) {
|
|
3412
|
-
this.head.data[key] = data;
|
|
3413
|
-
this.writeHead(this.head);
|
|
3414
|
-
}
|
|
3415
|
-
autoIncrement(key, defaultValue) {
|
|
3416
|
-
const current = this.getHeadData(key, defaultValue);
|
|
3417
|
-
const next = current + 1;
|
|
3418
|
-
this.setHeadData(key, next);
|
|
3419
|
-
return current;
|
|
3420
|
-
}
|
|
3421
|
-
};
|
|
3422
|
-
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3423
|
-
node;
|
|
3424
|
-
constructor(order) {
|
|
3425
|
-
super(order);
|
|
3426
|
-
this.node = {};
|
|
3427
|
-
}
|
|
3428
|
-
id(isLeaf) {
|
|
3429
|
-
return this.autoIncrement("index", 1).toString();
|
|
3430
|
-
}
|
|
3431
|
-
read(id) {
|
|
3432
|
-
if (!Object.hasOwn(this.node, id)) {
|
|
3433
|
-
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3434
|
-
}
|
|
3435
|
-
const node = this.node[id];
|
|
3436
|
-
return JSON.parse(JSON.stringify(node));
|
|
3437
|
-
}
|
|
3438
|
-
write(id, node) {
|
|
3439
|
-
this.node[id] = node;
|
|
3440
|
-
}
|
|
3441
|
-
delete(id) {
|
|
3442
|
-
delete this.node[id];
|
|
3443
|
-
}
|
|
3444
|
-
readHead() {
|
|
3445
|
-
if (this.head.root === null) {
|
|
3446
|
-
return null;
|
|
3447
|
-
}
|
|
3448
|
-
return this.head;
|
|
3449
|
-
}
|
|
3450
|
-
writeHead(head) {
|
|
3451
|
-
this.head = head;
|
|
3452
|
-
}
|
|
3453
|
-
};
|
|
3454
|
-
|
|
3455
|
-
// node_modules/ryoiki/dist/esm/index.mjs
|
|
3456
|
-
var Ryoiki2 = class _Ryoiki2 {
|
|
3457
|
-
readings;
|
|
3458
|
-
writings;
|
|
3459
|
-
readQueue;
|
|
3460
|
-
writeQueue;
|
|
3461
|
-
static async CatchError(promise) {
|
|
3462
|
-
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
3463
|
-
}
|
|
3464
|
-
static IsRangeOverlap(a, b) {
|
|
3465
|
-
const [start1, end1] = a;
|
|
3466
|
-
const [start2, end2] = b;
|
|
3467
|
-
if (end1 <= start2 || end2 <= start1) {
|
|
3468
|
-
return false;
|
|
3469
|
-
}
|
|
3470
|
-
return true;
|
|
3471
|
-
}
|
|
3472
|
-
static ERR_ALREADY_EXISTS(lockId) {
|
|
3473
|
-
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
3474
|
-
}
|
|
3475
|
-
static ERR_NOT_EXISTS(lockId) {
|
|
3476
|
-
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
3477
|
-
}
|
|
3478
|
-
static ERR_TIMEOUT(lockId, timeout) {
|
|
3479
|
-
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
3480
|
-
}
|
|
3481
|
-
/**
|
|
3482
|
-
* Constructs a new instance of the Ryoiki class.
|
|
3483
|
-
*/
|
|
3484
|
-
constructor() {
|
|
3485
|
-
this.readings = /* @__PURE__ */ new Map();
|
|
3486
|
-
this.writings = /* @__PURE__ */ new Map();
|
|
3487
|
-
this.readQueue = /* @__PURE__ */ new Map();
|
|
3488
|
-
this.writeQueue = /* @__PURE__ */ new Map();
|
|
3489
|
-
}
|
|
3490
|
-
/**
|
|
3491
|
-
* Creates a range based on a start value and length.
|
|
3492
|
-
* @param start - The starting value of the range.
|
|
3493
|
-
* @param length - The length of the range.
|
|
3494
|
-
* @returns A range tuple [start, start + length].
|
|
3495
|
-
*/
|
|
3496
|
-
range(start, length) {
|
|
3497
|
-
return [start, start + length];
|
|
3498
|
-
}
|
|
3499
|
-
rangeOverlapping(tasks, range) {
|
|
3500
|
-
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
3501
|
-
}
|
|
3502
|
-
isSameRange(a, b) {
|
|
3503
|
-
const [a1, a2] = a;
|
|
3504
|
-
const [b1, b2] = b;
|
|
3505
|
-
return a1 === b1 && a2 === b2;
|
|
3506
|
-
}
|
|
3507
|
-
fetchUnitAndRun(queue, workspaces) {
|
|
3508
|
-
for (const [id, unit] of queue) {
|
|
3509
|
-
if (!unit.condition()) {
|
|
3510
|
-
continue;
|
|
3511
|
-
}
|
|
3512
|
-
this._alloc(queue, workspaces, id);
|
|
3513
|
-
}
|
|
3514
|
-
}
|
|
3515
|
-
_handleOverload(args, handlers, argPatterns) {
|
|
3516
|
-
for (const [key, pattern] of Object.entries(argPatterns)) {
|
|
3517
|
-
if (this._matchArgs(args, pattern)) {
|
|
3518
|
-
return handlers[key](...args);
|
|
3519
|
-
}
|
|
3520
|
-
}
|
|
3521
|
-
throw new Error("Invalid arguments");
|
|
3522
|
-
}
|
|
3523
|
-
_matchArgs(args, pattern) {
|
|
3524
|
-
return args.every((arg, index) => {
|
|
3525
|
-
const expectedType = pattern[index];
|
|
3526
|
-
if (expectedType === void 0) return typeof arg === "undefined";
|
|
3527
|
-
if (expectedType === Function) return typeof arg === "function";
|
|
3528
|
-
if (expectedType === Number) return typeof arg === "number";
|
|
3529
|
-
if (expectedType === Array) return Array.isArray(arg);
|
|
3530
|
-
return false;
|
|
3531
|
-
});
|
|
3532
|
-
}
|
|
3533
|
-
_createRandomId() {
|
|
3534
|
-
const timestamp = Date.now().toString(36);
|
|
3535
|
-
const random = Math.random().toString(36).substring(2);
|
|
3536
|
-
return `${timestamp}${random}`;
|
|
3537
|
-
}
|
|
3538
|
-
_alloc(queue, workspaces, lockId) {
|
|
3539
|
-
const unit = queue.get(lockId);
|
|
3540
|
-
if (!unit) {
|
|
3541
|
-
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
3542
|
-
}
|
|
3543
|
-
workspaces.set(lockId, unit);
|
|
3544
|
-
queue.delete(lockId);
|
|
3545
|
-
unit.alloc();
|
|
3546
|
-
}
|
|
3547
|
-
_free(workspaces, lockId) {
|
|
3548
|
-
const unit = workspaces.get(lockId);
|
|
3549
|
-
if (!unit) {
|
|
3550
|
-
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
3551
|
-
}
|
|
3552
|
-
workspaces.delete(lockId);
|
|
3553
|
-
unit.free();
|
|
3554
|
-
}
|
|
3555
|
-
_lock(queue, range, timeout, task, condition) {
|
|
3556
|
-
return new Promise((resolve, reject) => {
|
|
3557
|
-
let timeoutId = null;
|
|
3558
|
-
if (timeout >= 0) {
|
|
3559
|
-
timeoutId = setTimeout(() => {
|
|
3560
|
-
reject(_Ryoiki2.ERR_TIMEOUT(id, timeout));
|
|
3561
|
-
}, timeout);
|
|
3562
|
-
}
|
|
3563
|
-
const id = this._createRandomId();
|
|
3564
|
-
const alloc = async () => {
|
|
3565
|
-
if (timeoutId !== null) {
|
|
3566
|
-
clearTimeout(timeoutId);
|
|
3567
|
-
}
|
|
3568
|
-
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
3569
|
-
if (err) reject(err);
|
|
3570
|
-
else resolve(v);
|
|
3571
|
-
};
|
|
3572
|
-
const fetch = () => {
|
|
3573
|
-
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
3574
|
-
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
3575
|
-
};
|
|
3576
|
-
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
3577
|
-
fetch();
|
|
3469
|
+
if (head === null) {
|
|
3470
|
+
throw new Error("Head not found");
|
|
3471
|
+
}
|
|
3472
|
+
await this._writeHead({
|
|
3473
|
+
root: head.root,
|
|
3474
|
+
order: head.order,
|
|
3475
|
+
data
|
|
3578
3476
|
});
|
|
3579
3477
|
}
|
|
3580
|
-
|
|
3581
|
-
let
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3478
|
+
async commit(label) {
|
|
3479
|
+
let result = await this.mvcc.commit(label);
|
|
3480
|
+
if (result.success) {
|
|
3481
|
+
const isRootTx = this.rootTx === this;
|
|
3482
|
+
if (!isRootTx) {
|
|
3483
|
+
result = await this.rootTx.commit(label);
|
|
3484
|
+
if (result.success) {
|
|
3485
|
+
this.rootTx.rootId = this.rootId;
|
|
3486
|
+
}
|
|
3586
3487
|
}
|
|
3587
3488
|
}
|
|
3588
|
-
return
|
|
3489
|
+
return result;
|
|
3589
3490
|
}
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
* @param range The range to check for active read locks.
|
|
3593
|
-
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
3594
|
-
*/
|
|
3595
|
-
isReading(range) {
|
|
3596
|
-
return this._checkWorking(range, this.readings);
|
|
3491
|
+
async rollback() {
|
|
3492
|
+
return this.mvcc.rollback();
|
|
3597
3493
|
}
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3494
|
+
};
|
|
3495
|
+
|
|
3496
|
+
// src/transaction/BPTreeMVCCStrategyAsync.ts
|
|
3497
|
+
var BPTreeMVCCStrategyAsync = class extends AsyncMVCCStrategy {
|
|
3498
|
+
constructor(strategy) {
|
|
3499
|
+
super();
|
|
3500
|
+
this.strategy = strategy;
|
|
3605
3501
|
}
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
canRead(range) {
|
|
3612
|
-
const writing = this.isWriting(range);
|
|
3613
|
-
return !writing;
|
|
3502
|
+
async read(key) {
|
|
3503
|
+
if (key === "__HEAD__") {
|
|
3504
|
+
return await this.strategy.readHead();
|
|
3505
|
+
}
|
|
3506
|
+
return await this.strategy.read(key);
|
|
3614
3507
|
}
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
const reading = this.isReading(range);
|
|
3622
|
-
const writing = this.isWriting(range);
|
|
3623
|
-
return !reading && !writing;
|
|
3508
|
+
async write(key, value) {
|
|
3509
|
+
if (key === "__HEAD__") {
|
|
3510
|
+
await this.strategy.writeHead(value);
|
|
3511
|
+
} else {
|
|
3512
|
+
await this.strategy.write(key, value);
|
|
3513
|
+
}
|
|
3624
3514
|
}
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
range,
|
|
3655
|
-
timeout,
|
|
3656
|
-
task,
|
|
3657
|
-
() => !this.rangeOverlapping(this.writings, range)
|
|
3515
|
+
async delete(key) {
|
|
3516
|
+
await this.strategy.delete(key);
|
|
3517
|
+
}
|
|
3518
|
+
async exists(key) {
|
|
3519
|
+
if (key === "__HEAD__") {
|
|
3520
|
+
return await this.strategy.readHead() !== null;
|
|
3521
|
+
}
|
|
3522
|
+
try {
|
|
3523
|
+
const node = await this.strategy.read(key);
|
|
3524
|
+
return node !== null && node !== void 0;
|
|
3525
|
+
} catch {
|
|
3526
|
+
return false;
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
};
|
|
3530
|
+
|
|
3531
|
+
// src/BPTreeAsync.ts
|
|
3532
|
+
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3533
|
+
constructor(strategy, comparator, option) {
|
|
3534
|
+
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy), {
|
|
3535
|
+
cacheCapacity: option?.capacity ?? void 0
|
|
3536
|
+
});
|
|
3537
|
+
super(
|
|
3538
|
+
null,
|
|
3539
|
+
mvccRoot,
|
|
3540
|
+
mvccRoot,
|
|
3541
|
+
strategy,
|
|
3542
|
+
comparator,
|
|
3543
|
+
option
|
|
3658
3544
|
);
|
|
3659
3545
|
}
|
|
3660
3546
|
/**
|
|
3661
|
-
*
|
|
3662
|
-
* @
|
|
3663
|
-
* @param arg0 - Either a range or a task callback.
|
|
3664
|
-
* If a range is provided, the task is the second argument.
|
|
3665
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
3666
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
3667
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
3668
|
-
* If this value is not provided, no timeout will be set.
|
|
3669
|
-
* @returns A promise resolving to the result of the task execution.
|
|
3547
|
+
* Creates a new asynchronous transaction.
|
|
3548
|
+
* @returns A new BPTreeAsyncTransaction.
|
|
3670
3549
|
*/
|
|
3671
|
-
|
|
3672
|
-
const
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
{
|
|
3681
|
-
task: [Function],
|
|
3682
|
-
taskTimeout: [Function, Number],
|
|
3683
|
-
rangeTask: [Array, Function],
|
|
3684
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
3685
|
-
}
|
|
3550
|
+
async createTransaction() {
|
|
3551
|
+
const nestedTx = this.mvcc.createNested();
|
|
3552
|
+
const tx = new BPTreeAsyncTransaction(
|
|
3553
|
+
this,
|
|
3554
|
+
this.mvcc,
|
|
3555
|
+
nestedTx,
|
|
3556
|
+
this.strategy,
|
|
3557
|
+
this.comparator,
|
|
3558
|
+
this.option
|
|
3686
3559
|
);
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
()
|
|
3693
|
-
|
|
3560
|
+
await tx._initInternal();
|
|
3561
|
+
return tx;
|
|
3562
|
+
}
|
|
3563
|
+
async insert(key, value) {
|
|
3564
|
+
return this.writeLock(1, async () => {
|
|
3565
|
+
const tx = await this.createTransaction();
|
|
3566
|
+
await tx.insert(key, value);
|
|
3567
|
+
const result = await tx.commit();
|
|
3568
|
+
if (!result.success) {
|
|
3569
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3694
3570
|
}
|
|
3695
|
-
);
|
|
3571
|
+
});
|
|
3696
3572
|
}
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3573
|
+
async delete(key, value) {
|
|
3574
|
+
return this.writeLock(1, async () => {
|
|
3575
|
+
const tx = await this.createTransaction();
|
|
3576
|
+
await tx.delete(key, value);
|
|
3577
|
+
const result = await tx.commit();
|
|
3578
|
+
if (!result.success) {
|
|
3579
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3580
|
+
}
|
|
3581
|
+
});
|
|
3703
3582
|
}
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3583
|
+
};
|
|
3584
|
+
|
|
3585
|
+
// src/base/SerializeStrategy.ts
|
|
3586
|
+
var SerializeStrategy = class {
|
|
3587
|
+
order;
|
|
3588
|
+
head;
|
|
3589
|
+
constructor(order) {
|
|
3590
|
+
this.order = order;
|
|
3591
|
+
this.head = {
|
|
3592
|
+
order,
|
|
3593
|
+
root: null,
|
|
3594
|
+
data: {}
|
|
3595
|
+
};
|
|
3596
|
+
}
|
|
3597
|
+
};
|
|
3598
|
+
|
|
3599
|
+
// src/SerializeStrategySync.ts
|
|
3600
|
+
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3601
|
+
getHeadData(key, defaultValue) {
|
|
3602
|
+
if (!Object.hasOwn(this.head.data, key)) {
|
|
3603
|
+
this.setHeadData(key, defaultValue);
|
|
3604
|
+
}
|
|
3605
|
+
return this.head.data[key];
|
|
3606
|
+
}
|
|
3607
|
+
setHeadData(key, data) {
|
|
3608
|
+
this.head.data[key] = data;
|
|
3609
|
+
this.writeHead(this.head);
|
|
3610
|
+
}
|
|
3611
|
+
autoIncrement(key, defaultValue) {
|
|
3612
|
+
const current = this.getHeadData(key, defaultValue);
|
|
3613
|
+
const next = current + 1;
|
|
3614
|
+
this.setHeadData(key, next);
|
|
3615
|
+
return current;
|
|
3616
|
+
}
|
|
3617
|
+
};
|
|
3618
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3619
|
+
node;
|
|
3620
|
+
constructor(order) {
|
|
3621
|
+
super(order);
|
|
3622
|
+
this.node = {};
|
|
3623
|
+
}
|
|
3624
|
+
id(isLeaf) {
|
|
3625
|
+
return this.autoIncrement("index", 1).toString();
|
|
3626
|
+
}
|
|
3627
|
+
read(id) {
|
|
3628
|
+
if (!Object.hasOwn(this.node, id)) {
|
|
3629
|
+
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3630
|
+
}
|
|
3631
|
+
const node = this.node[id];
|
|
3632
|
+
return JSON.parse(JSON.stringify(node));
|
|
3633
|
+
}
|
|
3634
|
+
write(id, node) {
|
|
3635
|
+
this.node[id] = node;
|
|
3636
|
+
}
|
|
3637
|
+
delete(id) {
|
|
3638
|
+
delete this.node[id];
|
|
3639
|
+
}
|
|
3640
|
+
readHead() {
|
|
3641
|
+
if (this.head.root === null) {
|
|
3642
|
+
return null;
|
|
3643
|
+
}
|
|
3644
|
+
return this.head;
|
|
3645
|
+
}
|
|
3646
|
+
writeHead(head) {
|
|
3647
|
+
this.head = head;
|
|
3710
3648
|
}
|
|
3711
3649
|
};
|
|
3712
3650
|
|