dataply 0.0.19 → 0.0.20-alpha.1

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 CHANGED
@@ -254,6 +254,45 @@ var MVCCTransaction = class {
254
254
  }
255
255
  return { success: true, created, updated, deleted };
256
256
  }
257
+ /**
258
+ * Cleans up both deletedCache and versionIndex based on minActiveVersion.
259
+ * Root transactions call this after commit to reclaim memory.
260
+ */
261
+ _cleanupDeletedCache() {
262
+ if (this.deletedCache.size === 0 && this.versionIndex.size === 0) return;
263
+ let minActiveVersion = this.version;
264
+ if (this.activeTransactions.size > 0) {
265
+ for (const tx of this.activeTransactions) {
266
+ if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
267
+ minActiveVersion = tx.snapshotVersion;
268
+ }
269
+ }
270
+ }
271
+ if (this.deletedCache.size > 0) {
272
+ for (const [key, cachedList] of this.deletedCache) {
273
+ const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
274
+ if (remaining.length === 0) {
275
+ this.deletedCache.delete(key);
276
+ } else {
277
+ this.deletedCache.set(key, remaining);
278
+ }
279
+ }
280
+ }
281
+ if (this.versionIndex.size > 0) {
282
+ for (const [key, versions] of this.versionIndex) {
283
+ let latestInSnapshotIdx = -1;
284
+ for (let i = versions.length - 1; i >= 0; i--) {
285
+ if (versions[i].version <= minActiveVersion) {
286
+ latestInSnapshotIdx = i;
287
+ break;
288
+ }
289
+ }
290
+ if (latestInSnapshotIdx > 0) {
291
+ versions.splice(0, latestInSnapshotIdx);
292
+ }
293
+ }
294
+ }
295
+ }
257
296
  };
258
297
  var SyncMVCCStrategy = class extends MVCCStrategy {
259
298
  };
@@ -593,25 +632,6 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
593
632
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
594
633
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
595
634
  }
596
- _cleanupDeletedCache() {
597
- if (this.deletedCache.size === 0) return;
598
- let minActiveVersion = this.version;
599
- if (this.activeTransactions.size > 0) {
600
- for (const tx of this.activeTransactions) {
601
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
602
- minActiveVersion = tx.snapshotVersion;
603
- }
604
- }
605
- }
606
- for (const [key, cachedList] of this.deletedCache) {
607
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
608
- if (remaining.length === 0) {
609
- this.deletedCache.delete(key);
610
- } else {
611
- this.deletedCache.set(key, remaining);
612
- }
613
- }
614
- }
615
635
  };
616
636
  var AsyncMVCCStrategy = class extends MVCCStrategy {
617
637
  };
@@ -1220,25 +1240,6 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1220
1240
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
1221
1241
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
1222
1242
  }
1223
- _cleanupDeletedCache() {
1224
- if (this.deletedCache.size === 0) return;
1225
- let minActiveVersion = this.version;
1226
- if (this.activeTransactions.size > 0) {
1227
- for (const tx of this.activeTransactions) {
1228
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
1229
- minActiveVersion = tx.snapshotVersion;
1230
- }
1231
- }
1232
- }
1233
- for (const [key, cachedList] of this.deletedCache) {
1234
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
1235
- if (remaining.length === 0) {
1236
- this.deletedCache.delete(key);
1237
- } else {
1238
- this.deletedCache.set(key, remaining);
1239
- }
1240
- }
1241
- }
1242
1243
  };
1243
1244
  var LRUMap = class {
1244
1245
  capacity;
@@ -4704,6 +4705,45 @@ var MVCCTransaction2 = class {
4704
4705
  }
4705
4706
  return { success: true, created, updated, deleted };
4706
4707
  }
