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/cjs/index.cjs
CHANGED
|
@@ -93,6 +93,42 @@ var StringComparator = class extends ValueComparator {
|
|
|
93
93
|
// node_modules/mvcc-api/dist/esm/index.mjs
|
|
94
94
|
var MVCCStrategy = class {
|
|
95
95
|
};
|
|
96
|
+
var LRUMap = class {
|
|
97
|
+
cache = /* @__PURE__ */ new Map();
|
|
98
|
+
capacity;
|
|
99
|
+
constructor(capacity) {
|
|
100
|
+
this.capacity = capacity;
|
|
101
|
+
}
|
|
102
|
+
get(key) {
|
|
103
|
+
if (!this.cache.has(key)) return void 0;
|
|
104
|
+
const value = this.cache.get(key);
|
|
105
|
+
this.cache.delete(key);
|
|
106
|
+
this.cache.set(key, value);
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
set(key, value) {
|
|
110
|
+
if (this.cache.has(key)) {
|
|
111
|
+
this.cache.delete(key);
|
|
112
|
+
} else if (this.cache.size >= this.capacity) {
|
|
113
|
+
const oldestKey = this.cache.keys().next().value;
|
|
114
|
+
if (oldestKey !== void 0) this.cache.delete(oldestKey);
|
|
115
|
+
}
|
|
116
|
+
this.cache.set(key, value);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
has(key) {
|
|
120
|
+
return this.cache.has(key);
|
|
121
|
+
}
|
|
122
|
+
delete(key) {
|
|
123
|
+
return this.cache.delete(key);
|
|
124
|
+
}
|
|
125
|
+
clear() {
|
|
126
|
+
this.cache.clear();
|
|
127
|
+
}
|
|
128
|
+
get size() {
|
|
129
|
+
return this.cache.size;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
96
132
|
var MVCCTransaction = class {
|
|
97
133
|
committed;
|
|
98
134
|
snapshotVersion;
|
|
@@ -100,11 +136,11 @@ var MVCCTransaction = class {
|
|
|
100
136
|
writeBuffer;
|
|
101
137
|
deleteBuffer;
|
|
102
138
|
createdKeys;
|
|
103
|
-
// create()로 생성된 키 추적
|
|
104
139
|
deletedValues;
|
|
105
140
|
// delete 시 삭제 전 값 저장
|
|
106
141
|
originallyExisted;
|
|
107
142
|
// 트랜잭션 시작 시점에 디스크에 존재했던 키 (deleted 결과 필터링용)
|
|
143
|
+
bufferHistory = /* @__PURE__ */ new Map();
|
|
108
144
|
// Nested Transaction Properties
|
|
109
145
|
parent;
|
|
110
146
|
localVersion;
|
|
@@ -118,7 +154,8 @@ var MVCCTransaction = class {
|
|
|
118
154
|
versionIndex = /* @__PURE__ */ new Map();
|
|
119
155
|
deletedCache = /* @__PURE__ */ new Map();
|
|
120
156
|
activeTransactions = /* @__PURE__ */ new Set();
|
|
121
|
-
|
|
157
|
+
diskCache;
|
|
158
|
+
constructor(strategy, options, parent, snapshotVersion) {
|
|
122
159
|
this.snapshotVersion = snapshotVersion ?? 0;
|
|
123
160
|
this.writeBuffer = /* @__PURE__ */ new Map();
|
|
124
161
|
this.deleteBuffer = /* @__PURE__ */ new Set();
|
|
@@ -133,6 +170,7 @@ var MVCCTransaction = class {
|
|
|
133
170
|
this.snapshotLocalVersion = parent.localVersion;
|
|
134
171
|
this.strategy = void 0;
|
|
135
172
|
this.root = parent.root;
|
|
173
|
+
this.diskCache = parent.diskCache;
|
|
136
174
|
} else {
|
|
137
175
|
if (!strategy) throw new Error("Root Transaction must get Strategy");
|
|
138
176
|
this.strategy = strategy;
|
|
@@ -140,8 +178,13 @@ var MVCCTransaction = class {
|
|
|
140
178
|
this.localVersion = 0;
|
|
141
179
|
this.snapshotLocalVersion = 0;
|
|
142
180
|
this.root = this;
|
|
181
|
+
this.diskCache = new LRUMap(options?.cacheCapacity ?? 1e3);
|
|
143
182
|
}
|
|
144
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Checks if the transaction is a root transaction.
|
|
186
|
+
* @returns True if the transaction is a root transaction, false otherwise.
|
|
187
|
+
*/
|
|
145
188
|
isRoot() {
|
|
146
189
|
return !this.parent;
|
|
147
190
|
}
|
|
@@ -158,27 +201,61 @@ var MVCCTransaction = class {
|
|
|
158
201
|
}
|
|
159
202
|
return false;
|
|
160
203
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
204
|
+
/**
|
|
205
|
+
* Checks if a key was written in this transaction.
|
|
206
|
+
* @param key The key to check.
|
|
207
|
+
* @returns True if the key was written in this transaction, false otherwise.
|
|
208
|
+
*/
|
|
209
|
+
isWrote(key) {
|
|
210
|
+
return this.writeBuffer.has(key);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Checks if a key was deleted in this transaction.
|
|
214
|
+
* @param key The key to check.
|
|
215
|
+
* @returns True if the key was deleted in this transaction, false otherwise.
|
|
216
|
+
*/
|
|
217
|
+
isDeleted(key) {
|
|
218
|
+
return this.deleteBuffer.has(key);
|
|
219
|
+
}
|
|
220
|
+
_recordHistory(key) {
|
|
221
|
+
const existsInWriteBuffer = this.writeBuffer.has(key);
|
|
222
|
+
const existsInDeleteBuffer = this.deleteBuffer.has(key);
|
|
223
|
+
const currentVer = this.keyVersions.get(key);
|
|
224
|
+
if (currentVer !== void 0) {
|
|
225
|
+
if (!this.bufferHistory.has(key)) this.bufferHistory.set(key, []);
|
|
226
|
+
this.bufferHistory.get(key).push({
|
|
227
|
+
value: existsInWriteBuffer ? this.writeBuffer.get(key) : this.deletedValues.get(key) ?? null,
|
|
228
|
+
exists: existsInWriteBuffer || !existsInDeleteBuffer,
|
|
229
|
+
version: currentVer
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
_bufferCreate(key, value, version) {
|
|
234
|
+
if (version === void 0) this.localVersion++;
|
|
235
|
+
const targetVersion = version ?? this.localVersion;
|
|
236
|
+
this._recordHistory(key);
|
|
164
237
|
this.writeBuffer.set(key, value);
|
|
165
238
|
this.createdKeys.add(key);
|
|
166
239
|
this.deleteBuffer.delete(key);
|
|
167
240
|
this.originallyExisted.delete(key);
|
|
168
|
-
this.keyVersions.set(key,
|
|
241
|
+
this.keyVersions.set(key, targetVersion);
|
|
169
242
|
}
|
|
170
|
-
_bufferWrite(key, value) {
|
|
171
|
-
this.localVersion++;
|
|
243
|
+
_bufferWrite(key, value, version) {
|
|
244
|
+
if (version === void 0) this.localVersion++;
|
|
245
|
+
const targetVersion = version ?? this.localVersion;
|
|
246
|
+
this._recordHistory(key);
|
|
172
247
|
this.writeBuffer.set(key, value);
|
|
173
248
|
this.deleteBuffer.delete(key);
|
|
174
|
-
this.keyVersions.set(key,
|
|
249
|
+
this.keyVersions.set(key, targetVersion);
|
|
175
250
|
}
|
|
176
|
-
_bufferDelete(key) {
|
|
177
|
-
this.localVersion++;
|
|
251
|
+
_bufferDelete(key, version) {
|
|
252
|
+
if (version === void 0) this.localVersion++;
|
|
253
|
+
const targetVersion = version ?? this.localVersion;
|
|
254
|
+
this._recordHistory(key);
|
|
178
255
|
this.deleteBuffer.add(key);
|
|
179
256
|
this.writeBuffer.delete(key);
|
|
180
257
|
this.createdKeys.delete(key);
|
|
181
|
-
this.keyVersions.set(key,
|
|
258
|
+
this.keyVersions.set(key, targetVersion);
|
|
182
259
|
}
|
|
183
260
|
/**
|
|
184
261
|
* Returns the entries that will be created, updated, and deleted by this transaction.
|
|
@@ -202,7 +279,11 @@ var MVCCTransaction = class {
|
|
|
202
279
|
deleted.push({ key, data });
|
|
203
280
|
}
|
|
204
281
|
}
|
|
205
|
-
return {
|
|
282
|
+
return {
|
|
283
|
+
created,
|
|
284
|
+
updated,
|
|
285
|
+
deleted
|
|
286
|
+
};
|
|
206
287
|
}
|
|
207
288
|
/**
|
|
208
289
|
* Rolls back the transaction.
|
|
@@ -220,7 +301,12 @@ var MVCCTransaction = class {
|
|
|
220
301
|
if (this.root !== this) {
|
|
221
302
|
this.root.activeTransactions.delete(this);
|
|
222
303
|
}
|
|
223
|
-
return {
|
|
304
|
+
return {
|
|
305
|
+
success: true,
|
|
306
|
+
created,
|
|
307
|
+
updated,
|
|
308
|
+
deleted
|
|
309
|
+
};
|
|
224
310
|
}
|
|
225
311
|
/**
|
|
226
312
|
* Cleans up both deletedCache and versionIndex based on minActiveVersion.
|
|
@@ -255,7 +341,9 @@ var MVCCTransaction = class {
|
|
|
255
341
|
break;
|
|
256
342
|
}
|
|
257
343
|
}
|
|
258
|
-
if (latestInSnapshotIdx
|
|
344
|
+
if (latestInSnapshotIdx === versions.length - 1) {
|
|
345
|
+
this.versionIndex.delete(key);
|
|
346
|
+
} else if (latestInSnapshotIdx > 0) {
|
|
259
347
|
versions.splice(0, latestInSnapshotIdx);
|
|
260
348
|
}
|
|
261
349
|
}
|
|
@@ -304,7 +392,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
304
392
|
createNested() {
|
|
305
393
|
if (this.committed) throw new Error("Transaction already committed");
|
|
306
394
|
const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
|
|
307
|
-
const child = new _SyncMVCCTransaction(void 0, this, childVersion);
|
|
395
|
+
const child = new _SyncMVCCTransaction(void 0, void 0, this, childVersion);
|
|
308
396
|
this.root.activeTransactions.add(child);
|
|
309
397
|
return child;
|
|
310
398
|
}
|
|
@@ -312,27 +400,68 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
312
400
|
if (this.committed) throw new Error("Transaction already committed");
|
|
313
401
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
314
402
|
if (this.deleteBuffer.has(key)) return null;
|
|
315
|
-
|
|
403
|
+
if (this.parent) {
|
|
404
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
405
|
+
}
|
|
406
|
+
return this._diskRead(key, this.snapshotVersion);
|
|
316
407
|
}
|
|
317
408
|
exists(key) {
|
|
318
409
|
if (this.committed) throw new Error("Transaction already committed");
|
|
319
410
|
if (this.deleteBuffer.has(key)) return false;
|
|
320
411
|
if (this.writeBuffer.has(key)) return true;
|
|
321
|
-
|
|
412
|
+
if (this.parent) {
|
|
413
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
414
|
+
}
|
|
415
|
+
return this._diskExists(key, this.snapshotVersion);
|
|
416
|
+
}
|
|
417
|
+
_existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
418
|
+
if (this.writeBuffer.has(key)) {
|
|
419
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
420
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (this.deleteBuffer.has(key)) {
|
|
425
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
426
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
427
|
+
return false;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
const history = this.bufferHistory.get(key);
|
|
431
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
432
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
433
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
434
|
+
return history[i].exists;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (this.parent) {
|
|
439
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
440
|
+
} else {
|
|
441
|
+
return this._diskExists(key, snapshotVersion);
|
|
442
|
+
}
|
|
322
443
|
}
|
|
323
444
|
_readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
324
445
|
if (this.writeBuffer.has(key)) {
|
|
325
446
|
const keyModVersion = this.keyVersions.get(key);
|
|
326
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
447
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
327
448
|
return this.writeBuffer.get(key);
|
|
328
449
|
}
|
|
329
450
|
}
|
|
330
451
|
if (this.deleteBuffer.has(key)) {
|
|
331
452
|
const keyModVersion = this.keyVersions.get(key);
|
|
332
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
453
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
333
454
|
return null;
|
|
334
455
|
}
|
|
335
456
|
}
|
|
457
|
+
const history = this.bufferHistory.get(key);
|
|
458
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
459
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
460
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
461
|
+
return history[i].exists ? history[i].value : null;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
336
465
|
if (this.parent) {
|
|
337
466
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
338
467
|
} else {
|
|
@@ -397,6 +526,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
397
526
|
this.deletedValues.clear();
|
|
398
527
|
this.originallyExisted.clear();
|
|
399
528
|
this.keyVersions.clear();
|
|
529
|
+
this.bufferHistory.clear();
|
|
400
530
|
this.localVersion = 0;
|
|
401
531
|
this.snapshotVersion = this.version;
|
|
402
532
|
}
|
|
@@ -437,32 +567,22 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
437
567
|
};
|
|
438
568
|
}
|
|
439
569
|
}
|
|
440
|
-
const
|
|
441
|
-
for (const key of child.writeBuffer
|
|
442
|
-
|
|
443
|
-
this.
|
|
444
|
-
this.
|
|
445
|
-
if (child.createdKeys.has(key)) {
|
|
446
|
-
this.createdKeys.add(key);
|
|
447
|
-
}
|
|
570
|
+
const mergeVersion = ++this.localVersion;
|
|
571
|
+
for (const [key, value] of child.writeBuffer) {
|
|
572
|
+
const wasCreated = child.createdKeys.has(key);
|
|
573
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
574
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
448
575
|
}
|
|
449
576
|
for (const key of child.deleteBuffer) {
|
|
450
|
-
this.deleteBuffer.add(key);
|
|
451
|
-
this.writeBuffer.delete(key);
|
|
452
|
-
this.createdKeys.delete(key);
|
|
453
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
454
577
|
const deletedValue = child.deletedValues.get(key);
|
|
455
|
-
if (deletedValue !== void 0)
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
if (child.originallyExisted.has(key)) {
|
|
578
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
579
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
459
580
|
this.originallyExisted.add(key);
|
|
460
581
|
}
|
|
582
|
+
this._bufferDelete(key, mergeVersion);
|
|
461
583
|
}
|
|
462
|
-
this.localVersion = newLocalVersion;
|
|
463
584
|
this.root.activeTransactions.delete(child);
|
|
464
585
|
} else {
|
|
465
|
-
const newVersion = this.version + 1;
|
|
466
586
|
if (child !== this) {
|
|
467
587
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
468
588
|
for (const key of modifiedKeys) {
|
|
@@ -480,50 +600,57 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
480
600
|
};
|
|
481
601
|
}
|
|
482
602
|
}
|
|
603
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
604
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
605
|
+
return {
|
|
606
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
607
|
+
conflict: {
|
|
608
|
+
key,
|
|
609
|
+
parent: this.read(key),
|
|
610
|
+
child: child.read(key)
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
}
|
|
483
614
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
for (const key of child.deleteBuffer) {
|
|
493
|
-
this.deleteBuffer.add(key);
|
|
494
|
-
this.writeBuffer.delete(key);
|
|
495
|
-
this.createdKeys.delete(key);
|
|
496
|
-
const deletedValue = child.deletedValues.get(key);
|
|
497
|
-
if (deletedValue !== void 0) {
|
|
498
|
-
this.deletedValues.set(key, deletedValue);
|
|
615
|
+
const mergeVersion = ++this.localVersion;
|
|
616
|
+
for (const [key, value] of child.writeBuffer) {
|
|
617
|
+
const wasCreated = child.createdKeys.has(key);
|
|
618
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
619
|
+
this.originallyExisted.add(key);
|
|
620
|
+
}
|
|
621
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
622
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
499
623
|
}
|
|
500
|
-
|
|
501
|
-
|
|
624
|
+
for (const key of child.deleteBuffer) {
|
|
625
|
+
const deletedValue = child.deletedValues.get(key);
|
|
626
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
627
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
628
|
+
this.originallyExisted.add(key);
|
|
629
|
+
}
|
|
630
|
+
this._bufferDelete(key, mergeVersion);
|
|
502
631
|
}
|
|
632
|
+
this.root.activeTransactions.delete(child);
|
|
633
|
+
} else {
|
|
634
|
+
const newVersion = this.version + 1;
|
|
635
|
+
for (const [key, value] of this.writeBuffer) this._diskWrite(key, value, newVersion);
|
|
636
|
+
for (const key of this.deleteBuffer) this._diskDelete(key, newVersion);
|
|
637
|
+
this.version = newVersion;
|
|
638
|
+
this._cleanupDeletedCache();
|
|
503
639
|
}
|
|
504
|
-
for (const [key, value] of child.writeBuffer) {
|
|
505
|
-
this._diskWrite(key, value, newVersion);
|
|
506
|
-
}
|
|
507
|
-
for (const key of child.deleteBuffer) {
|
|
508
|
-
this._diskDelete(key, newVersion);
|
|
509
|
-
}
|
|
510
|
-
this.version = newVersion;
|
|
511
|
-
this.root.activeTransactions.delete(child);
|
|
512
|
-
this._cleanupDeletedCache();
|
|
513
640
|
}
|
|
514
641
|
return null;
|
|
515
642
|
}
|
|
516
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
517
643
|
_diskWrite(key, value, version) {
|
|
518
644
|
const strategy = this.strategy;
|
|
519
645
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
520
|
-
|
|
521
|
-
|
|
646
|
+
const rootAsAny = this.root;
|
|
647
|
+
if (this._diskExists(key, version)) {
|
|
648
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
522
649
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
523
|
-
this.deletedCache.get(key).push({
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
650
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
651
|
+
rootAsAny.diskCache.set(key, value);
|
|
652
|
+
} else {
|
|
653
|
+
rootAsAny.diskCache.set(key, value);
|
|
527
654
|
}
|
|
528
655
|
strategy.write(key, value);
|
|
529
656
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -534,14 +661,19 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
534
661
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
535
662
|
const versions = this.versionIndex.get(key);
|
|
536
663
|
if (!versions) {
|
|
537
|
-
|
|
664
|
+
const rootAsAny = this.root;
|
|
665
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
666
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
667
|
+
rootAsAny.diskCache.set(key, val);
|
|
668
|
+
return val;
|
|
669
|
+
}
|
|
670
|
+
return null;
|
|
538
671
|
}
|
|
539
672
|
let targetVerObj = null;
|
|
540
673
|
let nextVerObj = null;
|
|
541
674
|
for (const v of versions) {
|
|
542
|
-
if (v.version <= snapshotVersion)
|
|
543
|
-
|
|
544
|
-
} else {
|
|
675
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
676
|
+
else {
|
|
545
677
|
nextVerObj = v;
|
|
546
678
|
break;
|
|
547
679
|
}
|
|
@@ -558,7 +690,14 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
558
690
|
}
|
|
559
691
|
if (!targetVerObj.exists) return null;
|
|
560
692
|
if (!nextVerObj) {
|
|
561
|
-
return
|
|
693
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
694
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
695
|
+
const rootAsAny = this.root;
|
|
696
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
697
|
+
rootAsAny.diskCache.set(key, val);
|
|
698
|
+
return val;
|
|
699
|
+
}
|
|
700
|
+
return null;
|
|
562
701
|
}
|
|
563
702
|
const cached = this.deletedCache.get(key);
|
|
564
703
|
if (cached) {
|
|
@@ -572,31 +711,44 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
572
711
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
573
712
|
const versions = this.versionIndex.get(key);
|
|
574
713
|
if (!versions) {
|
|
575
|
-
|
|
714
|
+
const rootAsAny = this.root;
|
|
715
|
+
if (rootAsAny.diskCache.has(key)) return rootAsAny.diskCache.get(key) !== null;
|
|
716
|
+
const exists = strategy.exists(key);
|
|
717
|
+
if (!exists) rootAsAny.diskCache.set(key, null);
|
|
718
|
+
return exists;
|
|
576
719
|
}
|
|
577
720
|
let targetVerObj = null;
|
|
721
|
+
let nextVerObj = null;
|
|
578
722
|
for (const v of versions) {
|
|
579
|
-
if (v.version <= snapshotVersion)
|
|
580
|
-
|
|
581
|
-
|
|
723
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
724
|
+
else {
|
|
725
|
+
nextVerObj = v;
|
|
582
726
|
break;
|
|
583
727
|
}
|
|
584
728
|
}
|
|
585
|
-
if (!targetVerObj)
|
|
729
|
+
if (!targetVerObj) {
|
|
730
|
+
if (nextVerObj) {
|
|
731
|
+
const cached = this.deletedCache.get(key);
|
|
732
|
+
if (cached) {
|
|
733
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
734
|
+
if (match) return true;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return false;
|
|
738
|
+
}
|
|
586
739
|
return targetVerObj.exists;
|
|
587
740
|
}
|
|
588
741
|
_diskDelete(key, snapshotVersion) {
|
|
589
742
|
const strategy = this.strategy;
|
|
590
743
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
591
|
-
|
|
592
|
-
|
|
744
|
+
const rootAsAny = this.root;
|
|
745
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
746
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
593
747
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
594
|
-
this.deletedCache.get(key).push({
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
});
|
|
748
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
749
|
+
strategy.delete(key);
|
|
750
|
+
rootAsAny.diskCache.delete(key);
|
|
598
751
|
}
|
|
599
|
-
strategy.delete(key);
|
|
600
752
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
601
753
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
602
754
|
}
|
|
@@ -909,7 +1061,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
909
1061
|
createNested() {
|
|
910
1062
|
if (this.committed) throw new Error("Transaction already committed");
|
|
911
1063
|
const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
|
|
912
|
-
const child = new _AsyncMVCCTransaction(void 0, this, childVersion);
|
|
1064
|
+
const child = new _AsyncMVCCTransaction(void 0, void 0, this, childVersion);
|
|
913
1065
|
this.root.activeTransactions.add(child);
|
|
914
1066
|
return child;
|
|
915
1067
|
}
|
|
@@ -917,31 +1069,72 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
917
1069
|
if (this.committed) throw new Error("Transaction already committed");
|
|
918
1070
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
919
1071
|
if (this.deleteBuffer.has(key)) return null;
|
|
920
|
-
|
|
1072
|
+
if (this.parent) {
|
|
1073
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
1074
|
+
}
|
|
1075
|
+
return await this._diskRead(key, this.snapshotVersion);
|
|
921
1076
|
}
|
|
922
1077
|
async exists(key) {
|
|
923
1078
|
if (this.committed) throw new Error("Transaction already committed");
|
|
924
1079
|
if (this.deleteBuffer.has(key)) return false;
|
|
925
1080
|
if (this.writeBuffer.has(key)) return true;
|
|
926
|
-
|
|
1081
|
+
if (this.parent) {
|
|
1082
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
1083
|
+
}
|
|
1084
|
+
return await this._diskExists(key, this.snapshotVersion);
|
|
1085
|
+
}
|
|
1086
|
+
async _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
1087
|
+
if (this.writeBuffer.has(key)) {
|
|
1088
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
1089
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
1090
|
+
return true;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (this.deleteBuffer.has(key)) {
|
|
1094
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
1095
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
1096
|
+
return false;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
const history = this.bufferHistory.get(key);
|
|
1100
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
1101
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
1102
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
1103
|
+
return history[i].exists;
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
if (this.parent) {
|
|
1108
|
+
return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
1109
|
+
} else {
|
|
1110
|
+
return await this._diskExists(key, snapshotVersion);
|
|
1111
|
+
}
|
|
927
1112
|
}
|
|
928
1113
|
async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
929
1114
|
if (this.writeBuffer.has(key)) {
|
|
930
1115
|
const keyModVersion = this.keyVersions.get(key);
|
|
931
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
1116
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
932
1117
|
return this.writeBuffer.get(key);
|
|
933
1118
|
}
|
|
934
1119
|
}
|
|
935
1120
|
if (this.deleteBuffer.has(key)) {
|
|
936
1121
|
const keyModVersion = this.keyVersions.get(key);
|
|
937
|
-
if (snapshotLocalVersion === void 0 || keyModVersion
|
|
1122
|
+
if (snapshotLocalVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
938
1123
|
return null;
|
|
939
1124
|
}
|
|
940
1125
|
}
|
|
1126
|
+
const history = this.bufferHistory.get(key);
|
|
1127
|
+
if (history && snapshotLocalVersion !== void 0) {
|
|
1128
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
1129
|
+
if (history[i].version <= snapshotLocalVersion) {
|
|
1130
|
+
return history[i].exists ? history[i].value : null;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
941
1134
|
if (this.parent) {
|
|
942
1135
|
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
943
1136
|
} else {
|
|
944
|
-
return this._diskRead(key, snapshotVersion);
|
|
1137
|
+
return await this._diskRead(key, snapshotVersion);
|
|
945
1138
|
}
|
|
946
1139
|
}
|
|
947
1140
|
async commit(label) {
|
|
@@ -1002,6 +1195,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1002
1195
|
this.deletedValues.clear();
|
|
1003
1196
|
this.originallyExisted.clear();
|
|
1004
1197
|
this.keyVersions.clear();
|
|
1198
|
+
this.bufferHistory.clear();
|
|
1005
1199
|
this.localVersion = 0;
|
|
1006
1200
|
this.snapshotVersion = this.version;
|
|
1007
1201
|
}
|
|
@@ -1043,33 +1237,23 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1043
1237
|
};
|
|
1044
1238
|
}
|
|
1045
1239
|
}
|
|
1046
|
-
const
|
|
1047
|
-
for (const key of child.writeBuffer
|
|
1048
|
-
|
|
1049
|
-
this.
|
|
1050
|
-
this.
|
|
1051
|
-
if (child.createdKeys.has(key)) {
|
|
1052
|
-
this.createdKeys.add(key);
|
|
1053
|
-
}
|
|
1240
|
+
const mergeVersion = ++this.localVersion;
|
|
1241
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1242
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1243
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1244
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1054
1245
|
}
|
|
1055
1246
|
for (const key of child.deleteBuffer) {
|
|
1056
|
-
this.deleteBuffer.add(key);
|
|
1057
|
-
this.writeBuffer.delete(key);
|
|
1058
|
-
this.createdKeys.delete(key);
|
|
1059
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
1060
1247
|
const deletedValue = child.deletedValues.get(key);
|
|
1061
|
-
if (deletedValue !== void 0)
|
|
1062
|
-
|
|
1063
|
-
}
|
|
1064
|
-
if (child.originallyExisted.has(key)) {
|
|
1248
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1249
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1065
1250
|
this.originallyExisted.add(key);
|
|
1066
1251
|
}
|
|
1252
|
+
this._bufferDelete(key, mergeVersion);
|
|
1067
1253
|
}
|
|
1068
|
-
this.localVersion = newLocalVersion;
|
|
1069
1254
|
this.root.activeTransactions.delete(child);
|
|
1070
1255
|
return null;
|
|
1071
1256
|
} else {
|
|
1072
|
-
const newVersion = this.version + 1;
|
|
1073
1257
|
if (child !== this) {
|
|
1074
1258
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
1075
1259
|
for (const key of modifiedKeys) {
|
|
@@ -1087,51 +1271,58 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1087
1271
|
};
|
|
1088
1272
|
}
|
|
1089
1273
|
}
|
|
1274
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
1275
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1276
|
+
return {
|
|
1277
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
1278
|
+
conflict: {
|
|
1279
|
+
key,
|
|
1280
|
+
parent: await this.read(key),
|
|
1281
|
+
child: await child.read(key)
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1090
1285
|
}
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
for (const key of child.deleteBuffer) {
|
|
1100
|
-
this.deleteBuffer.add(key);
|
|
1101
|
-
this.writeBuffer.delete(key);
|
|
1102
|
-
this.createdKeys.delete(key);
|
|
1103
|
-
const deletedValue = child.deletedValues.get(key);
|
|
1104
|
-
if (deletedValue !== void 0) {
|
|
1105
|
-
this.deletedValues.set(key, deletedValue);
|
|
1286
|
+
const mergeVersion = ++this.localVersion;
|
|
1287
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1288
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1289
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1290
|
+
this.originallyExisted.add(key);
|
|
1291
|
+
}
|
|
1292
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1293
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1106
1294
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1295
|
+
for (const key of child.deleteBuffer) {
|
|
1296
|
+
const deletedValue = child.deletedValues.get(key);
|
|
1297
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1298
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1299
|
+
this.originallyExisted.add(key);
|
|
1300
|
+
}
|
|
1301
|
+
this._bufferDelete(key, mergeVersion);
|
|
1109
1302
|
}
|
|
1303
|
+
this.root.activeTransactions.delete(child);
|
|
1304
|
+
} else {
|
|
1305
|
+
const newVersion = this.version + 1;
|
|
1306
|
+
for (const [key, value] of this.writeBuffer) await this._diskWrite(key, value, newVersion);
|
|
1307
|
+
for (const key of this.deleteBuffer) await this._diskDelete(key, newVersion);
|
|
1308
|
+
this.version = newVersion;
|
|
1309
|
+
this._cleanupDeletedCache();
|
|
1110
1310
|
}
|
|
1111
|
-
for (const [key, value] of child.writeBuffer) {
|
|
1112
|
-
await this._diskWrite(key, value, newVersion);
|
|
1113
|
-
}
|
|
1114
|
-
for (const key of child.deleteBuffer) {
|
|
1115
|
-
await this._diskDelete(key, newVersion);
|
|
1116
|
-
}
|
|
1117
|
-
this.version = newVersion;
|
|
1118
|
-
this.root.activeTransactions.delete(child);
|
|
1119
|
-
this._cleanupDeletedCache();
|
|
1120
1311
|
return null;
|
|
1121
1312
|
}
|
|
1122
1313
|
});
|
|
1123
1314
|
}
|
|
1124
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
1125
1315
|
async _diskWrite(key, value, version) {
|
|
1126
1316
|
const strategy = this.strategy;
|
|
1127
1317
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1128
|
-
|
|
1129
|
-
|
|
1318
|
+
const rootAsAny = this.root;
|
|
1319
|
+
if (await this._diskExists(key, version)) {
|
|
1320
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1130
1321
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1131
|
-
this.deletedCache.get(key).push({
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1322
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
1323
|
+
rootAsAny.diskCache.set(key, value);
|
|
1324
|
+
} else {
|
|
1325
|
+
rootAsAny.diskCache.set(key, value);
|
|
1135
1326
|
}
|
|
1136
1327
|
await strategy.write(key, value);
|
|
1137
1328
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -1142,14 +1333,19 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1142
1333
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1143
1334
|
const versions = this.versionIndex.get(key);
|
|
1144
1335
|
if (!versions) {
|
|
1145
|
-
|
|
1336
|
+
const rootAsAny = this.root;
|
|
1337
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
1338
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1339
|
+
rootAsAny.diskCache.set(key, val);
|
|
1340
|
+
return val;
|
|
1341
|
+
}
|
|
1342
|
+
return null;
|
|
1146
1343
|
}
|
|
1147
1344
|
let targetVerObj = null;
|
|
1148
1345
|
let nextVerObj = null;
|
|
1149
1346
|
for (const v of versions) {
|
|
1150
|
-
if (v.version <= snapshotVersion)
|
|
1151
|
-
|
|
1152
|
-
} else {
|
|
1347
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
1348
|
+
else {
|
|
1153
1349
|
nextVerObj = v;
|
|
1154
1350
|
break;
|
|
1155
1351
|
}
|
|
@@ -1166,7 +1362,14 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1166
1362
|
}
|
|
1167
1363
|
if (!targetVerObj.exists) return null;
|
|
1168
1364
|
if (!nextVerObj) {
|
|
1169
|
-
return
|
|
1365
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
1366
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
1367
|
+
const rootAsAny = this.root;
|
|
1368
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1369
|
+
rootAsAny.diskCache.set(key, val);
|
|
1370
|
+
return val;
|
|
1371
|
+
}
|
|
1372
|
+
return null;
|
|
1170
1373
|
}
|
|
1171
1374
|
const cached = this.deletedCache.get(key);
|
|
1172
1375
|
if (cached) {
|
|
@@ -1180,393 +1383,89 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1180
1383
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1181
1384
|
const versions = this.versionIndex.get(key);
|
|
1182
1385
|
if (!versions) {
|
|
1183
|
-
|
|
1386
|
+
const rootAsAny = this.root;
|
|
1387
|
+
if (rootAsAny.diskCache.has(key)) return rootAsAny.diskCache.get(key) !== null;
|
|
1388
|
+
const exists = await strategy.exists(key);
|
|
1389
|
+
if (!exists) rootAsAny.diskCache.set(key, null);
|
|
1390
|
+
return exists;
|
|
1184
1391
|
}
|
|
1185
1392
|
let targetVerObj = null;
|
|
1393
|
+
let nextVerObj = null;
|
|
1186
1394
|
for (const v of versions) {
|
|
1187
|
-
if (v.version <= snapshotVersion)
|
|
1188
|
-
|
|
1189
|
-
|
|
1395
|
+
if (v.version <= snapshotVersion) targetVerObj = v;
|
|
1396
|
+
else {
|
|
1397
|
+
nextVerObj = v;
|
|
1190
1398
|
break;
|
|
1191
1399
|
}
|
|
1192
1400
|
}
|
|
1193
|
-
if (!targetVerObj)
|
|
1401
|
+
if (!targetVerObj) {
|
|
1402
|
+
if (nextVerObj) {
|
|
1403
|
+
const cached = this.deletedCache.get(key);
|
|
1404
|
+
if (cached) {
|
|
1405
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
1406
|
+
if (match) return true;
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
return false;
|
|
1410
|
+
}
|
|
1194
1411
|
return targetVerObj.exists;
|
|
1195
1412
|
}
|
|
1196
1413
|
async _diskDelete(key, snapshotVersion) {
|
|
1197
1414
|
const strategy = this.strategy;
|
|
1198
1415
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1199
|
-
|
|
1200
|
-
|
|
1416
|
+
const rootAsAny = this.root;
|
|
1417
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
1418
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1201
1419
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1202
|
-
this.deletedCache.get(key).push({
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
});
|
|
1420
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
1421
|
+
await strategy.delete(key);
|
|
1422
|
+
rootAsAny.diskCache.delete(key);
|
|
1206
1423
|
}
|
|
1207
|
-
await strategy.delete(key);
|
|
1208
1424
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
1209
1425
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
1210
1426
|
}
|
|
1211
1427
|
};
|
|
1212
1428
|
|
|
1213
|
-
//
|
|
1214
|
-
var
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
this.
|
|
1233
|
-
this.
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
if (this.head) this.head.prev = node;
|
|
1254
|
-
this.head = node;
|
|
1255
|
-
if (!this.tail) this.tail = node;
|
|
1256
|
-
}
|
|
1257
|
-
/**
|
|
1258
|
-
* Stores or updates a value by key.
|
|
1259
|
-
* If the capacity is exceeded, the least recently used item (tail) is removed.
|
|
1260
|
-
* @param key The key to store.
|
|
1261
|
-
* @param value The value to store.
|
|
1262
|
-
*/
|
|
1263
|
-
set(key, value) {
|
|
1264
|
-
const existing = this.map.get(key);
|
|
1265
|
-
if (existing) {
|
|
1266
|
-
existing.value = value;
|
|
1267
|
-
this.promote(existing);
|
|
1268
|
-
return;
|
|
1269
|
-
}
|
|
1270
|
-
const newNode = { key, value, prev: null, next: null };
|
|
1271
|
-
this.map.set(key, newNode);
|
|
1272
|
-
this.prepend(newNode);
|
|
1273
|
-
if (this.map.size > this.capacity && this.tail) {
|
|
1274
|
-
this.map.delete(this.tail.key);
|
|
1275
|
-
this.extract(this.tail);
|
|
1276
|
-
}
|
|
1277
|
-
}
|
|
1278
|
-
/**
|
|
1279
|
-
* Retrieves a value by key.
|
|
1280
|
-
* Accessing the item moves it to the "most recently used" position.
|
|
1281
|
-
* @param key The key to look for.
|
|
1282
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
1283
|
-
*/
|
|
1284
|
-
get(key) {
|
|
1285
|
-
const node = this.map.get(key);
|
|
1286
|
-
if (!node) return void 0;
|
|
1287
|
-
this.promote(node);
|
|
1288
|
-
return node.value;
|
|
1289
|
-
}
|
|
1290
|
-
/**
|
|
1291
|
-
* Checks if a key exists in the cache without changing its access order.
|
|
1292
|
-
* @param key The key to check.
|
|
1293
|
-
* @returns True if the key exists, false otherwise.
|
|
1294
|
-
*/
|
|
1295
|
-
has(key) {
|
|
1296
|
-
return this.map.has(key);
|
|
1297
|
-
}
|
|
1298
|
-
/**
|
|
1299
|
-
* Removes a key and its associated value from the cache.
|
|
1300
|
-
* @param key The key to remove.
|
|
1301
|
-
* @returns True if the key was found and removed, false otherwise.
|
|
1302
|
-
*/
|
|
1303
|
-
delete(key) {
|
|
1304
|
-
const node = this.map.get(key);
|
|
1305
|
-
if (!node) return false;
|
|
1306
|
-
this.extract(node);
|
|
1307
|
-
this.map.delete(key);
|
|
1308
|
-
return true;
|
|
1309
|
-
}
|
|
1310
|
-
/**
|
|
1311
|
-
* Returns an iterator of keys in the order of most recently used to least recently used.
|
|
1312
|
-
* @returns An iterable iterator of keys.
|
|
1313
|
-
*/
|
|
1314
|
-
*keys() {
|
|
1315
|
-
let current = this.head;
|
|
1316
|
-
while (current) {
|
|
1317
|
-
yield current.key;
|
|
1318
|
-
current = current.next;
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
/**
|
|
1322
|
-
* Returns the current number of items in the cache.
|
|
1323
|
-
*/
|
|
1324
|
-
get size() {
|
|
1325
|
-
return this.map.size;
|
|
1326
|
-
}
|
|
1327
|
-
/**
|
|
1328
|
-
* Clears all items from the cache.
|
|
1329
|
-
*/
|
|
1330
|
-
clear() {
|
|
1331
|
-
this.map.clear();
|
|
1332
|
-
this.head = null;
|
|
1333
|
-
this.tail = null;
|
|
1334
|
-
}
|
|
1335
|
-
};
|
|
1336
|
-
var CacheEntanglement = class {
|
|
1337
|
-
creation;
|
|
1338
|
-
beforeUpdateHook;
|
|
1339
|
-
capacity;
|
|
1340
|
-
dependencies;
|
|
1341
|
-
caches;
|
|
1342
|
-
parameters;
|
|
1343
|
-
assignments;
|
|
1344
|
-
dependencyProperties;
|
|
1345
|
-
updateRequirements;
|
|
1346
|
-
constructor(creation, option) {
|
|
1347
|
-
option = option ?? {};
|
|
1348
|
-
const {
|
|
1349
|
-
dependencies,
|
|
1350
|
-
capacity,
|
|
1351
|
-
beforeUpdateHook
|
|
1352
|
-
} = option;
|
|
1353
|
-
this.creation = creation;
|
|
1354
|
-
this.beforeUpdateHook = beforeUpdateHook ?? (() => {
|
|
1355
|
-
});
|
|
1356
|
-
this.capacity = capacity ?? 100;
|
|
1357
|
-
this.assignments = [];
|
|
1358
|
-
this.caches = new LRUMap(this.capacity);
|
|
1359
|
-
this.parameters = /* @__PURE__ */ new Map();
|
|
1360
|
-
this.dependencies = dependencies ?? {};
|
|
1361
|
-
this.dependencyProperties = Object.keys(this.dependencies);
|
|
1362
|
-
this.updateRequirements = /* @__PURE__ */ new Set();
|
|
1363
|
-
for (const name in this.dependencies) {
|
|
1364
|
-
const dependency = this.dependencies[name];
|
|
1365
|
-
if (!dependency.assignments.includes(this)) {
|
|
1366
|
-
dependency.assignments.push(this);
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
}
|
|
1370
|
-
bubbleUpdateSignal(key) {
|
|
1371
|
-
this.updateRequirements.add(key);
|
|
1372
|
-
for (let i = 0, len = this.assignments.length; i < len; i++) {
|
|
1373
|
-
const t = this.assignments[i];
|
|
1374
|
-
const instance = t;
|
|
1375
|
-
for (const cacheKey of instance.caches.keys()) {
|
|
1376
|
-
if (cacheKey === key || cacheKey.startsWith(`${key}/`)) {
|
|
1377
|
-
instance.bubbleUpdateSignal(cacheKey);
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
}
|
|
1382
|
-
dependencyKey(key) {
|
|
1383
|
-
const i = key.lastIndexOf("/");
|
|
1384
|
-
if (i === -1) {
|
|
1385
|
-
return key;
|
|
1386
|
-
}
|
|
1387
|
-
return key.substring(0, i);
|
|
1388
|
-
}
|
|
1389
|
-
/**
|
|
1390
|
-
* Returns all keys stored in the instance.
|
|
1391
|
-
*/
|
|
1392
|
-
keys() {
|
|
1393
|
-
return this.parameters.keys();
|
|
1394
|
-
}
|
|
1395
|
-
/**
|
|
1396
|
-
* Deletes all cache values stored in the instance.
|
|
1397
|
-
*/
|
|
1398
|
-
clear() {
|
|
1399
|
-
for (const key of this.keys()) {
|
|
1400
|
-
this.delete(key);
|
|
1401
|
-
}
|
|
1402
|
-
}
|
|
1403
|
-
/**
|
|
1404
|
-
* Checks if there is a cache value stored in the key within the instance.
|
|
1405
|
-
* @param key The key to search.
|
|
1406
|
-
*/
|
|
1407
|
-
exists(key) {
|
|
1408
|
-
return this.parameters.has(key);
|
|
1409
|
-
}
|
|
1410
|
-
/**
|
|
1411
|
-
* Checks if there is a cache value stored in the key within the instance.
|
|
1412
|
-
* This method is an alias for `exists`.
|
|
1413
|
-
* @param key The key to search.
|
|
1414
|
-
*/
|
|
1415
|
-
has(key) {
|
|
1416
|
-
return this.exists(key);
|
|
1417
|
-
}
|
|
1418
|
-
/**
|
|
1419
|
-
* Deletes the cache value stored in the key within the instance.
|
|
1420
|
-
* @param key The key to delete.
|
|
1421
|
-
*/
|
|
1422
|
-
delete(key) {
|
|
1423
|
-
this.caches.delete(key);
|
|
1424
|
-
this.parameters.delete(key);
|
|
1425
|
-
this.updateRequirements.delete(key);
|
|
1426
|
-
for (let i = 0, len = this.assignments.length; i < len; i++) {
|
|
1427
|
-
const t = this.assignments[i];
|
|
1428
|
-
const instance = t;
|
|
1429
|
-
for (const cacheKey of instance.keys()) {
|
|
1430
|
-
if (cacheKey === key || cacheKey.startsWith(`${key}/`)) {
|
|
1431
|
-
instance.delete(cacheKey);
|
|
1432
|
-
}
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
};
|
|
1437
|
-
var CacheData = class _CacheData {
|
|
1438
|
-
static StructuredClone = globalThis.structuredClone.bind(globalThis);
|
|
1439
|
-
_value;
|
|
1440
|
-
constructor(value) {
|
|
1441
|
-
this._value = value;
|
|
1442
|
-
}
|
|
1443
|
-
/**
|
|
1444
|
-
* This is cached data.
|
|
1445
|
-
* It was generated at the time of caching, so there is a risk of modification if it's an object due to shallow copying.
|
|
1446
|
-
* 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.
|
|
1447
|
-
*/
|
|
1448
|
-
get raw() {
|
|
1449
|
-
return this._value;
|
|
1450
|
-
}
|
|
1451
|
-
/**
|
|
1452
|
-
* The method returns a copied value of the cached data.
|
|
1453
|
-
* You can pass a function as a parameter to copy the value. This parameter function should return the copied value.
|
|
1454
|
-
*
|
|
1455
|
-
* If no parameter is passed, it defaults to using `structuredClone` function to copy the value.
|
|
1456
|
-
* If you prefer shallow copying instead of deep copying,
|
|
1457
|
-
* you can use the default options `array-shallow-copy`, `object-shallow-copy` and `deep-copy`,
|
|
1458
|
-
* which are replaced with functions to shallow copy arrays and objects, respectively. This is a syntactic sugar.
|
|
1459
|
-
* @param strategy The function that returns the copied value.
|
|
1460
|
-
* If you want to perform a shallow copy, simply pass the strings `array-shallow-copy` or `object-shallow-copy` for easy use.
|
|
1461
|
-
* The `array-shallow-copy` strategy performs a shallow copy of an array.
|
|
1462
|
-
* The `object-shallow-copy` strategy performs a shallow copy of an object.
|
|
1463
|
-
* The `deep-copy` strategy performs a deep copy of the value using `structuredClone`.
|
|
1464
|
-
* The default is `deep-copy`.
|
|
1465
|
-
*/
|
|
1466
|
-
clone(strategy = "deep-copy") {
|
|
1467
|
-
if (strategy && typeof strategy !== "string") {
|
|
1468
|
-
return strategy(this.raw);
|
|
1469
|
-
}
|
|
1470
|
-
switch (strategy) {
|
|
1471
|
-
case "array-shallow-copy":
|
|
1472
|
-
return [].concat(this.raw);
|
|
1473
|
-
case "object-shallow-copy":
|
|
1474
|
-
return Object.assign({}, this.raw);
|
|
1475
|
-
case "deep-copy":
|
|
1476
|
-
default:
|
|
1477
|
-
return _CacheData.StructuredClone(this.raw);
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
};
|
|
1481
|
-
var CacheEntanglementSync = class extends CacheEntanglement {
|
|
1482
|
-
constructor(creation, option) {
|
|
1483
|
-
super(creation, option);
|
|
1484
|
-
}
|
|
1485
|
-
recache(key) {
|
|
1486
|
-
if (!this.parameters.has(key)) {
|
|
1487
|
-
return;
|
|
1488
|
-
}
|
|
1489
|
-
if (!this.caches.has(key) || this.updateRequirements.has(key)) {
|
|
1490
|
-
this.resolve(key, ...this.parameters.get(key));
|
|
1491
|
-
}
|
|
1492
|
-
return this.caches.get(key);
|
|
1493
|
-
}
|
|
1494
|
-
resolve(key, ...parameter) {
|
|
1495
|
-
const resolved = {};
|
|
1496
|
-
const dependencyKey = this.dependencyKey(key);
|
|
1497
|
-
this.beforeUpdateHook(key, dependencyKey, ...parameter);
|
|
1498
|
-
for (let i = 0, len = this.dependencyProperties.length; i < len; i++) {
|
|
1499
|
-
const name = this.dependencyProperties[i];
|
|
1500
|
-
const dependency = this.dependencies[name];
|
|
1501
|
-
if (!dependency.exists(key) && !dependency.exists(dependencyKey)) {
|
|
1502
|
-
throw new Error(`The key '${key}' or '${dependencyKey}' has not been assigned yet in dependency '${name.toString()}'.`, {
|
|
1503
|
-
cause: {
|
|
1504
|
-
from: this
|
|
1505
|
-
}
|
|
1506
|
-
});
|
|
1507
|
-
}
|
|
1508
|
-
const dependencyValue = dependency.recache(key) ?? dependency.recache(dependencyKey);
|
|
1509
|
-
resolved[name] = dependencyValue;
|
|
1510
|
-
}
|
|
1511
|
-
const value = new CacheData(this.creation(key, resolved, ...parameter));
|
|
1512
|
-
this.updateRequirements.delete(key);
|
|
1513
|
-
this.parameters.set(key, parameter);
|
|
1514
|
-
this.caches.set(key, value);
|
|
1515
|
-
return value;
|
|
1516
|
-
}
|
|
1517
|
-
get(key) {
|
|
1518
|
-
if (!this.parameters.has(key)) {
|
|
1519
|
-
throw new Error(`Cache value not found: ${key}`);
|
|
1520
|
-
}
|
|
1521
|
-
return this.cache(key, ...this.parameters.get(key));
|
|
1522
|
-
}
|
|
1523
|
-
cache(key, ...parameter) {
|
|
1524
|
-
if (!this.caches.has(key) || this.updateRequirements.has(key)) {
|
|
1525
|
-
this.resolve(key, ...parameter);
|
|
1526
|
-
}
|
|
1527
|
-
return this.caches.get(key);
|
|
1528
|
-
}
|
|
1529
|
-
update(key, ...parameter) {
|
|
1530
|
-
this.bubbleUpdateSignal(key);
|
|
1531
|
-
this.resolve(key, ...parameter);
|
|
1532
|
-
return this.caches.get(key);
|
|
1533
|
-
}
|
|
1534
|
-
};
|
|
1535
|
-
|
|
1536
|
-
// src/base/BPTreeTransaction.ts
|
|
1537
|
-
var BPTreeTransaction = class _BPTreeTransaction {
|
|
1538
|
-
_cachedRegexp;
|
|
1539
|
-
nodes;
|
|
1540
|
-
deletedNodeBuffer = /* @__PURE__ */ new Map();
|
|
1541
|
-
rootTx;
|
|
1542
|
-
mvccRoot;
|
|
1543
|
-
mvcc;
|
|
1544
|
-
strategy;
|
|
1545
|
-
comparator;
|
|
1546
|
-
option;
|
|
1547
|
-
order;
|
|
1548
|
-
rootId;
|
|
1549
|
-
verifierMap = {
|
|
1550
|
-
gt: (nv, v) => this.comparator.isHigher(nv, v),
|
|
1551
|
-
gte: (nv, v) => this.comparator.isHigher(nv, v) || this.comparator.isSame(nv, v),
|
|
1552
|
-
lt: (nv, v) => this.comparator.isLower(nv, v),
|
|
1553
|
-
lte: (nv, v) => this.comparator.isLower(nv, v) || this.comparator.isSame(nv, v),
|
|
1554
|
-
equal: (nv, v) => this.comparator.isSame(nv, v),
|
|
1555
|
-
notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
|
|
1556
|
-
or: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isSame(nv, v2)),
|
|
1557
|
-
primaryGt: (nv, v) => this.comparator.isPrimaryHigher(nv, v),
|
|
1558
|
-
primaryGte: (nv, v) => this.comparator.isPrimaryHigher(nv, v) || this.comparator.isPrimarySame(nv, v),
|
|
1559
|
-
primaryLt: (nv, v) => this.comparator.isPrimaryLower(nv, v),
|
|
1560
|
-
primaryLte: (nv, v) => this.comparator.isPrimaryLower(nv, v) || this.comparator.isPrimarySame(nv, v),
|
|
1561
|
-
primaryEqual: (nv, v) => this.comparator.isPrimarySame(nv, v),
|
|
1562
|
-
primaryNotEqual: (nv, v) => this.comparator.isPrimarySame(nv, v) === false,
|
|
1563
|
-
primaryOr: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isPrimarySame(nv, v2)),
|
|
1564
|
-
like: (nv, v) => {
|
|
1565
|
-
const nodeValue = this.comparator.match(nv);
|
|
1566
|
-
const value = v;
|
|
1567
|
-
const cache = this._cachedRegexp.cache(value);
|
|
1568
|
-
const regexp = cache.raw;
|
|
1569
|
-
return regexp.test(nodeValue);
|
|
1429
|
+
// src/base/BPTreeTransaction.ts
|
|
1430
|
+
var BPTreeTransaction = class _BPTreeTransaction {
|
|
1431
|
+
_cachedRegexp = /* @__PURE__ */ new Map();
|
|
1432
|
+
nodes = /* @__PURE__ */ new Map();
|
|
1433
|
+
deletedNodeBuffer = /* @__PURE__ */ new Map();
|
|
1434
|
+
rootTx;
|
|
1435
|
+
mvccRoot;
|
|
1436
|
+
mvcc;
|
|
1437
|
+
strategy;
|
|
1438
|
+
comparator;
|
|
1439
|
+
option;
|
|
1440
|
+
order;
|
|
1441
|
+
rootId;
|
|
1442
|
+
isInitialized = false;
|
|
1443
|
+
isDestroyed = false;
|
|
1444
|
+
verifierMap = {
|
|
1445
|
+
gt: (nv, v) => this.comparator.isHigher(nv, v),
|
|
1446
|
+
gte: (nv, v) => this.comparator.isHigher(nv, v) || this.comparator.isSame(nv, v),
|
|
1447
|
+
lt: (nv, v) => this.comparator.isLower(nv, v),
|
|
1448
|
+
lte: (nv, v) => this.comparator.isLower(nv, v) || this.comparator.isSame(nv, v),
|
|
1449
|
+
equal: (nv, v) => this.comparator.isSame(nv, v),
|
|
1450
|
+
notEqual: (nv, v) => this.comparator.isSame(nv, v) === false,
|
|
1451
|
+
or: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isSame(nv, v2)),
|
|
1452
|
+
primaryGt: (nv, v) => this.comparator.isPrimaryHigher(nv, v),
|
|
1453
|
+
primaryGte: (nv, v) => this.comparator.isPrimaryHigher(nv, v) || this.comparator.isPrimarySame(nv, v),
|
|
1454
|
+
primaryLt: (nv, v) => this.comparator.isPrimaryLower(nv, v),
|
|
1455
|
+
primaryLte: (nv, v) => this.comparator.isPrimaryLower(nv, v) || this.comparator.isPrimarySame(nv, v),
|
|
1456
|
+
primaryEqual: (nv, v) => this.comparator.isPrimarySame(nv, v),
|
|
1457
|
+
primaryNotEqual: (nv, v) => this.comparator.isPrimarySame(nv, v) === false,
|
|
1458
|
+
primaryOr: (nv, v) => this.ensureValues(v).some((v2) => this.comparator.isPrimarySame(nv, v2)),
|
|
1459
|
+
like: (nv, v) => {
|
|
1460
|
+
const nodeValue = this.comparator.match(nv);
|
|
1461
|
+
const value = v;
|
|
1462
|
+
if (!this._cachedRegexp.has(value)) {
|
|
1463
|
+
const pattern = value.replace(/%/g, ".*").replace(/_/g, ".");
|
|
1464
|
+
const regexp2 = new RegExp(`^${pattern}$`, "i");
|
|
1465
|
+
this._cachedRegexp.set(value, regexp2);
|
|
1466
|
+
}
|
|
1467
|
+
const regexp = this._cachedRegexp.get(value);
|
|
1468
|
+
return regexp.test(nodeValue);
|
|
1570
1469
|
}
|
|
1571
1470
|
};
|
|
1572
1471
|
verifierStartNode = {
|
|
@@ -1737,6 +1636,9 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1737
1636
|
}
|
|
1738
1637
|
return true;
|
|
1739
1638
|
}
|
|
1639
|
+
_cloneNode(node) {
|
|
1640
|
+
return JSON.parse(JSON.stringify(node));
|
|
1641
|
+
}
|
|
1740
1642
|
/**
|
|
1741
1643
|
* Selects the best driver key from a condition object.
|
|
1742
1644
|
* The driver key determines the starting point and traversal direction for queries.
|
|
@@ -1769,17 +1671,6 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1769
1671
|
this.strategy = strategy;
|
|
1770
1672
|
this.comparator = comparator;
|
|
1771
1673
|
this.option = option ?? {};
|
|
1772
|
-
this.nodes = new LRUMap(this.option.capacity ?? 1e3);
|
|
1773
|
-
this._cachedRegexp = this._createCachedRegexp();
|
|
1774
|
-
}
|
|
1775
|
-
_createCachedRegexp() {
|
|
1776
|
-
return new CacheEntanglementSync((key) => {
|
|
1777
|
-
const pattern = key.replace(/%/g, ".*").replace(/_/g, ".");
|
|
1778
|
-
const regexp = new RegExp(`^${pattern}$`, "i");
|
|
1779
|
-
return regexp;
|
|
1780
|
-
}, {
|
|
1781
|
-
capacity: this.option.capacity ?? 1e3
|
|
1782
|
-
});
|
|
1783
1674
|
}
|
|
1784
1675
|
ensureValues(v) {
|
|
1785
1676
|
if (!Array.isArray(v)) {
|
|
@@ -1810,13 +1701,25 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1810
1701
|
getResultEntries() {
|
|
1811
1702
|
return this.mvcc.getResultEntries();
|
|
1812
1703
|
}
|
|
1704
|
+
_clearCache() {
|
|
1705
|
+
this._cachedRegexp.clear();
|
|
1706
|
+
}
|
|
1813
1707
|
/**
|
|
1814
1708
|
* Clears all cached nodes.
|
|
1815
1709
|
* This method is useful for freeing up memory when the tree is no longer needed.
|
|
1816
1710
|
*/
|
|
1817
1711
|
clear() {
|
|
1818
|
-
this.
|
|
1819
|
-
|
|
1712
|
+
if (this.rootTx !== this) {
|
|
1713
|
+
throw new Error("Cannot call clear on a nested transaction");
|
|
1714
|
+
}
|
|
1715
|
+
this._clearInternal();
|
|
1716
|
+
}
|
|
1717
|
+
_clearInternal() {
|
|
1718
|
+
if (this.isDestroyed) {
|
|
1719
|
+
throw new Error("Transaction already destroyed");
|
|
1720
|
+
}
|
|
1721
|
+
this._clearCache();
|
|
1722
|
+
this.isDestroyed = true;
|
|
1820
1723
|
}
|
|
1821
1724
|
_binarySearchValues(values, target, usePrimary = false, upperBound = false) {
|
|
1822
1725
|
let low = 0;
|
|
@@ -1852,9 +1755,6 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1852
1755
|
);
|
|
1853
1756
|
}
|
|
1854
1757
|
getNode(id) {
|
|
1855
|
-
if (this.nodes.has(id)) {
|
|
1856
|
-
return this.nodes.get(id);
|
|
1857
|
-
}
|
|
1858
1758
|
return this.mvcc.read(id);
|
|
1859
1759
|
}
|
|
1860
1760
|
/**
|
|
@@ -1872,23 +1772,22 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1872
1772
|
prev
|
|
1873
1773
|
};
|
|
1874
1774
|
this.mvcc.create(id, node);
|
|
1875
|
-
this.nodes.set(id, node);
|
|
1876
1775
|
return node;
|
|
1877
1776
|
}
|
|
1878
1777
|
_updateNode(node) {
|
|
1778
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1879
1781
|
this.mvcc.write(node.id, node);
|
|
1880
|
-
this.nodes.set(node.id, node);
|
|
1881
1782
|
}
|
|
1882
1783
|
_deleteNode(node) {
|
|
1784
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1883
1787
|
this.mvcc.delete(node.id);
|
|
1884
|
-
this.nodes.delete(node.id);
|
|
1885
1788
|
}
|
|
1886
1789
|
_readHead() {
|
|
1887
|
-
|
|
1888
|
-
return this.nodes.get("__HEAD__") ?? null;
|
|
1889
|
-
}
|
|
1890
|
-
const head = this.mvcc.read("__HEAD__");
|
|
1891
|
-
return head ?? null;
|
|
1790
|
+
return this.mvcc.read("__HEAD__");
|
|
1892
1791
|
}
|
|
1893
1792
|
_writeHead(head) {
|
|
1894
1793
|
if (!this.mvcc.exists("__HEAD__")) {
|
|
@@ -1896,41 +1795,45 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1896
1795
|
} else {
|
|
1897
1796
|
this.mvcc.write("__HEAD__", head);
|
|
1898
1797
|
}
|
|
1899
|
-
this.nodes.set("__HEAD__", head);
|
|
1900
1798
|
this.rootId = head.root;
|
|
1901
1799
|
}
|
|
1902
1800
|
_insertAtLeaf(node, key, value) {
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1801
|
+
let leaf = node;
|
|
1802
|
+
leaf = this._cloneNode(leaf);
|
|
1803
|
+
if (leaf.values.length) {
|
|
1804
|
+
for (let i = 0, len = leaf.values.length; i < len; i++) {
|
|
1805
|
+
const nValue = leaf.values[i];
|
|
1906
1806
|
if (this.comparator.isSame(value, nValue)) {
|
|
1907
|
-
const keys =
|
|
1807
|
+
const keys = leaf.keys[i];
|
|
1908
1808
|
if (keys.includes(key)) {
|
|
1909
1809
|
break;
|
|
1910
1810
|
}
|
|
1911
1811
|
keys.push(key);
|
|
1912
|
-
this._updateNode(
|
|
1913
|
-
return;
|
|
1812
|
+
this._updateNode(leaf);
|
|
1813
|
+
return leaf;
|
|
1914
1814
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
this._updateNode(
|
|
1918
|
-
return;
|
|
1919
|
-
} else if (i + 1 ===
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
this._updateNode(
|
|
1923
|
-
return;
|
|
1815
|
+
leaf.values.splice(i, 0, value);
|
|
1816
|
+
leaf.keys.splice(i, 0, [key]);
|
|
1817
|
+
this._updateNode(leaf);
|
|
1818
|
+
return leaf;
|
|
1819
|
+
} else if (i + 1 === leaf.values.length) {
|
|
1820
|
+
leaf.values.push(value);
|
|
1821
|
+
leaf.keys.push([key]);
|
|
1822
|
+
this._updateNode(leaf);
|
|
1823
|
+
return leaf;
|
|
1924
1824
|
}
|
|
1925
1825
|
}
|
|
1926
1826
|
} else {
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
this._updateNode(
|
|
1930
|
-
return;
|
|
1827
|
+
leaf.values = [value];
|
|
1828
|
+
leaf.keys = [[key]];
|
|
1829
|
+
this._updateNode(leaf);
|
|
1830
|
+
return leaf;
|
|
1931
1831
|
}
|
|
1832
|
+
return leaf;
|
|
1932
1833
|
}
|
|
1933
1834
|
_insertInParent(node, value, pointer) {
|
|
1835
|
+
node = this._cloneNode(node);
|
|
1836
|
+
pointer = this._cloneNode(pointer);
|
|
1934
1837
|
if (this.rootId === node.id) {
|
|
1935
1838
|
const root = this._createNode(false, [node.id, pointer.id], [value]);
|
|
1936
1839
|
this.rootId = root.id;
|
|
@@ -1949,7 +1852,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1949
1852
|
this._updateNode(pointer);
|
|
1950
1853
|
return;
|
|
1951
1854
|
}
|
|
1952
|
-
const parentNode = this.getNode(node.parent);
|
|
1855
|
+
const parentNode = this._cloneNode(this.getNode(node.parent));
|
|
1953
1856
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
1954
1857
|
if (nodeIndex === -1) {
|
|
1955
1858
|
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
@@ -1965,7 +1868,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1965
1868
|
leftSibling.next = pointer.id;
|
|
1966
1869
|
this._updateNode(leftSibling);
|
|
1967
1870
|
if (oldNextId) {
|
|
1968
|
-
const oldNext = this.getNode(oldNextId);
|
|
1871
|
+
const oldNext = this._cloneNode(this.getNode(oldNextId));
|
|
1969
1872
|
oldNext.prev = pointer.id;
|
|
1970
1873
|
this._updateNode(oldNext);
|
|
1971
1874
|
}
|
|
@@ -1982,12 +1885,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1982
1885
|
parentNode.values = parentNode.values.slice(0, mid);
|
|
1983
1886
|
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
1984
1887
|
for (const k of parentNode.keys) {
|
|
1985
|
-
const n = this.getNode(k);
|
|
1888
|
+
const n = this._cloneNode(this.getNode(k));
|
|
1986
1889
|
n.parent = parentNode.id;
|
|
1987
1890
|
this._updateNode(n);
|
|
1988
1891
|
}
|
|
1989
1892
|
for (const k of parentPointer.keys) {
|
|
1990
|
-
const n = this.getNode(k);
|
|
1893
|
+
const n = this._cloneNode(this.getNode(k));
|
|
1991
1894
|
n.parent = parentPointer.id;
|
|
1992
1895
|
this._updateNode(n);
|
|
1993
1896
|
}
|
|
@@ -2122,28 +2025,46 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2122
2025
|
}
|
|
2123
2026
|
}
|
|
2124
2027
|
init() {
|
|
2125
|
-
this.
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
const { root, order } = head;
|
|
2137
|
-
this.strategy.head = head;
|
|
2138
|
-
this.order = order;
|
|
2139
|
-
this._writeHead({
|
|
2140
|
-
root,
|
|
2141
|
-
order: this.order,
|
|
2142
|
-
data: this.strategy.head.data
|
|
2143
|
-
});
|
|
2028
|
+
if (this.rootTx !== this) {
|
|
2029
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
2030
|
+
}
|
|
2031
|
+
this._initInternal();
|
|
2032
|
+
}
|
|
2033
|
+
_initInternal() {
|
|
2034
|
+
if (this.isInitialized) {
|
|
2035
|
+
throw new Error("Transaction already initialized");
|
|
2036
|
+
}
|
|
2037
|
+
if (this.isDestroyed) {
|
|
2038
|
+
throw new Error("Transaction already destroyed");
|
|
2144
2039
|
}
|
|
2145
|
-
|
|
2146
|
-
|
|
2040
|
+
this.isInitialized = true;
|
|
2041
|
+
try {
|
|
2042
|
+
this._clearCache();
|
|
2043
|
+
const head = this._readHead();
|
|
2044
|
+
if (head === null) {
|
|
2045
|
+
this.order = this.strategy.order;
|
|
2046
|
+
const root = this._createNode(true, [], []);
|
|
2047
|
+
this._writeHead({
|
|
2048
|
+
root: root.id,
|
|
2049
|
+
order: this.order,
|
|
2050
|
+
data: this.strategy.head.data
|
|
2051
|
+
});
|
|
2052
|
+
} else {
|
|
2053
|
+
const { root, order } = head;
|
|
2054
|
+
this.strategy.head = head;
|
|
2055
|
+
this.order = order;
|
|
2056
|
+
this._writeHead({
|
|
2057
|
+
root,
|
|
2058
|
+
order: this.order,
|
|
2059
|
+
data: this.strategy.head.data
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
if (this.order < 3) {
|
|
2063
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
2064
|
+
}
|
|
2065
|
+
} catch (e) {
|
|
2066
|
+
this.isInitialized = false;
|
|
2067
|
+
throw e;
|
|
2147
2068
|
}
|
|
2148
2069
|
}
|
|
2149
2070
|
exists(key, value) {
|
|
@@ -2157,21 +2078,6 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2157
2078
|
}
|
|
2158
2079
|
return false;
|
|
2159
2080
|
}
|
|
2160
|
-
forceUpdate(id) {
|
|
2161
|
-
if (id) {
|
|
2162
|
-
this.nodes.delete(id);
|
|
2163
|
-
this.getNode(id);
|
|
2164
|
-
return 1;
|
|
2165
|
-
}
|
|
2166
|
-
const keys = Array.from(this.nodes.keys());
|
|
2167
|
-
for (const key of keys) {
|
|
2168
|
-
this.nodes.delete(key);
|
|
2169
|
-
}
|
|
2170
|
-
for (const key of keys) {
|
|
2171
|
-
this.getNode(key);
|
|
2172
|
-
}
|
|
2173
|
-
return keys.length;
|
|
2174
|
-
}
|
|
2175
2081
|
get(key) {
|
|
2176
2082
|
let node = this.leftestNode();
|
|
2177
2083
|
while (true) {
|
|
@@ -2257,10 +2163,10 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2257
2163
|
return map;
|
|
2258
2164
|
}
|
|
2259
2165
|
insert(key, value) {
|
|
2260
|
-
|
|
2261
|
-
this._insertAtLeaf(before, key, value);
|
|
2166
|
+
let before = this.insertableNode(value);
|
|
2167
|
+
before = this._insertAtLeaf(before, key, value);
|
|
2262
2168
|
if (before.values.length === this.order) {
|
|
2263
|
-
|
|
2169
|
+
let after = this._createNode(
|
|
2264
2170
|
true,
|
|
2265
2171
|
[],
|
|
2266
2172
|
[],
|
|
@@ -2269,14 +2175,18 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2269
2175
|
null
|
|
2270
2176
|
);
|
|
2271
2177
|
const mid = Math.ceil(this.order / 2) - 1;
|
|
2178
|
+
after = this._cloneNode(after);
|
|
2272
2179
|
after.values = before.values.slice(mid + 1);
|
|
2273
2180
|
after.keys = before.keys.slice(mid + 1);
|
|
2274
2181
|
before.values = before.values.slice(0, mid + 1);
|
|
2275
2182
|
before.keys = before.keys.slice(0, mid + 1);
|
|
2183
|
+
this._updateNode(before);
|
|
2184
|
+
this._updateNode(after);
|
|
2276
2185
|
this._insertInParent(before, after.values[0], after);
|
|
2277
2186
|
}
|
|
2278
2187
|
}
|
|
2279
2188
|
_deleteEntry(node, key) {
|
|
2189
|
+
node = this._cloneNode(node);
|
|
2280
2190
|
if (!node.leaf) {
|
|
2281
2191
|
let keyIndex = -1;
|
|
2282
2192
|
for (let i = 0, len = node.keys.length; i < len; i++) {
|
|
@@ -2295,7 +2205,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2295
2205
|
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
2296
2206
|
const keys = node.keys;
|
|
2297
2207
|
this._deleteNode(node);
|
|
2298
|
-
const newRoot = this.getNode(keys[0]);
|
|
2208
|
+
const newRoot = this._cloneNode(this.getNode(keys[0]));
|
|
2299
2209
|
newRoot.parent = null;
|
|
2300
2210
|
this._updateNode(newRoot);
|
|
2301
2211
|
this._writeHead({
|
|
@@ -2303,17 +2213,17 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2303
2213
|
order: this.order,
|
|
2304
2214
|
data: this.strategy.head.data
|
|
2305
2215
|
});
|
|
2306
|
-
return;
|
|
2216
|
+
return node;
|
|
2307
2217
|
} else if (this.rootId === node.id) {
|
|
2308
2218
|
this._writeHead({
|
|
2309
2219
|
root: node.id,
|
|
2310
2220
|
order: this.order,
|
|
2311
2221
|
data: this.strategy.head.data
|
|
2312
2222
|
});
|
|
2313
|
-
return;
|
|
2223
|
+
return node;
|
|
2314
2224
|
} else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
|
|
2315
2225
|
if (node.parent === null) {
|
|
2316
|
-
return;
|
|
2226
|
+
return node;
|
|
2317
2227
|
}
|
|
2318
2228
|
let isPredecessor = false;
|
|
2319
2229
|
let parentNode = this.getNode(node.parent);
|
|
@@ -2325,11 +2235,11 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2325
2235
|
const nKey = parentNode.keys[i];
|
|
2326
2236
|
if (nKey === node.id) {
|
|
2327
2237
|
if (i > 0) {
|
|
2328
|
-
prevNode = this.getNode(parentNode.keys[i - 1]);
|
|
2238
|
+
prevNode = this._cloneNode(this.getNode(parentNode.keys[i - 1]));
|
|
2329
2239
|
prevValue = parentNode.values[i - 1];
|
|
2330
2240
|
}
|
|
2331
2241
|
if (i < parentNode.keys.length - 1) {
|
|
2332
|
-
nextNode = this.getNode(parentNode.keys[i + 1]);
|
|
2242
|
+
nextNode = this._cloneNode(this.getNode(parentNode.keys[i + 1]));
|
|
2333
2243
|
postValue = parentNode.values[i];
|
|
2334
2244
|
}
|
|
2335
2245
|
}
|
|
@@ -2354,7 +2264,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2354
2264
|
}
|
|
2355
2265
|
}
|
|
2356
2266
|
if (!pointer) {
|
|
2357
|
-
return;
|
|
2267
|
+
return node;
|
|
2358
2268
|
}
|
|
2359
2269
|
if (node.values.length + pointer.values.length < this.order) {
|
|
2360
2270
|
if (!isPredecessor) {
|
|
@@ -2368,7 +2278,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2368
2278
|
} else {
|
|
2369
2279
|
pointer.next = node.next;
|
|
2370
2280
|
if (pointer.next) {
|
|
2371
|
-
const n = this.getNode(pointer.next);
|
|
2281
|
+
const n = this._cloneNode(this.getNode(pointer.next));
|
|
2372
2282
|
n.prev = pointer.id;
|
|
2373
2283
|
this._updateNode(n);
|
|
2374
2284
|
}
|
|
@@ -2377,14 +2287,14 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2377
2287
|
if (!pointer.leaf) {
|
|
2378
2288
|
const keys = pointer.keys;
|
|
2379
2289
|
for (const key2 of keys) {
|
|
2380
|
-
const node2 = this.getNode(key2);
|
|
2290
|
+
const node2 = this._cloneNode(this.getNode(key2));
|
|
2381
2291
|
node2.parent = pointer.id;
|
|
2382
2292
|
this._updateNode(node2);
|
|
2383
2293
|
}
|
|
2384
2294
|
}
|
|
2385
2295
|
this._deleteNode(node);
|
|
2386
2296
|
this._updateNode(pointer);
|
|
2387
|
-
this._deleteEntry(this.getNode(node.parent), node.id);
|
|
2297
|
+
this._deleteEntry(this._cloneNode(this.getNode(node.parent)), node.id);
|
|
2388
2298
|
} else {
|
|
2389
2299
|
if (isPredecessor) {
|
|
2390
2300
|
let pointerPm;
|
|
@@ -2394,7 +2304,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2394
2304
|
pointerKm = pointer.values.splice(-1)[0];
|
|
2395
2305
|
node.keys = [pointerPm, ...node.keys];
|
|
2396
2306
|
node.values = [guess, ...node.values];
|
|
2397
|
-
parentNode = this.getNode(node.parent);
|
|
2307
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2398
2308
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2399
2309
|
if (nodeIndex > 0) {
|
|
2400
2310
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
@@ -2405,7 +2315,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2405
2315
|
pointerKm = pointer.values.splice(-1)[0];
|
|
2406
2316
|
node.keys = [pointerPm, ...node.keys];
|
|
2407
2317
|
node.values = [pointerKm, ...node.values];
|
|
2408
|
-
parentNode = this.getNode(node.parent);
|
|
2318
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2409
2319
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2410
2320
|
if (nodeIndex > 0) {
|
|
2411
2321
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
@@ -2422,7 +2332,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2422
2332
|
pointerK0 = pointer.values.splice(0, 1)[0];
|
|
2423
2333
|
node.keys = [...node.keys, pointerP0];
|
|
2424
2334
|
node.values = [...node.values, guess];
|
|
2425
|
-
parentNode = this.getNode(node.parent);
|
|
2335
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2426
2336
|
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
2427
2337
|
if (pointerIndex > 0) {
|
|
2428
2338
|
parentNode.values[pointerIndex - 1] = pointerK0;
|
|
@@ -2433,7 +2343,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2433
2343
|
pointerK0 = pointer.values.splice(0, 1)[0];
|
|
2434
2344
|
node.keys = [...node.keys, pointerP0];
|
|
2435
2345
|
node.values = [...node.values, pointerK0];
|
|
2436
|
-
parentNode = this.getNode(node.parent);
|
|
2346
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2437
2347
|
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
2438
2348
|
if (pointerIndex > 0) {
|
|
2439
2349
|
parentNode.values[pointerIndex - 1] = pointer.values[0];
|
|
@@ -2445,21 +2355,21 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2445
2355
|
}
|
|
2446
2356
|
if (!pointer.leaf) {
|
|
2447
2357
|
for (const key2 of pointer.keys) {
|
|
2448
|
-
const n = this.getNode(key2);
|
|
2358
|
+
const n = this._cloneNode(this.getNode(key2));
|
|
2449
2359
|
n.parent = pointer.id;
|
|
2450
2360
|
this._updateNode(n);
|
|
2451
2361
|
}
|
|
2452
2362
|
}
|
|
2453
2363
|
if (!node.leaf) {
|
|
2454
2364
|
for (const key2 of node.keys) {
|
|
2455
|
-
const n = this.getNode(key2);
|
|
2365
|
+
const n = this._cloneNode(this.getNode(key2));
|
|
2456
2366
|
n.parent = node.id;
|
|
2457
2367
|
this._updateNode(n);
|
|
2458
2368
|
}
|
|
2459
2369
|
}
|
|
2460
2370
|
if (!parentNode.leaf) {
|
|
2461
2371
|
for (const key2 of parentNode.keys) {
|
|
2462
|
-
const n = this.getNode(key2);
|
|
2372
|
+
const n = this._cloneNode(this.getNode(key2));
|
|
2463
2373
|
n.parent = parentNode.id;
|
|
2464
2374
|
this._updateNode(n);
|
|
2465
2375
|
}
|
|
@@ -2468,10 +2378,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2468
2378
|
} else {
|
|
2469
2379
|
this._updateNode(node);
|
|
2470
2380
|
}
|
|
2381
|
+
return node;
|
|
2471
2382
|
}
|
|
2472
2383
|
delete(key, value) {
|
|
2473
2384
|
let node = this.insertableNodeByPrimary(value);
|
|
2474
2385
|
let found = false;
|
|
2386
|
+
node = this._cloneNode(node);
|
|
2475
2387
|
while (true) {
|
|
2476
2388
|
let i = node.values.length;
|
|
2477
2389
|
while (i--) {
|
|
@@ -2486,7 +2398,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2486
2398
|
node.values.splice(i, 1);
|
|
2487
2399
|
}
|
|
2488
2400
|
this._updateNode(node);
|
|
2489
|
-
this._deleteEntry(node, key);
|
|
2401
|
+
node = this._deleteEntry(node, key);
|
|
2490
2402
|
found = true;
|
|
2491
2403
|
break;
|
|
2492
2404
|
}
|
|
@@ -2521,20 +2433,14 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2521
2433
|
commit(label) {
|
|
2522
2434
|
let result = this.mvcc.commit(label);
|
|
2523
2435
|
if (result.success) {
|
|
2524
|
-
|
|
2525
|
-
if (
|
|
2526
|
-
|
|
2436
|
+
const isRootTx = this.rootTx !== this;
|
|
2437
|
+
if (isRootTx) {
|
|
2438
|
+
result = this.rootTx.commit(label);
|
|
2439
|
+
if (result.success) {
|
|
2440
|
+
this.rootTx.rootId = this.rootId;
|
|
2441
|
+
}
|
|
2527
2442
|
}
|
|
2528
2443
|
if (result.success) {
|
|
2529
|
-
for (const r of result.created) {
|
|
2530
|
-
this.nodes.set(r.key, r.data);
|
|
2531
|
-
}
|
|
2532
|
-
for (const r of result.updated) {
|
|
2533
|
-
this.nodes.set(r.key, r.data);
|
|
2534
|
-
}
|
|
2535
|
-
for (const r of result.deleted) {
|
|
2536
|
-
this.nodes.delete(r.key);
|
|
2537
|
-
}
|
|
2538
2444
|
}
|
|
2539
2445
|
}
|
|
2540
2446
|
return result;
|
|
@@ -2582,7 +2488,9 @@ var BPTreeMVCCStrategySync = class extends SyncMVCCStrategy {
|
|
|
2582
2488
|
// src/BPTreeSync.ts
|
|
2583
2489
|
var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
2584
2490
|
constructor(strategy, comparator, option) {
|
|
2585
|
-
const mvccRoot = new SyncMVCCTransaction(new BPTreeMVCCStrategySync(strategy)
|
|
2491
|
+
const mvccRoot = new SyncMVCCTransaction(new BPTreeMVCCStrategySync(strategy), {
|
|
2492
|
+
cacheCapacity: option?.capacity ?? void 0
|
|
2493
|
+
});
|
|
2586
2494
|
super(
|
|
2587
2495
|
null,
|
|
2588
2496
|
mvccRoot,
|
|
@@ -2606,7 +2514,7 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2606
2514
|
this.comparator,
|
|
2607
2515
|
this.option
|
|
2608
2516
|
);
|
|
2609
|
-
tx.
|
|
2517
|
+
tx._initInternal();
|
|
2610
2518
|
return tx;
|
|
2611
2519
|
}
|
|
2612
2520
|
insert(key, value) {
|
|
@@ -2616,7 +2524,6 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2616
2524
|
if (!result.success) {
|
|
2617
2525
|
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2618
2526
|
}
|
|
2619
|
-
this.rootId = tx.getRootId();
|
|
2620
2527
|
}
|
|
2621
2528
|
delete(key, value) {
|
|
2622
2529
|
const tx = this.createTransaction();
|
|
@@ -2625,394 +2532,673 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
|
2625
2532
|
if (!result.success) {
|
|
2626
2533
|
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2627
2534
|
}
|
|
2628
|
-
this.rootId = tx.getRootId();
|
|
2629
2535
|
}
|
|
2630
2536
|
};
|
|
2631
2537
|
|
|
2632
|
-
//
|
|
2633
|
-
var
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
comparator,
|
|
2641
|
-
option
|
|
2642
|
-
);
|
|
2538
|
+
// node_modules/ryoiki/dist/esm/index.mjs
|
|
2539
|
+
var Ryoiki2 = class _Ryoiki2 {
|
|
2540
|
+
readings;
|
|
2541
|
+
writings;
|
|
2542
|
+
readQueue;
|
|
2543
|
+
writeQueue;
|
|
2544
|
+
static async CatchError(promise) {
|
|
2545
|
+
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
2643
2546
|
}
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2547
|
+
static IsRangeOverlap(a, b) {
|
|
2548
|
+
const [start1, end1] = a;
|
|
2549
|
+
const [start2, end2] = b;
|
|
2550
|
+
if (end1 <= start2 || end2 <= start1) {
|
|
2551
|
+
return false;
|
|
2647
2552
|
}
|
|
2648
|
-
return
|
|
2553
|
+
return true;
|
|
2554
|
+
}
|
|
2555
|
+
static ERR_ALREADY_EXISTS(lockId) {
|
|
2556
|
+
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
2557
|
+
}
|
|
2558
|
+
static ERR_NOT_EXISTS(lockId) {
|
|
2559
|
+
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
2560
|
+
}
|
|
2561
|
+
static ERR_TIMEOUT(lockId, timeout) {
|
|
2562
|
+
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
2649
2563
|
}
|
|
2650
2564
|
/**
|
|
2651
|
-
*
|
|
2565
|
+
* Constructs a new instance of the Ryoiki class.
|
|
2652
2566
|
*/
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
values,
|
|
2659
|
-
leaf,
|
|
2660
|
-
parent,
|
|
2661
|
-
next,
|
|
2662
|
-
prev
|
|
2663
|
-
};
|
|
2664
|
-
await this.mvcc.create(id, node);
|
|
2665
|
-
this.nodes.set(id, node);
|
|
2666
|
-
return node;
|
|
2567
|
+
constructor() {
|
|
2568
|
+
this.readings = /* @__PURE__ */ new Map();
|
|
2569
|
+
this.writings = /* @__PURE__ */ new Map();
|
|
2570
|
+
this.readQueue = /* @__PURE__ */ new Map();
|
|
2571
|
+
this.writeQueue = /* @__PURE__ */ new Map();
|
|
2667
2572
|
}
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2573
|
+
/**
|
|
2574
|
+
* Creates a range based on a start value and length.
|
|
2575
|
+
* @param start - The starting value of the range.
|
|
2576
|
+
* @param length - The length of the range.
|
|
2577
|
+
* @returns A range tuple [start, start + length].
|
|
2578
|
+
*/
|
|
2579
|
+
range(start, length) {
|
|
2580
|
+
return [start, start + length];
|
|
2671
2581
|
}
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
this.nodes.delete(node.id);
|
|
2582
|
+
rangeOverlapping(tasks, range) {
|
|
2583
|
+
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
2675
2584
|
}
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
const head = await this.mvcc.read("__HEAD__");
|
|
2681
|
-
return head ?? null;
|
|
2585
|
+
isSameRange(a, b) {
|
|
2586
|
+
const [a1, a2] = a;
|
|
2587
|
+
const [b1, b2] = b;
|
|
2588
|
+
return a1 === b1 && a2 === b2;
|
|
2682
2589
|
}
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2590
|
+
fetchUnitAndRun(queue, workspaces) {
|
|
2591
|
+
for (const [id, unit] of queue) {
|
|
2592
|
+
if (!unit.condition()) {
|
|
2593
|
+
continue;
|
|
2594
|
+
}
|
|
2595
|
+
this._alloc(queue, workspaces, id);
|
|
2688
2596
|
}
|
|
2689
|
-
this.nodes.set("__HEAD__", head);
|
|
2690
|
-
this.rootId = head.root;
|
|
2691
2597
|
}
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
if (this.comparator.isSame(value, nValue)) {
|
|
2697
|
-
const keys = node.keys[i];
|
|
2698
|
-
if (keys.includes(key)) {
|
|
2699
|
-
break;
|
|
2700
|
-
}
|
|
2701
|
-
keys.push(key);
|
|
2702
|
-
await this._updateNode(node);
|
|
2703
|
-
return;
|
|
2704
|
-
} else if (this.comparator.isLower(value, nValue)) {
|
|
2705
|
-
node.values.splice(i, 0, value);
|
|
2706
|
-
node.keys.splice(i, 0, [key]);
|
|
2707
|
-
await this._updateNode(node);
|
|
2708
|
-
return;
|
|
2709
|
-
} else if (i + 1 === node.values.length) {
|
|
2710
|
-
node.values.push(value);
|
|
2711
|
-
node.keys.push([key]);
|
|
2712
|
-
await this._updateNode(node);
|
|
2713
|
-
return;
|
|
2714
|
-
}
|
|
2715
|
-
}
|
|
2716
|
-
} else {
|
|
2717
|
-
node.values = [value];
|
|
2718
|
-
node.keys = [[key]];
|
|
2719
|
-
await this._updateNode(node);
|
|
2720
|
-
return;
|
|
2721
|
-
}
|
|
2722
|
-
}
|
|
2723
|
-
async _insertInParent(node, value, pointer) {
|
|
2724
|
-
if (this.rootId === node.id) {
|
|
2725
|
-
const root = await this._createNode(false, [node.id, pointer.id], [value]);
|
|
2726
|
-
this.rootId = root.id;
|
|
2727
|
-
node.parent = root.id;
|
|
2728
|
-
pointer.parent = root.id;
|
|
2729
|
-
if (pointer.leaf) {
|
|
2730
|
-
node.next = pointer.id;
|
|
2731
|
-
pointer.prev = node.id;
|
|
2732
|
-
}
|
|
2733
|
-
await this._writeHead({
|
|
2734
|
-
root: root.id,
|
|
2735
|
-
order: this.order,
|
|
2736
|
-
data: this.strategy.head.data
|
|
2737
|
-
});
|
|
2738
|
-
await this._updateNode(node);
|
|
2739
|
-
await this._updateNode(pointer);
|
|
2740
|
-
return;
|
|
2741
|
-
}
|
|
2742
|
-
const parentNode = await this.getNode(node.parent);
|
|
2743
|
-
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2744
|
-
if (nodeIndex === -1) {
|
|
2745
|
-
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
2746
|
-
}
|
|
2747
|
-
parentNode.values.splice(nodeIndex, 0, value);
|
|
2748
|
-
parentNode.keys.splice(nodeIndex + 1, 0, pointer.id);
|
|
2749
|
-
pointer.parent = parentNode.id;
|
|
2750
|
-
if (pointer.leaf) {
|
|
2751
|
-
const leftSibling = node;
|
|
2752
|
-
const oldNextId = leftSibling.next;
|
|
2753
|
-
pointer.prev = leftSibling.id;
|
|
2754
|
-
pointer.next = oldNextId;
|
|
2755
|
-
leftSibling.next = pointer.id;
|
|
2756
|
-
await this._updateNode(leftSibling);
|
|
2757
|
-
if (oldNextId) {
|
|
2758
|
-
const oldNext = await this.getNode(oldNextId);
|
|
2759
|
-
oldNext.prev = pointer.id;
|
|
2760
|
-
await this._updateNode(oldNext);
|
|
2761
|
-
}
|
|
2762
|
-
}
|
|
2763
|
-
await this._updateNode(parentNode);
|
|
2764
|
-
await this._updateNode(pointer);
|
|
2765
|
-
if (parentNode.keys.length > this.order) {
|
|
2766
|
-
const parentPointer = await this._createNode(false, [], []);
|
|
2767
|
-
parentPointer.parent = parentNode.parent;
|
|
2768
|
-
const mid = Math.ceil(this.order / 2) - 1;
|
|
2769
|
-
parentPointer.values = parentNode.values.slice(mid + 1);
|
|
2770
|
-
parentPointer.keys = parentNode.keys.slice(mid + 1);
|
|
2771
|
-
const midValue = parentNode.values[mid];
|
|
2772
|
-
parentNode.values = parentNode.values.slice(0, mid);
|
|
2773
|
-
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2774
|
-
for (const k of parentNode.keys) {
|
|
2775
|
-
const n = await this.getNode(k);
|
|
2776
|
-
n.parent = parentNode.id;
|
|
2777
|
-
await this._updateNode(n);
|
|
2778
|
-
}
|
|
2779
|
-
for (const k of parentPointer.keys) {
|
|
2780
|
-
const n = await this.getNode(k);
|
|
2781
|
-
n.parent = parentPointer.id;
|
|
2782
|
-
await this._updateNode(n);
|
|
2598
|
+
_handleOverload(args, handlers, argPatterns) {
|
|
2599
|
+
for (const [key, pattern] of Object.entries(argPatterns)) {
|
|
2600
|
+
if (this._matchArgs(args, pattern)) {
|
|
2601
|
+
return handlers[key](...args);
|
|
2783
2602
|
}
|
|
2784
|
-
await this._updateNode(parentNode);
|
|
2785
|
-
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2786
|
-
}
|
|
2787
|
-
}
|
|
2788
|
-
async insertableNode(value) {
|
|
2789
|
-
let node = await this.getNode(this.rootId);
|
|
2790
|
-
while (!node.leaf) {
|
|
2791
|
-
const { index } = this._binarySearchValues(node.values, value, false, true);
|
|
2792
|
-
node = await this.getNode(node.keys[index]);
|
|
2793
|
-
}
|
|
2794
|
-
return node;
|
|
2795
|
-
}
|
|
2796
|
-
async insertableNodeByPrimary(value) {
|
|
2797
|
-
let node = await this.getNode(this.rootId);
|
|
2798
|
-
while (!node.leaf) {
|
|
2799
|
-
const { index } = this._binarySearchValues(node.values, value, true, false);
|
|
2800
|
-
node = await this.getNode(node.keys[index]);
|
|
2801
|
-
}
|
|
2802
|
-
return node;
|
|
2803
|
-
}
|
|
2804
|
-
async insertableRightestNodeByPrimary(value) {
|
|
2805
|
-
let node = await this.getNode(this.rootId);
|
|
2806
|
-
while (!node.leaf) {
|
|
2807
|
-
const { index } = this._binarySearchValues(node.values, value, true, true);
|
|
2808
|
-
node = await this.getNode(node.keys[index]);
|
|
2809
2603
|
}
|
|
2810
|
-
|
|
2604
|
+
throw new Error("Invalid arguments");
|
|
2811
2605
|
}
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
return
|
|
2816
|
-
|
|
2817
|
-
|
|
2606
|
+
_matchArgs(args, pattern) {
|
|
2607
|
+
return args.every((arg, index) => {
|
|
2608
|
+
const expectedType = pattern[index];
|
|
2609
|
+
if (expectedType === void 0) return typeof arg === "undefined";
|
|
2610
|
+
if (expectedType === Function) return typeof arg === "function";
|
|
2611
|
+
if (expectedType === Number) return typeof arg === "number";
|
|
2612
|
+
if (expectedType === Array) return Array.isArray(arg);
|
|
2613
|
+
return false;
|
|
2614
|
+
});
|
|
2818
2615
|
}
|
|
2819
|
-
|
|
2820
|
-
const
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
case -1:
|
|
2824
|
-
key = "prev";
|
|
2825
|
-
break;
|
|
2826
|
-
case 1:
|
|
2827
|
-
key = "next";
|
|
2828
|
-
break;
|
|
2829
|
-
default:
|
|
2830
|
-
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
2831
|
-
}
|
|
2832
|
-
const guessNode = insertableNode[key];
|
|
2833
|
-
if (!guessNode) {
|
|
2834
|
-
return null;
|
|
2835
|
-
}
|
|
2836
|
-
return await this.getNode(guessNode);
|
|
2616
|
+
_createRandomId() {
|
|
2617
|
+
const timestamp = Date.now().toString(36);
|
|
2618
|
+
const random = Math.random().toString(36).substring(2);
|
|
2619
|
+
return `${timestamp}${random}`;
|
|
2837
2620
|
}
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
if (
|
|
2841
|
-
|
|
2842
|
-
}
|
|
2843
|
-
while (!node.leaf) {
|
|
2844
|
-
const keys = node.keys;
|
|
2845
|
-
node = await this.getNode(keys[0]);
|
|
2621
|
+
_alloc(queue, workspaces, lockId) {
|
|
2622
|
+
const unit = queue.get(lockId);
|
|
2623
|
+
if (!unit) {
|
|
2624
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2846
2625
|
}
|
|
2847
|
-
|
|
2626
|
+
workspaces.set(lockId, unit);
|
|
2627
|
+
queue.delete(lockId);
|
|
2628
|
+
unit.alloc();
|
|
2848
2629
|
}
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
node = await this.getNode(keys[keys.length - 1]);
|
|
2630
|
+
_free(workspaces, lockId) {
|
|
2631
|
+
const unit = workspaces.get(lockId);
|
|
2632
|
+
if (!unit) {
|
|
2633
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2854
2634
|
}
|
|
2855
|
-
|
|
2635
|
+
workspaces.delete(lockId);
|
|
2636
|
+
unit.free();
|
|
2856
2637
|
}
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
done = true;
|
|
2865
|
-
break;
|
|
2866
|
-
}
|
|
2867
|
-
if (direction === 1) {
|
|
2868
|
-
if (node.next && !done) {
|
|
2869
|
-
nextNodePromise = this.getNode(node.next);
|
|
2870
|
-
}
|
|
2871
|
-
} else {
|
|
2872
|
-
if (node.prev && !done) {
|
|
2873
|
-
nextNodePromise = this.getNode(node.prev);
|
|
2874
|
-
}
|
|
2638
|
+
_lock(queue, range, timeout, task, condition) {
|
|
2639
|
+
return new Promise((resolve, reject) => {
|
|
2640
|
+
let timeoutId = null;
|
|
2641
|
+
if (timeout >= 0) {
|
|
2642
|
+
timeoutId = setTimeout(() => {
|
|
2643
|
+
reject(_Ryoiki2.ERR_TIMEOUT(id, timeout));
|
|
2644
|
+
}, timeout);
|
|
2875
2645
|
}
|
|
2876
|
-
const
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
const keys = node.keys[i];
|
|
2881
|
-
if (comparator(nValue, value)) {
|
|
2882
|
-
hasMatched = true;
|
|
2883
|
-
for (let j = 0; j < keys.length; j++) {
|
|
2884
|
-
yield [keys[j], nValue];
|
|
2885
|
-
}
|
|
2886
|
-
} else if (earlyTerminate && hasMatched) {
|
|
2887
|
-
done = true;
|
|
2888
|
-
break;
|
|
2889
|
-
}
|
|
2890
|
-
}
|
|
2891
|
-
} else {
|
|
2892
|
-
let i = len;
|
|
2893
|
-
while (i--) {
|
|
2894
|
-
const nValue = node.values[i];
|
|
2895
|
-
const keys = node.keys[i];
|
|
2896
|
-
if (comparator(nValue, value)) {
|
|
2897
|
-
hasMatched = true;
|
|
2898
|
-
let j = keys.length;
|
|
2899
|
-
while (j--) {
|
|
2900
|
-
yield [keys[j], nValue];
|
|
2901
|
-
}
|
|
2902
|
-
} else if (earlyTerminate && hasMatched) {
|
|
2903
|
-
done = true;
|
|
2904
|
-
break;
|
|
2905
|
-
}
|
|
2646
|
+
const id = this._createRandomId();
|
|
2647
|
+
const alloc = async () => {
|
|
2648
|
+
if (timeoutId !== null) {
|
|
2649
|
+
clearTimeout(timeoutId);
|
|
2906
2650
|
}
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2651
|
+
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
2652
|
+
if (err) reject(err);
|
|
2653
|
+
else resolve(v);
|
|
2654
|
+
};
|
|
2655
|
+
const fetch = () => {
|
|
2656
|
+
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
2657
|
+
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
2658
|
+
};
|
|
2659
|
+
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
2660
|
+
fetch();
|
|
2661
|
+
});
|
|
2662
|
+
}
|
|
2663
|
+
_checkWorking(range, workspaces) {
|
|
2664
|
+
let isLocked = false;
|
|
2665
|
+
for (const lock of workspaces.values()) {
|
|
2666
|
+
if (_Ryoiki2.IsRangeOverlap(range, lock.range)) {
|
|
2667
|
+
isLocked = true;
|
|
2910
2668
|
break;
|
|
2911
2669
|
}
|
|
2912
|
-
if (nextNodePromise) {
|
|
2913
|
-
node = await nextNodePromise;
|
|
2914
|
-
nextNodePromise = null;
|
|
2915
|
-
} else {
|
|
2916
|
-
done = true;
|
|
2917
|
-
}
|
|
2918
2670
|
}
|
|
2671
|
+
return isLocked;
|
|
2919
2672
|
}
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2673
|
+
/**
|
|
2674
|
+
* Checks if there is any active read lock within the specified range.
|
|
2675
|
+
* @param range The range to check for active read locks.
|
|
2676
|
+
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
2677
|
+
*/
|
|
2678
|
+
isReading(range) {
|
|
2679
|
+
return this._checkWorking(range, this.readings);
|
|
2680
|
+
}
|
|
2681
|
+
/**
|
|
2682
|
+
* Checks if there is any active write lock within the specified range.
|
|
2683
|
+
* @param range The range to check for active write locks.
|
|
2684
|
+
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
2685
|
+
*/
|
|
2686
|
+
isWriting(range) {
|
|
2687
|
+
return this._checkWorking(range, this.writings);
|
|
2688
|
+
}
|
|
2689
|
+
/**
|
|
2690
|
+
* Checks if a read lock can be acquired within the specified range.
|
|
2691
|
+
* @param range The range to check for read lock availability.
|
|
2692
|
+
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
2693
|
+
*/
|
|
2694
|
+
canRead(range) {
|
|
2695
|
+
const writing = this.isWriting(range);
|
|
2696
|
+
return !writing;
|
|
2697
|
+
}
|
|
2698
|
+
/**
|
|
2699
|
+
* Checks if a write lock can be acquired within the specified range.
|
|
2700
|
+
* @param range The range to check for write lock availability.
|
|
2701
|
+
* @returns `true` if a write lock can be acquired, `false` otherwise.
|
|
2702
|
+
*/
|
|
2703
|
+
canWrite(range) {
|
|
2704
|
+
const reading = this.isReading(range);
|
|
2705
|
+
const writing = this.isWriting(range);
|
|
2706
|
+
return !reading && !writing;
|
|
2707
|
+
}
|
|
2708
|
+
/**
|
|
2709
|
+
* Internal implementation of the read lock. Handles both overloads.
|
|
2710
|
+
* @template T - The return type of the task.
|
|
2711
|
+
* @param arg0 - Either a range or a task callback.
|
|
2712
|
+
* If a range is provided, the task is the second argument.
|
|
2713
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2714
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2715
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2716
|
+
* If this value is not provided, no timeout will be set.
|
|
2717
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2718
|
+
*/
|
|
2719
|
+
readLock(arg0, arg1, arg2) {
|
|
2720
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2721
|
+
[arg0, arg1, arg2],
|
|
2722
|
+
{
|
|
2723
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2724
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2725
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2726
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2727
|
+
},
|
|
2728
|
+
{
|
|
2729
|
+
task: [Function],
|
|
2730
|
+
taskTimeout: [Function, Number],
|
|
2731
|
+
rangeTask: [Array, Function],
|
|
2732
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2733
|
+
}
|
|
2734
|
+
);
|
|
2735
|
+
return this._lock(
|
|
2736
|
+
this.readQueue,
|
|
2737
|
+
range,
|
|
2738
|
+
timeout,
|
|
2739
|
+
task,
|
|
2740
|
+
() => !this.rangeOverlapping(this.writings, range)
|
|
2741
|
+
);
|
|
2742
|
+
}
|
|
2743
|
+
/**
|
|
2744
|
+
* Internal implementation of the write lock. Handles both overloads.
|
|
2745
|
+
* @template T - The return type of the task.
|
|
2746
|
+
* @param arg0 - Either a range or a task callback.
|
|
2747
|
+
* If a range is provided, the task is the second argument.
|
|
2748
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2749
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2750
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2751
|
+
* If this value is not provided, no timeout will be set.
|
|
2752
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2753
|
+
*/
|
|
2754
|
+
writeLock(arg0, arg1, arg2) {
|
|
2755
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2756
|
+
[arg0, arg1, arg2],
|
|
2757
|
+
{
|
|
2758
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2759
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2760
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2761
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2762
|
+
},
|
|
2763
|
+
{
|
|
2764
|
+
task: [Function],
|
|
2765
|
+
taskTimeout: [Function, Number],
|
|
2766
|
+
rangeTask: [Array, Function],
|
|
2767
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2768
|
+
}
|
|
2769
|
+
);
|
|
2770
|
+
return this._lock(
|
|
2771
|
+
this.writeQueue,
|
|
2772
|
+
range,
|
|
2773
|
+
timeout,
|
|
2774
|
+
task,
|
|
2775
|
+
() => {
|
|
2776
|
+
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
2777
|
+
}
|
|
2778
|
+
);
|
|
2779
|
+
}
|
|
2780
|
+
/**
|
|
2781
|
+
* Releases a read lock by its lock ID.
|
|
2782
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2783
|
+
*/
|
|
2784
|
+
readUnlock(lockId) {
|
|
2785
|
+
this._free(this.readings, lockId);
|
|
2786
|
+
}
|
|
2787
|
+
/**
|
|
2788
|
+
* Releases a write lock by its lock ID.
|
|
2789
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2790
|
+
*/
|
|
2791
|
+
writeUnlock(lockId) {
|
|
2792
|
+
this._free(this.writings, lockId);
|
|
2793
|
+
}
|
|
2794
|
+
};
|
|
2795
|
+
|
|
2796
|
+
// src/transaction/BPTreeAsyncTransaction.ts
|
|
2797
|
+
var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
2798
|
+
lock;
|
|
2799
|
+
constructor(rootTx, mvccRoot, mvcc, strategy, comparator, option) {
|
|
2800
|
+
super(
|
|
2801
|
+
rootTx,
|
|
2802
|
+
mvccRoot,
|
|
2803
|
+
mvcc,
|
|
2804
|
+
strategy,
|
|
2805
|
+
comparator,
|
|
2806
|
+
option
|
|
2807
|
+
);
|
|
2808
|
+
this.lock = new Ryoiki2();
|
|
2809
|
+
}
|
|
2810
|
+
async writeLock(id, fn) {
|
|
2811
|
+
let lockId;
|
|
2812
|
+
return this.lock.writeLock([id, id + 0.1], async (_lockId) => {
|
|
2813
|
+
lockId = _lockId;
|
|
2814
|
+
return fn();
|
|
2815
|
+
}).finally(() => {
|
|
2816
|
+
this.lock.writeUnlock(lockId);
|
|
2817
|
+
});
|
|
2818
|
+
}
|
|
2819
|
+
async getNode(id) {
|
|
2820
|
+
return await this.mvcc.read(id);
|
|
2821
|
+
}
|
|
2822
|
+
/**
|
|
2823
|
+
* Create a new node with a unique ID.
|
|
2824
|
+
*/
|
|
2825
|
+
async _createNode(leaf, keys, values, parent = null, next = null, prev = null) {
|
|
2826
|
+
const id = await this.strategy.id(leaf);
|
|
2827
|
+
const node = {
|
|
2828
|
+
id,
|
|
2829
|
+
keys,
|
|
2830
|
+
values,
|
|
2831
|
+
leaf,
|
|
2832
|
+
parent,
|
|
2833
|
+
next,
|
|
2834
|
+
prev
|
|
2835
|
+
};
|
|
2836
|
+
const head = await this._readHead();
|
|
2837
|
+
if (head) {
|
|
2935
2838
|
await this._writeHead({
|
|
2936
|
-
root,
|
|
2937
|
-
order:
|
|
2839
|
+
root: head.root,
|
|
2840
|
+
order: head.order,
|
|
2938
2841
|
data: this.strategy.head.data
|
|
2939
2842
|
});
|
|
2940
2843
|
}
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
}
|
|
2844
|
+
await this.mvcc.create(id, node);
|
|
2845
|
+
return node;
|
|
2944
2846
|
}
|
|
2945
|
-
async
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
if (found) {
|
|
2949
|
-
const keys = node.keys[index];
|
|
2950
|
-
if (keys.includes(key)) {
|
|
2951
|
-
return true;
|
|
2952
|
-
}
|
|
2847
|
+
async _updateNode(node) {
|
|
2848
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
2849
|
+
return;
|
|
2953
2850
|
}
|
|
2954
|
-
|
|
2851
|
+
await this.mvcc.write(node.id, this._cloneNode(node));
|
|
2955
2852
|
}
|
|
2956
|
-
async
|
|
2957
|
-
if (id) {
|
|
2958
|
-
|
|
2959
|
-
await this.getNode(id);
|
|
2960
|
-
return 1;
|
|
2961
|
-
}
|
|
2962
|
-
const keys = Array.from(this.nodes.keys());
|
|
2963
|
-
for (const key of keys) {
|
|
2964
|
-
this.nodes.delete(key);
|
|
2853
|
+
async _deleteNode(node) {
|
|
2854
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
2855
|
+
return;
|
|
2965
2856
|
}
|
|
2966
|
-
|
|
2967
|
-
|
|
2857
|
+
await this.mvcc.delete(node.id);
|
|
2858
|
+
}
|
|
2859
|
+
async _readHead() {
|
|
2860
|
+
return await this.mvcc.read("__HEAD__");
|
|
2861
|
+
}
|
|
2862
|
+
async _writeHead(head) {
|
|
2863
|
+
if (!await this.mvcc.exists("__HEAD__")) {
|
|
2864
|
+
await this.mvcc.create("__HEAD__", this._cloneNode(head));
|
|
2865
|
+
} else {
|
|
2866
|
+
await this.mvcc.write("__HEAD__", this._cloneNode(head));
|
|
2968
2867
|
}
|
|
2969
|
-
|
|
2868
|
+
this.rootId = head.root;
|
|
2970
2869
|
}
|
|
2971
|
-
async
|
|
2972
|
-
let
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2870
|
+
async _insertAtLeaf(node, key, value) {
|
|
2871
|
+
let leaf = node;
|
|
2872
|
+
leaf = this._cloneNode(leaf);
|
|
2873
|
+
if (leaf.values.length) {
|
|
2874
|
+
for (let i = 0, len = leaf.values.length; i < len; i++) {
|
|
2875
|
+
const nValue = leaf.values[i];
|
|
2876
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
2877
|
+
const keys = leaf.keys[i];
|
|
2878
|
+
if (keys.includes(key)) {
|
|
2879
|
+
break;
|
|
2979
2880
|
}
|
|
2881
|
+
keys.push(key);
|
|
2882
|
+
await this._updateNode(leaf);
|
|
2883
|
+
return leaf;
|
|
2884
|
+
} else if (this.comparator.isLower(value, nValue)) {
|
|
2885
|
+
leaf.values.splice(i, 0, value);
|
|
2886
|
+
leaf.keys.splice(i, 0, [key]);
|
|
2887
|
+
await this._updateNode(leaf);
|
|
2888
|
+
return leaf;
|
|
2889
|
+
} else if (i + 1 === leaf.values.length) {
|
|
2890
|
+
leaf.values.push(value);
|
|
2891
|
+
leaf.keys.push([key]);
|
|
2892
|
+
await this._updateNode(leaf);
|
|
2893
|
+
return leaf;
|
|
2980
2894
|
}
|
|
2981
2895
|
}
|
|
2982
|
-
|
|
2983
|
-
|
|
2896
|
+
} else {
|
|
2897
|
+
leaf.values = [value];
|
|
2898
|
+
leaf.keys = [[key]];
|
|
2899
|
+
await this._updateNode(leaf);
|
|
2900
|
+
return leaf;
|
|
2984
2901
|
}
|
|
2985
|
-
return
|
|
2902
|
+
return leaf;
|
|
2986
2903
|
}
|
|
2987
|
-
async
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2904
|
+
async _insertInParent(node, value, pointer) {
|
|
2905
|
+
node = this._cloneNode(node);
|
|
2906
|
+
pointer = this._cloneNode(pointer);
|
|
2907
|
+
if (this.rootId === node.id) {
|
|
2908
|
+
const root = await this._createNode(false, [node.id, pointer.id], [value]);
|
|
2909
|
+
this.rootId = root.id;
|
|
2910
|
+
node.parent = root.id;
|
|
2911
|
+
pointer.parent = root.id;
|
|
2912
|
+
if (pointer.leaf) {
|
|
2913
|
+
node.next = pointer.id;
|
|
2914
|
+
pointer.prev = node.id;
|
|
2993
2915
|
}
|
|
2994
|
-
|
|
2916
|
+
await this._writeHead({
|
|
2917
|
+
root: root.id,
|
|
2918
|
+
order: this.order,
|
|
2919
|
+
data: this.strategy.head.data
|
|
2920
|
+
});
|
|
2921
|
+
await this._updateNode(node);
|
|
2922
|
+
await this._updateNode(pointer);
|
|
2923
|
+
return;
|
|
2995
2924
|
}
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
const value = condition[driverKey];
|
|
3001
|
-
let startNode = await this.verifierStartNode[driverKey](value);
|
|
3002
|
-
let endNode = await this.verifierEndNode[driverKey](value);
|
|
3003
|
-
let direction = this.verifierDirection[driverKey];
|
|
3004
|
-
const comparator = this.verifierMap[driverKey];
|
|
3005
|
-
const earlyTerminate = this.verifierEarlyTerminate[driverKey];
|
|
3006
|
-
if (order === "desc") {
|
|
3007
|
-
startNode = endNode ?? await this.rightestNode();
|
|
3008
|
-
endNode = null;
|
|
3009
|
-
direction *= -1;
|
|
2925
|
+
const parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
2926
|
+
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2927
|
+
if (nodeIndex === -1) {
|
|
2928
|
+
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
3010
2929
|
}
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
2930
|
+
parentNode.values.splice(nodeIndex, 0, value);
|
|
2931
|
+
parentNode.keys.splice(nodeIndex + 1, 0, pointer.id);
|
|
2932
|
+
pointer.parent = parentNode.id;
|
|
2933
|
+
if (pointer.leaf) {
|
|
2934
|
+
const leftSibling = node;
|
|
2935
|
+
const oldNextId = leftSibling.next;
|
|
2936
|
+
pointer.prev = leftSibling.id;
|
|
2937
|
+
pointer.next = oldNextId;
|
|
2938
|
+
leftSibling.next = pointer.id;
|
|
2939
|
+
await this._updateNode(leftSibling);
|
|
2940
|
+
if (oldNextId) {
|
|
2941
|
+
const oldNext = this._cloneNode(await this.getNode(oldNextId));
|
|
2942
|
+
oldNext.prev = pointer.id;
|
|
2943
|
+
await this._updateNode(oldNext);
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
await this._updateNode(parentNode);
|
|
2947
|
+
await this._updateNode(pointer);
|
|
2948
|
+
if (parentNode.keys.length > this.order) {
|
|
2949
|
+
const parentPointer = await this._createNode(false, [], []);
|
|
2950
|
+
parentPointer.parent = parentNode.parent;
|
|
2951
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
2952
|
+
parentPointer.values = parentNode.values.slice(mid + 1);
|
|
2953
|
+
parentPointer.keys = parentNode.keys.slice(mid + 1);
|
|
2954
|
+
const midValue = parentNode.values[mid];
|
|
2955
|
+
parentNode.values = parentNode.values.slice(0, mid);
|
|
2956
|
+
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2957
|
+
for (const k of parentNode.keys) {
|
|
2958
|
+
const n = this._cloneNode(await this.getNode(k));
|
|
2959
|
+
n.parent = parentNode.id;
|
|
2960
|
+
await this._updateNode(n);
|
|
2961
|
+
}
|
|
2962
|
+
for (const k of parentPointer.keys) {
|
|
2963
|
+
const n = this._cloneNode(await this.getNode(k));
|
|
2964
|
+
n.parent = parentPointer.id;
|
|
2965
|
+
await this._updateNode(n);
|
|
2966
|
+
}
|
|
2967
|
+
await this._updateNode(parentNode);
|
|
2968
|
+
await this._insertInParent(parentNode, midValue, parentPointer);
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
async insertableNode(value) {
|
|
2972
|
+
let node = await this.getNode(this.rootId);
|
|
2973
|
+
while (!node.leaf) {
|
|
2974
|
+
const { index } = this._binarySearchValues(node.values, value, false, true);
|
|
2975
|
+
node = await this.getNode(node.keys[index]);
|
|
2976
|
+
}
|
|
2977
|
+
return node;
|
|
2978
|
+
}
|
|
2979
|
+
async insertableNodeByPrimary(value) {
|
|
2980
|
+
let node = await this.getNode(this.rootId);
|
|
2981
|
+
while (!node.leaf) {
|
|
2982
|
+
const { index } = this._binarySearchValues(node.values, value, true, false);
|
|
2983
|
+
node = await this.getNode(node.keys[index]);
|
|
2984
|
+
}
|
|
2985
|
+
return node;
|
|
2986
|
+
}
|
|
2987
|
+
async insertableRightestNodeByPrimary(value) {
|
|
2988
|
+
let node = await this.getNode(this.rootId);
|
|
2989
|
+
while (!node.leaf) {
|
|
2990
|
+
const { index } = this._binarySearchValues(node.values, value, true, true);
|
|
2991
|
+
node = await this.getNode(node.keys[index]);
|
|
2992
|
+
}
|
|
2993
|
+
return node;
|
|
2994
|
+
}
|
|
2995
|
+
async insertableRightestEndNodeByPrimary(value) {
|
|
2996
|
+
const node = await this.insertableRightestNodeByPrimary(value);
|
|
2997
|
+
if (!node.next) {
|
|
2998
|
+
return null;
|
|
2999
|
+
}
|
|
3000
|
+
return await this.getNode(node.next);
|
|
3001
|
+
}
|
|
3002
|
+
async insertableEndNode(value, direction) {
|
|
3003
|
+
const insertableNode = await this.insertableNode(value);
|
|
3004
|
+
let key;
|
|
3005
|
+
switch (direction) {
|
|
3006
|
+
case -1:
|
|
3007
|
+
key = "prev";
|
|
3008
|
+
break;
|
|
3009
|
+
case 1:
|
|
3010
|
+
key = "next";
|
|
3011
|
+
break;
|
|
3012
|
+
default:
|
|
3013
|
+
throw new Error(`Direction must be -1 or 1. but got a ${direction}`);
|
|
3014
|
+
}
|
|
3015
|
+
const guessNode = insertableNode[key];
|
|
3016
|
+
if (!guessNode) {
|
|
3017
|
+
return null;
|
|
3018
|
+
}
|
|
3019
|
+
return await this.getNode(guessNode);
|
|
3020
|
+
}
|
|
3021
|
+
async leftestNode() {
|
|
3022
|
+
let node = await this.getNode(this.rootId);
|
|
3023
|
+
if (node === null) {
|
|
3024
|
+
debugger;
|
|
3025
|
+
}
|
|
3026
|
+
while (!node.leaf) {
|
|
3027
|
+
const keys = node.keys;
|
|
3028
|
+
node = await this.getNode(keys[0]);
|
|
3029
|
+
}
|
|
3030
|
+
return node;
|
|
3031
|
+
}
|
|
3032
|
+
async rightestNode() {
|
|
3033
|
+
let node = await this.getNode(this.rootId);
|
|
3034
|
+
while (!node.leaf) {
|
|
3035
|
+
const keys = node.keys;
|
|
3036
|
+
node = await this.getNode(keys[keys.length - 1]);
|
|
3037
|
+
}
|
|
3038
|
+
return node;
|
|
3039
|
+
}
|
|
3040
|
+
async *getPairsGenerator(value, startNode, endNode, comparator, direction, earlyTerminate) {
|
|
3041
|
+
let node = startNode;
|
|
3042
|
+
let done = false;
|
|
3043
|
+
let hasMatched = false;
|
|
3044
|
+
let nextNodePromise = null;
|
|
3045
|
+
while (!done) {
|
|
3046
|
+
if (endNode && node.id === endNode.id) {
|
|
3047
|
+
done = true;
|
|
3048
|
+
break;
|
|
3049
|
+
}
|
|
3050
|
+
if (direction === 1) {
|
|
3051
|
+
if (node.next && !done) {
|
|
3052
|
+
nextNodePromise = this.getNode(node.next);
|
|
3053
|
+
}
|
|
3054
|
+
} else {
|
|
3055
|
+
if (node.prev && !done) {
|
|
3056
|
+
nextNodePromise = this.getNode(node.prev);
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
const len = node.values.length;
|
|
3060
|
+
if (direction === 1) {
|
|
3061
|
+
for (let i = 0; i < len; i++) {
|
|
3062
|
+
const nValue = node.values[i];
|
|
3063
|
+
const keys = node.keys[i];
|
|
3064
|
+
if (comparator(nValue, value)) {
|
|
3065
|
+
hasMatched = true;
|
|
3066
|
+
for (let j = 0; j < keys.length; j++) {
|
|
3067
|
+
yield [keys[j], nValue];
|
|
3068
|
+
}
|
|
3069
|
+
} else if (earlyTerminate && hasMatched) {
|
|
3070
|
+
done = true;
|
|
3071
|
+
break;
|
|
3072
|
+
}
|
|
3073
|
+
}
|
|
3074
|
+
} else {
|
|
3075
|
+
let i = len;
|
|
3076
|
+
while (i--) {
|
|
3077
|
+
const nValue = node.values[i];
|
|
3078
|
+
const keys = node.keys[i];
|
|
3079
|
+
if (comparator(nValue, value)) {
|
|
3080
|
+
hasMatched = true;
|
|
3081
|
+
let j = keys.length;
|
|
3082
|
+
while (j--) {
|
|
3083
|
+
yield [keys[j], nValue];
|
|
3084
|
+
}
|
|
3085
|
+
} else if (earlyTerminate && hasMatched) {
|
|
3086
|
+
done = true;
|
|
3087
|
+
break;
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
if (done) {
|
|
3092
|
+
if (nextNodePromise) await nextNodePromise;
|
|
3093
|
+
break;
|
|
3094
|
+
}
|
|
3095
|
+
if (nextNodePromise) {
|
|
3096
|
+
node = await nextNodePromise;
|
|
3097
|
+
nextNodePromise = null;
|
|
3098
|
+
} else {
|
|
3099
|
+
done = true;
|
|
3100
|
+
}
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
async init() {
|
|
3104
|
+
if (this.rootTx !== this) {
|
|
3105
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
3106
|
+
}
|
|
3107
|
+
return await this._initInternal();
|
|
3108
|
+
}
|
|
3109
|
+
async _initInternal() {
|
|
3110
|
+
if (this.isInitialized) {
|
|
3111
|
+
throw new Error("Transaction already initialized");
|
|
3112
|
+
}
|
|
3113
|
+
if (this.isDestroyed) {
|
|
3114
|
+
throw new Error("Transaction already destroyed");
|
|
3115
|
+
}
|
|
3116
|
+
this.isInitialized = true;
|
|
3117
|
+
try {
|
|
3118
|
+
this._clearCache();
|
|
3119
|
+
const head = await this._readHead();
|
|
3120
|
+
if (head === null) {
|
|
3121
|
+
this.order = this.strategy.order;
|
|
3122
|
+
const root = await this._createNode(true, [], []);
|
|
3123
|
+
await this._writeHead({
|
|
3124
|
+
root: root.id,
|
|
3125
|
+
order: this.order,
|
|
3126
|
+
data: this.strategy.head.data
|
|
3127
|
+
});
|
|
3128
|
+
} else {
|
|
3129
|
+
const { root, order } = head;
|
|
3130
|
+
this.strategy.head = head;
|
|
3131
|
+
this.order = order;
|
|
3132
|
+
await this._writeHead({
|
|
3133
|
+
root,
|
|
3134
|
+
order: this.order,
|
|
3135
|
+
data: this.strategy.head.data
|
|
3136
|
+
});
|
|
3137
|
+
}
|
|
3138
|
+
if (this.order < 3) {
|
|
3139
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
3140
|
+
}
|
|
3141
|
+
} catch (e) {
|
|
3142
|
+
this.isInitialized = false;
|
|
3143
|
+
throw e;
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
async exists(key, value) {
|
|
3147
|
+
const node = await this.insertableNode(value);
|
|
3148
|
+
const { index, found } = this._binarySearchValues(node.values, value);
|
|
3149
|
+
if (found) {
|
|
3150
|
+
const keys = node.keys[index];
|
|
3151
|
+
if (keys.includes(key)) {
|
|
3152
|
+
return true;
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
return false;
|
|
3156
|
+
}
|
|
3157
|
+
async get(key) {
|
|
3158
|
+
let node = await this.leftestNode();
|
|
3159
|
+
while (true) {
|
|
3160
|
+
for (let i = 0, len = node.values.length; i < len; i++) {
|
|
3161
|
+
const keys = node.keys[i];
|
|
3162
|
+
for (let j = 0, kLen = keys.length; j < kLen; j++) {
|
|
3163
|
+
if (keys[j] === key) {
|
|
3164
|
+
return node.values[i];
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
if (!node.next) break;
|
|
3169
|
+
node = await this.getNode(node.next);
|
|
3170
|
+
}
|
|
3171
|
+
return void 0;
|
|
3172
|
+
}
|
|
3173
|
+
async *keysStream(condition, filterValues, limit, order = "asc") {
|
|
3174
|
+
const stream = this.whereStream(condition, limit, order);
|
|
3175
|
+
const intersection = filterValues && filterValues.size > 0 ? filterValues : null;
|
|
3176
|
+
for await (const [key] of stream) {
|
|
3177
|
+
if (intersection && !intersection.has(key)) {
|
|
3178
|
+
continue;
|
|
3179
|
+
}
|
|
3180
|
+
yield key;
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
async *whereStream(condition, limit, order = "asc") {
|
|
3184
|
+
const driverKey = this.getDriverKey(condition);
|
|
3185
|
+
if (!driverKey) return;
|
|
3186
|
+
const value = condition[driverKey];
|
|
3187
|
+
let startNode = await this.verifierStartNode[driverKey](value);
|
|
3188
|
+
let endNode = await this.verifierEndNode[driverKey](value);
|
|
3189
|
+
let direction = this.verifierDirection[driverKey];
|
|
3190
|
+
const comparator = this.verifierMap[driverKey];
|
|
3191
|
+
const earlyTerminate = this.verifierEarlyTerminate[driverKey];
|
|
3192
|
+
if (order === "desc") {
|
|
3193
|
+
startNode = endNode ?? await this.rightestNode();
|
|
3194
|
+
endNode = null;
|
|
3195
|
+
direction *= -1;
|
|
3196
|
+
}
|
|
3197
|
+
const generator = this.getPairsGenerator(
|
|
3198
|
+
value,
|
|
3199
|
+
startNode,
|
|
3200
|
+
endNode,
|
|
3201
|
+
comparator,
|
|
3016
3202
|
direction,
|
|
3017
3203
|
earlyTerminate
|
|
3018
3204
|
);
|
|
@@ -3053,26 +3239,32 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3053
3239
|
return map;
|
|
3054
3240
|
}
|
|
3055
3241
|
async insert(key, value) {
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3242
|
+
return this.writeLock(0, async () => {
|
|
3243
|
+
let before = await this.insertableNode(value);
|
|
3244
|
+
before = await this._insertAtLeaf(before, key, value);
|
|
3245
|
+
if (before.values.length === this.order) {
|
|
3246
|
+
let after = await this._createNode(
|
|
3247
|
+
true,
|
|
3248
|
+
[],
|
|
3249
|
+
[],
|
|
3250
|
+
before.parent,
|
|
3251
|
+
null,
|
|
3252
|
+
null
|
|
3253
|
+
);
|
|
3254
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
3255
|
+
after = this._cloneNode(after);
|
|
3256
|
+
after.values = before.values.slice(mid + 1);
|
|
3257
|
+
after.keys = before.keys.slice(mid + 1);
|
|
3258
|
+
before.values = before.values.slice(0, mid + 1);
|
|
3259
|
+
before.keys = before.keys.slice(0, mid + 1);
|
|
3260
|
+
await this._updateNode(before);
|
|
3261
|
+
await this._updateNode(after);
|
|
3262
|
+
await this._insertInParent(before, after.values[0], after);
|
|
3263
|
+
}
|
|
3264
|
+
});
|
|
3074
3265
|
}
|
|
3075
3266
|
async _deleteEntry(node, key) {
|
|
3267
|
+
node = this._cloneNode(node);
|
|
3076
3268
|
if (!node.leaf) {
|
|
3077
3269
|
let keyIndex = -1;
|
|
3078
3270
|
for (let i = 0, len = node.keys.length; i < len; i++) {
|
|
@@ -3091,7 +3283,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3091
3283
|
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
3092
3284
|
const keys = node.keys;
|
|
3093
3285
|
this._deleteNode(node);
|
|
3094
|
-
const newRoot = await this.getNode(keys[0]);
|
|
3286
|
+
const newRoot = this._cloneNode(await this.getNode(keys[0]));
|
|
3095
3287
|
newRoot.parent = null;
|
|
3096
3288
|
await this._updateNode(newRoot);
|
|
3097
3289
|
await this._writeHead({
|
|
@@ -3099,14 +3291,17 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3099
3291
|
order: this.order,
|
|
3100
3292
|
data: this.strategy.head.data
|
|
3101
3293
|
});
|
|
3102
|
-
return;
|
|
3294
|
+
return node;
|
|
3103
3295
|
} else if (this.rootId === node.id) {
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3296
|
+
await this._writeHead({
|
|
3297
|
+
root: node.id,
|
|
3298
|
+
order: this.order,
|
|
3299
|
+
data: this.strategy.head.data
|
|
3300
|
+
});
|
|
3301
|
+
return node;
|
|
3107
3302
|
} else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
|
|
3108
3303
|
if (node.parent === null) {
|
|
3109
|
-
return;
|
|
3304
|
+
return node;
|
|
3110
3305
|
}
|
|
3111
3306
|
let isPredecessor = false;
|
|
3112
3307
|
let parentNode = await this.getNode(node.parent);
|
|
@@ -3118,11 +3313,11 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3118
3313
|
const nKey = parentNode.keys[i];
|
|
3119
3314
|
if (nKey === node.id) {
|
|
3120
3315
|
if (i > 0) {
|
|
3121
|
-
prevNode = await this.getNode(parentNode.keys[i - 1]);
|
|
3316
|
+
prevNode = this._cloneNode(await this.getNode(parentNode.keys[i - 1]));
|
|
3122
3317
|
prevValue = parentNode.values[i - 1];
|
|
3123
3318
|
}
|
|
3124
3319
|
if (i < parentNode.keys.length - 1) {
|
|
3125
|
-
nextNode = await this.getNode(parentNode.keys[i + 1]);
|
|
3320
|
+
nextNode = this._cloneNode(await this.getNode(parentNode.keys[i + 1]));
|
|
3126
3321
|
postValue = parentNode.values[i];
|
|
3127
3322
|
}
|
|
3128
3323
|
}
|
|
@@ -3147,7 +3342,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3147
3342
|
}
|
|
3148
3343
|
}
|
|
3149
3344
|
if (!pointer) {
|
|
3150
|
-
return;
|
|
3345
|
+
return node;
|
|
3151
3346
|
}
|
|
3152
3347
|
if (node.values.length + pointer.values.length < this.order) {
|
|
3153
3348
|
if (!isPredecessor) {
|
|
@@ -3161,7 +3356,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3161
3356
|
} else {
|
|
3162
3357
|
pointer.next = node.next;
|
|
3163
3358
|
if (pointer.next) {
|
|
3164
|
-
const n = await this.getNode(pointer.next);
|
|
3359
|
+
const n = this._cloneNode(await this.getNode(pointer.next));
|
|
3165
3360
|
n.prev = pointer.id;
|
|
3166
3361
|
await this._updateNode(n);
|
|
3167
3362
|
}
|
|
@@ -3170,14 +3365,14 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3170
3365
|
if (!pointer.leaf) {
|
|
3171
3366
|
const keys = pointer.keys;
|
|
3172
3367
|
for (const key2 of keys) {
|
|
3173
|
-
const node2 = await this.getNode(key2);
|
|
3368
|
+
const node2 = this._cloneNode(await this.getNode(key2));
|
|
3174
3369
|
node2.parent = pointer.id;
|
|
3175
3370
|
await this._updateNode(node2);
|
|
3176
3371
|
}
|
|
3177
3372
|
}
|
|
3178
3373
|
this._deleteNode(node);
|
|
3179
3374
|
await this._updateNode(pointer);
|
|
3180
|
-
await this._deleteEntry(await this.getNode(node.parent), node.id);
|
|
3375
|
+
await this._deleteEntry(this._cloneNode(await this.getNode(node.parent)), node.id);
|
|
3181
3376
|
} else {
|
|
3182
3377
|
if (isPredecessor) {
|
|
3183
3378
|
let pointerPm;
|
|
@@ -3187,7 +3382,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3187
3382
|
pointerKm = pointer.values.splice(-1)[0];
|
|
3188
3383
|
node.keys = [pointerPm, ...node.keys];
|
|
3189
3384
|
node.values = [guess, ...node.values];
|
|
3190
|
-
parentNode = await this.getNode(node.parent);
|
|
3385
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3191
3386
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
3192
3387
|
if (nodeIndex > 0) {
|
|
3193
3388
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
@@ -3198,7 +3393,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3198
3393
|
pointerKm = pointer.values.splice(-1)[0];
|
|
3199
3394
|
node.keys = [pointerPm, ...node.keys];
|
|
3200
3395
|
node.values = [pointerKm, ...node.values];
|
|
3201
|
-
parentNode = await this.getNode(node.parent);
|
|
3396
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3202
3397
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
3203
3398
|
if (nodeIndex > 0) {
|
|
3204
3399
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
@@ -3215,7 +3410,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3215
3410
|
pointerK0 = pointer.values.splice(0, 1)[0];
|
|
3216
3411
|
node.keys = [...node.keys, pointerP0];
|
|
3217
3412
|
node.values = [...node.values, guess];
|
|
3218
|
-
parentNode = await this.getNode(node.parent);
|
|
3413
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3219
3414
|
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
3220
3415
|
if (pointerIndex > 0) {
|
|
3221
3416
|
parentNode.values[pointerIndex - 1] = pointerK0;
|
|
@@ -3226,7 +3421,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3226
3421
|
pointerK0 = pointer.values.splice(0, 1)[0];
|
|
3227
3422
|
node.keys = [...node.keys, pointerP0];
|
|
3228
3423
|
node.values = [...node.values, pointerK0];
|
|
3229
|
-
parentNode = await this.getNode(node.parent);
|
|
3424
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3230
3425
|
const pointerIndex = parentNode.keys.indexOf(pointer.id);
|
|
3231
3426
|
if (pointerIndex > 0) {
|
|
3232
3427
|
parentNode.values[pointerIndex - 1] = pointer.values[0];
|
|
@@ -3238,21 +3433,21 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3238
3433
|
}
|
|
3239
3434
|
if (!pointer.leaf) {
|
|
3240
3435
|
for (const key2 of pointer.keys) {
|
|
3241
|
-
const n = await this.getNode(key2);
|
|
3436
|
+
const n = this._cloneNode(await this.getNode(key2));
|
|
3242
3437
|
n.parent = pointer.id;
|
|
3243
3438
|
await this._updateNode(n);
|
|
3244
3439
|
}
|
|
3245
3440
|
}
|
|
3246
3441
|
if (!node.leaf) {
|
|
3247
3442
|
for (const key2 of node.keys) {
|
|
3248
|
-
const n = await this.getNode(key2);
|
|
3443
|
+
const n = this._cloneNode(await this.getNode(key2));
|
|
3249
3444
|
n.parent = node.id;
|
|
3250
3445
|
await this._updateNode(n);
|
|
3251
3446
|
}
|
|
3252
3447
|
}
|
|
3253
3448
|
if (!parentNode.leaf) {
|
|
3254
3449
|
for (const key2 of parentNode.keys) {
|
|
3255
|
-
const n = await this.getNode(key2);
|
|
3450
|
+
const n = this._cloneNode(await this.getNode(key2));
|
|
3256
3451
|
n.parent = parentNode.id;
|
|
3257
3452
|
await this._updateNode(n);
|
|
3258
3453
|
}
|
|
@@ -3261,37 +3456,42 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3261
3456
|
} else {
|
|
3262
3457
|
await this._updateNode(node);
|
|
3263
3458
|
}
|
|
3459
|
+
await this._updateNode(node);
|
|
3460
|
+
return node;
|
|
3264
3461
|
}
|
|
3265
3462
|
async delete(key, value) {
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
while (
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
const
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
keys.
|
|
3277
|
-
if (
|
|
3278
|
-
|
|
3279
|
-
|
|
3463
|
+
return this.writeLock(0, async () => {
|
|
3464
|
+
let node = await this.insertableNodeByPrimary(value);
|
|
3465
|
+
let found = false;
|
|
3466
|
+
node = this._cloneNode(node);
|
|
3467
|
+
while (true) {
|
|
3468
|
+
let i = node.values.length;
|
|
3469
|
+
while (i--) {
|
|
3470
|
+
const nValue = node.values[i];
|
|
3471
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
3472
|
+
const keys = node.keys[i];
|
|
3473
|
+
const keyIndex = keys.indexOf(key);
|
|
3474
|
+
if (keyIndex !== -1) {
|
|
3475
|
+
keys.splice(keyIndex, 1);
|
|
3476
|
+
if (keys.length === 0) {
|
|
3477
|
+
node.keys.splice(i, 1);
|
|
3478
|
+
node.values.splice(i, 1);
|
|
3479
|
+
}
|
|
3480
|
+
await this._updateNode(node);
|
|
3481
|
+
node = await this._deleteEntry(node, key);
|
|
3482
|
+
found = true;
|
|
3483
|
+
break;
|
|
3280
3484
|
}
|
|
3281
|
-
await this._updateNode(node);
|
|
3282
|
-
await this._deleteEntry(node, key);
|
|
3283
|
-
found = true;
|
|
3284
|
-
break;
|
|
3285
3485
|
}
|
|
3286
3486
|
}
|
|
3487
|
+
if (found) break;
|
|
3488
|
+
if (node.next) {
|
|
3489
|
+
node = await this.getNode(node.next);
|
|
3490
|
+
continue;
|
|
3491
|
+
}
|
|
3492
|
+
break;
|
|
3287
3493
|
}
|
|
3288
|
-
|
|
3289
|
-
if (node.next) {
|
|
3290
|
-
node = await this.getNode(node.next);
|
|
3291
|
-
continue;
|
|
3292
|
-
}
|
|
3293
|
-
break;
|
|
3294
|
-
}
|
|
3494
|
+
});
|
|
3295
3495
|
}
|
|
3296
3496
|
async getHeadData() {
|
|
3297
3497
|
const head = await this._readHead();
|
|
@@ -3302,447 +3502,185 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3302
3502
|
}
|
|
3303
3503
|
async setHeadData(data) {
|
|
3304
3504
|
const head = await this._readHead();
|
|
3305
|
-
if (head === null) {
|
|
3306
|
-
throw new Error("Head not found");
|
|
3307
|
-
}
|
|
3308
|
-
await this._writeHead({
|
|
3309
|
-
root: head.root,
|
|
3310
|
-
order: head.order,
|
|
3311
|
-
data
|
|
3312
|
-
});
|
|
3313
|
-
}
|
|
3314
|
-
async commit(label) {
|
|
3315
|
-
let result = await this.mvcc.commit(label);
|
|
3316
|
-
if (result.success) {
|
|
3317
|
-
result = await this.mvccRoot.commit(label);
|
|
3318
|
-
if (result.success && this.rootTx !== this) {
|
|
3319
|
-
this.rootTx.rootId = this.rootId;
|
|
3320
|
-
}
|
|
3321
|
-
if (result.success) {
|
|
3322
|
-
for (const r of result.created) {
|
|
3323
|
-
this.nodes.set(r.key, r.data);
|
|
3324
|
-
}
|
|
3325
|
-
for (const r of result.updated) {
|
|
3326
|
-
this.nodes.set(r.key, r.data);
|
|
3327
|
-
}
|
|
3328
|
-
for (const r of result.deleted) {
|
|
3329
|
-
this.nodes.delete(r.key);
|
|
3330
|
-
}
|
|
3331
|
-
}
|
|
3332
|
-
}
|
|
3333
|
-
return result;
|
|
3334
|
-
}
|
|
3335
|
-
rollback() {
|
|
3336
|
-
return this.mvcc.rollback();
|
|
3337
|
-
}
|
|
3338
|
-
};
|
|
3339
|
-
|
|
3340
|
-
// src/transaction/BPTreeMVCCStrategyAsync.ts
|
|
3341
|
-
var BPTreeMVCCStrategyAsync = class extends AsyncMVCCStrategy {
|
|
3342
|
-
constructor(strategy) {
|
|
3343
|
-
super();
|
|
3344
|
-
this.strategy = strategy;
|
|
3345
|
-
}
|
|
3346
|
-
async read(key) {
|
|
3347
|
-
if (key === "__HEAD__") {
|
|
3348
|
-
return await this.strategy.readHead();
|
|
3349
|
-
}
|
|
3350
|
-
return await this.strategy.read(key);
|
|
3351
|
-
}
|
|
3352
|
-
async write(key, value) {
|
|
3353
|
-
if (key === "__HEAD__") {
|
|
3354
|
-
await this.strategy.writeHead(value);
|
|
3355
|
-
} else {
|
|
3356
|
-
await this.strategy.write(key, value);
|
|
3357
|
-
}
|
|
3358
|
-
}
|
|
3359
|
-
async delete(key) {
|
|
3360
|
-
await this.strategy.delete(key);
|
|
3361
|
-
}
|
|
3362
|
-
async exists(key) {
|
|
3363
|
-
if (key === "__HEAD__") {
|
|
3364
|
-
return await this.strategy.readHead() !== null;
|
|
3365
|
-
}
|
|
3366
|
-
try {
|
|
3367
|
-
const node = await this.strategy.read(key);
|
|
3368
|
-
return node !== null && node !== void 0;
|
|
3369
|
-
} catch {
|
|
3370
|
-
return false;
|
|
3371
|
-
}
|
|
3372
|
-
}
|
|
3373
|
-
};
|
|
3374
|
-
|
|
3375
|
-
// src/BPTreeAsync.ts
|
|
3376
|
-
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3377
|
-
constructor(strategy, comparator, option) {
|
|
3378
|
-
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy));
|
|
3379
|
-
super(
|
|
3380
|
-
null,
|
|
3381
|
-
mvccRoot,
|
|
3382
|
-
mvccRoot,
|
|
3383
|
-
strategy,
|
|
3384
|
-
comparator,
|
|
3385
|
-
option
|
|
3386
|
-
);
|
|
3387
|
-
}
|
|
3388
|
-
/**
|
|
3389
|
-
* Creates a new asynchronous transaction.
|
|
3390
|
-
* @returns A new BPTreeAsyncTransaction.
|
|
3391
|
-
*/
|
|
3392
|
-
async createTransaction() {
|
|
3393
|
-
const nestedTx = await this.mvcc.createNested();
|
|
3394
|
-
const tx = new BPTreeAsyncTransaction(
|
|
3395
|
-
this,
|
|
3396
|
-
this.mvcc,
|
|
3397
|
-
nestedTx,
|
|
3398
|
-
this.strategy,
|
|
3399
|
-
this.comparator,
|
|
3400
|
-
this.option
|
|
3401
|
-
);
|
|
3402
|
-
await tx.init();
|
|
3403
|
-
return tx;
|
|
3404
|
-
}
|
|
3405
|
-
async insert(key, value) {
|
|
3406
|
-
const tx = await this.createTransaction();
|
|
3407
|
-
await tx.insert(key, value);
|
|
3408
|
-
const result = await tx.commit();
|
|
3409
|
-
if (!result.success) {
|
|
3410
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3411
|
-
}
|
|
3412
|
-
this.rootId = tx.getRootId();
|
|
3413
|
-
}
|
|
3414
|
-
async delete(key, value) {
|
|
3415
|
-
const tx = await this.createTransaction();
|
|
3416
|
-
await tx.delete(key, value);
|
|
3417
|
-
const result = await tx.commit();
|
|
3418
|
-
if (!result.success) {
|
|
3419
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3420
|
-
}
|
|
3421
|
-
this.rootId = tx.getRootId();
|
|
3422
|
-
}
|
|
3423
|
-
};
|
|
3424
|
-
|
|
3425
|
-
// src/base/SerializeStrategy.ts
|
|
3426
|
-
var SerializeStrategy = class {
|
|
3427
|
-
order;
|
|
3428
|
-
head;
|
|
3429
|
-
constructor(order) {
|
|
3430
|
-
this.order = order;
|
|
3431
|
-
this.head = {
|
|
3432
|
-
order,
|
|
3433
|
-
root: null,
|
|
3434
|
-
data: {}
|
|
3435
|
-
};
|
|
3436
|
-
}
|
|
3437
|
-
};
|
|
3438
|
-
|
|
3439
|
-
// src/SerializeStrategySync.ts
|
|
3440
|
-
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3441
|
-
getHeadData(key, defaultValue) {
|
|
3442
|
-
if (!Object.hasOwn(this.head.data, key)) {
|
|
3443
|
-
this.setHeadData(key, defaultValue);
|
|
3444
|
-
}
|
|
3445
|
-
return this.head.data[key];
|
|
3446
|
-
}
|
|
3447
|
-
setHeadData(key, data) {
|
|
3448
|
-
this.head.data[key] = data;
|
|
3449
|
-
this.writeHead(this.head);
|
|
3450
|
-
}
|
|
3451
|
-
autoIncrement(key, defaultValue) {
|
|
3452
|
-
const current = this.getHeadData(key, defaultValue);
|
|
3453
|
-
const next = current + 1;
|
|
3454
|
-
this.setHeadData(key, next);
|
|
3455
|
-
return current;
|
|
3456
|
-
}
|
|
3457
|
-
};
|
|
3458
|
-
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3459
|
-
node;
|
|
3460
|
-
constructor(order) {
|
|
3461
|
-
super(order);
|
|
3462
|
-
this.node = {};
|
|
3463
|
-
}
|
|
3464
|
-
id(isLeaf) {
|
|
3465
|
-
return this.autoIncrement("index", 1).toString();
|
|
3466
|
-
}
|
|
3467
|
-
read(id) {
|
|
3468
|
-
if (!Object.hasOwn(this.node, id)) {
|
|
3469
|
-
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3470
|
-
}
|
|
3471
|
-
const node = this.node[id];
|
|
3472
|
-
return JSON.parse(JSON.stringify(node));
|
|
3473
|
-
}
|
|
3474
|
-
write(id, node) {
|
|
3475
|
-
this.node[id] = node;
|
|
3476
|
-
}
|
|
3477
|
-
delete(id) {
|
|
3478
|
-
delete this.node[id];
|
|
3479
|
-
}
|
|
3480
|
-
readHead() {
|
|
3481
|
-
if (this.head.root === null) {
|
|
3482
|
-
return null;
|
|
3483
|
-
}
|
|
3484
|
-
return this.head;
|
|
3485
|
-
}
|
|
3486
|
-
writeHead(head) {
|
|
3487
|
-
this.head = head;
|
|
3488
|
-
}
|
|
3489
|
-
};
|
|
3490
|
-
|
|
3491
|
-
// node_modules/ryoiki/dist/esm/index.mjs
|
|
3492
|
-
var Ryoiki2 = class _Ryoiki2 {
|
|
3493
|
-
readings;
|
|
3494
|
-
writings;
|
|
3495
|
-
readQueue;
|
|
3496
|
-
writeQueue;
|
|
3497
|
-
static async CatchError(promise) {
|
|
3498
|
-
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
3499
|
-
}
|
|
3500
|
-
static IsRangeOverlap(a, b) {
|
|
3501
|
-
const [start1, end1] = a;
|
|
3502
|
-
const [start2, end2] = b;
|
|
3503
|
-
if (end1 <= start2 || end2 <= start1) {
|
|
3504
|
-
return false;
|
|
3505
|
-
}
|
|
3506
|
-
return true;
|
|
3507
|
-
}
|
|
3508
|
-
static ERR_ALREADY_EXISTS(lockId) {
|
|
3509
|
-
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
3510
|
-
}
|
|
3511
|
-
static ERR_NOT_EXISTS(lockId) {
|
|
3512
|
-
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
3513
|
-
}
|
|
3514
|
-
static ERR_TIMEOUT(lockId, timeout) {
|
|
3515
|
-
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
3516
|
-
}
|
|
3517
|
-
/**
|
|
3518
|
-
* Constructs a new instance of the Ryoiki class.
|
|
3519
|
-
*/
|
|
3520
|
-
constructor() {
|
|
3521
|
-
this.readings = /* @__PURE__ */ new Map();
|
|
3522
|
-
this.writings = /* @__PURE__ */ new Map();
|
|
3523
|
-
this.readQueue = /* @__PURE__ */ new Map();
|
|
3524
|
-
this.writeQueue = /* @__PURE__ */ new Map();
|
|
3525
|
-
}
|
|
3526
|
-
/**
|
|
3527
|
-
* Creates a range based on a start value and length.
|
|
3528
|
-
* @param start - The starting value of the range.
|
|
3529
|
-
* @param length - The length of the range.
|
|
3530
|
-
* @returns A range tuple [start, start + length].
|
|
3531
|
-
*/
|
|
3532
|
-
range(start, length) {
|
|
3533
|
-
return [start, start + length];
|
|
3534
|
-
}
|
|
3535
|
-
rangeOverlapping(tasks, range) {
|
|
3536
|
-
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
3537
|
-
}
|
|
3538
|
-
isSameRange(a, b) {
|
|
3539
|
-
const [a1, a2] = a;
|
|
3540
|
-
const [b1, b2] = b;
|
|
3541
|
-
return a1 === b1 && a2 === b2;
|
|
3542
|
-
}
|
|
3543
|
-
fetchUnitAndRun(queue, workspaces) {
|
|
3544
|
-
for (const [id, unit] of queue) {
|
|
3545
|
-
if (!unit.condition()) {
|
|
3546
|
-
continue;
|
|
3547
|
-
}
|
|
3548
|
-
this._alloc(queue, workspaces, id);
|
|
3549
|
-
}
|
|
3550
|
-
}
|
|
3551
|
-
_handleOverload(args, handlers, argPatterns) {
|
|
3552
|
-
for (const [key, pattern] of Object.entries(argPatterns)) {
|
|
3553
|
-
if (this._matchArgs(args, pattern)) {
|
|
3554
|
-
return handlers[key](...args);
|
|
3555
|
-
}
|
|
3556
|
-
}
|
|
3557
|
-
throw new Error("Invalid arguments");
|
|
3558
|
-
}
|
|
3559
|
-
_matchArgs(args, pattern) {
|
|
3560
|
-
return args.every((arg, index) => {
|
|
3561
|
-
const expectedType = pattern[index];
|
|
3562
|
-
if (expectedType === void 0) return typeof arg === "undefined";
|
|
3563
|
-
if (expectedType === Function) return typeof arg === "function";
|
|
3564
|
-
if (expectedType === Number) return typeof arg === "number";
|
|
3565
|
-
if (expectedType === Array) return Array.isArray(arg);
|
|
3566
|
-
return false;
|
|
3567
|
-
});
|
|
3568
|
-
}
|
|
3569
|
-
_createRandomId() {
|
|
3570
|
-
const timestamp = Date.now().toString(36);
|
|
3571
|
-
const random = Math.random().toString(36).substring(2);
|
|
3572
|
-
return `${timestamp}${random}`;
|
|
3573
|
-
}
|
|
3574
|
-
_alloc(queue, workspaces, lockId) {
|
|
3575
|
-
const unit = queue.get(lockId);
|
|
3576
|
-
if (!unit) {
|
|
3577
|
-
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
3578
|
-
}
|
|
3579
|
-
workspaces.set(lockId, unit);
|
|
3580
|
-
queue.delete(lockId);
|
|
3581
|
-
unit.alloc();
|
|
3582
|
-
}
|
|
3583
|
-
_free(workspaces, lockId) {
|
|
3584
|
-
const unit = workspaces.get(lockId);
|
|
3585
|
-
if (!unit) {
|
|
3586
|
-
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
3587
|
-
}
|
|
3588
|
-
workspaces.delete(lockId);
|
|
3589
|
-
unit.free();
|
|
3590
|
-
}
|
|
3591
|
-
_lock(queue, range, timeout, task, condition) {
|
|
3592
|
-
return new Promise((resolve, reject) => {
|
|
3593
|
-
let timeoutId = null;
|
|
3594
|
-
if (timeout >= 0) {
|
|
3595
|
-
timeoutId = setTimeout(() => {
|
|
3596
|
-
reject(_Ryoiki2.ERR_TIMEOUT(id, timeout));
|
|
3597
|
-
}, timeout);
|
|
3598
|
-
}
|
|
3599
|
-
const id = this._createRandomId();
|
|
3600
|
-
const alloc = async () => {
|
|
3601
|
-
if (timeoutId !== null) {
|
|
3602
|
-
clearTimeout(timeoutId);
|
|
3603
|
-
}
|
|
3604
|
-
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
3605
|
-
if (err) reject(err);
|
|
3606
|
-
else resolve(v);
|
|
3607
|
-
};
|
|
3608
|
-
const fetch = () => {
|
|
3609
|
-
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
3610
|
-
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
3611
|
-
};
|
|
3612
|
-
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
3613
|
-
fetch();
|
|
3505
|
+
if (head === null) {
|
|
3506
|
+
throw new Error("Head not found");
|
|
3507
|
+
}
|
|
3508
|
+
await this._writeHead({
|
|
3509
|
+
root: head.root,
|
|
3510
|
+
order: head.order,
|
|
3511
|
+
data
|
|
3614
3512
|
});
|
|
3615
3513
|
}
|
|
3616
|
-
|
|
3617
|
-
let
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3514
|
+
async commit(label) {
|
|
3515
|
+
let result = await this.mvcc.commit(label);
|
|
3516
|
+
if (result.success) {
|
|
3517
|
+
const isRootTx = this.rootTx === this;
|
|
3518
|
+
if (!isRootTx) {
|
|
3519
|
+
result = await this.rootTx.commit(label);
|
|
3520
|
+
if (result.success) {
|
|
3521
|
+
this.rootTx.rootId = this.rootId;
|
|
3522
|
+
}
|
|
3622
3523
|
}
|
|
3623
3524
|
}
|
|
3624
|
-
return
|
|
3525
|
+
return result;
|
|
3625
3526
|
}
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
* @param range The range to check for active read locks.
|
|
3629
|
-
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
3630
|
-
*/
|
|
3631
|
-
isReading(range) {
|
|
3632
|
-
return this._checkWorking(range, this.readings);
|
|
3527
|
+
async rollback() {
|
|
3528
|
+
return this.mvcc.rollback();
|
|
3633
3529
|
}
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3530
|
+
};
|
|
3531
|
+
|
|
3532
|
+
// src/transaction/BPTreeMVCCStrategyAsync.ts
|
|
3533
|
+
var BPTreeMVCCStrategyAsync = class extends AsyncMVCCStrategy {
|
|
3534
|
+
constructor(strategy) {
|
|
3535
|
+
super();
|
|
3536
|
+
this.strategy = strategy;
|
|
3641
3537
|
}
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
canRead(range) {
|
|
3648
|
-
const writing = this.isWriting(range);
|
|
3649
|
-
return !writing;
|
|
3538
|
+
async read(key) {
|
|
3539
|
+
if (key === "__HEAD__") {
|
|
3540
|
+
return await this.strategy.readHead();
|
|
3541
|
+
}
|
|
3542
|
+
return await this.strategy.read(key);
|
|
3650
3543
|
}
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
const reading = this.isReading(range);
|
|
3658
|
-
const writing = this.isWriting(range);
|
|
3659
|
-
return !reading && !writing;
|
|
3544
|
+
async write(key, value) {
|
|
3545
|
+
if (key === "__HEAD__") {
|
|
3546
|
+
await this.strategy.writeHead(value);
|
|
3547
|
+
} else {
|
|
3548
|
+
await this.strategy.write(key, value);
|
|
3549
|
+
}
|
|
3660
3550
|
}
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
range,
|
|
3691
|
-
timeout,
|
|
3692
|
-
task,
|
|
3693
|
-
() => !this.rangeOverlapping(this.writings, range)
|
|
3551
|
+
async delete(key) {
|
|
3552
|
+
await this.strategy.delete(key);
|
|
3553
|
+
}
|
|
3554
|
+
async exists(key) {
|
|
3555
|
+
if (key === "__HEAD__") {
|
|
3556
|
+
return await this.strategy.readHead() !== null;
|
|
3557
|
+
}
|
|
3558
|
+
try {
|
|
3559
|
+
const node = await this.strategy.read(key);
|
|
3560
|
+
return node !== null && node !== void 0;
|
|
3561
|
+
} catch {
|
|
3562
|
+
return false;
|
|
3563
|
+
}
|
|
3564
|
+
}
|
|
3565
|
+
};
|
|
3566
|
+
|
|
3567
|
+
// src/BPTreeAsync.ts
|
|
3568
|
+
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3569
|
+
constructor(strategy, comparator, option) {
|
|
3570
|
+
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy), {
|
|
3571
|
+
cacheCapacity: option?.capacity ?? void 0
|
|
3572
|
+
});
|
|
3573
|
+
super(
|
|
3574
|
+
null,
|
|
3575
|
+
mvccRoot,
|
|
3576
|
+
mvccRoot,
|
|
3577
|
+
strategy,
|
|
3578
|
+
comparator,
|
|
3579
|
+
option
|
|
3694
3580
|
);
|
|
3695
3581
|
}
|
|
3696
3582
|
/**
|
|
3697
|
-
*
|
|
3698
|
-
* @
|
|
3699
|
-
* @param arg0 - Either a range or a task callback.
|
|
3700
|
-
* If a range is provided, the task is the second argument.
|
|
3701
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
3702
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
3703
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
3704
|
-
* If this value is not provided, no timeout will be set.
|
|
3705
|
-
* @returns A promise resolving to the result of the task execution.
|
|
3583
|
+
* Creates a new asynchronous transaction.
|
|
3584
|
+
* @returns A new BPTreeAsyncTransaction.
|
|
3706
3585
|
*/
|
|
3707
|
-
|
|
3708
|
-
const
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
{
|
|
3717
|
-
task: [Function],
|
|
3718
|
-
taskTimeout: [Function, Number],
|
|
3719
|
-
rangeTask: [Array, Function],
|
|
3720
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
3721
|
-
}
|
|
3586
|
+
async createTransaction() {
|
|
3587
|
+
const nestedTx = this.mvcc.createNested();
|
|
3588
|
+
const tx = new BPTreeAsyncTransaction(
|
|
3589
|
+
this,
|
|
3590
|
+
this.mvcc,
|
|
3591
|
+
nestedTx,
|
|
3592
|
+
this.strategy,
|
|
3593
|
+
this.comparator,
|
|
3594
|
+
this.option
|
|
3722
3595
|
);
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
()
|
|
3729
|
-
|
|
3596
|
+
await tx._initInternal();
|
|
3597
|
+
return tx;
|
|
3598
|
+
}
|
|
3599
|
+
async insert(key, value) {
|
|
3600
|
+
return this.writeLock(1, async () => {
|
|
3601
|
+
const tx = await this.createTransaction();
|
|
3602
|
+
await tx.insert(key, value);
|
|
3603
|
+
const result = await tx.commit();
|
|
3604
|
+
if (!result.success) {
|
|
3605
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3730
3606
|
}
|
|
3731
|
-
);
|
|
3607
|
+
});
|
|
3732
3608
|
}
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3609
|
+
async delete(key, value) {
|
|
3610
|
+
return this.writeLock(1, async () => {
|
|
3611
|
+
const tx = await this.createTransaction();
|
|
3612
|
+
await tx.delete(key, value);
|
|
3613
|
+
const result = await tx.commit();
|
|
3614
|
+
if (!result.success) {
|
|
3615
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3616
|
+
}
|
|
3617
|
+
});
|
|
3739
3618
|
}
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3619
|
+
};
|
|
3620
|
+
|
|
3621
|
+
// src/base/SerializeStrategy.ts
|
|
3622
|
+
var SerializeStrategy = class {
|
|
3623
|
+
order;
|
|
3624
|
+
head;
|
|
3625
|
+
constructor(order) {
|
|
3626
|
+
this.order = order;
|
|
3627
|
+
this.head = {
|
|
3628
|
+
order,
|
|
3629
|
+
root: null,
|
|
3630
|
+
data: {}
|
|
3631
|
+
};
|
|
3632
|
+
}
|
|
3633
|
+
};
|
|
3634
|
+
|
|
3635
|
+
// src/SerializeStrategySync.ts
|
|
3636
|
+
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3637
|
+
getHeadData(key, defaultValue) {
|
|
3638
|
+
if (!Object.hasOwn(this.head.data, key)) {
|
|
3639
|
+
this.setHeadData(key, defaultValue);
|
|
3640
|
+
}
|
|
3641
|
+
return this.head.data[key];
|
|
3642
|
+
}
|
|
3643
|
+
setHeadData(key, data) {
|
|
3644
|
+
this.head.data[key] = data;
|
|
3645
|
+
this.writeHead(this.head);
|
|
3646
|
+
}
|
|
3647
|
+
autoIncrement(key, defaultValue) {
|
|
3648
|
+
const current = this.getHeadData(key, defaultValue);
|
|
3649
|
+
const next = current + 1;
|
|
3650
|
+
this.setHeadData(key, next);
|
|
3651
|
+
return current;
|
|
3652
|
+
}
|
|
3653
|
+
};
|
|
3654
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3655
|
+
node;
|
|
3656
|
+
constructor(order) {
|
|
3657
|
+
super(order);
|
|
3658
|
+
this.node = {};
|
|
3659
|
+
}
|
|
3660
|
+
id(isLeaf) {
|
|
3661
|
+
return this.autoIncrement("index", 1).toString();
|
|
3662
|
+
}
|
|
3663
|
+
read(id) {
|
|
3664
|
+
if (!Object.hasOwn(this.node, id)) {
|
|
3665
|
+
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3666
|
+
}
|
|
3667
|
+
const node = this.node[id];
|
|
3668
|
+
return JSON.parse(JSON.stringify(node));
|
|
3669
|
+
}
|
|
3670
|
+
write(id, node) {
|
|
3671
|
+
this.node[id] = node;
|
|
3672
|
+
}
|
|
3673
|
+
delete(id) {
|
|
3674
|
+
delete this.node[id];
|
|
3675
|
+
}
|
|
3676
|
+
readHead() {
|
|
3677
|
+
if (this.head.root === null) {
|
|
3678
|
+
return null;
|
|
3679
|
+
}
|
|
3680
|
+
return this.head;
|
|
3681
|
+
}
|
|
3682
|
+
writeHead(head) {
|
|
3683
|
+
this.head = head;
|
|
3746
3684
|
}
|
|
3747
3685
|
};
|
|
3748
3686
|
|