serializable-bptree 8.4.0 → 8.4.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/README.md CHANGED
@@ -84,6 +84,7 @@ Additionally, this library supports asynchronous operations and rule-based query
84
84
  - **Async/Sync Support**: Provides both synchronous and asynchronous APIs.
85
85
  - **Query Optimization**: Rule-based optimizer to choose the best index for complex queries.
86
86
  - **TypeScript**: Fully typed for a better developer experience.
87
+ - **Auto Rollback**: Automatically cleans up internal memory buffers on commit failures, preventing memory leaks without manual intervention.
87
88
 
88
89
  ## How to use
89
90
 
@@ -413,11 +413,7 @@ var MVCCTransaction = class {
413
413
  */
414
414
  rollback() {
415
415
  const { created, updated, deleted } = this.getResultEntries();
416
- this.writeBuffer.clear();
417
- this.deleteBuffer.clear();
418
- this.createdKeys.clear();
419
- this.deletedValues.clear();
420
- this.originallyExisted.clear();
416
+ this._cleanupAll();
421
417
  this.committed = true;
422
418
  if (this.root !== this) {
423
419
  this.root.activeTransactions.delete(this);
@@ -453,6 +449,19 @@ var MVCCTransaction = class {
453
449
  }
454
450
  return Array.from(conflicts);
455
451
  }
452
+ /**
453
+ * Cleans up all buffers and history.
454
+ * This method is called by the commit method.
455
+ */
456
+ _cleanupAll() {
457
+ this.writeBuffer.clear();
458
+ this.deleteBuffer.clear();
459
+ this.createdKeys.clear();
460
+ this.deletedValues.clear();
461
+ this.originallyExisted.clear();
462
+ this.keyVersions.clear();
463
+ this.bufferHistory.clear();
464
+ }
456
465
  /**
457
466
  * Cleans up both deletedCache and versionIndex based on minActiveVersion.
458
467
  * Root transactions call this after commit to reclaim memory.
@@ -644,6 +653,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
644
653
  if (this.parent) {
645
654
  const failure = this.parent._merge(this);
646
655
  if (failure) {
656
+ this.rollback();
647
657
  return {
648
658
  label,
649
659
  success: false,
@@ -654,11 +664,13 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
654
664
  deleted
655
665
  };
656
666
  }
667
+ this._cleanupAll();
657
668
  this.committed = true;
658
669
  } else {
659
670
  if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
660
671
  const failure = this._merge(this);
661
672
  if (failure) {
673
+ this.rollback();
662
674
  return {
663
675
  label,
664
676
  success: false,
@@ -669,13 +681,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
669
681
  deleted: []
670
682
  };
671
683
  }
672
- this.writeBuffer.clear();
673
- this.deleteBuffer.clear();
674
- this.createdKeys.clear();
675
- this.deletedValues.clear();
676
- this.originallyExisted.clear();
677
- this.keyVersions.clear();
678
- this.bufferHistory.clear();
684
+ this._cleanupAll();
679
685
  this.localVersion = 0;
680
686
  this.snapshotVersion = this.version;
681
687
  }
@@ -1309,6 +1315,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1309
1315
  if (this.parent) {
1310
1316
  const failure = await this.parent._merge(this);
1311
1317
  if (failure) {
1318
+ this.rollback();
1312
1319
  return {
1313
1320
  label,
1314
1321
  success: false,
@@ -1319,11 +1326,13 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1319
1326
  deleted
1320
1327
  };
1321
1328
  }
1329
+ this._cleanupAll();
1322
1330
  this.committed = true;
1323
1331
  } else {
1324
1332
  if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
1325
1333
  const failure = await this._merge(this);
1326
1334
  if (failure) {
1335
+ this.rollback();
1327
1336
  return {
1328
1337
  label,
1329
1338
  success: false,
@@ -1334,13 +1343,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1334
1343
  deleted: []
1335
1344
  };
1336
1345
  }
1337
- this.writeBuffer.clear();
1338
- this.deleteBuffer.clear();
1339
- this.createdKeys.clear();
1340
- this.deletedValues.clear();
1341
- this.originallyExisted.clear();
1342
- this.keyVersions.clear();
1343
- this.bufferHistory.clear();
1346
+ this._cleanupAll();
1344
1347
  this.localVersion = 0;
1345
1348
  this.snapshotVersion = this.version;
1346
1349
  }
@@ -1566,8 +1569,6 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1566
1569
  // src/base/BPTreeTransaction.ts
1567
1570
  var BPTreeTransaction = class _BPTreeTransaction {
1568
1571
  _cachedRegexp = /* @__PURE__ */ new Map();
1569
- nodes = /* @__PURE__ */ new Map();
1570
- deletedNodeBuffer = /* @__PURE__ */ new Map();
1571
1572
  rootTx;
1572
1573
  mvccRoot;
1573
1574
  mvcc;
@@ -2048,6 +2049,12 @@ var BPTreeTransaction = class _BPTreeTransaction {
2048
2049
  _clearCache() {
2049
2050
  this._cachedRegexp.clear();
2050
2051
  }
2052
+ _resetForReload() {
2053
+ this._cachedRegexp.clear();
2054
+ this.isInitialized = false;
2055
+ this.isDestroyed = false;
2056
+ this.mvccRoot.diskCache.clear();
2057
+ }
2051
2058
  /**
2052
2059
  * Clears all cached nodes.
2053
2060
  * This method is useful for freeing up memory when the tree is no longer needed.
@@ -2378,6 +2385,16 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2378
2385
  throw e;
2379
2386
  }
2380
2387
  }
2388
+ reload() {
2389
+ if (this.rootTx !== this) {
2390
+ throw new Error("Cannot call reload on a nested transaction");
2391
+ }
2392
+ this._reloadInternal();
2393
+ }
2394
+ _reloadInternal() {
2395
+ this._resetForReload();
2396
+ this._initInternal();
2397
+ }
2381
2398
  exists(key, value) {
2382
2399
  const node = this.locateLeaf(value);
2383
2400
  const { index, found } = this._binarySearchValues(node.values, value);
@@ -2899,8 +2916,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2899
2916
  result = this.rootTx.commit(label);
2900
2917
  if (result.success) {
2901
2918
  this.rootTx.rootId = this.rootId;
2919
+ } else {
2920
+ this.mvcc.rollback();
2902
2921
  }
2903
2922
  }
2923
+ } else {
2924
+ this.mvcc.rollback();
2904
2925
  }
2905
2926
  return result;
2906
2927
  }
@@ -3580,6 +3601,16 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3580
3601
  throw e;
3581
3602
  }
3582
3603
  }
3604
+ async reload() {
3605
+ if (this.rootTx !== this) {
3606
+ throw new Error("Cannot call reload on a nested transaction");
3607
+ }
3608
+ return await this._reloadInternal();
3609
+ }
3610
+ async _reloadInternal() {
3611
+ this._resetForReload();
3612
+ await this._initInternal();
3613
+ }
3583
3614
  async exists(key, value) {
3584
3615
  const node = await this.locateLeaf(value);
3585
3616
  const { index, found } = this._binarySearchValues(node.values, value);
@@ -4109,8 +4140,12 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
4109
4140
  result = await this.rootTx.commit(label);
4110
4141
  if (result.success) {
4111
4142
  this.rootTx.rootId = this.rootId;
4143
+ } else {
4144
+ this.mvcc.rollback();
4112
4145
  }
4113
4146
  }
4147
+ } else {
4148
+ this.mvcc.rollback();
4114
4149
  }
4115
4150
  return result;
4116
4151
  }
@@ -377,11 +377,7 @@ var MVCCTransaction = class {
377
377
  */
378
378
  rollback() {
379
379
  const { created, updated, deleted } = this.getResultEntries();
380
- this.writeBuffer.clear();
381
- this.deleteBuffer.clear();
382
- this.createdKeys.clear();
383
- this.deletedValues.clear();
384
- this.originallyExisted.clear();
380
+ this._cleanupAll();
385
381
  this.committed = true;
386
382
  if (this.root !== this) {
387
383
  this.root.activeTransactions.delete(this);
@@ -417,6 +413,19 @@ var MVCCTransaction = class {
417
413
  }
418
414
  return Array.from(conflicts);
419
415
  }
416
+ /**
417
+ * Cleans up all buffers and history.
418
+ * This method is called by the commit method.
419
+ */
420
+ _cleanupAll() {
421
+ this.writeBuffer.clear();
422
+ this.deleteBuffer.clear();
423
+ this.createdKeys.clear();
424
+ this.deletedValues.clear();
425
+ this.originallyExisted.clear();
426
+ this.keyVersions.clear();
427
+ this.bufferHistory.clear();
428
+ }
420
429
  /**
421
430
  * Cleans up both deletedCache and versionIndex based on minActiveVersion.
422
431
  * Root transactions call this after commit to reclaim memory.
@@ -608,6 +617,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
608
617
  if (this.parent) {
609
618
  const failure = this.parent._merge(this);
610
619
  if (failure) {
620
+ this.rollback();
611
621
  return {
612
622
  label,
613
623
  success: false,
@@ -618,11 +628,13 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
618
628
  deleted
619
629
  };
620
630
  }
631
+ this._cleanupAll();
621
632
  this.committed = true;
622
633
  } else {
623
634
  if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
624
635
  const failure = this._merge(this);
625
636
  if (failure) {
637
+ this.rollback();
626
638
  return {
627
639
  label,
628
640
  success: false,
@@ -633,13 +645,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
633
645
  deleted: []
634
646
  };
635
647
  }
636
- this.writeBuffer.clear();
637
- this.deleteBuffer.clear();
638
- this.createdKeys.clear();
639
- this.deletedValues.clear();
640
- this.originallyExisted.clear();
641
- this.keyVersions.clear();
642
- this.bufferHistory.clear();
648
+ this._cleanupAll();
643
649
  this.localVersion = 0;
644
650
  this.snapshotVersion = this.version;
645
651
  }
@@ -1273,6 +1279,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1273
1279
  if (this.parent) {
1274
1280
  const failure = await this.parent._merge(this);
1275
1281
  if (failure) {
1282
+ this.rollback();
1276
1283
  return {
1277
1284
  label,
1278
1285
  success: false,
@@ -1283,11 +1290,13 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1283
1290
  deleted
1284
1291
  };
1285
1292
  }
1293
+ this._cleanupAll();
1286
1294
  this.committed = true;
1287
1295
  } else {
1288
1296
  if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
1289
1297
  const failure = await this._merge(this);
1290
1298
  if (failure) {
1299
+ this.rollback();
1291
1300
  return {
1292
1301
  label,
1293
1302
  success: false,
@@ -1298,13 +1307,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1298
1307
  deleted: []
1299
1308
  };
1300
1309
  }
1301
- this.writeBuffer.clear();
1302
- this.deleteBuffer.clear();
1303
- this.createdKeys.clear();
1304
- this.deletedValues.clear();
1305
- this.originallyExisted.clear();
1306
- this.keyVersions.clear();
1307
- this.bufferHistory.clear();
1310
+ this._cleanupAll();
1308
1311
  this.localVersion = 0;
1309
1312
  this.snapshotVersion = this.version;
1310
1313
  }
@@ -1530,8 +1533,6 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1530
1533
  // src/base/BPTreeTransaction.ts
1531
1534
  var BPTreeTransaction = class _BPTreeTransaction {
1532
1535
  _cachedRegexp = /* @__PURE__ */ new Map();
1533
- nodes = /* @__PURE__ */ new Map();
1534
- deletedNodeBuffer = /* @__PURE__ */ new Map();
1535
1536
  rootTx;
1536
1537
  mvccRoot;
1537
1538
  mvcc;
@@ -2012,6 +2013,12 @@ var BPTreeTransaction = class _BPTreeTransaction {
2012
2013
  _clearCache() {
2013
2014
  this._cachedRegexp.clear();
2014
2015
  }
2016
+ _resetForReload() {
2017
+ this._cachedRegexp.clear();
2018
+ this.isInitialized = false;
2019
+ this.isDestroyed = false;
2020
+ this.mvccRoot.diskCache.clear();
2021
+ }
2015
2022
  /**
2016
2023
  * Clears all cached nodes.
2017
2024
  * This method is useful for freeing up memory when the tree is no longer needed.
@@ -2342,6 +2349,16 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2342
2349
  throw e;
2343
2350
  }
2344
2351
  }
2352
+ reload() {
2353
+ if (this.rootTx !== this) {
2354
+ throw new Error("Cannot call reload on a nested transaction");
2355
+ }
2356
+ this._reloadInternal();
2357
+ }
2358
+ _reloadInternal() {
2359
+ this._resetForReload();
2360
+ this._initInternal();
2361
+ }
2345
2362
  exists(key, value) {
2346
2363
  const node = this.locateLeaf(value);
2347
2364
  const { index, found } = this._binarySearchValues(node.values, value);
@@ -2863,8 +2880,12 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
2863
2880
  result = this.rootTx.commit(label);
2864
2881
  if (result.success) {
2865
2882
  this.rootTx.rootId = this.rootId;
2883
+ } else {
2884
+ this.mvcc.rollback();
2866
2885
  }
2867
2886
  }
2887
+ } else {
2888
+ this.mvcc.rollback();
2868
2889
  }
2869
2890
  return result;
2870
2891
  }
@@ -3544,6 +3565,16 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
3544
3565
  throw e;
3545
3566
  }
3546
3567
  }
3568
+ async reload() {
3569
+ if (this.rootTx !== this) {
3570
+ throw new Error("Cannot call reload on a nested transaction");
3571
+ }
3572
+ return await this._reloadInternal();
3573
+ }
3574
+ async _reloadInternal() {
3575
+ this._resetForReload();
3576
+ await this._initInternal();
3577
+ }
3547
3578
  async exists(key, value) {
3548
3579
  const node = await this.locateLeaf(value);
3549
3580
  const { index, found } = this._binarySearchValues(node.values, value);
@@ -4073,8 +4104,12 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
4073
4104
  result = await this.rootTx.commit(label);
4074
4105
  if (result.success) {
4075
4106
  this.rootTx.rootId = this.rootId;
4107
+ } else {
4108
+ this.mvcc.rollback();
4076
4109
  }
4077
4110
  }
4111
+ } else {
4112
+ this.mvcc.rollback();
4078
4113
  }
4079
4114
  return result;
4080
4115
  }
@@ -4,8 +4,6 @@ import { ValueComparator } from './ValueComparator';
4
4
  import { SerializeStrategy } from './SerializeStrategy';
5
5
  export declare abstract class BPTreeTransaction<K, V> {
6
6
  private readonly _cachedRegexp;
7
- protected readonly nodes: Map<string, BPTreeUnknownNode<K, V>>;
8
- protected readonly deletedNodeBuffer: Map<string, BPTreeUnknownNode<K, V>>;
9
7
  protected readonly rootTx: BPTreeTransaction<K, V>;
10
8
  protected readonly mvccRoot: BPTreeMVCC<K, V>;
11
9
  protected readonly mvcc: BPTreeMVCC<K, V>;
@@ -122,6 +120,12 @@ export declare abstract class BPTreeTransaction<K, V> {
122
120
  * If it is not called, the tree will not function.
123
121
  */
124
122
  abstract init(): Deferred<void>;
123
+ /**
124
+ * Clears all in-memory caches and re-initializes the tree from storage.
125
+ * This is equivalent to calling `clear()` followed by `init()`, but reuses the same instance.
126
+ * Useful when the underlying storage has been modified externally and the tree needs to reflect current state.
127
+ */
128
+ abstract reload(): Deferred<void>;
125
129
  /**
126
130
  * Retrieves the value associated with the given key.
127
131
  * @param key The key to search for.
@@ -233,6 +237,7 @@ export declare abstract class BPTreeTransaction<K, V> {
233
237
  deleted: TransactionEntry<string, BPTreeNode<K, V>>[];
234
238
  };
235
239
  protected _clearCache(): void;
240
+ protected _resetForReload(): void;
236
241
  /**
237
242
  * Clears all cached nodes.
238
243
  * This method is useful for freeing up memory when the tree is no longer needed.
@@ -34,6 +34,8 @@ export declare class BPTreeAsyncTransaction<K, V> extends BPTreeTransaction<K, V
34
34
  protected getPairsGenerator(startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, direction: 1 | -1): AsyncGenerator<[K, V]>;
35
35
  init(): Promise<void>;
36
36
  protected _initInternal(): Promise<void>;
37
+ reload(): Promise<void>;
38
+ protected _reloadInternal(): Promise<void>;
37
39
  exists(key: K, value: V): Promise<boolean>;
38
40
  get(key: K): Promise<V | undefined>;
39
41
  keysStream(condition: BPTreeCondition<V>, options?: BPTreeSearchOption<K>): AsyncGenerator<K>;
@@ -31,6 +31,8 @@ export declare class BPTreeSyncTransaction<K, V> extends BPTreeTransaction<K, V>
31
31
  protected getPairsGenerator(startNode: BPTreeLeafNode<K, V>, endNode: BPTreeLeafNode<K, V> | null, direction: 1 | -1): Generator<[K, V]>;
32
32
  init(): void;
33
33
  protected _initInternal(): void;
34
+ reload(): void;
35
+ protected _reloadInternal(): void;
34
36
  exists(key: K, value: V): boolean;
35
37
  get(key: K): V | undefined;
36
38
  keysStream(condition: BPTreeCondition<V>, options?: BPTreeSearchOption<K>): Generator<K>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serializable-bptree",
3
- "version": "8.4.0",
3
+ "version": "8.4.2",
4
4
  "description": "Store the B+tree flexibly, not only in-memory.",
5
5
  "types": "./dist/types/index.d.ts",
6
6
  "main": "./dist/cjs/index.cjs",
@@ -44,7 +44,7 @@
44
44
  "typescript": "^5.9.3"
45
45
  },
46
46
  "dependencies": {
47
- "mvcc-api": "^1.3.5",
47
+ "mvcc-api": "^1.3.6",
48
48
  "ryoiki": "^1.2.0"
49
49
  }
50
50
  }