4708
+ /**
4709
+ * Cleans up both deletedCache and versionIndex based on minActiveVersion.
4710
+ * Root transactions call this after commit to reclaim memory.
4711
+ */
4712
+ _cleanupDeletedCache() {
4713
+ if (this.deletedCache.size === 0 && this.versionIndex.size === 0) return;
4714
+ let minActiveVersion = this.version;
4715
+ if (this.activeTransactions.size > 0) {
4716
+ for (const tx of this.activeTransactions) {
4717
+ if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
4718
+ minActiveVersion = tx.snapshotVersion;
4719
+ }
4720
+ }
4721
+ }
4722
+ if (this.deletedCache.size > 0) {
4723
+ for (const [key, cachedList] of this.deletedCache) {
4724
+ const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
4725
+ if (remaining.length === 0) {
4726
+ this.deletedCache.delete(key);
4727
+ } else {
4728
+ this.deletedCache.set(key, remaining);
4729
+ }
4730
+ }
4731
+ }
4732
+ if (this.versionIndex.size > 0) {
4733
+ for (const [key, versions] of this.versionIndex) {
4734
+ let latestInSnapshotIdx = -1;
4735
+ for (let i = versions.length - 1; i >= 0; i--) {
4736
+ if (versions[i].version <= minActiveVersion) {
4737
+ latestInSnapshotIdx = i;
4738
+ break;
4739
+ }
4740
+ }
4741
+ if (latestInSnapshotIdx > 0) {
4742
+ versions.splice(0, latestInSnapshotIdx);
4743
+ }
4744
+ }
4745
+ }
4746
+ }
4707
4747
  };
4708
4748
  var SyncMVCCStrategy2 = class extends MVCCStrategy2 {
4709
4749
  };
@@ -5043,25 +5083,6 @@ var SyncMVCCTransaction2 = class _SyncMVCCTransaction2 extends MVCCTransaction2
5043
5083
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
5044
5084
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
5045
5085
  }
5046
- _cleanupDeletedCache() {
5047
- if (this.deletedCache.size === 0) return;
5048
- let minActiveVersion = this.version;
5049
- if (this.activeTransactions.size > 0) {
5050
- for (const tx of this.activeTransactions) {
5051
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
5052
- minActiveVersion = tx.snapshotVersion;
5053
- }
5054
- }
5055
- }
5056
- for (const [key, cachedList] of this.deletedCache) {
5057
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
5058
- if (remaining.length === 0) {
5059
- this.deletedCache.delete(key);
5060
- } else {
5061
- this.deletedCache.set(key, remaining);
5062
- }
5063
- }
5064
- }
5065
5086
  };
5066
5087
  var AsyncMVCCStrategy2 = class extends MVCCStrategy2 {
5067
5088
  };
@@ -5670,25 +5691,6 @@ var AsyncMVCCTransaction2 = class _AsyncMVCCTransaction2 extends MVCCTransaction
5670
5691
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
5671
5692
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
5672
5693
  }
5673
- _cleanupDeletedCache() {
5674
- if (this.deletedCache.size === 0) return;
5675
- let minActiveVersion = this.version;
5676
- if (this.activeTransactions.size > 0) {
5677
- for (const tx of this.activeTransactions) {
5678
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
5679
- minActiveVersion = tx.snapshotVersion;
5680
- }
5681
- }
5682
- }
5683
- for (const [key, cachedList] of this.deletedCache) {
5684
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
5685
- if (remaining.length === 0) {
5686
- this.deletedCache.delete(key);
5687
- } else {
5688
- this.deletedCache.set(key, remaining);
5689
- }
5690
- }
5691
- }
5692
5694
  };
5693
5695
 
5694
5696
  // src/utils/numberToBytes.ts
