dataply 0.0.21 → 0.0.22-alpha.2
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/dist/cjs/index.js +1819 -1597
- package/package.json +4 -4
package/dist/cjs/index.js
CHANGED
|
@@ -38,7 +38,7 @@ __export(src_exports, {
|
|
|
38
38
|
BPTreeSyncTransaction: () => BPTreeSyncTransaction,
|
|
39
39
|
BitmapPageManager: () => BitmapPageManager,
|
|
40
40
|
CacheEntanglementAsync: () => CacheEntanglementAsync,
|
|
41
|
-
CacheEntanglementSync: () =>
|
|
41
|
+
CacheEntanglementSync: () => CacheEntanglementSync,
|
|
42
42
|
DataPageManager: () => DataPageManager,
|
|
43
43
|
Dataply: () => Dataply,
|
|
44
44
|
DataplyAPI: () => DataplyAPI,
|
|
@@ -125,6 +125,42 @@ var StringComparator = class extends ValueComparator {
|
|
|
125
125
|
};
|
|
126
126
|
var MVCCStrategy = class {
|
|
127
127
|
};
|
|
128
|
+
var LRUMap = class {
|
|
129
|
+
cache = /* @__PURE__ */ new Map();
|
|
130
|
+
capacity;
|
|
131
|
+
constructor(capacity) {
|
|
132
|
+
this.capacity = capacity;
|
|
133
|
+
}
|
|
134
|
+
get(key) {
|
|
135
|
+
if (!this.cache.has(key)) return void 0;
|
|
136
|
+
const value = this.cache.get(key);
|
|
137
|
+
this.cache.delete(key);
|
|
138
|
+
this.cache.set(key, value);
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
set(key, value) {
|
|
142
|
+
if (this.cache.has(key)) {
|
|
143
|
+
this.cache.delete(key);
|
|
144
|
+
} else if (this.cache.size >= this.capacity) {
|
|
145
|
+
const oldestKey = this.cache.keys().next().value;
|
|
146
|
+
if (oldestKey !== void 0) this.cache.delete(oldestKey);
|
|
147
|
+
}
|
|
148
|
+
this.cache.set(key, value);
|
|
149
|
+
return this;
|
|
150
|
+
}
|
|
151
|
+
has(key) {
|
|
152
|
+
return this.cache.has(key);
|
|
153
|
+
}
|
|
154
|
+
delete(key) {
|
|
155
|
+
return this.cache.delete(key);
|
|
156
|
+
}
|
|
157
|
+
clear() {
|
|
158
|
+
this.cache.clear();
|
|
159
|
+
}
|
|
160
|
+
get size() {
|
|
161
|
+
return this.cache.size;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
128
164
|
var MVCCTransaction = class {
|
|
129
165
|
committed;
|
|
130
166
|
snapshotVersion;
|
|
@@ -132,11 +168,11 @@ var MVCCTransaction = class {
|
|
|
132
168
|
writeBuffer;
|
|
133
169
|
deleteBuffer;
|
|
134
170
|
createdKeys;
|
|
135
|
-
// create()로 생성된 키 추적
|
|
136
171
|
deletedValues;
|
|
137
172
|
// delete 시 삭제 전 값 저장
|
|
138
173
|
originallyExisted;
|
|
139
174
|
// 트랜잭션 시작 시점에 디스크에 존재했던 키 (deleted 결과 필터링용)
|
|
175
|
+
bufferHistory = /* @__PURE__ */ new Map();
|
|
140
176
|
// Nested Transaction Properties
|
|
141
177
|
parent;
|
|
142
178
|
localVersion;
|
|
@@ -150,7 +186,8 @@ var MVCCTransaction = class {
|
|
|
150
186
|
versionIndex = /* @__PURE__ */ new Map();
|
|
151
187
|
deletedCache = /* @__PURE__ */ new Map();
|
|
152
188
|
activeTransactions = /* @__PURE__ */ new Set();
|
|
153
|
-
|
|
189
|
+
diskCache;
|
|
190
|
+
constructor(strategy, options, parent, snapshotVersion) {
|
|
154
191
|
this.snapshotVersion = snapshotVersion ?? 0;
|
|
155
192
|
this.writeBuffer = /* @__PURE__ */ new Map();
|
|
156
193
|
this.deleteBuffer = /* @__PURE__ */ new Set();
|
|
@@ -165,6 +202,7 @@ var MVCCTransaction = class {
|
|
|
165
202
|
this.snapshotLocalVersion = parent.localVersion;
|
|
166
203
|
this.strategy = void 0;
|
|
167
204
|
this.root = parent.root;
|
|
205
|
+
this.diskCache = parent.diskCache;
|
|
168
206
|
} else {
|
|
169
207
|
if (!strategy) throw new Error("Root Transaction must get Strategy");
|
|
170
208
|
this.strategy = strategy;
|
|
@@ -172,8 +210,13 @@ var MVCCTransaction = class {
|
|
|
172
210
|
this.localVersion = 0;
|
|
173
211
|
this.snapshotLocalVersion = 0;
|
|
174
212
|
this.root = this;
|
|
213
|
+
this.diskCache = new LRUMap(options?.cacheCapacity ?? 1e3);
|
|
175
214
|
}
|
|
176
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Checks if the transaction is a root transaction.
|
|
218
|
+
* @returns True if the transaction is a root transaction, false otherwise.
|
|
219
|
+
*/
|
|
177
220
|
isRoot() {
|
|
178
221
|
return !this.parent;
|
|
179
222
|
}
|
|
@@ -190,27 +233,96 @@ var MVCCTransaction = class {
|
|
|
190
233
|
}
|
|
191
234
|
return false;
|
|
192
235
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
236
|
+
/**
|
|
237
|
+
* Checks if a key was written in this transaction.
|
|
238
|
+
* @param key The key to check.
|
|
239
|
+
* @returns True if the key was written in this transaction, false otherwise.
|
|
240
|
+
*/
|
|
241
|
+
isWrote(key) {
|
|
242
|
+
return this.writeBuffer.has(key);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Checks if a key was deleted in this transaction.
|
|
246
|
+
* @param key The key to check.
|
|
247
|
+
* @returns True if the key was deleted in this transaction, false otherwise.
|
|
248
|
+
*/
|
|
249
|
+
isDeleted(key) {
|
|
250
|
+
return this.deleteBuffer.has(key);
|
|
251
|
+
}
|
|
252
|
+
_recordHistory(key) {
|
|
253
|
+
const existsInWriteBuffer = this.writeBuffer.has(key);
|
|
254
|
+
const existsInDeleteBuffer = this.deleteBuffer.has(key);
|
|
255
|
+
const currentVer = this.keyVersions.get(key);
|
|
256
|
+
if (currentVer !== void 0) {
|
|
257
|
+
if (!this.bufferHistory.has(key)) this.bufferHistory.set(key, []);
|
|
258
|
+
this.bufferHistory.get(key).push({
|
|
259
|
+
value: existsInWriteBuffer ? this.writeBuffer.get(key) : this.deletedValues.get(key) ?? null,
|
|
260
|
+
exists: existsInWriteBuffer || !existsInDeleteBuffer,
|
|
261
|
+
version: currentVer
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* BINARY SEARCH HELPER: Finds the index of the last element in the array
|
|
267
|
+
* where item[key] <= target. Assumes the array is sorted by 'key' ascending.
|
|
268
|
+
*/
|
|
269
|
+
_findLastLE(array, target, property) {
|
|
270
|
+
let left = 0;
|
|
271
|
+
let right = array.length - 1;
|
|
272
|
+
let result = -1;
|
|
273
|
+
while (left <= right) {
|
|
274
|
+
const mid = left + right >> 1;
|
|
275
|
+
if (array[mid][property] <= target) {
|
|
276
|
+
result = mid;
|
|
277
|
+
left = mid + 1;
|
|
278
|
+
} else {
|
|
279
|
+
right = mid - 1;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return result;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* BINARY SEARCH HELPER: Finds the index of the element in the array
|
|
286
|
+
* where item[key] === target. Assumes the array is sorted by 'key' ascending.
|
|
287
|
+
*/
|
|
288
|
+
_findExact(array, target, property) {
|
|
289
|
+
let left = 0;
|
|
290
|
+
let right = array.length - 1;
|
|
291
|
+
while (left <= right) {
|
|
292
|
+
const mid = left + right >> 1;
|
|
293
|
+
const val = array[mid][property];
|
|
294
|
+
if (val === target) return mid;
|
|
295
|
+
if (val < target) left = mid + 1;
|
|
296
|
+
else right = mid - 1;
|
|
297
|
+
}
|
|
298
|
+
return -1;
|
|
299
|
+
}
|
|
300
|
+
_bufferCreate(key, value, version) {
|
|
301
|
+
if (version === void 0) this.localVersion++;
|
|
302
|
+
const targetVersion = version ?? this.localVersion;
|
|
303
|
+
this._recordHistory(key);
|
|
196
304
|
this.writeBuffer.set(key, value);
|
|
197
305
|
this.createdKeys.add(key);
|
|
198
306
|
this.deleteBuffer.delete(key);
|
|
199
307
|
this.originallyExisted.delete(key);
|
|
200
|
-
this.keyVersions.set(key,
|
|
308
|
+
this.keyVersions.set(key, targetVersion);
|
|
201
309
|
}
|
|
202
|
-
_bufferWrite(key, value) {
|
|
203
|
-
this.localVersion++;
|
|
310
|
+
_bufferWrite(key, value, version) {
|
|
311
|
+
if (version === void 0) this.localVersion++;
|
|
312
|
+
const targetVersion = version ?? this.localVersion;
|
|
313
|
+
this._recordHistory(key);
|
|
204
314
|
this.writeBuffer.set(key, value);
|
|
205
315
|
this.deleteBuffer.delete(key);
|
|
206
|
-
this.keyVersions.set(key,
|
|
316
|
+
this.keyVersions.set(key, targetVersion);
|
|
207
317
|
}
|
|
208
|
-
_bufferDelete(key) {
|
|
209
|
-
this.localVersion++;
|
|
318
|
+
_bufferDelete(key, version) {
|
|
319
|
+
if (version === void 0) this.localVersion++;
|
|
320
|
+
const targetVersion = version ?? this.localVersion;
|
|
321
|
+
this._recordHistory(key);
|
|
210
322
|
this.deleteBuffer.add(key);
|
|
211
323
|
this.writeBuffer.delete(key);
|
|
212
324
|
this.createdKeys.delete(key);
|
|
213
|
-
this.keyVersions.set(key,
|
|
325
|
+
this.keyVersions.set(key, targetVersion);
|
|
214
326
|
}
|
|
215
327
|
/**
|
|
216
328
|
* Returns the entries that will be created, updated, and deleted by this transaction.
|
|
@@ -234,7 +346,11 @@ var MVCCTransaction = class {
|
|
|
234
346
|
deleted.push({ key, data });
|
|
235
347
|
}
|
|
236
348
|
}
|
|
237
|
-
return {
|
|
349
|
+
return {
|
|
350
|
+
created,
|
|
351
|
+
updated,
|
|
352
|
+
deleted
|
|
353
|
+
};
|
|
238
354
|
}
|
|
239
355
|
/**
|
|
240
356
|
* Rolls back the transaction.
|
|
@@ -252,7 +368,12 @@ var MVCCTransaction = class {
|
|
|
252
368
|
if (this.root !== this) {
|
|
253
369
|
this.root.activeTransactions.delete(this);
|
|
254
370
|
}
|
|
255
|
-
return {
|
|
371
|
+
return {
|
|
372
|
+
success: true,
|
|
373
|
+
created,
|
|
374
|
+
updated,
|
|
375
|
+
deleted
|
|
376
|
+
};
|
|
256
377
|
}
|
|
257
378
|
/**
|
|
258
379
|
* Cleans up both deletedCache and versionIndex based on minActiveVersion.
|
|
@@ -287,7 +408,9 @@ var MVCCTransaction = class {
|
|
|
287
408
|
break;
|
|
288
409
|
}
|
|
289
410
|
}
|
|
290
|
-
if (latestInSnapshotIdx
|
|
411
|
+
if (latestInSnapshotIdx === versions.length - 1) {
|
|
412
|
+
this.versionIndex.delete(key);
|
|
413
|
+
} else if (latestInSnapshotIdx > 0) {
|
|
291
414
|
versions.splice(0, latestInSnapshotIdx);
|
|
292
415
|
}
|
|
293
416
|
}
|
|
@@ -336,7 +459,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
336
459
|
createNested() {
|
|
337
460
|
if (this.committed) throw new Error("Transaction already committed");
|
|
338
461
|
const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
|
|
339
|
-
const child = new _SyncMVCCTransaction(void 0, this, childVersion);
|
|
462
|
+
const child = new _SyncMVCCTransaction(void 0, void 0, this, childVersion);
|
|
340
463
|
this.root.activeTransactions.add(child);
|
|
341
464
|
return child;
|
|
342
465
|
}
|
|
@@ -344,32 +467,77 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
344
467
|
if (this.committed) throw new Error("Transaction already committed");
|
|
345
468
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
346
469
|
if (this.deleteBuffer.has(key)) return null;
|
|
347
|
-
|
|
470
|
+
if (this.parent) {
|
|
471
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
472
|
+
}
|
|
473
|
+
return this._diskRead(key, this.snapshotVersion);
|
|
348
474
|
}
|
|
349
475
|
exists(key) {
|
|
350
476
|
if (this.committed) throw new Error("Transaction already committed");
|
|
351
477
|
if (this.deleteBuffer.has(key)) return false;
|
|
352
478
|
if (this.writeBuffer.has(key)) return true;
|
|
353
|
-
|
|
479
|
+
if (this.parent) {
|
|
480
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
481
|
+
}
|
|
482
|
+
return this._diskExists(key, this.snapshotVersion);
|
|
354
483
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
484
|
+
_existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
485
|
+
let current = this;
|
|
486
|
+
let slVer = snapshotLocalVersion;
|
|
487
|
+
while (current) {
|
|
488
|
+
if (current.writeBuffer.has(key)) {
|
|
489
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
490
|
+
if (slVer === void 0 || keyModVersion <= slVer) return true;
|
|
491
|
+
}
|
|
492
|
+
if (current.deleteBuffer.has(key)) {
|
|
493
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
494
|
+
if (slVer === void 0 || keyModVersion <= slVer) return false;
|
|
495
|
+
}
|
|
496
|
+
const history = current.bufferHistory.get(key);
|
|
497
|
+
if (history && slVer !== void 0) {
|
|
498
|
+
const idx = current._findLastLE(history, slVer, "version");
|
|
499
|
+
if (idx >= 0) return history[idx].exists;
|
|
500
|
+
}
|
|
501
|
+
if (current.parent) {
|
|
502
|
+
slVer = current.snapshotLocalVersion;
|
|
503
|
+
current = current.parent;
|
|
504
|
+
} else {
|
|
505
|
+
return current._diskExists(key, snapshotVersion);
|
|
360
506
|
}
|
|
361
507
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
_readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
511
|
+
let current = this;
|
|
512
|
+
let slVer = snapshotLocalVersion;
|
|
513
|
+
while (current) {
|
|
514
|
+
if (current.writeBuffer.has(key)) {
|
|
515
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
516
|
+
if (slVer === void 0 || keyModVersion <= slVer) {
|
|
517
|
+
return current.writeBuffer.get(key);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (current.deleteBuffer.has(key)) {
|
|
521
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
522
|
+
if (slVer === void 0 || keyModVersion <= slVer) {
|
|
523
|
+
return null;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
const history = current.bufferHistory.get(key);
|
|
527
|
+
if (history && slVer !== void 0) {
|
|
528
|
+
const idx = current._findLastLE(history, slVer, "version");
|
|
529
|
+
if (idx >= 0) {
|
|
530
|
+
return history[idx].exists ? history[idx].value : null;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (current.parent) {
|
|
534
|
+
slVer = current.snapshotLocalVersion;
|
|
535
|
+
current = current.parent;
|
|
536
|
+
} else {
|
|
537
|
+
return current._diskRead(key, snapshotVersion);
|
|
366
538
|
}
|
|
367
539
|
}
|
|
368
|
-
|
|
369
|
-
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
370
|
-
} else {
|
|
371
|
-
return this._diskRead(key, snapshotVersion);
|
|
372
|
-
}
|
|
540
|
+
return null;
|
|
373
541
|
}
|
|
374
542
|
commit(label) {
|
|
375
543
|
const { created, updated, deleted } = this.getResultEntries();
|
|
@@ -429,6 +597,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
429
597
|
this.deletedValues.clear();
|
|
430
598
|
this.originallyExisted.clear();
|
|
431
599
|
this.keyVersions.clear();
|
|
600
|
+
this.bufferHistory.clear();
|
|
432
601
|
this.localVersion = 0;
|
|
433
602
|
this.snapshotVersion = this.version;
|
|
434
603
|
}
|
|
@@ -469,32 +638,22 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
469
638
|
};
|
|
470
639
|
}
|
|
471
640
|
}
|
|
472
|
-
const
|
|
473
|
-
for (const key of child.writeBuffer
|
|
474
|
-
|
|
475
|
-
this.
|
|
476
|
-
this.
|
|
477
|
-
if (child.createdKeys.has(key)) {
|
|
478
|
-
this.createdKeys.add(key);
|
|
479
|
-
}
|
|
641
|
+
const mergeVersion = ++this.localVersion;
|
|
642
|
+
for (const [key, value] of child.writeBuffer) {
|
|
643
|
+
const wasCreated = child.createdKeys.has(key);
|
|
644
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
645
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
480
646
|
}
|
|
481
647
|
for (const key of child.deleteBuffer) {
|
|
482
|
-
this.deleteBuffer.add(key);
|
|
483
|
-
this.writeBuffer.delete(key);
|
|
484
|
-
this.createdKeys.delete(key);
|
|
485
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
486
648
|
const deletedValue = child.deletedValues.get(key);
|
|
487
|
-
if (deletedValue !== void 0)
|
|
488
|
-
|
|
489
|
-
}
|
|
490
|
-
if (child.originallyExisted.has(key)) {
|
|
649
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
650
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
491
651
|
this.originallyExisted.add(key);
|
|
492
652
|
}
|
|
653
|
+
this._bufferDelete(key, mergeVersion);
|
|
493
654
|
}
|
|
494
|
-
this.localVersion = newLocalVersion;
|
|
495
655
|
this.root.activeTransactions.delete(child);
|
|
496
656
|
} else {
|
|
497
|
-
const newVersion = this.version + 1;
|
|
498
657
|
if (child !== this) {
|
|
499
658
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
500
659
|
for (const key of modifiedKeys) {
|
|
@@ -512,50 +671,57 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
512
671
|
};
|
|
513
672
|
}
|
|
514
673
|
}
|
|
674
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
675
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
676
|
+
return {
|
|
677
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
678
|
+
conflict: {
|
|
679
|
+
key,
|
|
680
|
+
parent: this.read(key),
|
|
681
|
+
child: child.read(key)
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
}
|
|
515
685
|
}
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
for (const key of child.deleteBuffer) {
|
|
525
|
-
this.deleteBuffer.add(key);
|
|
526
|
-
this.writeBuffer.delete(key);
|
|
527
|
-
this.createdKeys.delete(key);
|
|
528
|
-
const deletedValue = child.deletedValues.get(key);
|
|
529
|
-
if (deletedValue !== void 0) {
|
|
530
|
-
this.deletedValues.set(key, deletedValue);
|
|
686
|
+
const mergeVersion = ++this.localVersion;
|
|
687
|
+
for (const [key, value] of child.writeBuffer) {
|
|
688
|
+
const wasCreated = child.createdKeys.has(key);
|
|
689
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
690
|
+
this.originallyExisted.add(key);
|
|
691
|
+
}
|
|
692
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
693
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
531
694
|
}
|
|
532
|
-
|
|
533
|
-
|
|
695
|
+
for (const key of child.deleteBuffer) {
|
|
696
|
+
const deletedValue = child.deletedValues.get(key);
|
|
697
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
698
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
699
|
+
this.originallyExisted.add(key);
|
|
700
|
+
}
|
|
701
|
+
this._bufferDelete(key, mergeVersion);
|
|
534
702
|
}
|
|
703
|
+
this.root.activeTransactions.delete(child);
|
|
704
|
+
} else {
|
|
705
|
+
const newVersion = this.version + 1;
|
|
706
|
+
for (const [key, value] of this.writeBuffer) this._diskWrite(key, value, newVersion);
|
|
707
|
+
for (const key of this.deleteBuffer) this._diskDelete(key, newVersion);
|
|
708
|
+
this.version = newVersion;
|
|
709
|
+
this._cleanupDeletedCache();
|
|
535
710
|
}
|
|
536
|
-
for (const [key, value] of child.writeBuffer) {
|
|
537
|
-
this._diskWrite(key, value, newVersion);
|
|
538
|
-
}
|
|
539
|
-
for (const key of child.deleteBuffer) {
|
|
540
|
-
this._diskDelete(key, newVersion);
|
|
541
|
-
}
|
|
542
|
-
this.version = newVersion;
|
|
543
|
-
this.root.activeTransactions.delete(child);
|
|
544
|
-
this._cleanupDeletedCache();
|
|
545
711
|
}
|
|
546
712
|
return null;
|
|
547
713
|
}
|
|
548
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
549
714
|
_diskWrite(key, value, version) {
|
|
550
715
|
const strategy = this.strategy;
|
|
551
716
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
552
|
-
|
|
553
|
-
|
|
717
|
+
const rootAsAny = this.root;
|
|
718
|
+
if (this._diskExists(key, version)) {
|
|
719
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
554
720
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
555
|
-
this.deletedCache.get(key).push({
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
721
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
722
|
+
rootAsAny.diskCache.set(key, value);
|
|
723
|
+
} else {
|
|
724
|
+
rootAsAny.diskCache.set(key, value);
|
|
559
725
|
}
|
|
560
726
|
strategy.write(key, value);
|
|
561
727
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -566,36 +732,44 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
566
732
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
567
733
|
const versions = this.versionIndex.get(key);
|
|
568
734
|
if (!versions) {
|
|
569
|
-
|
|
735
|
+
const rootAsAny = this.root;
|
|
736
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
737
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
738
|
+
rootAsAny.diskCache.set(key, val);
|
|
739
|
+
return val;
|
|
740
|
+
}
|
|
741
|
+
return null;
|
|
570
742
|
}
|
|
571
743
|
let targetVerObj = null;
|
|
572
744
|
let nextVerObj = null;
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
} else {
|
|
577
|
-
nextVerObj = v;
|
|
578
|
-
break;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
745
|
+
const idx = this._findLastLE(versions, snapshotVersion, "version");
|
|
746
|
+
if (idx >= 0) targetVerObj = versions[idx];
|
|
747
|
+
if (idx + 1 < versions.length) nextVerObj = versions[idx + 1];
|
|
581
748
|
if (!targetVerObj) {
|
|
582
749
|
if (nextVerObj) {
|
|
583
750
|
const cached2 = this.deletedCache.get(key);
|
|
584
751
|
if (cached2) {
|
|
585
|
-
const
|
|
586
|
-
if (
|
|
752
|
+
const cIdx = this._findExact(cached2, nextVerObj.version, "deletedAtVersion");
|
|
753
|
+
if (cIdx >= 0) return cached2[cIdx].value;
|
|
587
754
|
}
|
|
588
755
|
}
|
|
589
756
|
return null;
|
|
590
757
|
}
|
|
591
758
|
if (!targetVerObj.exists) return null;
|
|
592
759
|
if (!nextVerObj) {
|
|
593
|
-
return
|
|
760
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
761
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
762
|
+
const rootAsAny = this.root;
|
|
763
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
764
|
+
rootAsAny.diskCache.set(key, val);
|
|
765
|
+
return val;
|
|
766
|
+
}
|
|
767
|
+
return null;
|
|
594
768
|
}
|
|
595
769
|
const cached = this.deletedCache.get(key);
|
|
596
770
|
if (cached) {
|
|
597
|
-
const
|
|
598
|
-
if (
|
|
771
|
+
const cIdx = this._findExact(cached, nextVerObj.version, "deletedAtVersion");
|
|
772
|
+
if (cIdx >= 0) return cached[cIdx].value;
|
|
599
773
|
}
|
|
600
774
|
return null;
|
|
601
775
|
}
|
|
@@ -604,31 +778,40 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
604
778
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
605
779
|
const versions = this.versionIndex.get(key);
|
|
606
780
|
if (!versions) {
|
|
607
|
-
|
|
781
|
+
const rootAsAny = this.root;
|
|
782
|
+
if (rootAsAny.diskCache.has(key)) return rootAsAny.diskCache.get(key) !== null;
|
|
783
|
+
const exists = strategy.exists(key);
|
|
784
|
+
if (!exists) rootAsAny.diskCache.set(key, null);
|
|
785
|
+
return exists;
|
|
608
786
|
}
|
|
609
787
|
let targetVerObj = null;
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
788
|
+
let nextVerObj = null;
|
|
789
|
+
const idx = this._findLastLE(versions, snapshotVersion, "version");
|
|
790
|
+
if (idx >= 0) targetVerObj = versions[idx];
|
|
791
|
+
if (idx + 1 < versions.length) nextVerObj = versions[idx + 1];
|
|
792
|
+
if (!targetVerObj) {
|
|
793
|
+
if (nextVerObj) {
|
|
794
|
+
const cached = this.deletedCache.get(key);
|
|
795
|
+
if (cached) {
|
|
796
|
+
const cIdx = this._findExact(cached, nextVerObj.version, "deletedAtVersion");
|
|
797
|
+
if (cIdx >= 0) return true;
|
|
798
|
+
}
|
|
615
799
|
}
|
|
800
|
+
return false;
|
|
616
801
|
}
|
|
617
|
-
if (!targetVerObj) return strategy.exists(key);
|
|
618
802
|
return targetVerObj.exists;
|
|
619
803
|
}
|
|
620
804
|
_diskDelete(key, snapshotVersion) {
|
|
621
805
|
const strategy = this.strategy;
|
|
622
806
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
623
|
-
|
|
624
|
-
|
|
807
|
+
const rootAsAny = this.root;
|
|
808
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
809
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
625
810
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
626
|
-
this.deletedCache.get(key).push({
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
});
|
|
811
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
812
|
+
strategy.delete(key);
|
|
813
|
+
rootAsAny.diskCache.delete(key);
|
|
630
814
|
}
|
|
631
|
-
strategy.delete(key);
|
|
632
815
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
633
816
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
634
817
|
}
|
|
@@ -941,7 +1124,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
941
1124
|
createNested() {
|
|
942
1125
|
if (this.committed) throw new Error("Transaction already committed");
|
|
943
1126
|
const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
|
|
944
|
-
const child = new _AsyncMVCCTransaction(void 0, this, childVersion);
|
|
1127
|
+
const child = new _AsyncMVCCTransaction(void 0, void 0, this, childVersion);
|
|
945
1128
|
this.root.activeTransactions.add(child);
|
|
946
1129
|
return child;
|
|
947
1130
|
}
|
|
@@ -949,32 +1132,77 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
949
1132
|
if (this.committed) throw new Error("Transaction already committed");
|
|
950
1133
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
951
1134
|
if (this.deleteBuffer.has(key)) return null;
|
|
952
|
-
|
|
1135
|
+
if (this.parent) {
|
|
1136
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
1137
|
+
}
|
|
1138
|
+
return await this._diskRead(key, this.snapshotVersion);
|
|
953
1139
|
}
|
|
954
1140
|
async exists(key) {
|
|
955
1141
|
if (this.committed) throw new Error("Transaction already committed");
|
|
956
1142
|
if (this.deleteBuffer.has(key)) return false;
|
|
957
1143
|
if (this.writeBuffer.has(key)) return true;
|
|
958
|
-
|
|
1144
|
+
if (this.parent) {
|
|
1145
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
1146
|
+
}
|
|
1147
|
+
return await this._diskExists(key, this.snapshotVersion);
|
|
959
1148
|
}
|
|
960
|
-
async
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1149
|
+
async _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
1150
|
+
let current = this;
|
|
1151
|
+
let slVer = snapshotLocalVersion;
|
|
1152
|
+
while (current) {
|
|
1153
|
+
if (current.writeBuffer.has(key)) {
|
|
1154
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
1155
|
+
if (slVer === void 0 || keyModVersion <= slVer) return true;
|
|
1156
|
+
}
|
|
1157
|
+
if (current.deleteBuffer.has(key)) {
|
|
1158
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
1159
|
+
if (slVer === void 0 || keyModVersion <= slVer) return false;
|
|
1160
|
+
}
|
|
1161
|
+
const history = current.bufferHistory.get(key);
|
|
1162
|
+
if (history && slVer !== void 0) {
|
|
1163
|
+
const idx = current._findLastLE(history, slVer, "version");
|
|
1164
|
+
if (idx >= 0) return history[idx].exists;
|
|
1165
|
+
}
|
|
1166
|
+
if (current.parent) {
|
|
1167
|
+
slVer = current.snapshotLocalVersion;
|
|
1168
|
+
current = current.parent;
|
|
1169
|
+
} else {
|
|
1170
|
+
return await current._diskExists(key, snapshotVersion);
|
|
965
1171
|
}
|
|
966
1172
|
}
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1173
|
+
return false;
|
|
1174
|
+
}
|
|
1175
|
+
async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
1176
|
+
let current = this;
|
|
1177
|
+
let slVer = snapshotLocalVersion;
|
|
1178
|
+
while (current) {
|
|
1179
|
+
if (current.writeBuffer.has(key)) {
|
|
1180
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
1181
|
+
if (slVer === void 0 || keyModVersion <= slVer) {
|
|
1182
|
+
return current.writeBuffer.get(key);
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
if (current.deleteBuffer.has(key)) {
|
|
1186
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
1187
|
+
if (slVer === void 0 || keyModVersion <= slVer) {
|
|
1188
|
+
return null;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
const history = current.bufferHistory.get(key);
|
|
1192
|
+
if (history && slVer !== void 0) {
|
|
1193
|
+
const idx = current._findLastLE(history, slVer, "version");
|
|
1194
|
+
if (idx >= 0) {
|
|
1195
|
+
return history[idx].exists ? history[idx].value : null;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
if (current.parent) {
|
|
1199
|
+
slVer = current.snapshotLocalVersion;
|
|
1200
|
+
current = current.parent;
|
|
1201
|
+
} else {
|
|
1202
|
+
return await current._diskRead(key, snapshotVersion);
|
|
971
1203
|
}
|
|
972
1204
|
}
|
|
973
|
-
|
|
974
|
-
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
975
|
-
} else {
|
|
976
|
-
return this._diskRead(key, snapshotVersion);
|
|
977
|
-
}
|
|
1205
|
+
return null;
|
|
978
1206
|
}
|
|
979
1207
|
async commit(label) {
|
|
980
1208
|
const { created, updated, deleted } = this.getResultEntries();
|
|
@@ -1034,6 +1262,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1034
1262
|
this.deletedValues.clear();
|
|
1035
1263
|
this.originallyExisted.clear();
|
|
1036
1264
|
this.keyVersions.clear();
|
|
1265
|
+
this.bufferHistory.clear();
|
|
1037
1266
|
this.localVersion = 0;
|
|
1038
1267
|
this.snapshotVersion = this.version;
|
|
1039
1268
|
}
|
|
@@ -1075,33 +1304,23 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1075
1304
|
};
|
|
1076
1305
|
}
|
|
1077
1306
|
}
|
|
1078
|
-
const
|
|
1079
|
-
for (const key of child.writeBuffer
|
|
1080
|
-
|
|
1081
|
-
this.
|
|
1082
|
-
this.
|
|
1083
|
-
if (child.createdKeys.has(key)) {
|
|
1084
|
-
this.createdKeys.add(key);
|
|
1085
|
-
}
|
|
1307
|
+
const mergeVersion = ++this.localVersion;
|
|
1308
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1309
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1310
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1311
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1086
1312
|
}
|
|
1087
1313
|
for (const key of child.deleteBuffer) {
|
|
1088
|
-
this.deleteBuffer.add(key);
|
|
1089
|
-
this.writeBuffer.delete(key);
|
|
1090
|
-
this.createdKeys.delete(key);
|
|
1091
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
1092
1314
|
const deletedValue = child.deletedValues.get(key);
|
|
1093
|
-
if (deletedValue !== void 0)
|
|
1094
|
-
|
|
1095
|
-
}
|
|
1096
|
-
if (child.originallyExisted.has(key)) {
|
|
1315
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1316
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1097
1317
|
this.originallyExisted.add(key);
|
|
1098
1318
|
}
|
|
1319
|
+
this._bufferDelete(key, mergeVersion);
|
|
1099
1320
|
}
|
|
1100
|
-
this.localVersion = newLocalVersion;
|
|
1101
1321
|
this.root.activeTransactions.delete(child);
|
|
1102
1322
|
return null;
|
|
1103
1323
|
} else {
|
|
1104
|
-
const newVersion = this.version + 1;
|
|
1105
1324
|
if (child !== this) {
|
|
1106
1325
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
1107
1326
|
for (const key of modifiedKeys) {
|
|
@@ -1119,51 +1338,58 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1119
1338
|
};
|
|
1120
1339
|
}
|
|
1121
1340
|
}
|
|
1341
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
1342
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
1343
|
+
return {
|
|
1344
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
1345
|
+
conflict: {
|
|
1346
|
+
key,
|
|
1347
|
+
parent: await this.read(key),
|
|
1348
|
+
child: await child.read(key)
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1122
1352
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
for (const key of child.deleteBuffer) {
|
|
1132
|
-
this.deleteBuffer.add(key);
|
|
1133
|
-
this.writeBuffer.delete(key);
|
|
1134
|
-
this.createdKeys.delete(key);
|
|
1135
|
-
const deletedValue = child.deletedValues.get(key);
|
|
1136
|
-
if (deletedValue !== void 0) {
|
|
1137
|
-
this.deletedValues.set(key, deletedValue);
|
|
1353
|
+
const mergeVersion = ++this.localVersion;
|
|
1354
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1355
|
+
const wasCreated = child.createdKeys.has(key);
|
|
1356
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1357
|
+
this.originallyExisted.add(key);
|
|
1358
|
+
}
|
|
1359
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
1360
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
1138
1361
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
1362
|
+
for (const key of child.deleteBuffer) {
|
|
1363
|
+
const deletedValue = child.deletedValues.get(key);
|
|
1364
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
1365
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
1366
|
+
this.originallyExisted.add(key);
|
|
1367
|
+
}
|
|
1368
|
+
this._bufferDelete(key, mergeVersion);
|
|
1141
1369
|
}
|
|
1370
|
+
this.root.activeTransactions.delete(child);
|
|
1371
|
+
} else {
|
|
1372
|
+
const newVersion = this.version + 1;
|
|
1373
|
+
for (const [key, value] of this.writeBuffer) await this._diskWrite(key, value, newVersion);
|
|
1374
|
+
for (const key of this.deleteBuffer) await this._diskDelete(key, newVersion);
|
|
1375
|
+
this.version = newVersion;
|
|
1376
|
+
this._cleanupDeletedCache();
|
|
1142
1377
|
}
|
|
1143
|
-
for (const [key, value] of child.writeBuffer) {
|
|
1144
|
-
await this._diskWrite(key, value, newVersion);
|
|
1145
|
-
}
|
|
1146
|
-
for (const key of child.deleteBuffer) {
|
|
1147
|
-
await this._diskDelete(key, newVersion);
|
|
1148
|
-
}
|
|
1149
|
-
this.version = newVersion;
|
|
1150
|
-
this.root.activeTransactions.delete(child);
|
|
1151
|
-
this._cleanupDeletedCache();
|
|
1152
1378
|
return null;
|
|
1153
1379
|
}
|
|
1154
1380
|
});
|
|
1155
1381
|
}
|
|
1156
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
1157
1382
|
async _diskWrite(key, value, version) {
|
|
1158
1383
|
const strategy = this.strategy;
|
|
1159
1384
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1160
|
-
|
|
1161
|
-
|
|
1385
|
+
const rootAsAny = this.root;
|
|
1386
|
+
if (await this._diskExists(key, version)) {
|
|
1387
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1162
1388
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1163
|
-
this.deletedCache.get(key).push({
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1389
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
1390
|
+
rootAsAny.diskCache.set(key, value);
|
|
1391
|
+
} else {
|
|
1392
|
+
rootAsAny.diskCache.set(key, value);
|
|
1167
1393
|
}
|
|
1168
1394
|
await strategy.write(key, value);
|
|
1169
1395
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -1174,36 +1400,44 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1174
1400
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1175
1401
|
const versions = this.versionIndex.get(key);
|
|
1176
1402
|
if (!versions) {
|
|
1177
|
-
|
|
1403
|
+
const rootAsAny = this.root;
|
|
1404
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
1405
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1406
|
+
rootAsAny.diskCache.set(key, val);
|
|
1407
|
+
return val;
|
|
1408
|
+
}
|
|
1409
|
+
return null;
|
|
1178
1410
|
}
|
|
1179
1411
|
let targetVerObj = null;
|
|
1180
1412
|
let nextVerObj = null;
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
} else {
|
|
1185
|
-
nextVerObj = v;
|
|
1186
|
-
break;
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1413
|
+
const idx = this._findLastLE(versions, snapshotVersion, "version");
|
|
1414
|
+
if (idx >= 0) targetVerObj = versions[idx];
|
|
1415
|
+
if (idx + 1 < versions.length) nextVerObj = versions[idx + 1];
|
|
1189
1416
|
if (!targetVerObj) {
|
|
1190
1417
|
if (nextVerObj) {
|
|
1191
1418
|
const cached2 = this.deletedCache.get(key);
|
|
1192
1419
|
if (cached2) {
|
|
1193
|
-
const
|
|
1194
|
-
if (
|
|
1420
|
+
const cIdx = this._findExact(cached2, nextVerObj.version, "deletedAtVersion");
|
|
1421
|
+
if (cIdx >= 0) return cached2[cIdx].value;
|
|
1195
1422
|
}
|
|
1196
1423
|
}
|
|
1197
1424
|
return null;
|
|
1198
1425
|
}
|
|
1199
1426
|
if (!targetVerObj.exists) return null;
|
|
1200
1427
|
if (!nextVerObj) {
|
|
1201
|
-
return
|
|
1428
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
1429
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
1430
|
+
const rootAsAny = this.root;
|
|
1431
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1432
|
+
rootAsAny.diskCache.set(key, val);
|
|
1433
|
+
return val;
|
|
1434
|
+
}
|
|
1435
|
+
return null;
|
|
1202
1436
|
}
|
|
1203
1437
|
const cached = this.deletedCache.get(key);
|
|
1204
1438
|
if (cached) {
|
|
1205
|
-
const
|
|
1206
|
-
if (
|
|
1439
|
+
const cIdx = this._findExact(cached, nextVerObj.version, "deletedAtVersion");
|
|
1440
|
+
if (cIdx >= 0) return cached[cIdx].value;
|
|
1207
1441
|
}
|
|
1208
1442
|
return null;
|
|
1209
1443
|
}
|
|
@@ -1212,359 +1446,47 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
1212
1446
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1213
1447
|
const versions = this.versionIndex.get(key);
|
|
1214
1448
|
if (!versions) {
|
|
1215
|
-
|
|
1449
|
+
const rootAsAny = this.root;
|
|
1450
|
+
if (rootAsAny.diskCache.has(key)) return rootAsAny.diskCache.get(key) !== null;
|
|
1451
|
+
const exists = await strategy.exists(key);
|
|
1452
|
+
if (!exists) rootAsAny.diskCache.set(key, null);
|
|
1453
|
+
return exists;
|
|
1216
1454
|
}
|
|
1217
1455
|
let targetVerObj = null;
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1456
|
+
let nextVerObj = null;
|
|
1457
|
+
const idx = this._findLastLE(versions, snapshotVersion, "version");
|
|
1458
|
+
if (idx >= 0) targetVerObj = versions[idx];
|
|
1459
|
+
if (idx + 1 < versions.length) nextVerObj = versions[idx + 1];
|
|
1460
|
+
if (!targetVerObj) {
|
|
1461
|
+
if (nextVerObj) {
|
|
1462
|
+
const cached = this.deletedCache.get(key);
|
|
1463
|
+
if (cached) {
|
|
1464
|
+
const cIdx = this._findExact(cached, nextVerObj.version, "deletedAtVersion");
|
|
1465
|
+
if (cIdx >= 0) return true;
|
|
1466
|
+
}
|
|
1223
1467
|
}
|
|
1468
|
+
return false;
|
|
1224
1469
|
}
|
|
1225
|
-
if (!targetVerObj) return strategy.exists(key);
|
|
1226
1470
|
return targetVerObj.exists;
|
|
1227
1471
|
}
|
|
1228
1472
|
async _diskDelete(key, snapshotVersion) {
|
|
1229
1473
|
const strategy = this.strategy;
|
|
1230
1474
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
1231
|
-
|
|
1232
|
-
|
|
1475
|
+
const rootAsAny = this.root;
|
|
1476
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
1477
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
1233
1478
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
1234
|
-
this.deletedCache.get(key).push({
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
});
|
|
1479
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
1480
|
+
await strategy.delete(key);
|
|
1481
|
+
rootAsAny.diskCache.delete(key);
|
|
1238
1482
|
}
|
|
1239
|
-
await strategy.delete(key);
|
|
1240
1483
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
1241
1484
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
1242
1485
|
}
|
|
1243
1486
|
};
|
|
1244
|
-
var LRUMap = class {
|
|
1245
|
-
capacity;
|
|
1246
|
-
map;
|
|
1247
|
-
head = null;
|
|
1248
|
-
tail = null;
|
|
1249
|
-
/**
|
|
1250
|
-
* Creates an instance of LRUMap.
|
|
1251
|
-
* @param capacity The maximum number of items the cache can hold.
|
|
1252
|
-
*/
|
|
1253
|
-
constructor(capacity) {
|
|
1254
|
-
this.capacity = capacity;
|
|
1255
|
-
this.map = /* @__PURE__ */ new Map();
|
|
1256
|
-
}
|
|
1257
|
-
/**
|
|
1258
|
-
* Promotes a node to the head of the linked list (marks as most recently used).
|
|
1259
|
-
* @param node The node to promote.
|
|
1260
|
-
*/
|
|
1261
|
-
promote(node) {
|
|
1262
|
-
this.extract(node);
|
|
1263
|
-
this.prepend(node);
|
|
1264
|
-
}
|
|
1265
|
-
/**
|
|
1266
|
-
* Disconnects a node from the doubly linked list.
|
|
1267
|
-
* @param node The node to extract.
|
|
1268
|
-
*/
|
|
1269
|
-
extract(node) {
|
|
1270
|
-
if (node.prev) node.prev.next = node.next;
|
|
1271
|
-
else this.head = node.next;
|
|
1272
|
-
if (node.next) node.next.prev = node.prev;
|
|
1273
|
-
else this.tail = node.prev;
|
|
1274
|
-
node.prev = null;
|
|
1275
|
-
node.next = null;
|
|
1276
|
-
}
|
|
1277
|
-
/**
|
|
1278
|
-
* Inserts a node at the head of the doubly linked list.
|
|
1279
|
-
* @param node The node to prepend.
|
|
1280
|
-
*/
|
|
1281
|
-
prepend(node) {
|
|
1282
|
-
node.next = this.head;
|
|
1283
|
-
if (this.head) this.head.prev = node;
|
|
1284
|
-
this.head = node;
|
|
1285
|
-
if (!this.tail) this.tail = node;
|
|
1286
|
-
}
|
|
1287
|
-
/**
|
|
1288
|
-
* Stores or updates a value by key.
|
|
1289
|
-
* If the capacity is exceeded, the least recently used item (tail) is removed.
|
|
1290
|
-
* @param key The key to store.
|
|
1291
|
-
* @param value The value to store.
|
|
1292
|
-
*/
|
|
1293
|
-
set(key, value) {
|
|
1294
|
-
const existing = this.map.get(key);
|
|
1295
|
-
if (existing) {
|
|
1296
|
-
existing.value = value;
|
|
1297
|
-
this.promote(existing);
|
|
1298
|
-
return;
|
|
1299
|
-
}
|
|
1300
|
-
const newNode = { key, value, prev: null, next: null };
|
|
1301
|
-
this.map.set(key, newNode);
|
|
1302
|
-
this.prepend(newNode);
|
|
1303
|
-
if (this.map.size > this.capacity && this.tail) {
|
|
1304
|
-
this.map.delete(this.tail.key);
|
|
1305
|
-
this.extract(this.tail);
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
/**
|
|
1309
|
-
* Retrieves a value by key.
|
|
1310
|
-
* Accessing the item moves it to the "most recently used" position.
|
|
1311
|
-
* @param key The key to look for.
|
|
1312
|
-
* @returns The value associated with the key, or undefined if not found.
|
|
1313
|
-
*/
|
|
1314
|
-
get(key) {
|
|
1315
|
-
const node = this.map.get(key);
|
|
1316
|
-
if (!node) return void 0;
|
|
1317
|
-
this.promote(node);
|
|
1318
|
-
return node.value;
|
|
1319
|
-
}
|
|
1320
|
-
/**
|
|
1321
|
-
* Checks if a key exists in the cache without changing its access order.
|
|
1322
|
-
* @param key The key to check.
|
|
1323
|
-
* @returns True if the key exists, false otherwise.
|
|
1324
|
-
*/
|
|
1325
|
-
has(key) {
|
|
1326
|
-
return this.map.has(key);
|
|
1327
|
-
}
|
|
1328
|
-
/**
|
|
1329
|
-
* Removes a key and its associated value from the cache.
|
|
1330
|
-
* @param key The key to remove.
|
|
1331
|
-
* @returns True if the key was found and removed, false otherwise.
|
|
1332
|
-
*/
|
|
1333
|
-
delete(key) {
|
|
1334
|
-
const node = this.map.get(key);
|
|
1335
|
-
if (!node) return false;
|
|
1336
|
-
this.extract(node);
|
|
1337
|
-
this.map.delete(key);
|
|
1338
|
-
return true;
|
|
1339
|
-
}
|
|
1340
|
-
/**
|
|
1341
|
-
* Returns an iterator of keys in the order of most recently used to least recently used.
|
|
1342
|
-
* @returns An iterable iterator of keys.
|
|
1343
|
-
*/
|
|
1344
|
-
*keys() {
|
|
1345
|
-
let current = this.head;
|
|
1346
|
-
while (current) {
|
|
1347
|
-
yield current.key;
|
|
1348
|
-
current = current.next;
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
/**
|
|
1352
|
-
* Returns the current number of items in the cache.
|
|
1353
|
-
*/
|
|
1354
|
-
get size() {
|
|
1355
|
-
return this.map.size;
|
|
1356
|
-
}
|
|
1357
|
-
/**
|
|
1358
|
-
* Clears all items from the cache.
|
|
1359
|
-
*/
|
|
1360
|
-
clear() {
|
|
1361
|
-
this.map.clear();
|
|
1362
|
-
this.head = null;
|
|
1363
|
-
this.tail = null;
|
|
1364
|
-
}
|
|
1365
|
-
};
|
|
1366
|
-
var CacheEntanglement = class {
|
|
1367
|
-
creation;
|
|
1368
|
-
beforeUpdateHook;
|
|
1369
|
-
capacity;
|
|
1370
|
-
dependencies;
|
|
1371
|
-
caches;
|
|
1372
|
-
parameters;
|
|
1373
|
-
assignments;
|
|
1374
|
-
dependencyProperties;
|
|
1375
|
-
updateRequirements;
|
|
1376
|
-
constructor(creation, option) {
|
|
1377
|
-
option = option ?? {};
|
|
1378
|
-
const {
|
|
1379
|
-
dependencies,
|
|
1380
|
-
capacity,
|
|
1381
|
-
beforeUpdateHook
|
|
1382
|
-
} = option;
|
|
1383
|
-
this.creation = creation;
|
|
1384
|
-
this.beforeUpdateHook = beforeUpdateHook ?? (() => {
|
|
1385
|
-
});
|
|
1386
|
-
this.capacity = capacity ?? 100;
|
|
1387
|
-
this.assignments = [];
|
|
1388
|
-
this.caches = new LRUMap(this.capacity);
|
|
1389
|
-
this.parameters = /* @__PURE__ */ new Map();
|
|
1390
|
-
this.dependencies = dependencies ?? {};
|
|
1391
|
-
this.dependencyProperties = Object.keys(this.dependencies);
|
|
1392
|
-
this.updateRequirements = /* @__PURE__ */ new Set();
|
|
1393
|
-
for (const name in this.dependencies) {
|
|
1394
|
-
const dependency = this.dependencies[name];
|
|
1395
|
-
if (!dependency.assignments.includes(this)) {
|
|
1396
|
-
dependency.assignments.push(this);
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
}
|
|
1400
|
-
bubbleUpdateSignal(key) {
|
|
1401
|
-
this.updateRequirements.add(key);
|
|
1402
|
-
for (let i = 0, len = this.assignments.length; i < len; i++) {
|
|
1403
|
-
const t = this.assignments[i];
|
|
1404
|
-
const instance = t;
|
|
1405
|
-
for (const cacheKey of instance.caches.keys()) {
|
|
1406
|
-
if (cacheKey === key || cacheKey.startsWith(`${key}/`)) {
|
|
1407
|
-
instance.bubbleUpdateSignal(cacheKey);
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
dependencyKey(key) {
|
|
1413
|
-
const i = key.lastIndexOf("/");
|
|
1414
|
-
if (i === -1) {
|
|
1415
|
-
return key;
|
|
1416
|
-
}
|
|
1417
|
-
return key.substring(0, i);
|
|
1418
|
-
}
|
|
1419
|
-
/**
|
|
1420
|
-
* Returns all keys stored in the instance.
|
|
1421
|
-
*/
|
|
1422
|
-
keys() {
|
|
1423
|
-
return this.parameters.keys();
|
|
1424
|
-
}
|
|
1425
|
-
/**
|
|
1426
|
-
* Deletes all cache values stored in the instance.
|
|
1427
|
-
*/
|
|
1428
|
-
clear() {
|
|
1429
|
-
for (const key of this.keys()) {
|
|
1430
|
-
this.delete(key);
|
|
1431
|
-
}
|
|
1432
|
-
}
|
|
1433
|
-
/**
|
|
1434
|
-
* Checks if there is a cache value stored in the key within the instance.
|
|
1435
|
-
* @param key The key to search.
|
|
1436
|
-
*/
|
|
1437
|
-
exists(key) {
|
|
1438
|
-
return this.parameters.has(key);
|
|
1439
|
-
}
|
|
1440
|
-
/**
|
|
1441
|
-
* Checks if there is a cache value stored in the key within the instance.
|
|
1442
|
-
* This method is an alias for `exists`.
|
|
1443
|
-
* @param key The key to search.
|
|
1444
|
-
*/
|
|
1445
|
-
has(key) {
|
|
1446
|
-
return this.exists(key);
|
|
1447
|
-
}
|
|
1448
|
-
/**
|
|
1449
|
-
* Deletes the cache value stored in the key within the instance.
|
|
1450
|
-
* @param key The key to delete.
|
|
1451
|
-
*/
|
|
1452
|
-
delete(key) {
|
|
1453
|
-
this.caches.delete(key);
|
|
1454
|
-
this.parameters.delete(key);
|
|
1455
|
-
this.updateRequirements.delete(key);
|
|
1456
|
-
for (let i = 0, len = this.assignments.length; i < len; i++) {
|
|
1457
|
-
const t = this.assignments[i];
|
|
1458
|
-
const instance = t;
|
|
1459
|
-
for (const cacheKey of instance.keys()) {
|
|
1460
|
-
if (cacheKey === key || cacheKey.startsWith(`${key}/`)) {
|
|
1461
|
-
instance.delete(cacheKey);
|
|
1462
|
-
}
|
|
1463
|
-
}
|
|
1464
|
-
}
|
|
1465
|
-
}
|
|
1466
|
-
};
|
|
1467
|
-
var CacheData = class _CacheData {
|
|
1468
|
-
static StructuredClone = globalThis.structuredClone.bind(globalThis);
|
|
1469
|
-
_value;
|
|
1470
|
-
constructor(value) {
|
|
1471
|
-
this._value = value;
|
|
1472
|
-
}
|
|
1473
|
-
/**
|
|
1474
|
-
* This is cached data.
|
|
1475
|
-
* It was generated at the time of caching, so there is a risk of modification if it's an object due to shallow copying.
|
|
1476
|
-
* 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.
|
|
1477
|
-
*/
|
|
1478
|
-
get raw() {
|
|
1479
|
-
return this._value;
|
|
1480
|
-
}
|
|
1481
|
-
/**
|
|
1482
|
-
* The method returns a copied value of the cached data.
|
|
1483
|
-
* You can pass a function as a parameter to copy the value. This parameter function should return the copied value.
|
|
1484
|
-
*
|
|
1485
|
-
* If no parameter is passed, it defaults to using `structuredClone` function to copy the value.
|
|
1486
|
-
* If you prefer shallow copying instead of deep copying,
|
|
1487
|
-
* you can use the default options `array-shallow-copy`, `object-shallow-copy` and `deep-copy`,
|
|
1488
|
-
* which are replaced with functions to shallow copy arrays and objects, respectively. This is a syntactic sugar.
|
|
1489
|
-
* @param strategy The function that returns the copied value.
|
|
1490
|
-
* If you want to perform a shallow copy, simply pass the strings `array-shallow-copy` or `object-shallow-copy` for easy use.
|
|
1491
|
-
* The `array-shallow-copy` strategy performs a shallow copy of an array.
|
|
1492
|
-
* The `object-shallow-copy` strategy performs a shallow copy of an object.
|
|
1493
|
-
* The `deep-copy` strategy performs a deep copy of the value using `structuredClone`.
|
|
1494
|
-
* The default is `deep-copy`.
|
|
1495
|
-
*/
|
|
1496
|
-
clone(strategy = "deep-copy") {
|
|
1497
|
-
if (strategy && typeof strategy !== "string") {
|
|
1498
|
-
return strategy(this.raw);
|
|
1499
|
-
}
|
|
1500
|
-
switch (strategy) {
|
|
1501
|
-
case "array-shallow-copy":
|
|
1502
|
-
return [].concat(this.raw);
|
|
1503
|
-
case "object-shallow-copy":
|
|
1504
|
-
return Object.assign({}, this.raw);
|
|
1505
|
-
case "deep-copy":
|
|
1506
|
-
default:
|
|
1507
|
-
return _CacheData.StructuredClone(this.raw);
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
};
|
|
1511
|
-
var CacheEntanglementSync = class extends CacheEntanglement {
|
|
1512
|
-
constructor(creation, option) {
|
|
1513
|
-
super(creation, option);
|
|
1514
|
-
}
|
|
1515
|
-
recache(key) {
|
|
1516
|
-
if (!this.parameters.has(key)) {
|
|
1517
|
-
return;
|
|
1518
|
-
}
|
|
1519
|
-
if (!this.caches.has(key) || this.updateRequirements.has(key)) {
|
|
1520
|
-
this.resolve(key, ...this.parameters.get(key));
|
|
1521
|
-
}
|
|
1522
|
-
return this.caches.get(key);
|
|
1523
|
-
}
|
|
1524
|
-
resolve(key, ...parameter) {
|
|
1525
|
-
const resolved = {};
|
|
1526
|
-
const dependencyKey = this.dependencyKey(key);
|
|
1527
|
-
this.beforeUpdateHook(key, dependencyKey, ...parameter);
|
|
1528
|
-
for (let i = 0, len = this.dependencyProperties.length; i < len; i++) {
|
|
1529
|
-
const name = this.dependencyProperties[i];
|
|
1530
|
-
const dependency = this.dependencies[name];
|
|
1531
|
-
if (!dependency.exists(key) && !dependency.exists(dependencyKey)) {
|
|
1532
|
-
throw new Error(`The key '${key}' or '${dependencyKey}' has not been assigned yet in dependency '${name.toString()}'.`, {
|
|
1533
|
-
cause: {
|
|
1534
|
-
from: this
|
|
1535
|
-
}
|
|
1536
|
-
});
|
|
1537
|
-
}
|
|
1538
|
-
const dependencyValue = dependency.recache(key) ?? dependency.recache(dependencyKey);
|
|
1539
|
-
resolved[name] = dependencyValue;
|
|
1540
|
-
}
|
|
1541
|
-
const value = new CacheData(this.creation(key, resolved, ...parameter));
|
|
1542
|
-
this.updateRequirements.delete(key);
|
|
1543
|
-
this.parameters.set(key, parameter);
|
|
1544
|
-
this.caches.set(key, value);
|
|
1545
|
-
return value;
|
|
1546
|
-
}
|
|
1547
|
-
get(key) {
|
|
1548
|
-
if (!this.parameters.has(key)) {
|
|
1549
|
-
throw new Error(`Cache value not found: ${key}`);
|
|
1550
|
-
}
|
|
1551
|
-
return this.cache(key, ...this.parameters.get(key));
|
|
1552
|
-
}
|
|
1553
|
-
cache(key, ...parameter) {
|
|
1554
|
-
if (!this.caches.has(key) || this.updateRequirements.has(key)) {
|
|
1555
|
-
this.resolve(key, ...parameter);
|
|
1556
|
-
}
|
|
1557
|
-
return this.caches.get(key);
|
|
1558
|
-
}
|
|
1559
|
-
update(key, ...parameter) {
|
|
1560
|
-
this.bubbleUpdateSignal(key);
|
|
1561
|
-
this.resolve(key, ...parameter);
|
|
1562
|
-
return this.caches.get(key);
|
|
1563
|
-
}
|
|
1564
|
-
};
|
|
1565
1487
|
var BPTreeTransaction = class _BPTreeTransaction {
|
|
1566
|
-
_cachedRegexp;
|
|
1567
|
-
nodes;
|
|
1488
|
+
_cachedRegexp = /* @__PURE__ */ new Map();
|
|
1489
|
+
nodes = /* @__PURE__ */ new Map();
|
|
1568
1490
|
deletedNodeBuffer = /* @__PURE__ */ new Map();
|
|
1569
1491
|
rootTx;
|
|
1570
1492
|
mvccRoot;
|
|
@@ -1574,6 +1496,8 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1574
1496
|
option;
|
|
1575
1497
|
order;
|
|
1576
1498
|
rootId;
|
|
1499
|
+
isInitialized = false;
|
|
1500
|
+
isDestroyed = false;
|
|
1577
1501
|
verifierMap = {
|
|
1578
1502
|
gt: (nv, v) => this.comparator.isHigher(nv, v),
|
|
1579
1503
|
gte: (nv, v) => this.comparator.isHigher(nv, v) || this.comparator.isSame(nv, v),
|
|
@@ -1592,8 +1516,12 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1592
1516
|
like: (nv, v) => {
|
|
1593
1517
|
const nodeValue = this.comparator.match(nv);
|
|
1594
1518
|
const value = v;
|
|
1595
|
-
|
|
1596
|
-
|
|
1519
|
+
if (!this._cachedRegexp.has(value)) {
|
|
1520
|
+
const pattern = value.replace(/%/g, ".*").replace(/_/g, ".");
|
|
1521
|
+
const regexp2 = new RegExp(`^${pattern}$`, "i");
|
|
1522
|
+
this._cachedRegexp.set(value, regexp2);
|
|
1523
|
+
}
|
|
1524
|
+
const regexp = this._cachedRegexp.get(value);
|
|
1597
1525
|
return regexp.test(nodeValue);
|
|
1598
1526
|
}
|
|
1599
1527
|
};
|
|
@@ -1765,6 +1693,9 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1765
1693
|
}
|
|
1766
1694
|
return true;
|
|
1767
1695
|
}
|
|
1696
|
+
_cloneNode(node) {
|
|
1697
|
+
return JSON.parse(JSON.stringify(node));
|
|
1698
|
+
}
|
|
1768
1699
|
/**
|
|
1769
1700
|
* Selects the best driver key from a condition object.
|
|
1770
1701
|
* The driver key determines the starting point and traversal direction for queries.
|
|
@@ -1797,17 +1728,6 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1797
1728
|
this.strategy = strategy;
|
|
1798
1729
|
this.comparator = comparator;
|
|
1799
1730
|
this.option = option ?? {};
|
|
1800
|
-
this.nodes = new LRUMap(this.option.capacity ?? 1e3);
|
|
1801
|
-
this._cachedRegexp = this._createCachedRegexp();
|
|
1802
|
-
}
|
|
1803
|
-
_createCachedRegexp() {
|
|
1804
|
-
return new CacheEntanglementSync((key) => {
|
|
1805
|
-
const pattern = key.replace(/%/g, ".*").replace(/_/g, ".");
|
|
1806
|
-
const regexp = new RegExp(`^${pattern}$`, "i");
|
|
1807
|
-
return regexp;
|
|
1808
|
-
}, {
|
|
1809
|
-
capacity: this.option.capacity ?? 1e3
|
|
1810
|
-
});
|
|
1811
1731
|
}
|
|
1812
1732
|
ensureValues(v) {
|
|
1813
1733
|
if (!Array.isArray(v)) {
|
|
@@ -1838,13 +1758,25 @@ var BPTreeTransaction = class _BPTreeTransaction {
|
|
|
1838
1758
|
getResultEntries() {
|
|
1839
1759
|
return this.mvcc.getResultEntries();
|
|
1840
1760
|
}
|
|
1761
|
+
_clearCache() {
|
|
1762
|
+
this._cachedRegexp.clear();
|
|
1763
|
+
}
|
|
1841
1764
|
/**
|
|
1842
1765
|
* Clears all cached nodes.
|
|
1843
1766
|
* This method is useful for freeing up memory when the tree is no longer needed.
|
|
1844
1767
|
*/
|
|
1845
1768
|
clear() {
|
|
1846
|
-
this.
|
|
1847
|
-
|
|
1769
|
+
if (this.rootTx !== this) {
|
|
1770
|
+
throw new Error("Cannot call clear on a nested transaction");
|
|
1771
|
+
}
|
|
1772
|
+
this._clearInternal();
|
|
1773
|
+
}
|
|
1774
|
+
_clearInternal() {
|
|
1775
|
+
if (this.isDestroyed) {
|
|
1776
|
+
throw new Error("Transaction already destroyed");
|
|
1777
|
+
}
|
|
1778
|
+
this._clearCache();
|
|
1779
|
+
this.isDestroyed = true;
|
|
1848
1780
|
}
|
|
1849
1781
|
_binarySearchValues(values, target, usePrimary = false, upperBound = false) {
|
|
1850
1782
|
let low = 0;
|
|
@@ -1878,9 +1810,6 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1878
1810
|
);
|
|
1879
1811
|
}
|
|
1880
1812
|
getNode(id) {
|
|
1881
|
-
if (this.nodes.has(id)) {
|
|
1882
|
-
return this.nodes.get(id);
|
|
1883
|
-
}
|
|
1884
1813
|
return this.mvcc.read(id);
|
|
1885
1814
|
}
|
|
1886
1815
|
/**
|
|
@@ -1898,23 +1827,22 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1898
1827
|
prev
|
|
1899
1828
|
};
|
|
1900
1829
|
this.mvcc.create(id, node);
|
|
1901
|
-
this.nodes.set(id, node);
|
|
1902
1830
|
return node;
|
|
1903
1831
|
}
|
|
1904
1832
|
_updateNode(node) {
|
|
1833
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1905
1836
|
this.mvcc.write(node.id, node);
|
|
1906
|
-
this.nodes.set(node.id, node);
|
|
1907
1837
|
}
|
|
1908
1838
|
_deleteNode(node) {
|
|
1839
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
1840
|
+
return;
|
|
1841
|
+
}
|
|
1909
1842
|
this.mvcc.delete(node.id);
|
|
1910
|
-
this.nodes.delete(node.id);
|
|
1911
1843
|
}
|
|
1912
1844
|
_readHead() {
|
|
1913
|
-
|
|
1914
|
-
return this.nodes.get("__HEAD__") ?? null;
|
|
1915
|
-
}
|
|
1916
|
-
const head = this.mvcc.read("__HEAD__");
|
|
1917
|
-
return head ?? null;
|
|
1845
|
+
return this.mvcc.read("__HEAD__");
|
|
1918
1846
|
}
|
|
1919
1847
|
_writeHead(head) {
|
|
1920
1848
|
if (!this.mvcc.exists("__HEAD__")) {
|
|
@@ -1922,49 +1850,56 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1922
1850
|
} else {
|
|
1923
1851
|
this.mvcc.write("__HEAD__", head);
|
|
1924
1852
|
}
|
|
1925
|
-
this.nodes.set("__HEAD__", head);
|
|
1926
1853
|
this.rootId = head.root;
|
|
1927
1854
|
}
|
|
1928
1855
|
_insertAtLeaf(node, key, value) {
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1856
|
+
let leaf = node;
|
|
1857
|
+
if (leaf.values.length) {
|
|
1858
|
+
for (let i = 0, len = leaf.values.length; i < len; i++) {
|
|
1859
|
+
const nValue = leaf.values[i];
|
|
1932
1860
|
if (this.comparator.isSame(value, nValue)) {
|
|
1933
|
-
const keys =
|
|
1861
|
+
const keys = leaf.keys[i];
|
|
1934
1862
|
if (keys.includes(key)) {
|
|
1935
1863
|
break;
|
|
1936
1864
|
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1865
|
+
leaf = this._cloneNode(leaf);
|
|
1866
|
+
leaf.keys[i].push(key);
|
|
1867
|
+
this._updateNode(leaf);
|
|
1868
|
+
return leaf;
|
|
1940
1869
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1870
|
+
leaf = this._cloneNode(leaf);
|
|
1871
|
+
leaf.values.splice(i, 0, value);
|
|
1872
|
+
leaf.keys.splice(i, 0, [key]);
|
|
1873
|
+
this._updateNode(leaf);
|
|
1874
|
+
return leaf;
|
|
1875
|
+
} else if (i + 1 === leaf.values.length) {
|
|
1876
|
+
leaf = this._cloneNode(leaf);
|
|
1877
|
+
leaf.values.push(value);
|
|
1878
|
+
leaf.keys.push([key]);
|
|
1879
|
+
this._updateNode(leaf);
|
|
1880
|
+
return leaf;
|
|
1950
1881
|
}
|
|
1951
1882
|
}
|
|
1952
1883
|
} else {
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1884
|
+
leaf = this._cloneNode(leaf);
|
|
1885
|
+
leaf.values = [value];
|
|
1886
|
+
leaf.keys = [[key]];
|
|
1887
|
+
this._updateNode(leaf);
|
|
1888
|
+
return leaf;
|
|
1957
1889
|
}
|
|
1890
|
+
return leaf;
|
|
1958
1891
|
}
|
|
1959
|
-
_insertInParent(node, value,
|
|
1892
|
+
_insertInParent(node, value, newSiblingNode) {
|
|
1960
1893
|
if (this.rootId === node.id) {
|
|
1961
|
-
|
|
1894
|
+
node = this._cloneNode(node);
|
|
1895
|
+
newSiblingNode = this._cloneNode(newSiblingNode);
|
|
1896
|
+
const root = this._createNode(false, [node.id, newSiblingNode.id], [value]);
|
|
1962
1897
|
this.rootId = root.id;
|
|
1963
1898
|
node.parent = root.id;
|
|
1964
|
-
|
|
1965
|
-
if (
|
|
1966
|
-
node.next =
|
|
1967
|
-
|
|
1899
|
+
newSiblingNode.parent = root.id;
|
|
1900
|
+
if (newSiblingNode.leaf) {
|
|
1901
|
+
node.next = newSiblingNode.id;
|
|
1902
|
+
newSiblingNode.prev = node.id;
|
|
1968
1903
|
}
|
|
1969
1904
|
this._writeHead({
|
|
1970
1905
|
root: root.id,
|
|
@@ -1972,53 +1907,54 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
1972
1907
|
data: this.strategy.head.data
|
|
1973
1908
|
});
|
|
1974
1909
|
this._updateNode(node);
|
|
1975
|
-
this._updateNode(
|
|
1910
|
+
this._updateNode(newSiblingNode);
|
|
1976
1911
|
return;
|
|
1977
1912
|
}
|
|
1978
|
-
const parentNode = this.getNode(node.parent);
|
|
1913
|
+
const parentNode = this._cloneNode(this.getNode(node.parent));
|
|
1979
1914
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
1980
1915
|
if (nodeIndex === -1) {
|
|
1981
1916
|
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
1982
1917
|
}
|
|
1983
1918
|
parentNode.values.splice(nodeIndex, 0, value);
|
|
1984
|
-
parentNode.keys.splice(nodeIndex + 1, 0,
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1919
|
+
parentNode.keys.splice(nodeIndex + 1, 0, newSiblingNode.id);
|
|
1920
|
+
newSiblingNode = this._cloneNode(newSiblingNode);
|
|
1921
|
+
newSiblingNode.parent = parentNode.id;
|
|
1922
|
+
if (newSiblingNode.leaf) {
|
|
1923
|
+
const leftSibling = this._cloneNode(node);
|
|
1988
1924
|
const oldNextId = leftSibling.next;
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
leftSibling.next =
|
|
1925
|
+
newSiblingNode.prev = leftSibling.id;
|
|
1926
|
+
newSiblingNode.next = oldNextId;
|
|
1927
|
+
leftSibling.next = newSiblingNode.id;
|
|
1992
1928
|
this._updateNode(leftSibling);
|
|
1993
1929
|
if (oldNextId) {
|
|
1994
|
-
const oldNext = this.getNode(oldNextId);
|
|
1995
|
-
oldNext.prev =
|
|
1930
|
+
const oldNext = this._cloneNode(this.getNode(oldNextId));
|
|
1931
|
+
oldNext.prev = newSiblingNode.id;
|
|
1996
1932
|
this._updateNode(oldNext);
|
|
1997
1933
|
}
|
|
1998
1934
|
}
|
|
1999
1935
|
this._updateNode(parentNode);
|
|
2000
|
-
this._updateNode(
|
|
1936
|
+
this._updateNode(newSiblingNode);
|
|
2001
1937
|
if (parentNode.keys.length > this.order) {
|
|
2002
|
-
const
|
|
2003
|
-
|
|
1938
|
+
const newSiblingNodeRecursive = this._createNode(false, [], []);
|
|
1939
|
+
newSiblingNodeRecursive.parent = parentNode.parent;
|
|
2004
1940
|
const mid = Math.ceil(this.order / 2) - 1;
|
|
2005
|
-
|
|
2006
|
-
|
|
1941
|
+
newSiblingNodeRecursive.values = parentNode.values.slice(mid + 1);
|
|
1942
|
+
newSiblingNodeRecursive.keys = parentNode.keys.slice(mid + 1);
|
|
2007
1943
|
const midValue = parentNode.values[mid];
|
|
2008
1944
|
parentNode.values = parentNode.values.slice(0, mid);
|
|
2009
1945
|
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2010
1946
|
for (const k of parentNode.keys) {
|
|
2011
|
-
const n = this.getNode(k);
|
|
1947
|
+
const n = this._cloneNode(this.getNode(k));
|
|
2012
1948
|
n.parent = parentNode.id;
|
|
2013
1949
|
this._updateNode(n);
|
|
2014
1950
|
}
|
|
2015
|
-
for (const k of
|
|
2016
|
-
const n = this.getNode(k);
|
|
2017
|
-
n.parent =
|
|
1951
|
+
for (const k of newSiblingNodeRecursive.keys) {
|
|
1952
|
+
const n = this._cloneNode(this.getNode(k));
|
|
1953
|
+
n.parent = newSiblingNodeRecursive.id;
|
|
2018
1954
|
this._updateNode(n);
|
|
2019
1955
|
}
|
|
2020
1956
|
this._updateNode(parentNode);
|
|
2021
|
-
this._insertInParent(parentNode, midValue,
|
|
1957
|
+
this._insertInParent(parentNode, midValue, newSiblingNodeRecursive);
|
|
2022
1958
|
}
|
|
2023
1959
|
}
|
|
2024
1960
|
insertableNode(value) {
|
|
@@ -2148,28 +2084,46 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2148
2084
|
}
|
|
2149
2085
|
}
|
|
2150
2086
|
init() {
|
|
2151
|
-
this.
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
const { root, order } = head;
|
|
2163
|
-
this.strategy.head = head;
|
|
2164
|
-
this.order = order;
|
|
2165
|
-
this._writeHead({
|
|
2166
|
-
root,
|
|
2167
|
-
order: this.order,
|
|
2168
|
-
data: this.strategy.head.data
|
|
2169
|
-
});
|
|
2087
|
+
if (this.rootTx !== this) {
|
|
2088
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
2089
|
+
}
|
|
2090
|
+
this._initInternal();
|
|
2091
|
+
}
|
|
2092
|
+
_initInternal() {
|
|
2093
|
+
if (this.isInitialized) {
|
|
2094
|
+
throw new Error("Transaction already initialized");
|
|
2095
|
+
}
|
|
2096
|
+
if (this.isDestroyed) {
|
|
2097
|
+
throw new Error("Transaction already destroyed");
|
|
2170
2098
|
}
|
|
2171
|
-
|
|
2172
|
-
|
|
2099
|
+
this.isInitialized = true;
|
|
2100
|
+
try {
|
|
2101
|
+
this._clearCache();
|
|
2102
|
+
const head = this._readHead();
|
|
2103
|
+
if (head === null) {
|
|
2104
|
+
this.order = this.strategy.order;
|
|
2105
|
+
const root = this._createNode(true, [], []);
|
|
2106
|
+
this._writeHead({
|
|
2107
|
+
root: root.id,
|
|
2108
|
+
order: this.order,
|
|
2109
|
+
data: this.strategy.head.data
|
|
2110
|
+
});
|
|
2111
|
+
} else {
|
|
2112
|
+
const { root, order } = head;
|
|
2113
|
+
this.strategy.head = head;
|
|
2114
|
+
this.order = order;
|
|
2115
|
+
this._writeHead({
|
|
2116
|
+
root,
|
|
2117
|
+
order: this.order,
|
|
2118
|
+
data: this.strategy.head.data
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
2121
|
+
if (this.order < 3) {
|
|
2122
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
2123
|
+
}
|
|
2124
|
+
} catch (e) {
|
|
2125
|
+
this.isInitialized = false;
|
|
2126
|
+
throw e;
|
|
2173
2127
|
}
|
|
2174
2128
|
}
|
|
2175
2129
|
exists(key, value) {
|
|
@@ -2183,21 +2137,6 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2183
2137
|
}
|
|
2184
2138
|
return false;
|
|
2185
2139
|
}
|
|
2186
|
-
forceUpdate(id) {
|
|
2187
|
-
if (id) {
|
|
2188
|
-
this.nodes.delete(id);
|
|
2189
|
-
this.getNode(id);
|
|
2190
|
-
return 1;
|
|
2191
|
-
}
|
|
2192
|
-
const keys = Array.from(this.nodes.keys());
|
|
2193
|
-
for (const key of keys) {
|
|
2194
|
-
this.nodes.delete(key);
|
|
2195
|
-
}
|
|
2196
|
-
for (const key of keys) {
|
|
2197
|
-
this.getNode(key);
|
|
2198
|
-
}
|
|
2199
|
-
return keys.length;
|
|
2200
|
-
}
|
|
2201
2140
|
get(key) {
|
|
2202
2141
|
let node = this.leftestNode();
|
|
2203
2142
|
while (true) {
|
|
@@ -2283,10 +2222,10 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2283
2222
|
return map;
|
|
2284
2223
|
}
|
|
2285
2224
|
insert(key, value) {
|
|
2286
|
-
|
|
2287
|
-
this._insertAtLeaf(before, key, value);
|
|
2225
|
+
let before = this.insertableNode(value);
|
|
2226
|
+
before = this._insertAtLeaf(before, key, value);
|
|
2288
2227
|
if (before.values.length === this.order) {
|
|
2289
|
-
|
|
2228
|
+
let after = this._createNode(
|
|
2290
2229
|
true,
|
|
2291
2230
|
[],
|
|
2292
2231
|
[],
|
|
@@ -2295,10 +2234,13 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2295
2234
|
null
|
|
2296
2235
|
);
|
|
2297
2236
|
const mid = Math.ceil(this.order / 2) - 1;
|
|
2237
|
+
after = this._cloneNode(after);
|
|
2298
2238
|
after.values = before.values.slice(mid + 1);
|
|
2299
2239
|
after.keys = before.keys.slice(mid + 1);
|
|
2300
2240
|
before.values = before.values.slice(0, mid + 1);
|
|
2301
2241
|
before.keys = before.keys.slice(0, mid + 1);
|
|
2242
|
+
this._updateNode(before);
|
|
2243
|
+
this._updateNode(after);
|
|
2302
2244
|
this._insertInParent(before, after.values[0], after);
|
|
2303
2245
|
}
|
|
2304
2246
|
}
|
|
@@ -2312,6 +2254,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2312
2254
|
}
|
|
2313
2255
|
}
|
|
2314
2256
|
if (keyIndex !== -1) {
|
|
2257
|
+
node = this._cloneNode(node);
|
|
2315
2258
|
node.keys.splice(keyIndex, 1);
|
|
2316
2259
|
const valueIndex = keyIndex > 0 ? keyIndex - 1 : 0;
|
|
2317
2260
|
node.values.splice(valueIndex, 1);
|
|
@@ -2321,7 +2264,7 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2321
2264
|
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
2322
2265
|
const keys = node.keys;
|
|
2323
2266
|
this._deleteNode(node);
|
|
2324
|
-
const newRoot = this.getNode(keys[0]);
|
|
2267
|
+
const newRoot = this._cloneNode(this.getNode(keys[0]));
|
|
2325
2268
|
newRoot.parent = null;
|
|
2326
2269
|
this._updateNode(newRoot);
|
|
2327
2270
|
this._writeHead({
|
|
@@ -2329,17 +2272,17 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2329
2272
|
order: this.order,
|
|
2330
2273
|
data: this.strategy.head.data
|
|
2331
2274
|
});
|
|
2332
|
-
return;
|
|
2275
|
+
return node;
|
|
2333
2276
|
} else if (this.rootId === node.id) {
|
|
2334
2277
|
this._writeHead({
|
|
2335
2278
|
root: node.id,
|
|
2336
2279
|
order: this.order,
|
|
2337
2280
|
data: this.strategy.head.data
|
|
2338
2281
|
});
|
|
2339
|
-
return;
|
|
2282
|
+
return node;
|
|
2340
2283
|
} else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
|
|
2341
2284
|
if (node.parent === null) {
|
|
2342
|
-
return;
|
|
2285
|
+
return node;
|
|
2343
2286
|
}
|
|
2344
2287
|
let isPredecessor = false;
|
|
2345
2288
|
let parentNode = this.getNode(node.parent);
|
|
@@ -2360,78 +2303,80 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2360
2303
|
}
|
|
2361
2304
|
}
|
|
2362
2305
|
}
|
|
2363
|
-
let
|
|
2306
|
+
let siblingNode;
|
|
2364
2307
|
let guess;
|
|
2365
2308
|
if (prevNode === null) {
|
|
2366
|
-
|
|
2309
|
+
siblingNode = nextNode;
|
|
2367
2310
|
guess = postValue;
|
|
2368
2311
|
} else if (nextNode === null) {
|
|
2369
2312
|
isPredecessor = true;
|
|
2370
|
-
|
|
2313
|
+
siblingNode = prevNode;
|
|
2371
2314
|
guess = prevValue;
|
|
2372
2315
|
} else {
|
|
2373
2316
|
if (node.values.length + nextNode.values.length < this.order) {
|
|
2374
|
-
|
|
2317
|
+
siblingNode = nextNode;
|
|
2375
2318
|
guess = postValue;
|
|
2376
2319
|
} else {
|
|
2377
2320
|
isPredecessor = true;
|
|
2378
|
-
|
|
2321
|
+
siblingNode = prevNode;
|
|
2379
2322
|
guess = prevValue;
|
|
2380
2323
|
}
|
|
2381
2324
|
}
|
|
2382
|
-
if (!
|
|
2383
|
-
return;
|
|
2325
|
+
if (!siblingNode) {
|
|
2326
|
+
return node;
|
|
2384
2327
|
}
|
|
2385
|
-
|
|
2328
|
+
node = this._cloneNode(node);
|
|
2329
|
+
siblingNode = this._cloneNode(siblingNode);
|
|
2330
|
+
if (node.values.length + siblingNode.values.length < this.order) {
|
|
2386
2331
|
if (!isPredecessor) {
|
|
2387
|
-
const pTemp =
|
|
2388
|
-
|
|
2332
|
+
const pTemp = siblingNode;
|
|
2333
|
+
siblingNode = node;
|
|
2389
2334
|
node = pTemp;
|
|
2390
2335
|
}
|
|
2391
|
-
|
|
2336
|
+
siblingNode.keys.push(...node.keys);
|
|
2392
2337
|
if (!node.leaf) {
|
|
2393
|
-
|
|
2338
|
+
siblingNode.values.push(guess);
|
|
2394
2339
|
} else {
|
|
2395
|
-
|
|
2396
|
-
if (
|
|
2397
|
-
const n = this.getNode(
|
|
2398
|
-
n.prev =
|
|
2340
|
+
siblingNode.next = node.next;
|
|
2341
|
+
if (siblingNode.next) {
|
|
2342
|
+
const n = this._cloneNode(this.getNode(siblingNode.next));
|
|
2343
|
+
n.prev = siblingNode.id;
|
|
2399
2344
|
this._updateNode(n);
|
|
2400
2345
|
}
|
|
2401
2346
|
}
|
|
2402
|
-
|
|
2403
|
-
if (!
|
|
2404
|
-
const keys =
|
|
2347
|
+
siblingNode.values.push(...node.values);
|
|
2348
|
+
if (!siblingNode.leaf) {
|
|
2349
|
+
const keys = siblingNode.keys;
|
|
2405
2350
|
for (const key2 of keys) {
|
|
2406
|
-
const node2 = this.getNode(key2);
|
|
2407
|
-
node2.parent =
|
|
2351
|
+
const node2 = this._cloneNode(this.getNode(key2));
|
|
2352
|
+
node2.parent = siblingNode.id;
|
|
2408
2353
|
this._updateNode(node2);
|
|
2409
2354
|
}
|
|
2410
2355
|
}
|
|
2411
2356
|
this._deleteNode(node);
|
|
2412
|
-
this._updateNode(
|
|
2357
|
+
this._updateNode(siblingNode);
|
|
2413
2358
|
this._deleteEntry(this.getNode(node.parent), node.id);
|
|
2414
2359
|
} else {
|
|
2415
2360
|
if (isPredecessor) {
|
|
2416
2361
|
let pointerPm;
|
|
2417
2362
|
let pointerKm;
|
|
2418
2363
|
if (!node.leaf) {
|
|
2419
|
-
pointerPm =
|
|
2420
|
-
pointerKm =
|
|
2364
|
+
pointerPm = siblingNode.keys.splice(-1)[0];
|
|
2365
|
+
pointerKm = siblingNode.values.splice(-1)[0];
|
|
2421
2366
|
node.keys = [pointerPm, ...node.keys];
|
|
2422
2367
|
node.values = [guess, ...node.values];
|
|
2423
|
-
parentNode = this.getNode(node.parent);
|
|
2368
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2424
2369
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2425
2370
|
if (nodeIndex > 0) {
|
|
2426
2371
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
2427
2372
|
this._updateNode(parentNode);
|
|
2428
2373
|
}
|
|
2429
2374
|
} else {
|
|
2430
|
-
pointerPm =
|
|
2431
|
-
pointerKm =
|
|
2375
|
+
pointerPm = siblingNode.keys.splice(-1)[0];
|
|
2376
|
+
pointerKm = siblingNode.values.splice(-1)[0];
|
|
2432
2377
|
node.keys = [pointerPm, ...node.keys];
|
|
2433
2378
|
node.values = [pointerKm, ...node.values];
|
|
2434
|
-
parentNode = this.getNode(node.parent);
|
|
2379
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2435
2380
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2436
2381
|
if (nodeIndex > 0) {
|
|
2437
2382
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
@@ -2439,61 +2384,62 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2439
2384
|
}
|
|
2440
2385
|
}
|
|
2441
2386
|
this._updateNode(node);
|
|
2442
|
-
this._updateNode(
|
|
2387
|
+
this._updateNode(siblingNode);
|
|
2443
2388
|
} else {
|
|
2444
2389
|
let pointerP0;
|
|
2445
2390
|
let pointerK0;
|
|
2446
2391
|
if (!node.leaf) {
|
|
2447
|
-
pointerP0 =
|
|
2448
|
-
pointerK0 =
|
|
2392
|
+
pointerP0 = siblingNode.keys.splice(0, 1)[0];
|
|
2393
|
+
pointerK0 = siblingNode.values.splice(0, 1)[0];
|
|
2449
2394
|
node.keys = [...node.keys, pointerP0];
|
|
2450
2395
|
node.values = [...node.values, guess];
|
|
2451
|
-
parentNode = this.getNode(node.parent);
|
|
2452
|
-
const pointerIndex = parentNode.keys.indexOf(
|
|
2396
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2397
|
+
const pointerIndex = parentNode.keys.indexOf(siblingNode.id);
|
|
2453
2398
|
if (pointerIndex > 0) {
|
|
2454
2399
|
parentNode.values[pointerIndex - 1] = pointerK0;
|
|
2455
2400
|
this._updateNode(parentNode);
|
|
2456
2401
|
}
|
|
2457
2402
|
} else {
|
|
2458
|
-
pointerP0 =
|
|
2459
|
-
pointerK0 =
|
|
2403
|
+
pointerP0 = siblingNode.keys.splice(0, 1)[0];
|
|
2404
|
+
pointerK0 = siblingNode.values.splice(0, 1)[0];
|
|
2460
2405
|
node.keys = [...node.keys, pointerP0];
|
|
2461
2406
|
node.values = [...node.values, pointerK0];
|
|
2462
|
-
parentNode = this.getNode(node.parent);
|
|
2463
|
-
const pointerIndex = parentNode.keys.indexOf(
|
|
2407
|
+
parentNode = this._cloneNode(this.getNode(node.parent));
|
|
2408
|
+
const pointerIndex = parentNode.keys.indexOf(siblingNode.id);
|
|
2464
2409
|
if (pointerIndex > 0) {
|
|
2465
|
-
parentNode.values[pointerIndex - 1] =
|
|
2410
|
+
parentNode.values[pointerIndex - 1] = siblingNode.values[0];
|
|
2466
2411
|
this._updateNode(parentNode);
|
|
2467
2412
|
}
|
|
2468
2413
|
}
|
|
2469
2414
|
this._updateNode(node);
|
|
2470
|
-
this._updateNode(
|
|
2415
|
+
this._updateNode(siblingNode);
|
|
2471
2416
|
}
|
|
2472
|
-
if (!
|
|
2473
|
-
for (const key2 of
|
|
2474
|
-
const n = this.getNode(key2);
|
|
2475
|
-
n.parent =
|
|
2417
|
+
if (!siblingNode.leaf) {
|
|
2418
|
+
for (const key2 of siblingNode.keys) {
|
|
2419
|
+
const n = this._cloneNode(this.getNode(key2));
|
|
2420
|
+
n.parent = siblingNode.id;
|
|
2476
2421
|
this._updateNode(n);
|
|
2477
2422
|
}
|
|
2478
2423
|
}
|
|
2479
2424
|
if (!node.leaf) {
|
|
2480
2425
|
for (const key2 of node.keys) {
|
|
2481
|
-
const n = this.getNode(key2);
|
|
2426
|
+
const n = this._cloneNode(this.getNode(key2));
|
|
2482
2427
|
n.parent = node.id;
|
|
2483
2428
|
this._updateNode(n);
|
|
2484
2429
|
}
|
|
2485
2430
|
}
|
|
2486
2431
|
if (!parentNode.leaf) {
|
|
2487
2432
|
for (const key2 of parentNode.keys) {
|
|
2488
|
-
const n = this.getNode(key2);
|
|
2433
|
+
const n = this._cloneNode(this.getNode(key2));
|
|
2489
2434
|
n.parent = parentNode.id;
|
|
2490
2435
|
this._updateNode(n);
|
|
2491
2436
|
}
|
|
2492
2437
|
}
|
|
2493
2438
|
}
|
|
2494
2439
|
} else {
|
|
2495
|
-
this._updateNode(node);
|
|
2440
|
+
this._updateNode(this._cloneNode(node));
|
|
2496
2441
|
}
|
|
2442
|
+
return node;
|
|
2497
2443
|
}
|
|
2498
2444
|
delete(key, value) {
|
|
2499
2445
|
let node = this.insertableNodeByPrimary(value);
|
|
@@ -2506,13 +2452,15 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2506
2452
|
const keys = node.keys[i];
|
|
2507
2453
|
const keyIndex = keys.indexOf(key);
|
|
2508
2454
|
if (keyIndex !== -1) {
|
|
2509
|
-
|
|
2510
|
-
|
|
2455
|
+
node = this._cloneNode(node);
|
|
2456
|
+
const freshKeys = node.keys[i];
|
|
2457
|
+
freshKeys.splice(keyIndex, 1);
|
|
2458
|
+
if (freshKeys.length === 0) {
|
|
2511
2459
|
node.keys.splice(i, 1);
|
|
2512
2460
|
node.values.splice(i, 1);
|
|
2513
2461
|
}
|
|
2514
2462
|
this._updateNode(node);
|
|
2515
|
-
this._deleteEntry(node, key);
|
|
2463
|
+
node = this._deleteEntry(node, key);
|
|
2516
2464
|
found = true;
|
|
2517
2465
|
break;
|
|
2518
2466
|
}
|
|
@@ -2547,110 +2495,361 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
|
|
|
2547
2495
|
commit(label) {
|
|
2548
2496
|
let result = this.mvcc.commit(label);
|
|
2549
2497
|
if (result.success) {
|
|
2550
|
-
|
|
2551
|
-
if (
|
|
2552
|
-
|
|
2498
|
+
const isRootTx = this.rootTx !== this;
|
|
2499
|
+
if (isRootTx) {
|
|
2500
|
+
result = this.rootTx.commit(label);
|
|
2501
|
+
if (result.success) {
|
|
2502
|
+
this.rootTx.rootId = this.rootId;
|
|
2503
|
+
}
|
|
2553
2504
|
}
|
|
2554
2505
|
if (result.success) {
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
return result;
|
|
2509
|
+
}
|
|
2510
|
+
rollback() {
|
|
2511
|
+
return this.mvcc.rollback();
|
|
2512
|
+
}
|
|
2513
|
+
};
|
|
2514
|
+
var BPTreeMVCCStrategySync = class extends SyncMVCCStrategy {
|
|
2515
|
+
constructor(strategy) {
|
|
2516
|
+
super();
|
|
2517
|
+
this.strategy = strategy;
|
|
2518
|
+
}
|
|
2519
|
+
read(key) {
|
|
2520
|
+
if (key === "__HEAD__") {
|
|
2521
|
+
return this.strategy.readHead();
|
|
2522
|
+
}
|
|
2523
|
+
return this.strategy.read(key);
|
|
2524
|
+
}
|
|
2525
|
+
write(key, value) {
|
|
2526
|
+
if (key === "__HEAD__") {
|
|
2527
|
+
this.strategy.writeHead(value);
|
|
2528
|
+
} else {
|
|
2529
|
+
this.strategy.write(key, value);
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
delete(key) {
|
|
2533
|
+
this.strategy.delete(key);
|
|
2534
|
+
}
|
|
2535
|
+
exists(key) {
|
|
2536
|
+
if (key === "__HEAD__") {
|
|
2537
|
+
return this.strategy.readHead() !== null;
|
|
2538
|
+
}
|
|
2539
|
+
try {
|
|
2540
|
+
const node = this.strategy.read(key);
|
|
2541
|
+
return node !== null && node !== void 0;
|
|
2542
|
+
} catch {
|
|
2543
|
+
return false;
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
};
|
|
2547
|
+
var BPTreeSync = class extends BPTreeSyncTransaction {
|
|
2548
|
+
constructor(strategy, comparator, option) {
|
|
2549
|
+
const mvccRoot = new SyncMVCCTransaction(new BPTreeMVCCStrategySync(strategy), {
|
|
2550
|
+
cacheCapacity: option?.capacity ?? void 0
|
|
2551
|
+
});
|
|
2552
|
+
super(
|
|
2553
|
+
null,
|
|
2554
|
+
mvccRoot,
|
|
2555
|
+
mvccRoot,
|
|
2556
|
+
strategy,
|
|
2557
|
+
comparator,
|
|
2558
|
+
option
|
|
2559
|
+
);
|
|
2560
|
+
}
|
|
2561
|
+
/**
|
|
2562
|
+
* Creates a new synchronous transaction.
|
|
2563
|
+
* @returns A new BPTreeSyncTransaction.
|
|
2564
|
+
*/
|
|
2565
|
+
createTransaction() {
|
|
2566
|
+
const nestedTx = this.mvcc.createNested();
|
|
2567
|
+
const tx = new BPTreeSyncTransaction(
|
|
2568
|
+
this,
|
|
2569
|
+
this.mvcc,
|
|
2570
|
+
nestedTx,
|
|
2571
|
+
this.strategy,
|
|
2572
|
+
this.comparator,
|
|
2573
|
+
this.option
|
|
2574
|
+
);
|
|
2575
|
+
tx._initInternal();
|
|
2576
|
+
return tx;
|
|
2577
|
+
}
|
|
2578
|
+
insert(key, value) {
|
|
2579
|
+
const tx = this.createTransaction();
|
|
2580
|
+
tx.insert(key, value);
|
|
2581
|
+
const result = tx.commit();
|
|
2582
|
+
if (!result.success) {
|
|
2583
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
delete(key, value) {
|
|
2587
|
+
const tx = this.createTransaction();
|
|
2588
|
+
tx.delete(key, value);
|
|
2589
|
+
const result = tx.commit();
|
|
2590
|
+
if (!result.success) {
|
|
2591
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
};
|
|
2595
|
+
var Ryoiki2 = class _Ryoiki2 {
|
|
2596
|
+
readings;
|
|
2597
|
+
writings;
|
|
2598
|
+
readQueue;
|
|
2599
|
+
writeQueue;
|
|
2600
|
+
static async CatchError(promise) {
|
|
2601
|
+
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
2602
|
+
}
|
|
2603
|
+
static IsRangeOverlap(a, b) {
|
|
2604
|
+
const [start1, end1] = a;
|
|
2605
|
+
const [start2, end2] = b;
|
|
2606
|
+
if (end1 <= start2 || end2 <= start1) {
|
|
2607
|
+
return false;
|
|
2608
|
+
}
|
|
2609
|
+
return true;
|
|
2610
|
+
}
|
|
2611
|
+
static ERR_ALREADY_EXISTS(lockId) {
|
|
2612
|
+
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
2613
|
+
}
|
|
2614
|
+
static ERR_NOT_EXISTS(lockId) {
|
|
2615
|
+
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
2616
|
+
}
|
|
2617
|
+
static ERR_TIMEOUT(lockId, timeout) {
|
|
2618
|
+
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
2619
|
+
}
|
|
2620
|
+
/**
|
|
2621
|
+
* Constructs a new instance of the Ryoiki class.
|
|
2622
|
+
*/
|
|
2623
|
+
constructor() {
|
|
2624
|
+
this.readings = /* @__PURE__ */ new Map();
|
|
2625
|
+
this.writings = /* @__PURE__ */ new Map();
|
|
2626
|
+
this.readQueue = /* @__PURE__ */ new Map();
|
|
2627
|
+
this.writeQueue = /* @__PURE__ */ new Map();
|
|
2628
|
+
}
|
|
2629
|
+
/**
|
|
2630
|
+
* Creates a range based on a start value and length.
|
|
2631
|
+
* @param start - The starting value of the range.
|
|
2632
|
+
* @param length - The length of the range.
|
|
2633
|
+
* @returns A range tuple [start, start + length].
|
|
2634
|
+
*/
|
|
2635
|
+
range(start, length) {
|
|
2636
|
+
return [start, start + length];
|
|
2637
|
+
}
|
|
2638
|
+
rangeOverlapping(tasks, range) {
|
|
2639
|
+
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
2640
|
+
}
|
|
2641
|
+
isSameRange(a, b) {
|
|
2642
|
+
const [a1, a2] = a;
|
|
2643
|
+
const [b1, b2] = b;
|
|
2644
|
+
return a1 === b1 && a2 === b2;
|
|
2645
|
+
}
|
|
2646
|
+
fetchUnitAndRun(queue, workspaces) {
|
|
2647
|
+
for (const [id, unit] of queue) {
|
|
2648
|
+
if (!unit.condition()) {
|
|
2649
|
+
continue;
|
|
2650
|
+
}
|
|
2651
|
+
this._alloc(queue, workspaces, id);
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
_handleOverload(args, handlers, argPatterns) {
|
|
2655
|
+
for (const [key, pattern] of Object.entries(argPatterns)) {
|
|
2656
|
+
if (this._matchArgs(args, pattern)) {
|
|
2657
|
+
return handlers[key](...args);
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
throw new Error("Invalid arguments");
|
|
2661
|
+
}
|
|
2662
|
+
_matchArgs(args, pattern) {
|
|
2663
|
+
return args.every((arg, index) => {
|
|
2664
|
+
const expectedType = pattern[index];
|
|
2665
|
+
if (expectedType === void 0) return typeof arg === "undefined";
|
|
2666
|
+
if (expectedType === Function) return typeof arg === "function";
|
|
2667
|
+
if (expectedType === Number) return typeof arg === "number";
|
|
2668
|
+
if (expectedType === Array) return Array.isArray(arg);
|
|
2669
|
+
return false;
|
|
2670
|
+
});
|
|
2671
|
+
}
|
|
2672
|
+
_createRandomId() {
|
|
2673
|
+
const timestamp = Date.now().toString(36);
|
|
2674
|
+
const random = Math.random().toString(36).substring(2);
|
|
2675
|
+
return `${timestamp}${random}`;
|
|
2676
|
+
}
|
|
2677
|
+
_alloc(queue, workspaces, lockId) {
|
|
2678
|
+
const unit = queue.get(lockId);
|
|
2679
|
+
if (!unit) {
|
|
2680
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2681
|
+
}
|
|
2682
|
+
workspaces.set(lockId, unit);
|
|
2683
|
+
queue.delete(lockId);
|
|
2684
|
+
unit.alloc();
|
|
2685
|
+
}
|
|
2686
|
+
_free(workspaces, lockId) {
|
|
2687
|
+
const unit = workspaces.get(lockId);
|
|
2688
|
+
if (!unit) {
|
|
2689
|
+
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
2690
|
+
}
|
|
2691
|
+
workspaces.delete(lockId);
|
|
2692
|
+
unit.free();
|
|
2693
|
+
}
|
|
2694
|
+
_lock(queue, range, timeout, task, condition) {
|
|
2695
|
+
return new Promise((resolve, reject) => {
|
|
2696
|
+
let timeoutId = null;
|
|
2697
|
+
if (timeout >= 0) {
|
|
2698
|
+
timeoutId = setTimeout(() => {
|
|
2699
|
+
reject(_Ryoiki2.ERR_TIMEOUT(id, timeout));
|
|
2700
|
+
}, timeout);
|
|
2701
|
+
}
|
|
2702
|
+
const id = this._createRandomId();
|
|
2703
|
+
const alloc = async () => {
|
|
2704
|
+
if (timeoutId !== null) {
|
|
2705
|
+
clearTimeout(timeoutId);
|
|
2563
2706
|
}
|
|
2707
|
+
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
2708
|
+
if (err) reject(err);
|
|
2709
|
+
else resolve(v);
|
|
2710
|
+
};
|
|
2711
|
+
const fetch = () => {
|
|
2712
|
+
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
2713
|
+
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
2714
|
+
};
|
|
2715
|
+
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
2716
|
+
fetch();
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
_checkWorking(range, workspaces) {
|
|
2720
|
+
let isLocked = false;
|
|
2721
|
+
for (const lock of workspaces.values()) {
|
|
2722
|
+
if (_Ryoiki2.IsRangeOverlap(range, lock.range)) {
|
|
2723
|
+
isLocked = true;
|
|
2724
|
+
break;
|
|
2564
2725
|
}
|
|
2565
2726
|
}
|
|
2566
|
-
return
|
|
2567
|
-
}
|
|
2568
|
-
rollback() {
|
|
2569
|
-
return this.mvcc.rollback();
|
|
2570
|
-
}
|
|
2571
|
-
};
|
|
2572
|
-
var BPTreeMVCCStrategySync = class extends SyncMVCCStrategy {
|
|
2573
|
-
constructor(strategy) {
|
|
2574
|
-
super();
|
|
2575
|
-
this.strategy = strategy;
|
|
2727
|
+
return isLocked;
|
|
2576
2728
|
}
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2729
|
+
/**
|
|
2730
|
+
* Checks if there is any active read lock within the specified range.
|
|
2731
|
+
* @param range The range to check for active read locks.
|
|
2732
|
+
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
2733
|
+
*/
|
|
2734
|
+
isReading(range) {
|
|
2735
|
+
return this._checkWorking(range, this.readings);
|
|
2582
2736
|
}
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2737
|
+
/**
|
|
2738
|
+
* Checks if there is any active write lock within the specified range.
|
|
2739
|
+
* @param range The range to check for active write locks.
|
|
2740
|
+
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
2741
|
+
*/
|
|
2742
|
+
isWriting(range) {
|
|
2743
|
+
return this._checkWorking(range, this.writings);
|
|
2589
2744
|
}
|
|
2590
|
-
|
|
2591
|
-
|
|
2745
|
+
/**
|
|
2746
|
+
* Checks if a read lock can be acquired within the specified range.
|
|
2747
|
+
* @param range The range to check for read lock availability.
|
|
2748
|
+
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
2749
|
+
*/
|
|
2750
|
+
canRead(range) {
|
|
2751
|
+
const writing = this.isWriting(range);
|
|
2752
|
+
return !writing;
|
|
2592
2753
|
}
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
}
|
|
2754
|
+
/**
|
|
2755
|
+
* Checks if a write lock can be acquired within the specified range.
|
|
2756
|
+
* @param range The range to check for write lock availability.
|
|
2757
|
+
* @returns `true` if a write lock can be acquired, `false` otherwise.
|
|
2758
|
+
*/
|
|
2759
|
+
canWrite(range) {
|
|
2760
|
+
const reading = this.isReading(range);
|
|
2761
|
+
const writing = this.isWriting(range);
|
|
2762
|
+
return !reading && !writing;
|
|
2603
2763
|
}
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2764
|
+
/**
|
|
2765
|
+
* Internal implementation of the read lock. Handles both overloads.
|
|
2766
|
+
* @template T - The return type of the task.
|
|
2767
|
+
* @param arg0 - Either a range or a task callback.
|
|
2768
|
+
* If a range is provided, the task is the second argument.
|
|
2769
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2770
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2771
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2772
|
+
* If this value is not provided, no timeout will be set.
|
|
2773
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2774
|
+
*/
|
|
2775
|
+
readLock(arg0, arg1, arg2) {
|
|
2776
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2777
|
+
[arg0, arg1, arg2],
|
|
2778
|
+
{
|
|
2779
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2780
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2781
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2782
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2783
|
+
},
|
|
2784
|
+
{
|
|
2785
|
+
task: [Function],
|
|
2786
|
+
taskTimeout: [Function, Number],
|
|
2787
|
+
rangeTask: [Array, Function],
|
|
2788
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2789
|
+
}
|
|
2790
|
+
);
|
|
2791
|
+
return this._lock(
|
|
2792
|
+
this.readQueue,
|
|
2793
|
+
range,
|
|
2794
|
+
timeout,
|
|
2795
|
+
task,
|
|
2796
|
+
() => !this.rangeOverlapping(this.writings, range)
|
|
2615
2797
|
);
|
|
2616
2798
|
}
|
|
2617
2799
|
/**
|
|
2618
|
-
*
|
|
2619
|
-
* @
|
|
2800
|
+
* Internal implementation of the write lock. Handles both overloads.
|
|
2801
|
+
* @template T - The return type of the task.
|
|
2802
|
+
* @param arg0 - Either a range or a task callback.
|
|
2803
|
+
* If a range is provided, the task is the second argument.
|
|
2804
|
+
* @param arg1 - The task to execute, required if a range is provided.
|
|
2805
|
+
* @param arg2 - The timeout for acquiring the lock.
|
|
2806
|
+
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
2807
|
+
* If this value is not provided, no timeout will be set.
|
|
2808
|
+
* @returns A promise resolving to the result of the task execution.
|
|
2620
2809
|
*/
|
|
2621
|
-
|
|
2622
|
-
const
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2810
|
+
writeLock(arg0, arg1, arg2) {
|
|
2811
|
+
const [range, task, timeout] = this._handleOverload(
|
|
2812
|
+
[arg0, arg1, arg2],
|
|
2813
|
+
{
|
|
2814
|
+
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
2815
|
+
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
2816
|
+
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
2817
|
+
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
2818
|
+
},
|
|
2819
|
+
{
|
|
2820
|
+
task: [Function],
|
|
2821
|
+
taskTimeout: [Function, Number],
|
|
2822
|
+
rangeTask: [Array, Function],
|
|
2823
|
+
rangeTaskTimeout: [Array, Function, Number]
|
|
2824
|
+
}
|
|
2825
|
+
);
|
|
2826
|
+
return this._lock(
|
|
2827
|
+
this.writeQueue,
|
|
2828
|
+
range,
|
|
2829
|
+
timeout,
|
|
2830
|
+
task,
|
|
2831
|
+
() => {
|
|
2832
|
+
return !this.rangeOverlapping(this.writings, range) && !this.rangeOverlapping(this.readings, range);
|
|
2833
|
+
}
|
|
2630
2834
|
);
|
|
2631
|
-
tx.init();
|
|
2632
|
-
return tx;
|
|
2633
2835
|
}
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
}
|
|
2641
|
-
this.rootId = tx.getRootId();
|
|
2836
|
+
/**
|
|
2837
|
+
* Releases a read lock by its lock ID.
|
|
2838
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2839
|
+
*/
|
|
2840
|
+
readUnlock(lockId) {
|
|
2841
|
+
this._free(this.readings, lockId);
|
|
2642
2842
|
}
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
}
|
|
2650
|
-
this.rootId = tx.getRootId();
|
|
2843
|
+
/**
|
|
2844
|
+
* Releases a write lock by its lock ID.
|
|
2845
|
+
* @param lockId - The unique identifier for the lock to release.
|
|
2846
|
+
*/
|
|
2847
|
+
writeUnlock(lockId) {
|
|
2848
|
+
this._free(this.writings, lockId);
|
|
2651
2849
|
}
|
|
2652
2850
|
};
|
|
2653
2851
|
var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
2852
|
+
lock;
|
|
2654
2853
|
constructor(rootTx, mvccRoot, mvcc, strategy, comparator, option) {
|
|
2655
2854
|
super(
|
|
2656
2855
|
rootTx,
|
|
@@ -2660,11 +2859,18 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
2660
2859
|
comparator,
|
|
2661
2860
|
option
|
|
2662
2861
|
);
|
|
2862
|
+
this.lock = new Ryoiki2();
|
|
2863
|
+
}
|
|
2864
|
+
async writeLock(id, fn) {
|
|
2865
|
+
let lockId;
|
|
2866
|
+
return this.lock.writeLock([id, id + 0.1], async (_lockId) => {
|
|
2867
|
+
lockId = _lockId;
|
|
2868
|
+
return fn();
|
|
2869
|
+
}).finally(() => {
|
|
2870
|
+
this.lock.writeUnlock(lockId);
|
|
2871
|
+
});
|
|
2663
2872
|
}
|
|
2664
2873
|
async getNode(id) {
|
|
2665
|
-
if (this.nodes.has(id)) {
|
|
2666
|
-
return this.nodes.get(id);
|
|
2667
|
-
}
|
|
2668
2874
|
return await this.mvcc.read(id);
|
|
2669
2875
|
}
|
|
2670
2876
|
/**
|
|
@@ -2681,24 +2887,31 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
2681
2887
|
next,
|
|
2682
2888
|
prev
|
|
2683
2889
|
};
|
|
2890
|
+
const head = await this._readHead();
|
|
2891
|
+
if (head) {
|
|
2892
|
+
await this._writeHead({
|
|
2893
|
+
root: head.root,
|
|
2894
|
+
order: head.order,
|
|
2895
|
+
data: this.strategy.head.data
|
|
2896
|
+
});
|
|
2897
|
+
}
|
|
2684
2898
|
await this.mvcc.create(id, node);
|
|
2685
|
-
this.nodes.set(id, node);
|
|
2686
2899
|
return node;
|
|
2687
2900
|
}
|
|
2688
2901
|
async _updateNode(node) {
|
|
2902
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
2903
|
+
return;
|
|
2904
|
+
}
|
|
2689
2905
|
await this.mvcc.write(node.id, node);
|
|
2690
|
-
this.nodes.set(node.id, node);
|
|
2691
2906
|
}
|
|
2692
2907
|
async _deleteNode(node) {
|
|
2908
|
+
if (this.mvcc.isDeleted(node.id)) {
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2693
2911
|
await this.mvcc.delete(node.id);
|
|
2694
|
-
this.nodes.delete(node.id);
|
|
2695
2912
|
}
|
|
2696
2913
|
async _readHead() {
|
|
2697
|
-
|
|
2698
|
-
return this.nodes.get("__HEAD__") ?? null;
|
|
2699
|
-
}
|
|
2700
|
-
const head = await this.mvcc.read("__HEAD__");
|
|
2701
|
-
return head ?? null;
|
|
2914
|
+
return await this.mvcc.read("__HEAD__");
|
|
2702
2915
|
}
|
|
2703
2916
|
async _writeHead(head) {
|
|
2704
2917
|
if (!await this.mvcc.exists("__HEAD__")) {
|
|
@@ -2706,49 +2919,56 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
2706
2919
|
} else {
|
|
2707
2920
|
await this.mvcc.write("__HEAD__", head);
|
|
2708
2921
|
}
|
|
2709
|
-
this.nodes.set("__HEAD__", head);
|
|
2710
2922
|
this.rootId = head.root;
|
|
2711
2923
|
}
|
|
2712
2924
|
async _insertAtLeaf(node, key, value) {
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2925
|
+
let leaf = node;
|
|
2926
|
+
if (leaf.values.length) {
|
|
2927
|
+
for (let i = 0, len = leaf.values.length; i < len; i++) {
|
|
2928
|
+
const nValue = leaf.values[i];
|
|
2716
2929
|
if (this.comparator.isSame(value, nValue)) {
|
|
2717
|
-
const keys =
|
|
2930
|
+
const keys = leaf.keys[i];
|
|
2718
2931
|
if (keys.includes(key)) {
|
|
2719
2932
|
break;
|
|
2720
2933
|
}
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2934
|
+
leaf = this._cloneNode(leaf);
|
|
2935
|
+
leaf.keys[i].push(key);
|
|
2936
|
+
await this._updateNode(leaf);
|
|
2937
|
+
return leaf;
|
|
2724
2938
|
} else if (this.comparator.isLower(value, nValue)) {
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2939
|
+
leaf = this._cloneNode(leaf);
|
|
2940
|
+
leaf.values.splice(i, 0, value);
|
|
2941
|
+
leaf.keys.splice(i, 0, [key]);
|
|
2942
|
+
await this._updateNode(leaf);
|
|
2943
|
+
return leaf;
|
|
2944
|
+
} else if (i + 1 === leaf.values.length) {
|
|
2945
|
+
leaf = this._cloneNode(leaf);
|
|
2946
|
+
leaf.values.push(value);
|
|
2947
|
+
leaf.keys.push([key]);
|
|
2948
|
+
await this._updateNode(leaf);
|
|
2949
|
+
return leaf;
|
|
2734
2950
|
}
|
|
2735
2951
|
}
|
|
2736
2952
|
} else {
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2953
|
+
leaf = this._cloneNode(leaf);
|
|
2954
|
+
leaf.values = [value];
|
|
2955
|
+
leaf.keys = [[key]];
|
|
2956
|
+
await this._updateNode(leaf);
|
|
2957
|
+
return leaf;
|
|
2741
2958
|
}
|
|
2959
|
+
return leaf;
|
|
2742
2960
|
}
|
|
2743
|
-
async _insertInParent(node, value,
|
|
2961
|
+
async _insertInParent(node, value, newSiblingNode) {
|
|
2744
2962
|
if (this.rootId === node.id) {
|
|
2745
|
-
|
|
2963
|
+
node = this._cloneNode(node);
|
|
2964
|
+
newSiblingNode = this._cloneNode(newSiblingNode);
|
|
2965
|
+
const root = await this._createNode(false, [node.id, newSiblingNode.id], [value]);
|
|
2746
2966
|
this.rootId = root.id;
|
|
2747
2967
|
node.parent = root.id;
|
|
2748
|
-
|
|
2749
|
-
if (
|
|
2750
|
-
node.next =
|
|
2751
|
-
|
|
2968
|
+
newSiblingNode.parent = root.id;
|
|
2969
|
+
if (newSiblingNode.leaf) {
|
|
2970
|
+
node.next = newSiblingNode.id;
|
|
2971
|
+
newSiblingNode.prev = node.id;
|
|
2752
2972
|
}
|
|
2753
2973
|
await this._writeHead({
|
|
2754
2974
|
root: root.id,
|
|
@@ -2756,53 +2976,54 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
2756
2976
|
data: this.strategy.head.data
|
|
2757
2977
|
});
|
|
2758
2978
|
await this._updateNode(node);
|
|
2759
|
-
await this._updateNode(
|
|
2979
|
+
await this._updateNode(newSiblingNode);
|
|
2760
2980
|
return;
|
|
2761
2981
|
}
|
|
2762
|
-
const parentNode = await this.getNode(node.parent);
|
|
2982
|
+
const parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
2763
2983
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
2764
2984
|
if (nodeIndex === -1) {
|
|
2765
2985
|
throw new Error(`Node ${node.id} not found in parent ${parentNode.id}`);
|
|
2766
2986
|
}
|
|
2767
2987
|
parentNode.values.splice(nodeIndex, 0, value);
|
|
2768
|
-
parentNode.keys.splice(nodeIndex + 1, 0,
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2988
|
+
parentNode.keys.splice(nodeIndex + 1, 0, newSiblingNode.id);
|
|
2989
|
+
newSiblingNode = this._cloneNode(newSiblingNode);
|
|
2990
|
+
newSiblingNode.parent = parentNode.id;
|
|
2991
|
+
if (newSiblingNode.leaf) {
|
|
2992
|
+
const leftSibling = this._cloneNode(node);
|
|
2772
2993
|
const oldNextId = leftSibling.next;
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
leftSibling.next =
|
|
2994
|
+
newSiblingNode.prev = leftSibling.id;
|
|
2995
|
+
newSiblingNode.next = oldNextId;
|
|
2996
|
+
leftSibling.next = newSiblingNode.id;
|
|
2776
2997
|
await this._updateNode(leftSibling);
|
|
2777
2998
|
if (oldNextId) {
|
|
2778
|
-
const oldNext = await this.getNode(oldNextId);
|
|
2779
|
-
oldNext.prev =
|
|
2999
|
+
const oldNext = this._cloneNode(await this.getNode(oldNextId));
|
|
3000
|
+
oldNext.prev = newSiblingNode.id;
|
|
2780
3001
|
await this._updateNode(oldNext);
|
|
2781
3002
|
}
|
|
2782
3003
|
}
|
|
2783
3004
|
await this._updateNode(parentNode);
|
|
2784
|
-
await this._updateNode(
|
|
3005
|
+
await this._updateNode(newSiblingNode);
|
|
2785
3006
|
if (parentNode.keys.length > this.order) {
|
|
2786
|
-
const
|
|
2787
|
-
|
|
3007
|
+
const newSiblingNodeRecursive = await this._createNode(false, [], []);
|
|
3008
|
+
newSiblingNodeRecursive.parent = parentNode.parent;
|
|
2788
3009
|
const mid = Math.ceil(this.order / 2) - 1;
|
|
2789
|
-
|
|
2790
|
-
|
|
3010
|
+
newSiblingNodeRecursive.values = parentNode.values.slice(mid + 1);
|
|
3011
|
+
newSiblingNodeRecursive.keys = parentNode.keys.slice(mid + 1);
|
|
2791
3012
|
const midValue = parentNode.values[mid];
|
|
2792
3013
|
parentNode.values = parentNode.values.slice(0, mid);
|
|
2793
3014
|
parentNode.keys = parentNode.keys.slice(0, mid + 1);
|
|
2794
3015
|
for (const k of parentNode.keys) {
|
|
2795
|
-
const n = await this.getNode(k);
|
|
3016
|
+
const n = this._cloneNode(await this.getNode(k));
|
|
2796
3017
|
n.parent = parentNode.id;
|
|
2797
3018
|
await this._updateNode(n);
|
|
2798
3019
|
}
|
|
2799
|
-
for (const k of
|
|
2800
|
-
const n = await this.getNode(k);
|
|
2801
|
-
n.parent =
|
|
3020
|
+
for (const k of newSiblingNodeRecursive.keys) {
|
|
3021
|
+
const n = this._cloneNode(await this.getNode(k));
|
|
3022
|
+
n.parent = newSiblingNodeRecursive.id;
|
|
2802
3023
|
await this._updateNode(n);
|
|
2803
3024
|
}
|
|
2804
3025
|
await this._updateNode(parentNode);
|
|
2805
|
-
await this._insertInParent(parentNode, midValue,
|
|
3026
|
+
await this._insertInParent(parentNode, midValue, newSiblingNodeRecursive);
|
|
2806
3027
|
}
|
|
2807
3028
|
}
|
|
2808
3029
|
async insertableNode(value) {
|
|
@@ -2938,28 +3159,46 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
2938
3159
|
}
|
|
2939
3160
|
}
|
|
2940
3161
|
async init() {
|
|
2941
|
-
this.
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
const { root, order } = head;
|
|
2953
|
-
this.strategy.head = head;
|
|
2954
|
-
this.order = order;
|
|
2955
|
-
await this._writeHead({
|
|
2956
|
-
root,
|
|
2957
|
-
order: this.order,
|
|
2958
|
-
data: this.strategy.head.data
|
|
2959
|
-
});
|
|
3162
|
+
if (this.rootTx !== this) {
|
|
3163
|
+
throw new Error("Cannot call init on a nested transaction");
|
|
3164
|
+
}
|
|
3165
|
+
return await this._initInternal();
|
|
3166
|
+
}
|
|
3167
|
+
async _initInternal() {
|
|
3168
|
+
if (this.isInitialized) {
|
|
3169
|
+
throw new Error("Transaction already initialized");
|
|
3170
|
+
}
|
|
3171
|
+
if (this.isDestroyed) {
|
|
3172
|
+
throw new Error("Transaction already destroyed");
|
|
2960
3173
|
}
|
|
2961
|
-
|
|
2962
|
-
|
|
3174
|
+
this.isInitialized = true;
|
|
3175
|
+
try {
|
|
3176
|
+
this._clearCache();
|
|
3177
|
+
const head = await this._readHead();
|
|
3178
|
+
if (head === null) {
|
|
3179
|
+
this.order = this.strategy.order;
|
|
3180
|
+
const root = await this._createNode(true, [], []);
|
|
3181
|
+
await this._writeHead({
|
|
3182
|
+
root: root.id,
|
|
3183
|
+
order: this.order,
|
|
3184
|
+
data: this.strategy.head.data
|
|
3185
|
+
});
|
|
3186
|
+
} else {
|
|
3187
|
+
const { root, order } = head;
|
|
3188
|
+
this.strategy.head = head;
|
|
3189
|
+
this.order = order;
|
|
3190
|
+
await this._writeHead({
|
|
3191
|
+
root,
|
|
3192
|
+
order: this.order,
|
|
3193
|
+
data: this.strategy.head.data
|
|
3194
|
+
});
|
|
3195
|
+
}
|
|
3196
|
+
if (this.order < 3) {
|
|
3197
|
+
throw new Error(`The 'order' parameter must be greater than 2. but got a '${this.order}'.`);
|
|
3198
|
+
}
|
|
3199
|
+
} catch (e) {
|
|
3200
|
+
this.isInitialized = false;
|
|
3201
|
+
throw e;
|
|
2963
3202
|
}
|
|
2964
3203
|
}
|
|
2965
3204
|
async exists(key, value) {
|
|
@@ -2973,21 +3212,6 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
2973
3212
|
}
|
|
2974
3213
|
return false;
|
|
2975
3214
|
}
|
|
2976
|
-
async forceUpdate(id) {
|
|
2977
|
-
if (id) {
|
|
2978
|
-
this.nodes.delete(id);
|
|
2979
|
-
await this.getNode(id);
|
|
2980
|
-
return 1;
|
|
2981
|
-
}
|
|
2982
|
-
const keys = Array.from(this.nodes.keys());
|
|
2983
|
-
for (const key of keys) {
|
|
2984
|
-
this.nodes.delete(key);
|
|
2985
|
-
}
|
|
2986
|
-
for (const key of keys) {
|
|
2987
|
-
await this.getNode(key);
|
|
2988
|
-
}
|
|
2989
|
-
return keys.length;
|
|
2990
|
-
}
|
|
2991
3215
|
async get(key) {
|
|
2992
3216
|
let node = await this.leftestNode();
|
|
2993
3217
|
while (true) {
|
|
@@ -3072,25 +3296,30 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3072
3296
|
}
|
|
3073
3297
|
return map;
|
|
3074
3298
|
}
|
|
3075
|
-
async insert(key, value) {
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3299
|
+
async insert(key, value) {
|
|
3300
|
+
return this.writeLock(0, async () => {
|
|
3301
|
+
let before = await this.insertableNode(value);
|
|
3302
|
+
before = await this._insertAtLeaf(before, key, value);
|
|
3303
|
+
if (before.values.length === this.order) {
|
|
3304
|
+
let after = await this._createNode(
|
|
3305
|
+
true,
|
|
3306
|
+
[],
|
|
3307
|
+
[],
|
|
3308
|
+
before.parent,
|
|
3309
|
+
null,
|
|
3310
|
+
null
|
|
3311
|
+
);
|
|
3312
|
+
const mid = Math.ceil(this.order / 2) - 1;
|
|
3313
|
+
after = this._cloneNode(after);
|
|
3314
|
+
after.values = before.values.slice(mid + 1);
|
|
3315
|
+
after.keys = before.keys.slice(mid + 1);
|
|
3316
|
+
before.values = before.values.slice(0, mid + 1);
|
|
3317
|
+
before.keys = before.keys.slice(0, mid + 1);
|
|
3318
|
+
await this._updateNode(before);
|
|
3319
|
+
await this._updateNode(after);
|
|
3320
|
+
await this._insertInParent(before, after.values[0], after);
|
|
3321
|
+
}
|
|
3322
|
+
});
|
|
3094
3323
|
}
|
|
3095
3324
|
async _deleteEntry(node, key) {
|
|
3096
3325
|
if (!node.leaf) {
|
|
@@ -3102,6 +3331,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3102
3331
|
}
|
|
3103
3332
|
}
|
|
3104
3333
|
if (keyIndex !== -1) {
|
|
3334
|
+
node = this._cloneNode(node);
|
|
3105
3335
|
node.keys.splice(keyIndex, 1);
|
|
3106
3336
|
const valueIndex = keyIndex > 0 ? keyIndex - 1 : 0;
|
|
3107
3337
|
node.values.splice(valueIndex, 1);
|
|
@@ -3111,7 +3341,7 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3111
3341
|
if (this.rootId === node.id && node.keys.length === 1 && !node.leaf) {
|
|
3112
3342
|
const keys = node.keys;
|
|
3113
3343
|
this._deleteNode(node);
|
|
3114
|
-
const newRoot = await this.getNode(keys[0]);
|
|
3344
|
+
const newRoot = this._cloneNode(await this.getNode(keys[0]));
|
|
3115
3345
|
newRoot.parent = null;
|
|
3116
3346
|
await this._updateNode(newRoot);
|
|
3117
3347
|
await this._writeHead({
|
|
@@ -3119,14 +3349,17 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3119
3349
|
order: this.order,
|
|
3120
3350
|
data: this.strategy.head.data
|
|
3121
3351
|
});
|
|
3122
|
-
return;
|
|
3352
|
+
return node;
|
|
3123
3353
|
} else if (this.rootId === node.id) {
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3354
|
+
await this._writeHead({
|
|
3355
|
+
root: node.id,
|
|
3356
|
+
order: this.order,
|
|
3357
|
+
data: this.strategy.head.data
|
|
3358
|
+
});
|
|
3359
|
+
return node;
|
|
3127
3360
|
} else if (node.keys.length < Math.ceil(this.order / 2) && !node.leaf || node.values.length < Math.ceil((this.order - 1) / 2) && node.leaf) {
|
|
3128
3361
|
if (node.parent === null) {
|
|
3129
|
-
return;
|
|
3362
|
+
return node;
|
|
3130
3363
|
}
|
|
3131
3364
|
let isPredecessor = false;
|
|
3132
3365
|
let parentNode = await this.getNode(node.parent);
|
|
@@ -3147,78 +3380,80 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3147
3380
|
}
|
|
3148
3381
|
}
|
|
3149
3382
|
}
|
|
3150
|
-
let
|
|
3383
|
+
let siblingNode;
|
|
3151
3384
|
let guess;
|
|
3152
3385
|
if (prevNode === null) {
|
|
3153
|
-
|
|
3386
|
+
siblingNode = nextNode;
|
|
3154
3387
|
guess = postValue;
|
|
3155
3388
|
} else if (nextNode === null) {
|
|
3156
3389
|
isPredecessor = true;
|
|
3157
|
-
|
|
3390
|
+
siblingNode = prevNode;
|
|
3158
3391
|
guess = prevValue;
|
|
3159
3392
|
} else {
|
|
3160
3393
|
if (node.values.length + nextNode.values.length < this.order) {
|
|
3161
|
-
|
|
3394
|
+
siblingNode = nextNode;
|
|
3162
3395
|
guess = postValue;
|
|
3163
3396
|
} else {
|
|
3164
3397
|
isPredecessor = true;
|
|
3165
|
-
|
|
3398
|
+
siblingNode = prevNode;
|
|
3166
3399
|
guess = prevValue;
|
|
3167
3400
|
}
|
|
3168
3401
|
}
|
|
3169
|
-
if (!
|
|
3170
|
-
return;
|
|
3402
|
+
if (!siblingNode) {
|
|
3403
|
+
return node;
|
|
3171
3404
|
}
|
|
3172
|
-
|
|
3405
|
+
node = this._cloneNode(node);
|
|
3406
|
+
siblingNode = this._cloneNode(siblingNode);
|
|
3407
|
+
if (node.values.length + siblingNode.values.length < this.order) {
|
|
3173
3408
|
if (!isPredecessor) {
|
|
3174
|
-
const pTemp =
|
|
3175
|
-
|
|
3409
|
+
const pTemp = siblingNode;
|
|
3410
|
+
siblingNode = node;
|
|
3176
3411
|
node = pTemp;
|
|
3177
3412
|
}
|
|
3178
|
-
|
|
3413
|
+
siblingNode.keys.push(...node.keys);
|
|
3179
3414
|
if (!node.leaf) {
|
|
3180
|
-
|
|
3415
|
+
siblingNode.values.push(guess);
|
|
3181
3416
|
} else {
|
|
3182
|
-
|
|
3183
|
-
if (
|
|
3184
|
-
const n = await this.getNode(
|
|
3185
|
-
n.prev =
|
|
3417
|
+
siblingNode.next = node.next;
|
|
3418
|
+
if (siblingNode.next) {
|
|
3419
|
+
const n = this._cloneNode(await this.getNode(siblingNode.next));
|
|
3420
|
+
n.prev = siblingNode.id;
|
|
3186
3421
|
await this._updateNode(n);
|
|
3187
3422
|
}
|
|
3188
3423
|
}
|
|
3189
|
-
|
|
3190
|
-
if (!
|
|
3191
|
-
const keys =
|
|
3424
|
+
siblingNode.values.push(...node.values);
|
|
3425
|
+
if (!siblingNode.leaf) {
|
|
3426
|
+
const keys = siblingNode.keys;
|
|
3192
3427
|
for (const key2 of keys) {
|
|
3193
|
-
const node2 = await this.getNode(key2);
|
|
3194
|
-
node2.parent =
|
|
3428
|
+
const node2 = this._cloneNode(await this.getNode(key2));
|
|
3429
|
+
node2.parent = siblingNode.id;
|
|
3195
3430
|
await this._updateNode(node2);
|
|
3196
3431
|
}
|
|
3197
3432
|
}
|
|
3198
3433
|
this._deleteNode(node);
|
|
3199
|
-
await this._updateNode(
|
|
3434
|
+
await this._updateNode(siblingNode);
|
|
3200
3435
|
await this._deleteEntry(await this.getNode(node.parent), node.id);
|
|
3201
3436
|
} else {
|
|
3202
3437
|
if (isPredecessor) {
|
|
3203
3438
|
let pointerPm;
|
|
3204
3439
|
let pointerKm;
|
|
3205
3440
|
if (!node.leaf) {
|
|
3206
|
-
pointerPm =
|
|
3207
|
-
pointerKm =
|
|
3441
|
+
pointerPm = siblingNode.keys.splice(-1)[0];
|
|
3442
|
+
pointerKm = siblingNode.values.splice(-1)[0];
|
|
3208
3443
|
node.keys = [pointerPm, ...node.keys];
|
|
3209
3444
|
node.values = [guess, ...node.values];
|
|
3210
|
-
parentNode = await this.getNode(node.parent);
|
|
3445
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3211
3446
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
3212
3447
|
if (nodeIndex > 0) {
|
|
3213
3448
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
3214
3449
|
await this._updateNode(parentNode);
|
|
3215
3450
|
}
|
|
3216
3451
|
} else {
|
|
3217
|
-
pointerPm =
|
|
3218
|
-
pointerKm =
|
|
3452
|
+
pointerPm = siblingNode.keys.splice(-1)[0];
|
|
3453
|
+
pointerKm = siblingNode.values.splice(-1)[0];
|
|
3219
3454
|
node.keys = [pointerPm, ...node.keys];
|
|
3220
3455
|
node.values = [pointerKm, ...node.values];
|
|
3221
|
-
parentNode = await this.getNode(node.parent);
|
|
3456
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3222
3457
|
const nodeIndex = parentNode.keys.indexOf(node.id);
|
|
3223
3458
|
if (nodeIndex > 0) {
|
|
3224
3459
|
parentNode.values[nodeIndex - 1] = pointerKm;
|
|
@@ -3226,92 +3461,97 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3226
3461
|
}
|
|
3227
3462
|
}
|
|
3228
3463
|
await this._updateNode(node);
|
|
3229
|
-
await this._updateNode(
|
|
3464
|
+
await this._updateNode(siblingNode);
|
|
3230
3465
|
} else {
|
|
3231
3466
|
let pointerP0;
|
|
3232
3467
|
let pointerK0;
|
|
3233
3468
|
if (!node.leaf) {
|
|
3234
|
-
pointerP0 =
|
|
3235
|
-
pointerK0 =
|
|
3469
|
+
pointerP0 = siblingNode.keys.splice(0, 1)[0];
|
|
3470
|
+
pointerK0 = siblingNode.values.splice(0, 1)[0];
|
|
3236
3471
|
node.keys = [...node.keys, pointerP0];
|
|
3237
3472
|
node.values = [...node.values, guess];
|
|
3238
|
-
parentNode = await this.getNode(node.parent);
|
|
3239
|
-
const pointerIndex = parentNode.keys.indexOf(
|
|
3473
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3474
|
+
const pointerIndex = parentNode.keys.indexOf(siblingNode.id);
|
|
3240
3475
|
if (pointerIndex > 0) {
|
|
3241
3476
|
parentNode.values[pointerIndex - 1] = pointerK0;
|
|
3242
3477
|
await this._updateNode(parentNode);
|
|
3243
3478
|
}
|
|
3244
3479
|
} else {
|
|
3245
|
-
pointerP0 =
|
|
3246
|
-
pointerK0 =
|
|
3480
|
+
pointerP0 = siblingNode.keys.splice(0, 1)[0];
|
|
3481
|
+
pointerK0 = siblingNode.values.splice(0, 1)[0];
|
|
3247
3482
|
node.keys = [...node.keys, pointerP0];
|
|
3248
3483
|
node.values = [...node.values, pointerK0];
|
|
3249
|
-
parentNode = await this.getNode(node.parent);
|
|
3250
|
-
const pointerIndex = parentNode.keys.indexOf(
|
|
3484
|
+
parentNode = this._cloneNode(await this.getNode(node.parent));
|
|
3485
|
+
const pointerIndex = parentNode.keys.indexOf(siblingNode.id);
|
|
3251
3486
|
if (pointerIndex > 0) {
|
|
3252
|
-
parentNode.values[pointerIndex - 1] =
|
|
3487
|
+
parentNode.values[pointerIndex - 1] = siblingNode.values[0];
|
|
3253
3488
|
await this._updateNode(parentNode);
|
|
3254
3489
|
}
|
|
3255
3490
|
}
|
|
3256
3491
|
await this._updateNode(node);
|
|
3257
|
-
await this._updateNode(
|
|
3492
|
+
await this._updateNode(siblingNode);
|
|
3258
3493
|
}
|
|
3259
|
-
if (!
|
|
3260
|
-
for (const key2 of
|
|
3261
|
-
const n = await this.getNode(key2);
|
|
3262
|
-
n.parent =
|
|
3494
|
+
if (!siblingNode.leaf) {
|
|
3495
|
+
for (const key2 of siblingNode.keys) {
|
|
3496
|
+
const n = this._cloneNode(await this.getNode(key2));
|
|
3497
|
+
n.parent = siblingNode.id;
|
|
3263
3498
|
await this._updateNode(n);
|
|
3264
3499
|
}
|
|
3265
3500
|
}
|
|
3266
3501
|
if (!node.leaf) {
|
|
3267
3502
|
for (const key2 of node.keys) {
|
|
3268
|
-
const n = await this.getNode(key2);
|
|
3503
|
+
const n = this._cloneNode(await this.getNode(key2));
|
|
3269
3504
|
n.parent = node.id;
|
|
3270
3505
|
await this._updateNode(n);
|
|
3271
3506
|
}
|
|
3272
3507
|
}
|
|
3273
3508
|
if (!parentNode.leaf) {
|
|
3274
3509
|
for (const key2 of parentNode.keys) {
|
|
3275
|
-
const n = await this.getNode(key2);
|
|
3510
|
+
const n = this._cloneNode(await this.getNode(key2));
|
|
3276
3511
|
n.parent = parentNode.id;
|
|
3277
3512
|
await this._updateNode(n);
|
|
3278
3513
|
}
|
|
3279
3514
|
}
|
|
3280
3515
|
}
|
|
3281
3516
|
} else {
|
|
3282
|
-
await this._updateNode(node);
|
|
3517
|
+
await this._updateNode(this._cloneNode(node));
|
|
3283
3518
|
}
|
|
3519
|
+
return node;
|
|
3284
3520
|
}
|
|
3285
3521
|
async delete(key, value) {
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
node.keys
|
|
3299
|
-
|
|
3522
|
+
return this.writeLock(0, async () => {
|
|
3523
|
+
let node = await this.insertableNodeByPrimary(value);
|
|
3524
|
+
let found = false;
|
|
3525
|
+
while (true) {
|
|
3526
|
+
let i = node.values.length;
|
|
3527
|
+
while (i--) {
|
|
3528
|
+
const nValue = node.values[i];
|
|
3529
|
+
if (this.comparator.isSame(value, nValue)) {
|
|
3530
|
+
const keys = node.keys[i];
|
|
3531
|
+
const keyIndex = keys.indexOf(key);
|
|
3532
|
+
if (keyIndex !== -1) {
|
|
3533
|
+
node = this._cloneNode(node);
|
|
3534
|
+
const freshKeys = node.keys[i];
|
|
3535
|
+
freshKeys.splice(keyIndex, 1);
|
|
3536
|
+
if (freshKeys.length === 0) {
|
|
3537
|
+
node.keys.splice(i, 1);
|
|
3538
|
+
node.values.splice(i, 1);
|
|
3539
|
+
}
|
|
3540
|
+
await this._updateNode(node);
|
|
3541
|
+
node = await this._deleteEntry(node, key);
|
|
3542
|
+
found = true;
|
|
3543
|
+
break;
|
|
3300
3544
|
}
|
|
3301
|
-
await this._updateNode(node);
|
|
3302
|
-
await this._deleteEntry(node, key);
|
|
3303
|
-
found = true;
|
|
3304
|
-
break;
|
|
3305
3545
|
}
|
|
3306
3546
|
}
|
|
3547
|
+
if (found) break;
|
|
3548
|
+
if (node.next) {
|
|
3549
|
+
node = await this.getNode(node.next);
|
|
3550
|
+
continue;
|
|
3551
|
+
}
|
|
3552
|
+
break;
|
|
3307
3553
|
}
|
|
3308
|
-
|
|
3309
|
-
if (node.next) {
|
|
3310
|
-
node = await this.getNode(node.next);
|
|
3311
|
-
continue;
|
|
3312
|
-
}
|
|
3313
|
-
break;
|
|
3314
|
-
}
|
|
3554
|
+
});
|
|
3315
3555
|
}
|
|
3316
3556
|
async getHeadData() {
|
|
3317
3557
|
const head = await this._readHead();
|
|
@@ -3320,439 +3560,179 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
|
|
|
3320
3560
|
}
|
|
3321
3561
|
return head.data;
|
|
3322
3562
|
}
|
|
3323
|
-
async setHeadData(data) {
|
|
3324
|
-
const head = await this._readHead();
|
|
3325
|
-
if (head === null) {
|
|
3326
|
-
throw new Error("Head not found");
|
|
3327
|
-
}
|
|
3328
|
-
await this._writeHead({
|
|
3329
|
-
root: head.root,
|
|
3330
|
-
order: head.order,
|
|
3331
|
-
data
|
|
3332
|
-
});
|
|
3333
|
-
}
|
|
3334
|
-
async commit(label) {
|
|
3335
|
-
let result = await this.mvcc.commit(label);
|
|
3336
|
-
if (result.success) {
|
|
3337
|
-
result = await this.mvccRoot.commit(label);
|
|
3338
|
-
if (result.success && this.rootTx !== this) {
|
|
3339
|
-
this.rootTx.rootId = this.rootId;
|
|
3340
|
-
}
|
|
3341
|
-
if (result.success) {
|
|
3342
|
-
for (const r of result.created) {
|
|
3343
|
-
this.nodes.set(r.key, r.data);
|
|
3344
|
-
}
|
|
3345
|
-
for (const r of result.updated) {
|
|
3346
|
-
this.nodes.set(r.key, r.data);
|
|
3347
|
-
}
|
|
3348
|
-
for (const r of result.deleted) {
|
|
3349
|
-
this.nodes.delete(r.key);
|
|
3350
|
-
}
|
|
3351
|
-
}
|
|
3352
|
-
}
|
|
3353
|
-
return result;
|
|
3354
|
-
}
|
|
3355
|
-
rollback() {
|
|
3356
|
-
return this.mvcc.rollback();
|
|
3357
|
-
}
|
|
3358
|
-
};
|
|
3359
|
-
var BPTreeMVCCStrategyAsync = class extends AsyncMVCCStrategy {
|
|
3360
|
-
constructor(strategy) {
|
|
3361
|
-
super();
|
|
3362
|
-
this.strategy = strategy;
|
|
3363
|
-
}
|
|
3364
|
-
async read(key) {
|
|
3365
|
-
if (key === "__HEAD__") {
|
|
3366
|
-
return await this.strategy.readHead();
|
|
3367
|
-
}
|
|
3368
|
-
return await this.strategy.read(key);
|
|
3369
|
-
}
|
|
3370
|
-
async write(key, value) {
|
|
3371
|
-
if (key === "__HEAD__") {
|
|
3372
|
-
await this.strategy.writeHead(value);
|
|
3373
|
-
} else {
|
|
3374
|
-
await this.strategy.write(key, value);
|
|
3375
|
-
}
|
|
3376
|
-
}
|
|
3377
|
-
async delete(key) {
|
|
3378
|
-
await this.strategy.delete(key);
|
|
3379
|
-
}
|
|
3380
|
-
async exists(key) {
|
|
3381
|
-
if (key === "__HEAD__") {
|
|
3382
|
-
return await this.strategy.readHead() !== null;
|
|
3383
|
-
}
|
|
3384
|
-
try {
|
|
3385
|
-
const node = await this.strategy.read(key);
|
|
3386
|
-
return node !== null && node !== void 0;
|
|
3387
|
-
} catch {
|
|
3388
|
-
return false;
|
|
3389
|
-
}
|
|
3390
|
-
}
|
|
3391
|
-
};
|
|
3392
|
-
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3393
|
-
constructor(strategy, comparator, option) {
|
|
3394
|
-
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy));
|
|
3395
|
-
super(
|
|
3396
|
-
null,
|
|
3397
|
-
mvccRoot,
|
|
3398
|
-
mvccRoot,
|
|
3399
|
-
strategy,
|
|
3400
|
-
comparator,
|
|
3401
|
-
option
|
|
3402
|
-
);
|
|
3403
|
-
}
|
|
3404
|
-
/**
|
|
3405
|
-
* Creates a new asynchronous transaction.
|
|
3406
|
-
* @returns A new BPTreeAsyncTransaction.
|
|
3407
|
-
*/
|
|
3408
|
-
async createTransaction() {
|
|
3409
|
-
const nestedTx = await this.mvcc.createNested();
|
|
3410
|
-
const tx = new BPTreeAsyncTransaction(
|
|
3411
|
-
this,
|
|
3412
|
-
this.mvcc,
|
|
3413
|
-
nestedTx,
|
|
3414
|
-
this.strategy,
|
|
3415
|
-
this.comparator,
|
|
3416
|
-
this.option
|
|
3417
|
-
);
|
|
3418
|
-
await tx.init();
|
|
3419
|
-
return tx;
|
|
3420
|
-
}
|
|
3421
|
-
async insert(key, value) {
|
|
3422
|
-
const tx = await this.createTransaction();
|
|
3423
|
-
await tx.insert(key, value);
|
|
3424
|
-
const result = await tx.commit();
|
|
3425
|
-
if (!result.success) {
|
|
3426
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3427
|
-
}
|
|
3428
|
-
this.rootId = tx.getRootId();
|
|
3429
|
-
}
|
|
3430
|
-
async delete(key, value) {
|
|
3431
|
-
const tx = await this.createTransaction();
|
|
3432
|
-
await tx.delete(key, value);
|
|
3433
|
-
const result = await tx.commit();
|
|
3434
|
-
if (!result.success) {
|
|
3435
|
-
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3436
|
-
}
|
|
3437
|
-
this.rootId = tx.getRootId();
|
|
3438
|
-
}
|
|
3439
|
-
};
|
|
3440
|
-
var SerializeStrategy = class {
|
|
3441
|
-
order;
|
|
3442
|
-
head;
|
|
3443
|
-
constructor(order) {
|
|
3444
|
-
this.order = order;
|
|
3445
|
-
this.head = {
|
|
3446
|
-
order,
|
|
3447
|
-
root: null,
|
|
3448
|
-
data: {}
|
|
3449
|
-
};
|
|
3450
|
-
}
|
|
3451
|
-
};
|
|
3452
|
-
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3453
|
-
getHeadData(key, defaultValue) {
|
|
3454
|
-
if (!Object.hasOwn(this.head.data, key)) {
|
|
3455
|
-
this.setHeadData(key, defaultValue);
|
|
3456
|
-
}
|
|
3457
|
-
return this.head.data[key];
|
|
3458
|
-
}
|
|
3459
|
-
setHeadData(key, data) {
|
|
3460
|
-
this.head.data[key] = data;
|
|
3461
|
-
this.writeHead(this.head);
|
|
3462
|
-
}
|
|
3463
|
-
autoIncrement(key, defaultValue) {
|
|
3464
|
-
const current = this.getHeadData(key, defaultValue);
|
|
3465
|
-
const next = current + 1;
|
|
3466
|
-
this.setHeadData(key, next);
|
|
3467
|
-
return current;
|
|
3468
|
-
}
|
|
3469
|
-
};
|
|
3470
|
-
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3471
|
-
node;
|
|
3472
|
-
constructor(order) {
|
|
3473
|
-
super(order);
|
|
3474
|
-
this.node = {};
|
|
3475
|
-
}
|
|
3476
|
-
id(isLeaf) {
|
|
3477
|
-
return this.autoIncrement("index", 1).toString();
|
|
3478
|
-
}
|
|
3479
|
-
read(id) {
|
|
3480
|
-
if (!Object.hasOwn(this.node, id)) {
|
|
3481
|
-
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3482
|
-
}
|
|
3483
|
-
const node = this.node[id];
|
|
3484
|
-
return JSON.parse(JSON.stringify(node));
|
|
3485
|
-
}
|
|
3486
|
-
write(id, node) {
|
|
3487
|
-
this.node[id] = node;
|
|
3488
|
-
}
|
|
3489
|
-
delete(id) {
|
|
3490
|
-
delete this.node[id];
|
|
3491
|
-
}
|
|
3492
|
-
readHead() {
|
|
3493
|
-
if (this.head.root === null) {
|
|
3494
|
-
return null;
|
|
3495
|
-
}
|
|
3496
|
-
return this.head;
|
|
3497
|
-
}
|
|
3498
|
-
writeHead(head) {
|
|
3499
|
-
this.head = head;
|
|
3500
|
-
}
|
|
3501
|
-
};
|
|
3502
|
-
var Ryoiki2 = class _Ryoiki2 {
|
|
3503
|
-
readings;
|
|
3504
|
-
writings;
|
|
3505
|
-
readQueue;
|
|
3506
|
-
writeQueue;
|
|
3507
|
-
static async CatchError(promise) {
|
|
3508
|
-
return await promise.then((v) => [void 0, v]).catch((err) => [err]);
|
|
3509
|
-
}
|
|
3510
|
-
static IsRangeOverlap(a, b) {
|
|
3511
|
-
const [start1, end1] = a;
|
|
3512
|
-
const [start2, end2] = b;
|
|
3513
|
-
if (end1 <= start2 || end2 <= start1) {
|
|
3514
|
-
return false;
|
|
3515
|
-
}
|
|
3516
|
-
return true;
|
|
3517
|
-
}
|
|
3518
|
-
static ERR_ALREADY_EXISTS(lockId) {
|
|
3519
|
-
return new Error(`The '${lockId}' task already existing in queue or running.`);
|
|
3520
|
-
}
|
|
3521
|
-
static ERR_NOT_EXISTS(lockId) {
|
|
3522
|
-
return new Error(`The '${lockId}' task not existing in task queue.`);
|
|
3523
|
-
}
|
|
3524
|
-
static ERR_TIMEOUT(lockId, timeout) {
|
|
3525
|
-
return new Error(`The task with ID '${lockId}' failed to acquire the lock within the timeout(${timeout}ms).`);
|
|
3526
|
-
}
|
|
3527
|
-
/**
|
|
3528
|
-
* Constructs a new instance of the Ryoiki class.
|
|
3529
|
-
*/
|
|
3530
|
-
constructor() {
|
|
3531
|
-
this.readings = /* @__PURE__ */ new Map();
|
|
3532
|
-
this.writings = /* @__PURE__ */ new Map();
|
|
3533
|
-
this.readQueue = /* @__PURE__ */ new Map();
|
|
3534
|
-
this.writeQueue = /* @__PURE__ */ new Map();
|
|
3535
|
-
}
|
|
3536
|
-
/**
|
|
3537
|
-
* Creates a range based on a start value and length.
|
|
3538
|
-
* @param start - The starting value of the range.
|
|
3539
|
-
* @param length - The length of the range.
|
|
3540
|
-
* @returns A range tuple [start, start + length].
|
|
3541
|
-
*/
|
|
3542
|
-
range(start, length) {
|
|
3543
|
-
return [start, start + length];
|
|
3544
|
-
}
|
|
3545
|
-
rangeOverlapping(tasks, range) {
|
|
3546
|
-
return Array.from(tasks.values()).some((t) => _Ryoiki2.IsRangeOverlap(t.range, range));
|
|
3547
|
-
}
|
|
3548
|
-
isSameRange(a, b) {
|
|
3549
|
-
const [a1, a2] = a;
|
|
3550
|
-
const [b1, b2] = b;
|
|
3551
|
-
return a1 === b1 && a2 === b2;
|
|
3552
|
-
}
|
|
3553
|
-
fetchUnitAndRun(queue, workspaces) {
|
|
3554
|
-
for (const [id, unit] of queue) {
|
|
3555
|
-
if (!unit.condition()) {
|
|
3556
|
-
continue;
|
|
3557
|
-
}
|
|
3558
|
-
this._alloc(queue, workspaces, id);
|
|
3563
|
+
async setHeadData(data) {
|
|
3564
|
+
const head = await this._readHead();
|
|
3565
|
+
if (head === null) {
|
|
3566
|
+
throw new Error("Head not found");
|
|
3559
3567
|
}
|
|
3568
|
+
await this._writeHead({
|
|
3569
|
+
root: head.root,
|
|
3570
|
+
order: head.order,
|
|
3571
|
+
data
|
|
3572
|
+
});
|
|
3560
3573
|
}
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3574
|
+
async commit(label) {
|
|
3575
|
+
let result = await this.mvcc.commit(label);
|
|
3576
|
+
if (result.success) {
|
|
3577
|
+
const isRootTx = this.rootTx === this;
|
|
3578
|
+
if (!isRootTx) {
|
|
3579
|
+
result = await this.rootTx.commit(label);
|
|
3580
|
+
if (result.success) {
|
|
3581
|
+
this.rootTx.rootId = this.rootId;
|
|
3582
|
+
}
|
|
3565
3583
|
}
|
|
3566
3584
|
}
|
|
3567
|
-
|
|
3585
|
+
return result;
|
|
3568
3586
|
}
|
|
3569
|
-
|
|
3570
|
-
return
|
|
3571
|
-
const expectedType = pattern[index];
|
|
3572
|
-
if (expectedType === void 0) return typeof arg === "undefined";
|
|
3573
|
-
if (expectedType === Function) return typeof arg === "function";
|
|
3574
|
-
if (expectedType === Number) return typeof arg === "number";
|
|
3575
|
-
if (expectedType === Array) return Array.isArray(arg);
|
|
3576
|
-
return false;
|
|
3577
|
-
});
|
|
3587
|
+
async rollback() {
|
|
3588
|
+
return this.mvcc.rollback();
|
|
3578
3589
|
}
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3590
|
+
};
|
|
3591
|
+
var BPTreeMVCCStrategyAsync = class extends AsyncMVCCStrategy {
|
|
3592
|
+
constructor(strategy) {
|
|
3593
|
+
super();
|
|
3594
|
+
this.strategy = strategy;
|
|
3583
3595
|
}
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
throw _Ryoiki2.ERR_NOT_EXISTS(lockId);
|
|
3596
|
+
async read(key) {
|
|
3597
|
+
if (key === "__HEAD__") {
|
|
3598
|
+
return await this.strategy.readHead();
|
|
3588
3599
|
}
|
|
3589
|
-
|
|
3590
|
-
queue.delete(lockId);
|
|
3591
|
-
unit.alloc();
|
|
3600
|
+
return await this.strategy.read(key);
|
|
3592
3601
|
}
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3602
|
+
async write(key, value) {
|
|
3603
|
+
if (key === "__HEAD__") {
|
|
3604
|
+
await this.strategy.writeHead(value);
|
|
3605
|
+
} else {
|
|
3606
|
+
await this.strategy.write(key, value);
|
|
3597
3607
|
}
|
|
3598
|
-
workspaces.delete(lockId);
|
|
3599
|
-
unit.free();
|
|
3600
3608
|
}
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
let timeoutId = null;
|
|
3604
|
-
if (timeout >= 0) {
|
|
3605
|
-
timeoutId = setTimeout(() => {
|
|
3606
|
-
reject(_Ryoiki2.ERR_TIMEOUT(id, timeout));
|
|
3607
|
-
}, timeout);
|
|
3608
|
-
}
|
|
3609
|
-
const id = this._createRandomId();
|
|
3610
|
-
const alloc = async () => {
|
|
3611
|
-
if (timeoutId !== null) {
|
|
3612
|
-
clearTimeout(timeoutId);
|
|
3613
|
-
}
|
|
3614
|
-
const [err, v] = await _Ryoiki2.CatchError(task(id));
|
|
3615
|
-
if (err) reject(err);
|
|
3616
|
-
else resolve(v);
|
|
3617
|
-
};
|
|
3618
|
-
const fetch = () => {
|
|
3619
|
-
this.fetchUnitAndRun(this.readQueue, this.readings);
|
|
3620
|
-
this.fetchUnitAndRun(this.writeQueue, this.writings);
|
|
3621
|
-
};
|
|
3622
|
-
queue.set(id, { id, range, condition, alloc, free: fetch });
|
|
3623
|
-
fetch();
|
|
3624
|
-
});
|
|
3609
|
+
async delete(key) {
|
|
3610
|
+
await this.strategy.delete(key);
|
|
3625
3611
|
}
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3612
|
+
async exists(key) {
|
|
3613
|
+
if (key === "__HEAD__") {
|
|
3614
|
+
return await this.strategy.readHead() !== null;
|
|
3615
|
+
}
|
|
3616
|
+
try {
|
|
3617
|
+
const node = await this.strategy.read(key);
|
|
3618
|
+
return node !== null && node !== void 0;
|
|
3619
|
+
} catch {
|
|
3620
|
+
return false;
|
|
3633
3621
|
}
|
|
3634
|
-
return isLocked;
|
|
3635
|
-
}
|
|
3636
|
-
/**
|
|
3637
|
-
* Checks if there is any active read lock within the specified range.
|
|
3638
|
-
* @param range The range to check for active read locks.
|
|
3639
|
-
* @returns `true` if there is an active read lock within the range, `false` otherwise.
|
|
3640
|
-
*/
|
|
3641
|
-
isReading(range) {
|
|
3642
|
-
return this._checkWorking(range, this.readings);
|
|
3643
|
-
}
|
|
3644
|
-
/**
|
|
3645
|
-
* Checks if there is any active write lock within the specified range.
|
|
3646
|
-
* @param range The range to check for active write locks.
|
|
3647
|
-
* @returns `true` if there is an active write lock within the range, `false` otherwise.
|
|
3648
|
-
*/
|
|
3649
|
-
isWriting(range) {
|
|
3650
|
-
return this._checkWorking(range, this.writings);
|
|
3651
|
-
}
|
|
3652
|
-
/**
|
|
3653
|
-
* Checks if a read lock can be acquired within the specified range.
|
|
3654
|
-
* @param range The range to check for read lock availability.
|
|
3655
|
-
* @returns `true` if a read lock can be acquired, `false` otherwise.
|
|
3656
|
-
*/
|
|
3657
|
-
canRead(range) {
|
|
3658
|
-
const writing = this.isWriting(range);
|
|
3659
|
-
return !writing;
|
|
3660
3622
|
}
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3623
|
+
};
|
|
3624
|
+
var BPTreeAsync = class extends BPTreeAsyncTransaction {
|
|
3625
|
+
constructor(strategy, comparator, option) {
|
|
3626
|
+
const mvccRoot = new AsyncMVCCTransaction(new BPTreeMVCCStrategyAsync(strategy), {
|
|
3627
|
+
cacheCapacity: option?.capacity ?? void 0
|
|
3628
|
+
});
|
|
3629
|
+
super(
|
|
3630
|
+
null,
|
|
3631
|
+
mvccRoot,
|
|
3632
|
+
mvccRoot,
|
|
3633
|
+
strategy,
|
|
3634
|
+
comparator,
|
|
3635
|
+
option
|
|
3636
|
+
);
|
|
3670
3637
|
}
|
|
3671
3638
|
/**
|
|
3672
|
-
*
|
|
3673
|
-
* @
|
|
3674
|
-
* @param arg0 - Either a range or a task callback.
|
|
3675
|
-
* If a range is provided, the task is the second argument.
|
|
3676
|
-
* @param arg1 - The task to execute, required if a range is provided.
|
|
3677
|
-
* @param arg2 - The timeout for acquiring the lock.
|
|
3678
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
3679
|
-
* If this value is not provided, no timeout will be set.
|
|
3680
|
-
* @returns A promise resolving to the result of the task execution.
|
|
3639
|
+
* Creates a new asynchronous transaction.
|
|
3640
|
+
* @returns A new BPTreeAsyncTransaction.
|
|
3681
3641
|
*/
|
|
3682
|
-
|
|
3683
|
-
const
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
{
|
|
3692
|
-
task: [Function],
|
|
3693
|
-
taskTimeout: [Function, Number],
|
|
3694
|
-
rangeTask: [Array, Function],
|
|
3695
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
3696
|
-
}
|
|
3697
|
-
);
|
|
3698
|
-
return this._lock(
|
|
3699
|
-
this.readQueue,
|
|
3700
|
-
range,
|
|
3701
|
-
timeout,
|
|
3702
|
-
task,
|
|
3703
|
-
() => !this.rangeOverlapping(this.writings, range)
|
|
3642
|
+
async createTransaction() {
|
|
3643
|
+
const nestedTx = this.mvcc.createNested();
|
|
3644
|
+
const tx = new BPTreeAsyncTransaction(
|
|
3645
|
+
this,
|
|
3646
|
+
this.mvcc,
|
|
3647
|
+
nestedTx,
|
|
3648
|
+
this.strategy,
|
|
3649
|
+
this.comparator,
|
|
3650
|
+
this.option
|
|
3704
3651
|
);
|
|
3652
|
+
await tx._initInternal();
|
|
3653
|
+
return tx;
|
|
3705
3654
|
}
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
* If the lock cannot be acquired within this period, an error will be thrown.
|
|
3714
|
-
* If this value is not provided, no timeout will be set.
|
|
3715
|
-
* @returns A promise resolving to the result of the task execution.
|
|
3716
|
-
*/
|
|
3717
|
-
writeLock(arg0, arg1, arg2) {
|
|
3718
|
-
const [range, task, timeout] = this._handleOverload(
|
|
3719
|
-
[arg0, arg1, arg2],
|
|
3720
|
-
{
|
|
3721
|
-
rangeTask: (range2, task2) => [range2, task2, -1],
|
|
3722
|
-
rangeTaskTimeout: (range2, task2, timeout2) => [range2, task2, timeout2],
|
|
3723
|
-
task: (task2) => [[-Infinity, Infinity], task2, -1],
|
|
3724
|
-
taskTimeout: (task2, timeout2) => [[-Infinity, Infinity], task2, timeout2]
|
|
3725
|
-
},
|
|
3726
|
-
{
|
|
3727
|
-
task: [Function],
|
|
3728
|
-
taskTimeout: [Function, Number],
|
|
3729
|
-
rangeTask: [Array, Function],
|
|
3730
|
-
rangeTaskTimeout: [Array, Function, Number]
|
|
3655
|
+
async insert(key, value) {
|
|
3656
|
+
return this.writeLock(1, async () => {
|
|
3657
|
+
const tx = await this.createTransaction();
|
|
3658
|
+
await tx.insert(key, value);
|
|
3659
|
+
const result = await tx.commit();
|
|
3660
|
+
if (!result.success) {
|
|
3661
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3731
3662
|
}
|
|
3732
|
-
);
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
()
|
|
3739
|
-
|
|
3663
|
+
});
|
|
3664
|
+
}
|
|
3665
|
+
async delete(key, value) {
|
|
3666
|
+
return this.writeLock(1, async () => {
|
|
3667
|
+
const tx = await this.createTransaction();
|
|
3668
|
+
await tx.delete(key, value);
|
|
3669
|
+
const result = await tx.commit();
|
|
3670
|
+
if (!result.success) {
|
|
3671
|
+
throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
|
|
3740
3672
|
}
|
|
3741
|
-
);
|
|
3673
|
+
});
|
|
3674
|
+
}
|
|
3675
|
+
};
|
|
3676
|
+
var SerializeStrategy = class {
|
|
3677
|
+
order;
|
|
3678
|
+
head;
|
|
3679
|
+
constructor(order) {
|
|
3680
|
+
this.order = order;
|
|
3681
|
+
this.head = {
|
|
3682
|
+
order,
|
|
3683
|
+
root: null,
|
|
3684
|
+
data: {}
|
|
3685
|
+
};
|
|
3686
|
+
}
|
|
3687
|
+
};
|
|
3688
|
+
var SerializeStrategySync = class extends SerializeStrategy {
|
|
3689
|
+
getHeadData(key, defaultValue) {
|
|
3690
|
+
if (!Object.hasOwn(this.head.data, key)) {
|
|
3691
|
+
this.setHeadData(key, defaultValue);
|
|
3692
|
+
}
|
|
3693
|
+
return this.head.data[key];
|
|
3694
|
+
}
|
|
3695
|
+
setHeadData(key, data) {
|
|
3696
|
+
this.head.data[key] = data;
|
|
3697
|
+
this.writeHead(this.head);
|
|
3742
3698
|
}
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
this._free(this.readings, lockId);
|
|
3699
|
+
autoIncrement(key, defaultValue) {
|
|
3700
|
+
const current = this.getHeadData(key, defaultValue);
|
|
3701
|
+
const next = current + 1;
|
|
3702
|
+
this.setHeadData(key, next);
|
|
3703
|
+
return current;
|
|
3749
3704
|
}
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
this.
|
|
3705
|
+
};
|
|
3706
|
+
var InMemoryStoreStrategySync = class extends SerializeStrategySync {
|
|
3707
|
+
node;
|
|
3708
|
+
constructor(order) {
|
|
3709
|
+
super(order);
|
|
3710
|
+
this.node = {};
|
|
3711
|
+
}
|
|
3712
|
+
id(isLeaf) {
|
|
3713
|
+
return this.autoIncrement("index", 1).toString();
|
|
3714
|
+
}
|
|
3715
|
+
read(id) {
|
|
3716
|
+
if (!Object.hasOwn(this.node, id)) {
|
|
3717
|
+
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`);
|
|
3718
|
+
}
|
|
3719
|
+
const node = this.node[id];
|
|
3720
|
+
return JSON.parse(JSON.stringify(node));
|
|
3721
|
+
}
|
|
3722
|
+
write(id, node) {
|
|
3723
|
+
this.node[id] = node;
|
|
3724
|
+
}
|
|
3725
|
+
delete(id) {
|
|
3726
|
+
delete this.node[id];
|
|
3727
|
+
}
|
|
3728
|
+
readHead() {
|
|
3729
|
+
if (this.head.root === null) {
|
|
3730
|
+
return null;
|
|
3731
|
+
}
|
|
3732
|
+
return this.head;
|
|
3733
|
+
}
|
|
3734
|
+
writeHead(head) {
|
|
3735
|
+
this.head = head;
|
|
3756
3736
|
}
|
|
3757
3737
|
};
|
|
3758
3738
|
var SerializeStrategyAsync = class extends SerializeStrategy {
|
|
@@ -4197,7 +4177,7 @@ var LRUMap2 = class {
|
|
|
4197
4177
|
this.tail = null;
|
|
4198
4178
|
}
|
|
4199
4179
|
};
|
|
4200
|
-
var
|
|
4180
|
+
var CacheEntanglement = class {
|
|
4201
4181
|
creation;
|
|
4202
4182
|
beforeUpdateHook;
|
|
4203
4183
|
capacity;
|
|
@@ -4298,7 +4278,7 @@ var CacheEntanglement2 = class {
|
|
|
4298
4278
|
}
|
|
4299
4279
|
}
|
|
4300
4280
|
};
|
|
4301
|
-
var
|
|
4281
|
+
var CacheData = class _CacheData {
|
|
4302
4282
|
static StructuredClone = globalThis.structuredClone.bind(globalThis);
|
|
4303
4283
|
_value;
|
|
4304
4284
|
constructor(value) {
|
|
@@ -4338,11 +4318,11 @@ var CacheData2 = class _CacheData2 {
|
|
|
4338
4318
|
return Object.assign({}, this.raw);
|
|
4339
4319
|
case "deep-copy":
|
|
4340
4320
|
default:
|
|
4341
|
-
return
|
|
4321
|
+
return _CacheData.StructuredClone(this.raw);
|
|
4342
4322
|
}
|
|
4343
4323
|
}
|
|
4344
4324
|
};
|
|
4345
|
-
var
|
|
4325
|
+
var CacheEntanglementSync = class extends CacheEntanglement {
|
|
4346
4326
|
constructor(creation, option) {
|
|
4347
4327
|
super(creation, option);
|
|
4348
4328
|
}
|
|
@@ -4372,7 +4352,7 @@ var CacheEntanglementSync2 = class extends CacheEntanglement2 {
|
|
|
4372
4352
|
const dependencyValue = dependency.recache(key) ?? dependency.recache(dependencyKey);
|
|
4373
4353
|
resolved[name] = dependencyValue;
|
|
4374
4354
|
}
|
|
4375
|
-
const value = new
|
|
4355
|
+
const value = new CacheData(this.creation(key, resolved, ...parameter));
|
|
4376
4356
|
this.updateRequirements.delete(key);
|
|
4377
4357
|
this.parameters.set(key, parameter);
|
|
4378
4358
|
this.caches.set(key, value);
|
|
@@ -4396,7 +4376,7 @@ var CacheEntanglementSync2 = class extends CacheEntanglement2 {
|
|
|
4396
4376
|
return this.caches.get(key);
|
|
4397
4377
|
}
|
|
4398
4378
|
};
|
|
4399
|
-
var CacheEntanglementAsync = class extends
|
|
4379
|
+
var CacheEntanglementAsync = class extends CacheEntanglement {
|
|
4400
4380
|
constructor(creation, option) {
|
|
4401
4381
|
super(creation, option);
|
|
4402
4382
|
}
|
|
@@ -4426,7 +4406,7 @@ var CacheEntanglementAsync = class extends CacheEntanglement2 {
|
|
|
4426
4406
|
const dependencyValue = await dependency.recache(key) ?? await dependency.recache(dependencyKey);
|
|
4427
4407
|
resolved[name] = dependencyValue;
|
|
4428
4408
|
}
|
|
4429
|
-
const value = new
|
|
4409
|
+
const value = new CacheData(await this.creation(key, resolved, ...parameter));
|
|
4430
4410
|
this.updateRequirements.delete(key);
|
|
4431
4411
|
this.parameters.set(key, parameter);
|
|
4432
4412
|
this.caches.set(key, value);
|
|
@@ -4531,6 +4511,42 @@ var InvertedWeakMap = class {
|
|
|
4531
4511
|
// node_modules/mvcc-api/dist/esm/index.mjs
|
|
4532
4512
|
var MVCCStrategy2 = class {
|
|
4533
4513
|
};
|
|
4514
|
+
var LRUMap3 = class {
|
|
4515
|
+
cache = /* @__PURE__ */ new Map();
|
|
4516
|
+
capacity;
|
|
4517
|
+
constructor(capacity) {
|
|
4518
|
+
this.capacity = capacity;
|
|
4519
|
+
}
|
|
4520
|
+
get(key) {
|
|
4521
|
+
if (!this.cache.has(key)) return void 0;
|
|
4522
|
+
const value = this.cache.get(key);
|
|
4523
|
+
this.cache.delete(key);
|
|
4524
|
+
this.cache.set(key, value);
|
|
4525
|
+
return value;
|
|
4526
|
+
}
|
|
4527
|
+
set(key, value) {
|
|
4528
|
+
if (this.cache.has(key)) {
|
|
4529
|
+
this.cache.delete(key);
|
|
4530
|
+
} else if (this.cache.size >= this.capacity) {
|
|
4531
|
+
const oldestKey = this.cache.keys().next().value;
|
|
4532
|
+
if (oldestKey !== void 0) this.cache.delete(oldestKey);
|
|
4533
|
+
}
|
|
4534
|
+
this.cache.set(key, value);
|
|
4535
|
+
return this;
|
|
4536
|
+
}
|
|
4537
|
+
has(key) {
|
|
4538
|
+
return this.cache.has(key);
|
|
4539
|
+
}
|
|
4540
|
+
delete(key) {
|
|
4541
|
+
return this.cache.delete(key);
|
|
4542
|
+
}
|
|
4543
|
+
clear() {
|
|
4544
|
+
this.cache.clear();
|
|
4545
|
+
}
|
|
4546
|
+
get size() {
|
|
4547
|
+
return this.cache.size;
|
|
4548
|
+
}
|
|
4549
|
+
};
|
|
4534
4550
|
var MVCCTransaction2 = class {
|
|
4535
4551
|
committed;
|
|
4536
4552
|
snapshotVersion;
|
|
@@ -4538,11 +4554,11 @@ var MVCCTransaction2 = class {
|
|
|
4538
4554
|
writeBuffer;
|
|
4539
4555
|
deleteBuffer;
|
|
4540
4556
|
createdKeys;
|
|
4541
|
-
// create()로 생성된 키 추적
|
|
4542
4557
|
deletedValues;
|
|
4543
4558
|
// delete 시 삭제 전 값 저장
|
|
4544
4559
|
originallyExisted;
|
|
4545
4560
|
// 트랜잭션 시작 시점에 디스크에 존재했던 키 (deleted 결과 필터링용)
|
|
4561
|
+
bufferHistory = /* @__PURE__ */ new Map();
|
|
4546
4562
|
// Nested Transaction Properties
|
|
4547
4563
|
parent;
|
|
4548
4564
|
localVersion;
|
|
@@ -4556,7 +4572,8 @@ var MVCCTransaction2 = class {
|
|
|
4556
4572
|
versionIndex = /* @__PURE__ */ new Map();
|
|
4557
4573
|
deletedCache = /* @__PURE__ */ new Map();
|
|
4558
4574
|
activeTransactions = /* @__PURE__ */ new Set();
|
|
4559
|
-
|
|
4575
|
+
diskCache;
|
|
4576
|
+
constructor(strategy, options, parent, snapshotVersion) {
|
|
4560
4577
|
this.snapshotVersion = snapshotVersion ?? 0;
|
|
4561
4578
|
this.writeBuffer = /* @__PURE__ */ new Map();
|
|
4562
4579
|
this.deleteBuffer = /* @__PURE__ */ new Set();
|
|
@@ -4571,6 +4588,7 @@ var MVCCTransaction2 = class {
|
|
|
4571
4588
|
this.snapshotLocalVersion = parent.localVersion;
|
|
4572
4589
|
this.strategy = void 0;
|
|
4573
4590
|
this.root = parent.root;
|
|
4591
|
+
this.diskCache = parent.diskCache;
|
|
4574
4592
|
} else {
|
|
4575
4593
|
if (!strategy) throw new Error("Root Transaction must get Strategy");
|
|
4576
4594
|
this.strategy = strategy;
|
|
@@ -4578,8 +4596,13 @@ var MVCCTransaction2 = class {
|
|
|
4578
4596
|
this.localVersion = 0;
|
|
4579
4597
|
this.snapshotLocalVersion = 0;
|
|
4580
4598
|
this.root = this;
|
|
4599
|
+
this.diskCache = new LRUMap3(options?.cacheCapacity ?? 1e3);
|
|
4581
4600
|
}
|
|
4582
4601
|
}
|
|
4602
|
+
/**
|
|
4603
|
+
* Checks if the transaction is a root transaction.
|
|
4604
|
+
* @returns True if the transaction is a root transaction, false otherwise.
|
|
4605
|
+
*/
|
|
4583
4606
|
isRoot() {
|
|
4584
4607
|
return !this.parent;
|
|
4585
4608
|
}
|
|
@@ -4596,27 +4619,96 @@ var MVCCTransaction2 = class {
|
|
|
4596
4619
|
}
|
|
4597
4620
|
return false;
|
|
4598
4621
|
}
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4622
|
+
/**
|
|
4623
|
+
* Checks if a key was written in this transaction.
|
|
4624
|
+
* @param key The key to check.
|
|
4625
|
+
* @returns True if the key was written in this transaction, false otherwise.
|
|
4626
|
+
*/
|
|
4627
|
+
isWrote(key) {
|
|
4628
|
+
return this.writeBuffer.has(key);
|
|
4629
|
+
}
|
|
4630
|
+
/**
|
|
4631
|
+
* Checks if a key was deleted in this transaction.
|
|
4632
|
+
* @param key The key to check.
|
|
4633
|
+
* @returns True if the key was deleted in this transaction, false otherwise.
|
|
4634
|
+
*/
|
|
4635
|
+
isDeleted(key) {
|
|
4636
|
+
return this.deleteBuffer.has(key);
|
|
4637
|
+
}
|
|
4638
|
+
_recordHistory(key) {
|
|
4639
|
+
const existsInWriteBuffer = this.writeBuffer.has(key);
|
|
4640
|
+
const existsInDeleteBuffer = this.deleteBuffer.has(key);
|
|
4641
|
+
const currentVer = this.keyVersions.get(key);
|
|
4642
|
+
if (currentVer !== void 0) {
|
|
4643
|
+
if (!this.bufferHistory.has(key)) this.bufferHistory.set(key, []);
|
|
4644
|
+
this.bufferHistory.get(key).push({
|
|
4645
|
+
value: existsInWriteBuffer ? this.writeBuffer.get(key) : this.deletedValues.get(key) ?? null,
|
|
4646
|
+
exists: existsInWriteBuffer || !existsInDeleteBuffer,
|
|
4647
|
+
version: currentVer
|
|
4648
|
+
});
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
/**
|
|
4652
|
+
* BINARY SEARCH HELPER: Finds the index of the last element in the array
|
|
4653
|
+
* where item[key] <= target. Assumes the array is sorted by 'key' ascending.
|
|
4654
|
+
*/
|
|
4655
|
+
_findLastLE(array, target, property) {
|
|
4656
|
+
let left = 0;
|
|
4657
|
+
let right = array.length - 1;
|
|
4658
|
+
let result = -1;
|
|
4659
|
+
while (left <= right) {
|
|
4660
|
+
const mid = left + right >> 1;
|
|
4661
|
+
if (array[mid][property] <= target) {
|
|
4662
|
+
result = mid;
|
|
4663
|
+
left = mid + 1;
|
|
4664
|
+
} else {
|
|
4665
|
+
right = mid - 1;
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
return result;
|
|
4669
|
+
}
|
|
4670
|
+
/**
|
|
4671
|
+
* BINARY SEARCH HELPER: Finds the index of the element in the array
|
|
4672
|
+
* where item[key] === target. Assumes the array is sorted by 'key' ascending.
|
|
4673
|
+
*/
|
|
4674
|
+
_findExact(array, target, property) {
|
|
4675
|
+
let left = 0;
|
|
4676
|
+
let right = array.length - 1;
|
|
4677
|
+
while (left <= right) {
|
|
4678
|
+
const mid = left + right >> 1;
|
|
4679
|
+
const val = array[mid][property];
|
|
4680
|
+
if (val === target) return mid;
|
|
4681
|
+
if (val < target) left = mid + 1;
|
|
4682
|
+
else right = mid - 1;
|
|
4683
|
+
}
|
|
4684
|
+
return -1;
|
|
4685
|
+
}
|
|
4686
|
+
_bufferCreate(key, value, version) {
|
|
4687
|
+
if (version === void 0) this.localVersion++;
|
|
4688
|
+
const targetVersion = version ?? this.localVersion;
|
|
4689
|
+
this._recordHistory(key);
|
|
4602
4690
|
this.writeBuffer.set(key, value);
|
|
4603
4691
|
this.createdKeys.add(key);
|
|
4604
4692
|
this.deleteBuffer.delete(key);
|
|
4605
4693
|
this.originallyExisted.delete(key);
|
|
4606
|
-
this.keyVersions.set(key,
|
|
4694
|
+
this.keyVersions.set(key, targetVersion);
|
|
4607
4695
|
}
|
|
4608
|
-
_bufferWrite(key, value) {
|
|
4609
|
-
this.localVersion++;
|
|
4696
|
+
_bufferWrite(key, value, version) {
|
|
4697
|
+
if (version === void 0) this.localVersion++;
|
|
4698
|
+
const targetVersion = version ?? this.localVersion;
|
|
4699
|
+
this._recordHistory(key);
|
|
4610
4700
|
this.writeBuffer.set(key, value);
|
|
4611
4701
|
this.deleteBuffer.delete(key);
|
|
4612
|
-
this.keyVersions.set(key,
|
|
4702
|
+
this.keyVersions.set(key, targetVersion);
|
|
4613
4703
|
}
|
|
4614
|
-
_bufferDelete(key) {
|
|
4615
|
-
this.localVersion++;
|
|
4704
|
+
_bufferDelete(key, version) {
|
|
4705
|
+
if (version === void 0) this.localVersion++;
|
|
4706
|
+
const targetVersion = version ?? this.localVersion;
|
|
4707
|
+
this._recordHistory(key);
|
|
4616
4708
|
this.deleteBuffer.add(key);
|
|
4617
4709
|
this.writeBuffer.delete(key);
|
|
4618
4710
|
this.createdKeys.delete(key);
|
|
4619
|
-
this.keyVersions.set(key,
|
|
4711
|
+
this.keyVersions.set(key, targetVersion);
|
|
4620
4712
|
}
|
|
4621
4713
|
/**
|
|
4622
4714
|
* Returns the entries that will be created, updated, and deleted by this transaction.
|
|
@@ -4640,7 +4732,11 @@ var MVCCTransaction2 = class {
|
|
|
4640
4732
|
deleted.push({ key, data });
|
|
4641
4733
|
}
|
|
4642
4734
|
}
|
|
4643
|
-
return {
|
|
4735
|
+
return {
|
|
4736
|
+
created,
|
|
4737
|
+
updated,
|
|
4738
|
+
deleted
|
|
4739
|
+
};
|
|
4644
4740
|
}
|
|
4645
4741
|
/**
|
|
4646
4742
|
* Rolls back the transaction.
|
|
@@ -4658,7 +4754,12 @@ var MVCCTransaction2 = class {
|
|
|
4658
4754
|
if (this.root !== this) {
|
|
4659
4755
|
this.root.activeTransactions.delete(this);
|
|
4660
4756
|
}
|
|
4661
|
-
return {
|
|
4757
|
+
return {
|
|
4758
|
+
success: true,
|
|
4759
|
+
created,
|
|
4760
|
+
updated,
|
|
4761
|
+
deleted
|
|
4762
|
+
};
|
|
4662
4763
|
}
|
|
4663
4764
|
/**
|
|
4664
4765
|
* Cleans up both deletedCache and versionIndex based on minActiveVersion.
|
|
@@ -4693,7 +4794,9 @@ var MVCCTransaction2 = class {
|
|
|
4693
4794
|
break;
|
|
4694
4795
|
}
|
|
4695
4796
|
}
|
|
4696
|
-
if (latestInSnapshotIdx
|
|
4797
|
+
if (latestInSnapshotIdx === versions.length - 1) {
|
|
4798
|
+
this.versionIndex.delete(key);
|
|
4799
|
+
} else if (latestInSnapshotIdx > 0) {
|
|
4697
4800
|
versions.splice(0, latestInSnapshotIdx);
|
|
4698
4801
|
}
|
|
4699
4802
|
}
|
|
@@ -4742,7 +4845,7 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4742
4845
|
createNested() {
|
|
4743
4846
|
if (this.committed) throw new Error("Transaction already committed");
|
|
4744
4847
|
const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
|
|
4745
|
-
const child = new _SyncMVCCTransaction2(void 0, this, childVersion);
|
|
4848
|
+
const child = new _SyncMVCCTransaction2(void 0, void 0, this, childVersion);
|
|
4746
4849
|
this.root.activeTransactions.add(child);
|
|
4747
4850
|
return child;
|
|
4748
4851
|
}
|
|
@@ -4750,32 +4853,77 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4750
4853
|
if (this.committed) throw new Error("Transaction already committed");
|
|
4751
4854
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
4752
4855
|
if (this.deleteBuffer.has(key)) return null;
|
|
4753
|
-
|
|
4856
|
+
if (this.parent) {
|
|
4857
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
4858
|
+
}
|
|
4859
|
+
return this._diskRead(key, this.snapshotVersion);
|
|
4754
4860
|
}
|
|
4755
4861
|
exists(key) {
|
|
4756
4862
|
if (this.committed) throw new Error("Transaction already committed");
|
|
4757
4863
|
if (this.deleteBuffer.has(key)) return false;
|
|
4758
4864
|
if (this.writeBuffer.has(key)) return true;
|
|
4759
|
-
|
|
4865
|
+
if (this.parent) {
|
|
4866
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
4867
|
+
}
|
|
4868
|
+
return this._diskExists(key, this.snapshotVersion);
|
|
4760
4869
|
}
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4870
|
+
_existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
4871
|
+
let current = this;
|
|
4872
|
+
let slVer = snapshotLocalVersion;
|
|
4873
|
+
while (current) {
|
|
4874
|
+
if (current.writeBuffer.has(key)) {
|
|
4875
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
4876
|
+
if (slVer === void 0 || keyModVersion <= slVer) return true;
|
|
4877
|
+
}
|
|
4878
|
+
if (current.deleteBuffer.has(key)) {
|
|
4879
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
4880
|
+
if (slVer === void 0 || keyModVersion <= slVer) return false;
|
|
4881
|
+
}
|
|
4882
|
+
const history = current.bufferHistory.get(key);
|
|
4883
|
+
if (history && slVer !== void 0) {
|
|
4884
|
+
const idx = current._findLastLE(history, slVer, "version");
|
|
4885
|
+
if (idx >= 0) return history[idx].exists;
|
|
4886
|
+
}
|
|
4887
|
+
if (current.parent) {
|
|
4888
|
+
slVer = current.snapshotLocalVersion;
|
|
4889
|
+
current = current.parent;
|
|
4890
|
+
} else {
|
|
4891
|
+
return current._diskExists(key, snapshotVersion);
|
|
4766
4892
|
}
|
|
4767
4893
|
}
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4894
|
+
return false;
|
|
4895
|
+
}
|
|
4896
|
+
_readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
4897
|
+
let current = this;
|
|
4898
|
+
let slVer = snapshotLocalVersion;
|
|
4899
|
+
while (current) {
|
|
4900
|
+
if (current.writeBuffer.has(key)) {
|
|
4901
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
4902
|
+
if (slVer === void 0 || keyModVersion <= slVer) {
|
|
4903
|
+
return current.writeBuffer.get(key);
|
|
4904
|
+
}
|
|
4905
|
+
}
|
|
4906
|
+
if (current.deleteBuffer.has(key)) {
|
|
4907
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
4908
|
+
if (slVer === void 0 || keyModVersion <= slVer) {
|
|
4909
|
+
return null;
|
|
4910
|
+
}
|
|
4911
|
+
}
|
|
4912
|
+
const history = current.bufferHistory.get(key);
|
|
4913
|
+
if (history && slVer !== void 0) {
|
|
4914
|
+
const idx = current._findLastLE(history, slVer, "version");
|
|
4915
|
+
if (idx >= 0) {
|
|
4916
|
+
return history[idx].exists ? history[idx].value : null;
|
|
4917
|
+
}
|
|
4918
|
+
}
|
|
4919
|
+
if (current.parent) {
|
|
4920
|
+
slVer = current.snapshotLocalVersion;
|
|
4921
|
+
current = current.parent;
|
|
4922
|
+
} else {
|
|
4923
|
+
return current._diskRead(key, snapshotVersion);
|
|
4772
4924
|
}
|
|
4773
4925
|
}
|
|
4774
|
-
|
|
4775
|
-
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
4776
|
-
} else {
|
|
4777
|
-
return this._diskRead(key, snapshotVersion);
|
|
4778
|
-
}
|
|
4926
|
+
return null;
|
|
4779
4927
|
}
|
|
4780
4928
|
commit(label) {
|
|
4781
4929
|
const { created, updated, deleted } = this.getResultEntries();
|
|
@@ -4835,6 +4983,7 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4835
4983
|
this.deletedValues.clear();
|
|
4836
4984
|
this.originallyExisted.clear();
|
|
4837
4985
|
this.keyVersions.clear();
|
|
4986
|
+
this.bufferHistory.clear();
|
|
4838
4987
|
this.localVersion = 0;
|
|
4839
4988
|
this.snapshotVersion = this.version;
|
|
4840
4989
|
}
|
|
@@ -4875,32 +5024,22 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4875
5024
|
};
|
|
4876
5025
|
}
|
|
4877
5026
|
}
|
|
4878
|
-
const
|
|
4879
|
-
for (const key of child.writeBuffer
|
|
4880
|
-
|
|
4881
|
-
this.
|
|
4882
|
-
this.
|
|
4883
|
-
if (child.createdKeys.has(key)) {
|
|
4884
|
-
this.createdKeys.add(key);
|
|
4885
|
-
}
|
|
5027
|
+
const mergeVersion = ++this.localVersion;
|
|
5028
|
+
for (const [key, value] of child.writeBuffer) {
|
|
5029
|
+
const wasCreated = child.createdKeys.has(key);
|
|
5030
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
5031
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
4886
5032
|
}
|
|
4887
5033
|
for (const key of child.deleteBuffer) {
|
|
4888
|
-
this.deleteBuffer.add(key);
|
|
4889
|
-
this.writeBuffer.delete(key);
|
|
4890
|
-
this.createdKeys.delete(key);
|
|
4891
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
4892
5034
|
const deletedValue = child.deletedValues.get(key);
|
|
4893
|
-
if (deletedValue !== void 0)
|
|
4894
|
-
|
|
4895
|
-
}
|
|
4896
|
-
if (child.originallyExisted.has(key)) {
|
|
5035
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
5036
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
4897
5037
|
this.originallyExisted.add(key);
|
|
4898
5038
|
}
|
|
5039
|
+
this._bufferDelete(key, mergeVersion);
|
|
4899
5040
|
}
|
|
4900
|
-
this.localVersion = newLocalVersion;
|
|
4901
5041
|
this.root.activeTransactions.delete(child);
|
|
4902
5042
|
} else {
|
|
4903
|
-
const newVersion = this.version + 1;
|
|
4904
5043
|
if (child !== this) {
|
|
4905
5044
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
4906
5045
|
for (const key of modifiedKeys) {
|
|
@@ -4918,50 +5057,57 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4918
5057
|
};
|
|
4919
5058
|
}
|
|
4920
5059
|
}
|
|
5060
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
5061
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
5062
|
+
return {
|
|
5063
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
5064
|
+
conflict: {
|
|
5065
|
+
key,
|
|
5066
|
+
parent: this.read(key),
|
|
5067
|
+
child: child.read(key)
|
|
5068
|
+
}
|
|
5069
|
+
};
|
|
5070
|
+
}
|
|
4921
5071
|
}
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
for (const key of child.deleteBuffer) {
|
|
4931
|
-
this.deleteBuffer.add(key);
|
|
4932
|
-
this.writeBuffer.delete(key);
|
|
4933
|
-
this.createdKeys.delete(key);
|
|
4934
|
-
const deletedValue = child.deletedValues.get(key);
|
|
4935
|
-
if (deletedValue !== void 0) {
|
|
4936
|
-
this.deletedValues.set(key, deletedValue);
|
|
5072
|
+
const mergeVersion = ++this.localVersion;
|
|
5073
|
+
for (const [key, value] of child.writeBuffer) {
|
|
5074
|
+
const wasCreated = child.createdKeys.has(key);
|
|
5075
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
5076
|
+
this.originallyExisted.add(key);
|
|
5077
|
+
}
|
|
5078
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
5079
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
4937
5080
|
}
|
|
4938
|
-
|
|
4939
|
-
|
|
5081
|
+
for (const key of child.deleteBuffer) {
|
|
5082
|
+
const deletedValue = child.deletedValues.get(key);
|
|
5083
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
5084
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
5085
|
+
this.originallyExisted.add(key);
|
|
5086
|
+
}
|
|
5087
|
+
this._bufferDelete(key, mergeVersion);
|
|
4940
5088
|
}
|
|
5089
|
+
this.root.activeTransactions.delete(child);
|
|
5090
|
+
} else {
|
|
5091
|
+
const newVersion = this.version + 1;
|
|
5092
|
+
for (const [key, value] of this.writeBuffer) this._diskWrite(key, value, newVersion);
|
|
5093
|
+
for (const key of this.deleteBuffer) this._diskDelete(key, newVersion);
|
|
5094
|
+
this.version = newVersion;
|
|
5095
|
+
this._cleanupDeletedCache();
|
|
4941
5096
|
}
|
|
4942
|
-
for (const [key, value] of child.writeBuffer) {
|
|
4943
|
-
this._diskWrite(key, value, newVersion);
|
|
4944
|
-
}
|
|
4945
|
-
for (const key of child.deleteBuffer) {
|
|
4946
|
-
this._diskDelete(key, newVersion);
|
|
4947
|
-
}
|
|
4948
|
-
this.version = newVersion;
|
|
4949
|
-
this.root.activeTransactions.delete(child);
|
|
4950
|
-
this._cleanupDeletedCache();
|
|
4951
5097
|
}
|
|
4952
5098
|
return null;
|
|
4953
5099
|
}
|
|
4954
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
4955
5100
|
_diskWrite(key, value, version) {
|
|
4956
5101
|
const strategy = this.strategy;
|
|
4957
5102
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
4958
|
-
|
|
4959
|
-
|
|
5103
|
+
const rootAsAny = this.root;
|
|
5104
|
+
if (this._diskExists(key, version)) {
|
|
5105
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
4960
5106
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
4961
|
-
this.deletedCache.get(key).push({
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
5107
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
5108
|
+
rootAsAny.diskCache.set(key, value);
|
|
5109
|
+
} else {
|
|
5110
|
+
rootAsAny.diskCache.set(key, value);
|
|
4965
5111
|
}
|
|
4966
5112
|
strategy.write(key, value);
|
|
4967
5113
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -4972,36 +5118,44 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
4972
5118
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
4973
5119
|
const versions = this.versionIndex.get(key);
|
|
4974
5120
|
if (!versions) {
|
|
4975
|
-
|
|
5121
|
+
const rootAsAny = this.root;
|
|
5122
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
5123
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
5124
|
+
rootAsAny.diskCache.set(key, val);
|
|
5125
|
+
return val;
|
|
5126
|
+
}
|
|
5127
|
+
return null;
|
|
4976
5128
|
}
|
|
4977
5129
|
let targetVerObj = null;
|
|
4978
5130
|
let nextVerObj = null;
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
} else {
|
|
4983
|
-
nextVerObj = v;
|
|
4984
|
-
break;
|
|
4985
|
-
}
|
|
4986
|
-
}
|
|
5131
|
+
const idx = this._findLastLE(versions, snapshotVersion, "version");
|
|
5132
|
+
if (idx >= 0) targetVerObj = versions[idx];
|
|
5133
|
+
if (idx + 1 < versions.length) nextVerObj = versions[idx + 1];
|
|
4987
5134
|
if (!targetVerObj) {
|
|
4988
5135
|
if (nextVerObj) {
|
|
4989
5136
|
const cached2 = this.deletedCache.get(key);
|
|
4990
5137
|
if (cached2) {
|
|
4991
|
-
const
|
|
4992
|
-
if (
|
|
5138
|
+
const cIdx = this._findExact(cached2, nextVerObj.version, "deletedAtVersion");
|
|
5139
|
+
if (cIdx >= 0) return cached2[cIdx].value;
|
|
4993
5140
|
}
|
|
4994
5141
|
}
|
|
4995
5142
|
return null;
|
|
4996
5143
|
}
|
|
4997
5144
|
if (!targetVerObj.exists) return null;
|
|
4998
5145
|
if (!nextVerObj) {
|
|
4999
|
-
return
|
|
5146
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
5147
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
5148
|
+
const rootAsAny = this.root;
|
|
5149
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
5150
|
+
rootAsAny.diskCache.set(key, val);
|
|
5151
|
+
return val;
|
|
5152
|
+
}
|
|
5153
|
+
return null;
|
|
5000
5154
|
}
|
|
5001
5155
|
const cached = this.deletedCache.get(key);
|
|
5002
5156
|
if (cached) {
|
|
5003
|
-
const
|
|
5004
|
-
if (
|
|
5157
|
+
const cIdx = this._findExact(cached, nextVerObj.version, "deletedAtVersion");
|
|
5158
|
+
if (cIdx >= 0) return cached[cIdx].value;
|
|
5005
5159
|
}
|
|
5006
5160
|
return null;
|
|
5007
5161
|
}
|
|
@@ -5010,31 +5164,40 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
|
|
|
5010
5164
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
5011
5165
|
const versions = this.versionIndex.get(key);
|
|
5012
5166
|
if (!versions) {
|
|
5013
|
-
|
|
5167
|
+
const rootAsAny = this.root;
|
|
5168
|
+
if (rootAsAny.diskCache.has(key)) return rootAsAny.diskCache.get(key) !== null;
|
|
5169
|
+
const exists = strategy.exists(key);
|
|
5170
|
+
if (!exists) rootAsAny.diskCache.set(key, null);
|
|
5171
|
+
return exists;
|
|
5014
5172
|
}
|
|
5015
5173
|
let targetVerObj = null;
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5174
|
+
let nextVerObj = null;
|
|
5175
|
+
const idx = this._findLastLE(versions, snapshotVersion, "version");
|
|
5176
|
+
if (idx >= 0) targetVerObj = versions[idx];
|
|
5177
|
+
if (idx + 1 < versions.length) nextVerObj = versions[idx + 1];
|
|
5178
|
+
if (!targetVerObj) {
|
|
5179
|
+
if (nextVerObj) {
|
|
5180
|
+
const cached = this.deletedCache.get(key);
|
|
5181
|
+
if (cached) {
|
|
5182
|
+
const cIdx = this._findExact(cached, nextVerObj.version, "deletedAtVersion");
|
|
5183
|
+
if (cIdx >= 0) return true;
|
|
5184
|
+
}
|
|
5021
5185
|
}
|
|
5186
|
+
return false;
|
|
5022
5187
|
}
|
|
5023
|
-
if (!targetVerObj) return strategy.exists(key);
|
|
5024
5188
|
return targetVerObj.exists;
|
|
5025
5189
|
}
|
|
5026
5190
|
_diskDelete(key, snapshotVersion) {
|
|
5027
5191
|
const strategy = this.strategy;
|
|
5028
5192
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
5029
|
-
|
|
5030
|
-
|
|
5193
|
+
const rootAsAny = this.root;
|
|
5194
|
+
if (this._diskExists(key, snapshotVersion)) {
|
|
5195
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : strategy.read(key);
|
|
5031
5196
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
5032
|
-
this.deletedCache.get(key).push({
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
});
|
|
5197
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
5198
|
+
strategy.delete(key);
|
|
5199
|
+
rootAsAny.diskCache.delete(key);
|
|
5036
5200
|
}
|
|
5037
|
-
strategy.delete(key);
|
|
5038
5201
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
5039
5202
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
5040
5203
|
}
|
|
@@ -5347,7 +5510,7 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5347
5510
|
createNested() {
|
|
5348
5511
|
if (this.committed) throw new Error("Transaction already committed");
|
|
5349
5512
|
const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
|
|
5350
|
-
const child = new _AsyncMVCCTransaction2(void 0, this, childVersion);
|
|
5513
|
+
const child = new _AsyncMVCCTransaction2(void 0, void 0, this, childVersion);
|
|
5351
5514
|
this.root.activeTransactions.add(child);
|
|
5352
5515
|
return child;
|
|
5353
5516
|
}
|
|
@@ -5355,32 +5518,77 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5355
5518
|
if (this.committed) throw new Error("Transaction already committed");
|
|
5356
5519
|
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
5357
5520
|
if (this.deleteBuffer.has(key)) return null;
|
|
5358
|
-
|
|
5521
|
+
if (this.parent) {
|
|
5522
|
+
return this.parent._readSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
5523
|
+
}
|
|
5524
|
+
return await this._diskRead(key, this.snapshotVersion);
|
|
5359
5525
|
}
|
|
5360
5526
|
async exists(key) {
|
|
5361
5527
|
if (this.committed) throw new Error("Transaction already committed");
|
|
5362
5528
|
if (this.deleteBuffer.has(key)) return false;
|
|
5363
5529
|
if (this.writeBuffer.has(key)) return true;
|
|
5364
|
-
|
|
5530
|
+
if (this.parent) {
|
|
5531
|
+
return this.parent._existsSnapshot(key, this.snapshotVersion, this.snapshotLocalVersion);
|
|
5532
|
+
}
|
|
5533
|
+
return await this._diskExists(key, this.snapshotVersion);
|
|
5365
5534
|
}
|
|
5366
|
-
async
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5535
|
+
async _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
5536
|
+
let current = this;
|
|
5537
|
+
let slVer = snapshotLocalVersion;
|
|
5538
|
+
while (current) {
|
|
5539
|
+
if (current.writeBuffer.has(key)) {
|
|
5540
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
5541
|
+
if (slVer === void 0 || keyModVersion <= slVer) return true;
|
|
5542
|
+
}
|
|
5543
|
+
if (current.deleteBuffer.has(key)) {
|
|
5544
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
5545
|
+
if (slVer === void 0 || keyModVersion <= slVer) return false;
|
|
5546
|
+
}
|
|
5547
|
+
const history = current.bufferHistory.get(key);
|
|
5548
|
+
if (history && slVer !== void 0) {
|
|
5549
|
+
const idx = current._findLastLE(history, slVer, "version");
|
|
5550
|
+
if (idx >= 0) return history[idx].exists;
|
|
5551
|
+
}
|
|
5552
|
+
if (current.parent) {
|
|
5553
|
+
slVer = current.snapshotLocalVersion;
|
|
5554
|
+
current = current.parent;
|
|
5555
|
+
} else {
|
|
5556
|
+
return await current._diskExists(key, snapshotVersion);
|
|
5371
5557
|
}
|
|
5372
5558
|
}
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5559
|
+
return false;
|
|
5560
|
+
}
|
|
5561
|
+
async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
5562
|
+
let current = this;
|
|
5563
|
+
let slVer = snapshotLocalVersion;
|
|
5564
|
+
while (current) {
|
|
5565
|
+
if (current.writeBuffer.has(key)) {
|
|
5566
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
5567
|
+
if (slVer === void 0 || keyModVersion <= slVer) {
|
|
5568
|
+
return current.writeBuffer.get(key);
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5571
|
+
if (current.deleteBuffer.has(key)) {
|
|
5572
|
+
const keyModVersion = current.keyVersions.get(key);
|
|
5573
|
+
if (slVer === void 0 || keyModVersion <= slVer) {
|
|
5574
|
+
return null;
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
const history = current.bufferHistory.get(key);
|
|
5578
|
+
if (history && slVer !== void 0) {
|
|
5579
|
+
const idx = current._findLastLE(history, slVer, "version");
|
|
5580
|
+
if (idx >= 0) {
|
|
5581
|
+
return history[idx].exists ? history[idx].value : null;
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
if (current.parent) {
|
|
5585
|
+
slVer = current.snapshotLocalVersion;
|
|
5586
|
+
current = current.parent;
|
|
5587
|
+
} else {
|
|
5588
|
+
return await current._diskRead(key, snapshotVersion);
|
|
5377
5589
|
}
|
|
5378
5590
|
}
|
|
5379
|
-
|
|
5380
|
-
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
5381
|
-
} else {
|
|
5382
|
-
return this._diskRead(key, snapshotVersion);
|
|
5383
|
-
}
|
|
5591
|
+
return null;
|
|
5384
5592
|
}
|
|
5385
5593
|
async commit(label) {
|
|
5386
5594
|
const { created, updated, deleted } = this.getResultEntries();
|
|
@@ -5440,6 +5648,7 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5440
5648
|
this.deletedValues.clear();
|
|
5441
5649
|
this.originallyExisted.clear();
|
|
5442
5650
|
this.keyVersions.clear();
|
|
5651
|
+
this.bufferHistory.clear();
|
|
5443
5652
|
this.localVersion = 0;
|
|
5444
5653
|
this.snapshotVersion = this.version;
|
|
5445
5654
|
}
|
|
@@ -5481,33 +5690,23 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5481
5690
|
};
|
|
5482
5691
|
}
|
|
5483
5692
|
}
|
|
5484
|
-
const
|
|
5485
|
-
for (const key of child.writeBuffer
|
|
5486
|
-
|
|
5487
|
-
this.
|
|
5488
|
-
this.
|
|
5489
|
-
if (child.createdKeys.has(key)) {
|
|
5490
|
-
this.createdKeys.add(key);
|
|
5491
|
-
}
|
|
5693
|
+
const mergeVersion = ++this.localVersion;
|
|
5694
|
+
for (const [key, value] of child.writeBuffer) {
|
|
5695
|
+
const wasCreated = child.createdKeys.has(key);
|
|
5696
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
5697
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
5492
5698
|
}
|
|
5493
5699
|
for (const key of child.deleteBuffer) {
|
|
5494
|
-
this.deleteBuffer.add(key);
|
|
5495
|
-
this.writeBuffer.delete(key);
|
|
5496
|
-
this.createdKeys.delete(key);
|
|
5497
|
-
this.keyVersions.set(key, newLocalVersion);
|
|
5498
5700
|
const deletedValue = child.deletedValues.get(key);
|
|
5499
|
-
if (deletedValue !== void 0)
|
|
5500
|
-
|
|
5501
|
-
}
|
|
5502
|
-
if (child.originallyExisted.has(key)) {
|
|
5701
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
5702
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
5503
5703
|
this.originallyExisted.add(key);
|
|
5504
5704
|
}
|
|
5705
|
+
this._bufferDelete(key, mergeVersion);
|
|
5505
5706
|
}
|
|
5506
|
-
this.localVersion = newLocalVersion;
|
|
5507
5707
|
this.root.activeTransactions.delete(child);
|
|
5508
5708
|
return null;
|
|
5509
5709
|
} else {
|
|
5510
|
-
const newVersion = this.version + 1;
|
|
5511
5710
|
if (child !== this) {
|
|
5512
5711
|
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
5513
5712
|
for (const key of modifiedKeys) {
|
|
@@ -5525,51 +5724,58 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5525
5724
|
};
|
|
5526
5725
|
}
|
|
5527
5726
|
}
|
|
5727
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
5728
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
5729
|
+
return {
|
|
5730
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction in the same session (Local v${lastModLocalVer})`,
|
|
5731
|
+
conflict: {
|
|
5732
|
+
key,
|
|
5733
|
+
parent: await this.read(key),
|
|
5734
|
+
child: await child.read(key)
|
|
5735
|
+
}
|
|
5736
|
+
};
|
|
5737
|
+
}
|
|
5528
5738
|
}
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
for (const key of child.deleteBuffer) {
|
|
5538
|
-
this.deleteBuffer.add(key);
|
|
5539
|
-
this.writeBuffer.delete(key);
|
|
5540
|
-
this.createdKeys.delete(key);
|
|
5541
|
-
const deletedValue = child.deletedValues.get(key);
|
|
5542
|
-
if (deletedValue !== void 0) {
|
|
5543
|
-
this.deletedValues.set(key, deletedValue);
|
|
5739
|
+
const mergeVersion = ++this.localVersion;
|
|
5740
|
+
for (const [key, value] of child.writeBuffer) {
|
|
5741
|
+
const wasCreated = child.createdKeys.has(key);
|
|
5742
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
5743
|
+
this.originallyExisted.add(key);
|
|
5744
|
+
}
|
|
5745
|
+
if (wasCreated) this._bufferCreate(key, value, mergeVersion);
|
|
5746
|
+
else this._bufferWrite(key, value, mergeVersion);
|
|
5544
5747
|
}
|
|
5545
|
-
|
|
5546
|
-
|
|
5748
|
+
for (const key of child.deleteBuffer) {
|
|
5749
|
+
const deletedValue = child.deletedValues.get(key);
|
|
5750
|
+
if (deletedValue !== void 0) this.deletedValues.set(key, deletedValue);
|
|
5751
|
+
if (child.originallyExisted.has(key) && !this.createdKeys.has(key)) {
|
|
5752
|
+
this.originallyExisted.add(key);
|
|
5753
|
+
}
|
|
5754
|
+
this._bufferDelete(key, mergeVersion);
|
|
5547
5755
|
}
|
|
5756
|
+
this.root.activeTransactions.delete(child);
|
|
5757
|
+
} else {
|
|
5758
|
+
const newVersion = this.version + 1;
|
|
5759
|
+
for (const [key, value] of this.writeBuffer) await this._diskWrite(key, value, newVersion);
|
|
5760
|
+
for (const key of this.deleteBuffer) await this._diskDelete(key, newVersion);
|
|
5761
|
+
this.version = newVersion;
|
|
5762
|
+
this._cleanupDeletedCache();
|
|
5548
5763
|
}
|
|
5549
|
-
for (const [key, value] of child.writeBuffer) {
|
|
5550
|
-
await this._diskWrite(key, value, newVersion);
|
|
5551
|
-
}
|
|
5552
|
-
for (const key of child.deleteBuffer) {
|
|
5553
|
-
await this._diskDelete(key, newVersion);
|
|
5554
|
-
}
|
|
5555
|
-
this.version = newVersion;
|
|
5556
|
-
this.root.activeTransactions.delete(child);
|
|
5557
|
-
this._cleanupDeletedCache();
|
|
5558
5764
|
return null;
|
|
5559
5765
|
}
|
|
5560
5766
|
});
|
|
5561
5767
|
}
|
|
5562
|
-
// --- Internal IO Helpers (Root Only) ---
|
|
5563
5768
|
async _diskWrite(key, value, version) {
|
|
5564
5769
|
const strategy = this.strategy;
|
|
5565
5770
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
5566
|
-
|
|
5567
|
-
|
|
5771
|
+
const rootAsAny = this.root;
|
|
5772
|
+
if (await this._diskExists(key, version)) {
|
|
5773
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
5568
5774
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
5569
|
-
this.deletedCache.get(key).push({
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5775
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: version });
|
|
5776
|
+
rootAsAny.diskCache.set(key, value);
|
|
5777
|
+
} else {
|
|
5778
|
+
rootAsAny.diskCache.set(key, value);
|
|
5573
5779
|
}
|
|
5574
5780
|
await strategy.write(key, value);
|
|
5575
5781
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
@@ -5580,36 +5786,44 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5580
5786
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
5581
5787
|
const versions = this.versionIndex.get(key);
|
|
5582
5788
|
if (!versions) {
|
|
5583
|
-
|
|
5789
|
+
const rootAsAny = this.root;
|
|
5790
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
5791
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
5792
|
+
rootAsAny.diskCache.set(key, val);
|
|
5793
|
+
return val;
|
|
5794
|
+
}
|
|
5795
|
+
return null;
|
|
5584
5796
|
}
|
|
5585
5797
|
let targetVerObj = null;
|
|
5586
5798
|
let nextVerObj = null;
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
} else {
|
|
5591
|
-
nextVerObj = v;
|
|
5592
|
-
break;
|
|
5593
|
-
}
|
|
5594
|
-
}
|
|
5799
|
+
const idx = this._findLastLE(versions, snapshotVersion, "version");
|
|
5800
|
+
if (idx >= 0) targetVerObj = versions[idx];
|
|
5801
|
+
if (idx + 1 < versions.length) nextVerObj = versions[idx + 1];
|
|
5595
5802
|
if (!targetVerObj) {
|
|
5596
5803
|
if (nextVerObj) {
|
|
5597
5804
|
const cached2 = this.deletedCache.get(key);
|
|
5598
5805
|
if (cached2) {
|
|
5599
|
-
const
|
|
5600
|
-
if (
|
|
5806
|
+
const cIdx = this._findExact(cached2, nextVerObj.version, "deletedAtVersion");
|
|
5807
|
+
if (cIdx >= 0) return cached2[cIdx].value;
|
|
5601
5808
|
}
|
|
5602
5809
|
}
|
|
5603
5810
|
return null;
|
|
5604
5811
|
}
|
|
5605
5812
|
if (!targetVerObj.exists) return null;
|
|
5606
5813
|
if (!nextVerObj) {
|
|
5607
|
-
return
|
|
5814
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
5815
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
5816
|
+
const rootAsAny = this.root;
|
|
5817
|
+
const val = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
5818
|
+
rootAsAny.diskCache.set(key, val);
|
|
5819
|
+
return val;
|
|
5820
|
+
}
|
|
5821
|
+
return null;
|
|
5608
5822
|
}
|
|
5609
5823
|
const cached = this.deletedCache.get(key);
|
|
5610
5824
|
if (cached) {
|
|
5611
|
-
const
|
|
5612
|
-
if (
|
|
5825
|
+
const cIdx = this._findExact(cached, nextVerObj.version, "deletedAtVersion");
|
|
5826
|
+
if (cIdx >= 0) return cached[cIdx].value;
|
|
5613
5827
|
}
|
|
5614
5828
|
return null;
|
|
5615
5829
|
}
|
|
@@ -5618,31 +5832,40 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
|
|
|
5618
5832
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
5619
5833
|
const versions = this.versionIndex.get(key);
|
|
5620
5834
|
if (!versions) {
|
|
5621
|
-
|
|
5835
|
+
const rootAsAny = this.root;
|
|
5836
|
+
if (rootAsAny.diskCache.has(key)) return rootAsAny.diskCache.get(key) !== null;
|
|
5837
|
+
const exists = await strategy.exists(key);
|
|
5838
|
+
if (!exists) rootAsAny.diskCache.set(key, null);
|
|
5839
|
+
return exists;
|
|
5622
5840
|
}
|
|
5623
5841
|
let targetVerObj = null;
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5842
|
+
let nextVerObj = null;
|
|
5843
|
+
const idx = this._findLastLE(versions, snapshotVersion, "version");
|
|
5844
|
+
if (idx >= 0) targetVerObj = versions[idx];
|
|
5845
|
+
if (idx + 1 < versions.length) nextVerObj = versions[idx + 1];
|
|
5846
|
+
if (!targetVerObj) {
|
|
5847
|
+
if (nextVerObj) {
|
|
5848
|
+
const cached = this.deletedCache.get(key);
|
|
5849
|
+
if (cached) {
|
|
5850
|
+
const cIdx = this._findExact(cached, nextVerObj.version, "deletedAtVersion");
|
|
5851
|
+
if (cIdx >= 0) return true;
|
|
5852
|
+
}
|
|
5629
5853
|
}
|
|
5854
|
+
return false;
|
|
5630
5855
|
}
|
|
5631
|
-
if (!targetVerObj) return strategy.exists(key);
|
|
5632
5856
|
return targetVerObj.exists;
|
|
5633
5857
|
}
|
|
5634
5858
|
async _diskDelete(key, snapshotVersion) {
|
|
5635
5859
|
const strategy = this.strategy;
|
|
5636
5860
|
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
5637
|
-
|
|
5638
|
-
|
|
5861
|
+
const rootAsAny = this.root;
|
|
5862
|
+
if (await this._diskExists(key, snapshotVersion)) {
|
|
5863
|
+
const currentVal = rootAsAny.diskCache.has(key) ? rootAsAny.diskCache.get(key) : await strategy.read(key);
|
|
5639
5864
|
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
5640
|
-
this.deletedCache.get(key).push({
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
});
|
|
5865
|
+
this.deletedCache.get(key).push({ value: currentVal, deletedAtVersion: snapshotVersion });
|
|
5866
|
+
await strategy.delete(key);
|
|
5867
|
+
rootAsAny.diskCache.delete(key);
|
|
5644
5868
|
}
|
|
5645
|
-
await strategy.delete(key);
|
|
5646
5869
|
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
5647
5870
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
5648
5871
|
}
|
|
@@ -8343,7 +8566,6 @@ var RowTableEngine = class {
|
|
|
8343
8566
|
if (!btx) return;
|
|
8344
8567
|
const result = await btx.commit();
|
|
8345
8568
|
if (result.success) {
|
|
8346
|
-
await this.bptree.init();
|
|
8347
8569
|
for (const entry of result.deleted) {
|
|
8348
8570
|
await this.strategy.delete(entry.key);
|
|
8349
8571
|
}
|