@@ -7373,6 +7375,7 @@ var WALManager = class {
7373
7375
  entrySize;
7374
7376
  buffer;
7375
7377
  view;
7378
+ commitCount = 0;
7376
7379
  /**
7377
7380
  * Constructor
7378
7381
  * @param walFilePath WAL file path
@@ -7532,6 +7535,20 @@ var WALManager = class {
7532
7535
  }
7533
7536
  return restoredPages;
7534
7537
  }
7538
+ /**
7539
+ * Increments the commit count.
7540
+ */
7541
+ incrementCommitCount() {
7542
+ this.commitCount++;
7543
+ }
7544
+ /**
7545
+ * Returns whether a checkpoint should be performed.
7546
+ * @param threshold Threshold
7547
+ * @returns Whether a checkpoint should be performed
7548
+ */
7549
+ shouldCheckpoint(threshold) {
7550
+ return this.commitCount >= threshold;
7551
+ }
7535
7552
  /**
7536
7553
  * Initializes (clears) the log file.
7537
7554
  * Should be called after a checkpoint.
@@ -7544,6 +7561,7 @@ var WALManager = class {
7544
7561
  return new Promise((resolve, reject) => {
7545
7562
  import_node_fs.default.truncate(this.walFilePath, 0, (err) => {
7546
7563
  if (err) return reject(err);
7564
+ this.commitCount = 0;
7547
7565
  resolve();
7548
7566
  });
7549
7567
  });
@@ -7669,16 +7687,15 @@ var PageMVCCStrategy = class extends AsyncMVCCStrategy2 {
7669
7687
  // src/core/PageFileSystem.ts
7670
7688
  var PageFileSystem = class {
7671
7689
  /**
7672
- * @param fileHandle 파일 핸들 (fs.open으로 얻은 핸들)
7673
- * @param pageSize 페이지 크기
7674
7690
  * @param pageCacheCapacity 페이지 캐시 크기
7675
- * @param walPath WAL 파일 경로 (기본값: null)
7691
+ * @param options 데이터플라이 옵션
7676
7692
  */
7677
- constructor(fileHandle, pageSize, pageCacheCapacity, walPath) {
7693
+ constructor(fileHandle, pageSize, pageCacheCapacity, options) {
7678
7694
  this.fileHandle = fileHandle;
7679
7695
  this.pageSize = pageSize;
7680
7696
  this.pageCacheCapacity = pageCacheCapacity;
7681
- this.walPath = walPath;
7697
+ this.options = options;
7698
+ const walPath = options.wal;
7682
7699
  this.walManager = walPath ? new WALManager(walPath, pageSize) : null;
7683
7700
  this.pageManagerFactory = new PageManagerFactory();
7684
7701
  this.pageStrategy = new PageMVCCStrategy(fileHandle, pageSize, pageCacheCapacity);
@@ -8003,6 +8020,7 @@ var PageFileSystem = class {
8003
8020
  */
8004
8021
  async close() {
8005
8022
  if (this.walManager) {
8023
+ await this.walManager.clear();
8006
8024
  this.walManager.close();
8007
8025
  }
8008
8026
  }
@@ -8714,12 +8732,15 @@ var LockManager = class {
8714
8732
  var Transaction = class {
8715
8733
  /**
8716
8734
  * @param id Transaction ID
8735
+ * @param context Transaction context
8717
8736
  * @param pageStrategy Page MVCC Strategy for disk I/O
8718
8737
  * @param lockManager LockManager instance
8738
+ * @param pfs Page File System
8719
8739
  */
8720
- constructor(id, context, pageStrategy, lockManager) {
8740
+ constructor(id, context, pageStrategy, lockManager, pfs) {
8721
8741
  this.context = context;
8722
8742
  this.lockManager = lockManager;
8743
+ this.pfs = pfs;
8723
8744
  this.id = id;
8724
8745
  this.pageStrategy = pageStrategy;
8725
8746
  }
@@ -8825,9 +8846,19 @@ var Transaction = class {
8825
8846
  await hook();
8826
8847
  }
8827
8848
  });
8849
+ if (this.pfs.wal && this.dirtyPages.size > 0) {
8850
+ await this.pfs.wal.prepareCommit(this.dirtyPages);
8851
+ await this.pfs.wal.writeCommitMarker();
8852
+ }
8828
8853
  for (const [pageId, data] of this.dirtyPages) {
8829
8854
  await this.pageStrategy.write(pageId, data);
8830
8855
  }
8856
+ if (this.pfs.wal) {
8857
+ this.pfs.wal.incrementCommitCount();
8858
+ if (this.pfs.wal.shouldCheckpoint(this.pfs.options.walCheckpointThreshold)) {
8859
+ await this.pfs.wal.clear();
8860
+ }
8861
+ }
8831
8862
  this.dirtyPages.clear();
8832
8863
  this.undoPages.clear();
8833
8864
  this.releaseAllLocks();
@@ -8888,7 +8919,7 @@ var DataplyAPI = class {
8888
8919
  this.fileHandle,
8889
8920
  this.options.pageSize,
8890
8921
  this.options.pageCacheCapacity,
8891
- this.options.wal
8922
+ this.options
8892
8923
  );
8893
8924
  this.textCodec = new TextCodec();
8894
8925
  this.txContext = new TransactionContext();
@@ -8946,7 +8977,8 @@ var DataplyAPI = class {
8946
8977
  return Object.assign({
8947
8978
  pageSize: 8192,
8948
8979
  pageCacheCapacity: 1e4,
8949
- wal: null
8980
+ wal: null,
8981
+ walCheckpointThreshold: 1e3
8950
8982
  }, options);
8951
8983
  }
8952
8984
  /**
@@ -9064,7 +9096,8 @@ var DataplyAPI = class {
9064
9096
  ++this.txIdCounter,
9065
9097
  this.txContext,
9066
9098
  this.pfs.getPageStrategy(),
9067
- this.lockManager
9099
+ this.lockManager,
9100
+ this.pfs
9068
9101
  );
9069
9102
  }
9070
9103
  /**
@@ -9120,9 +9153,10 @@ var DataplyAPI = class {
9120
9153
  await tx.rollback();
9121
9154
  }
9122
9155
  throw error;
9123
- }
9124
- if (!hasError && isInternalTx) {
9125
- await tx.commit();
9156
+ } finally {
9157
+ if (!hasError && isInternalTx) {
9158
+ await tx.commit();
9159
+ }
9126
9160
  }
9127
9161
  }
9128
9162
  /**
@@ -1,4 +1,4 @@
1
- import type { IndexPage, MetadataPage } from '../types';
1
+ import type { IndexPage, MetadataPage, DataplyOptions } from '../types';
2
2
  import type { Transaction } from './transaction/Transaction';
3
3
  import { PageManagerFactory } from './Page';
4
4
  import { WALManager } from './WALManager';
@@ -11,18 +11,16 @@ export declare class PageFileSystem {
11
11
  readonly fileHandle: number;
12
12
  readonly pageSize: number;
13
13
  readonly pageCacheCapacity: number;
14
- readonly walPath?: string | undefined | null;
14
+ readonly options: Required<DataplyOptions>;
15
15
  protected readonly pageFactory: PageManagerFactory;
16
16
  protected readonly walManager: WALManager | null;
17
17
  protected readonly pageManagerFactory: PageManagerFactory;
18
18
  protected readonly pageStrategy: PageMVCCStrategy;
19
19
  /**
20
- * @param fileHandle 파일 핸들 (fs.open으로 얻은 핸들)
21
- * @param pageSize 페이지 크기
22
20
  * @param pageCacheCapacity 페이지 캐시 크기
23
- * @param walPath WAL 파일 경로 (기본값: null)
21
+ * @param options 데이터플라이 옵션
24
22
  */
25
- constructor(fileHandle: number, pageSize: number, pageCacheCapacity: number, walPath?: string | undefined | null);
23
+ constructor(fileHandle: number, pageSize: number, pageCacheCapacity: number, options: Required<DataplyOptions>);
26
24
  /**
27
25
  * Initializes the page file system.
28
26
  * Performs WAL recovery if necessary.
@@ -10,6 +10,7 @@ export declare class WALManager {
10
10
  private readonly entrySize;
11
11
  private buffer;
12
12
  private view;
13
+ private commitCount;
13
14
  /**
14
15
  * Constructor
15
16
  * @param walFilePath WAL file path
@@ -54,6 +55,16 @@ export declare class WALManager {
54
55
  * @returns Recovered page map
55
56
  */
56
57
  readAllSync(): Map<number, Uint8Array>;
58
+ /**
59
+ * Increments the commit count.
60
+ */
61
+ incrementCommitCount(): void;
62
+ /**
63
+ * Returns whether a checkpoint should be performed.
64
+ * @param threshold Threshold
65
+ * @returns Whether a checkpoint should be performed
66
+ */
67
+ shouldCheckpoint(threshold: number): boolean;
57
68
  /**
58
69
  * Initializes (clears) the log file.
59
70
  * Should be called after a checkpoint.
@@ -1,3 +1,4 @@
1
+ import type { PageFileSystem } from '../PageFileSystem';
1
2
  import { BPTreeAsyncTransaction } from 'serializable-bptree';
2
3
  import { LockManager } from './LockManager';
3
4
  import { TransactionContext } from './TxContext';
@@ -9,6 +10,7 @@ import { PageMVCCStrategy } from '../PageMVCCStrategy';
9
10
  export declare class Transaction {
10
11
  readonly context: TransactionContext;
11
12
  private readonly lockManager;
13
+ private readonly pfs;
12
14
  /** Transaction ID */
13
15
  readonly id: number;
14
16
  /** List of held lock IDs (LOCK_ID) */
@@ -29,10 +31,12 @@ export declare class Transaction {
29
31
  private readonly pageStrategy;
30
32
  /**
31
33
  * @param id Transaction ID
34
+ * @param context Transaction context
32
35
  * @param pageStrategy Page MVCC Strategy for disk I/O
33
36
  * @param lockManager LockManager instance
37
+ * @param pfs Page File System
34
38
  */
35
- constructor(id: number, context: TransactionContext, pageStrategy: PageMVCCStrategy, lockManager: LockManager);
39
+ constructor(id: number, context: TransactionContext, pageStrategy: PageMVCCStrategy, lockManager: LockManager, pfs: PageFileSystem);
36
40
  /**
37
41
  * Sets the BPTree transaction.
38
42
  * @param tx BPTree transaction
@@ -1,6 +1,7 @@
1
1
  export interface DataplyOptions {
2
2
  /**
3
3
  * The size of a page in bytes.
4
+ * Default is 8192.
4
5
  */
5
6
  pageSize?: number;
6
7
  /**
@@ -9,8 +10,14 @@ export interface DataplyOptions {
9
10
  wal?: string | undefined | null;
10
11
  /**
11
12
  * The maximum number of pages to cache in memory.
13
+ * Default is 10000.
12
14
  */
13
15
  pageCacheCapacity?: number;
16
+ /**
17
+ * The number of commits before automatically clearing the WAL.
18
+ * Default is 1000.
19
+ */
20
+ walCheckpointThreshold?: number;
14
21
  }
15
22
  export interface DataplyMetadata {
16
23
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataply",
3
- "version": "0.0.19",
3
+ "version": "0.0.20-alpha.1",
4
4
  "description": "A lightweight storage engine for Node.js with support for MVCC, WAL.",
5
5
  "license": "MIT",
6
6
  "author": "izure <admin@izure.org>",
@@ -47,8 +47,8 @@
47
47
  "dependencies": {
48
48
  "cache-entanglement": "^1.7.1",
49
49
  "hookall": "^2.2.0",
50
- "mvcc-api": "^1.2.9",
50
+ "mvcc-api": "^1.2.11",
51
51
  "ryoiki": "^1.2.0",
52
- "serializable-bptree": "^8.1.0"
52
+ "serializable-bptree": "^8.1.1"
53
53
  }
54
54
  }