dataply 0.0.20-alpha.3 → 0.0.20-alpha.5

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
@@ -7606,9 +7606,7 @@ var PageMVCCStrategy = class extends AsyncMVCCStrategy2 {
7606
7606
  }
7607
7607
  const cached = this.cache.get(pageId);
7608
7608
  if (cached) {
7609
- const copy = new Uint8Array(this.pageSize);
7610
- copy.set(cached);
7611
- return copy;
7609
+ return cached;
7612
7610
  }
7613
7611
  const buffer = new Uint8Array(this.pageSize);
7614
7612
  const pageStartPos = pageId * this.pageSize;
@@ -7648,13 +7646,14 @@ var PageMVCCStrategy = class extends AsyncMVCCStrategy2 {
7648
7646
  if (this.dirtyPages.size === 0) {
7649
7647
  return;
7650
7648
  }
7651
- const sortedPageIds = Array.from(this.dirtyPages.keys()).sort((a, b) => a - b);
7649
+ const snapshot = new Map(this.dirtyPages);
7650
+ const sortedPageIds = Array.from(snapshot.keys()).sort((a, b) => a - b);
7652
7651
  for (const pageId of sortedPageIds) {
7653
- const data = this.dirtyPages.get(pageId);
7652
+ const data = snapshot.get(pageId);
7654
7653
  const position = pageId * this.pageSize;
7655
7654
  await this._writeToDisk(data, position);
7655
+ this.dirtyPages.delete(pageId);
7656
7656
  }
7657
- this.dirtyPages.clear();
7658
7657
  }
7659
7658
  /**
7660
7659
  * 메인 DB 파일의 물리적 동기화를 수행합니다 (fsync).
@@ -7736,13 +7735,30 @@ var PageFileSystem = class {
7736
7735
  this.walManager = walPath ? new WALManager(walPath, pageSize) : null;
7737
7736
  this.pageManagerFactory = new PageManagerFactory();
7738
7737
  this.pageStrategy = new PageMVCCStrategy(fileHandle, pageSize, pageCacheCapacity);
7739
- this.lock = new Ryoiki3();
7740
7738
  }
7741
7739
  pageFactory = new PageManagerFactory();
7742
7740
  walManager;
7743
7741
  pageManagerFactory;
7744
7742
  pageStrategy;
7745
- lock;
7743
+ /** 글로벌 동기화(체크포인트/커밋)를 위한 Mutex */
7744
+ lockPromise = Promise.resolve();
7745
+ /**
7746
+ * 글로벌 동기화 범위 내에서 작업을 실행합니다.
7747
+ * @param task 수행할 비동기 작업
7748
+ */
7749
+ async runGlobalLock(task) {
7750
+ const previous = this.lockPromise;
7751
+ let resolveLock;
7752
+ this.lockPromise = new Promise((resolve) => {
7753
+ resolveLock = resolve;
7754
+ });
7755
+ await previous;
7756
+ try {
7757
+ return await task();
7758
+ } finally {
7759
+ resolveLock();
7760
+ }
7761
+ }
7746
7762
  /**
7747
7763
  * Initializes the page file system.
7748
7764
  * Performs WAL recovery if necessary.
@@ -7755,15 +7771,6 @@ var PageFileSystem = class {
7755
7771
  await this.checkpoint();
7756
7772
  }
7757
7773
  }
7758
- async lockCheckpoint(fn) {
7759
- let lockId;
7760
- return this.lock.writeLock(async (_lockId) => {
7761
- lockId = _lockId;
7762
- await fn();
7763
- }).finally(() => {
7764
- this.lock.writeUnlock(lockId);
7765
- });
7766
- }
7767
7774
  /**
7768
7775
  * Returns the page strategy for transaction use.
7769
7776
  */
@@ -8071,7 +8078,7 @@ var PageFileSystem = class {
8071
8078
  * 3. WAL 로그 파일 비우기 (Clear/Truncate)
8072
8079
  */
8073
8080
  async checkpoint() {
8074
- return this.lockCheckpoint(async () => {
8081
+ await this.runGlobalLock(async () => {
8075
8082
  await this.pageStrategy.flush();
8076
8083
  await this.pageStrategy.sync();
8077
8084
  if (this.walManager) {
@@ -8707,6 +8714,30 @@ var RowTableEngine = class {
8707
8714
  }
8708
8715
  return this.fetchRowByRid(pk, rid, tx);
8709
8716
  }
8717
+ /**
8718
+ * Selects multiple rows by their PKs in a single B+ Tree traversal.
8719
+ * @param pks Array of PKs to look up
8720
+ * @param tx Transaction
8721
+ * @returns Array of raw data of the rows in the same order as input PKs
8722
+ */
8723
+ async selectMany(pks, tx) {
8724
+ if (pks.length === 0) {
8725
+ return [];
8726
+ }
8727
+ const minPk = Math.min(...pks);
8728
+ const maxPk = Math.max(...pks);
8729
+ const pkSet = new Set(pks);
8730
+ const resultMap = /* @__PURE__ */ new Map();
8731
+ const btx = await this.getBPTreeTransaction(tx);
8732
+ const stream = btx.whereStream({ gte: minPk, lte: maxPk });
8733
+ for await (const [rid, pk] of stream) {
8734
+ if (pkSet.has(pk)) {
8735
+ const rowData = await this.fetchRowByRid(pk, rid, tx);
8736
+ resultMap.set(pk, rowData);
8737
+ }
8738
+ }
8739
+ return pks.map((pk) => resultMap.get(pk) ?? null);
8740
+ }
8710
8741
  async fetchRowByRid(pk, rid, tx) {
8711
8742
  this.keyManager.setBufferFromKey(rid, this.ridBuffer);
8712
8743
  const pageId = this.keyManager.getPageId(this.ridBuffer);
@@ -8905,12 +8936,13 @@ var Transaction = class {
8905
8936
  * Commits the transaction.
8906
8937
  */
8907
8938
  async commit() {
8908
- return this.pfs.lockCheckpoint(async () => {
8909
- await this.context.run(this, async () => {
8910
- for (const hook of this.commitHooks) {
8911
- await hook();
8912
- }
8913
- });
8939
+ await this.context.run(this, async () => {
8940
+ for (const hook of this.commitHooks) {
8941
+ await hook();
8942
+ }
8943
+ });
8944
+ let shouldTriggerCheckpoint = false;
8945
+ await this.pfs.runGlobalLock(async () => {
8914
8946
  if (this.pfs.wal && this.dirtyPages.size > 0) {
8915
8947
  await this.pfs.wal.prepareCommit(this.dirtyPages);
8916
8948
  await this.pfs.wal.writeCommitMarker();
@@ -8921,13 +8953,16 @@ var Transaction = class {
8921
8953
  if (this.pfs.wal) {
8922
8954
  this.pfs.wal.incrementWrittenPages(this.dirtyPages.size);
8923
8955
  if (this.pfs.wal.shouldCheckpoint(this.pfs.options.walCheckpointThreshold)) {
8924
- await this.pfs.checkpoint();
8956
+ shouldTriggerCheckpoint = true;
8925
8957
  }
8926
8958
  }
8927
- this.dirtyPages.clear();
8928
- this.undoPages.clear();
8929
- this.releaseAllLocks();
8930
8959
  });
8960
+ if (shouldTriggerCheckpoint) {
8961
+ await this.pfs.checkpoint();
8962
+ }
8963
+ this.dirtyPages.clear();
8964
+ this.undoPages.clear();
8965
+ this.releaseAllLocks();
8931
8966
  }
8932
8967
  /**
8933
8968
  * Rolls back the transaction.
@@ -9338,6 +9373,19 @@ var DataplyAPI = class {
9338
9373
  return this.textCodec.decode(data);
9339
9374
  }, tx);
9340
9375
  }
9376
+ async selectMany(pks, asRaw = false, tx) {
9377
+ if (!this.initialized) {
9378
+ throw new Error("Dataply instance is not initialized");
9379
+ }
9380
+ return this.runWithDefault(async (tx2) => {
9381
+ const results = await this.rowTableEngine.selectMany(pks, tx2);
9382
+ return results.map((data) => {
9383
+ if (data === null) return null;
9384
+ if (asRaw) return data;
9385
+ return this.textCodec.decode(data);
9386
+ });
9387
+ }, tx);
9388
+ }
9341
9389
  /**
9342
9390
  * Closes the dataply file.
9343
9391
  */
@@ -9428,6 +9476,9 @@ var Dataply = class {
9428
9476
  async select(pk, asRaw = false, tx) {
9429
9477
  return this.api.select(pk, asRaw, tx);
9430
9478
  }
9479
+ async selectMany(pks, asRaw = false, tx) {
9480
+ return this.api.selectMany(pks, asRaw, tx);
9481
+ }
9431
9482
  /**
9432
9483
  * Closes the dataply file.
9433
9484
  */
@@ -68,6 +68,16 @@ export declare class Dataply {
68
68
  select(pk: number, asRaw: true, tx?: Transaction): Promise<Uint8Array | null>;
69
69
  select(pk: number, asRaw: false, tx?: Transaction): Promise<string | null>;
70
70
  select(pk: number, asRaw?: boolean, tx?: Transaction): Promise<string | null>;
71
+ /**
72
+ * Selects multiple data by their PKs.
73
+ * @param pks Array of PKs to select
74
+ * @param asRaw Whether to return the selected data as raw
75
+ * @param tx Transaction
76
+ * @returns Array of selected data in the same order as input PKs
77
+ */
78
+ selectMany(pks: number[], asRaw: true, tx?: Transaction): Promise<(Uint8Array | null)[]>;
79
+ selectMany(pks: number[], asRaw: false, tx?: Transaction): Promise<(string | null)[]>;
80
+ selectMany(pks: number[], asRaw?: boolean, tx?: Transaction): Promise<(string | null)[]>;
71
81
  /**
72
82
  * Closes the dataply file.
73
83
  */
@@ -157,6 +157,16 @@ export declare class DataplyAPI {
157
157
  select(pk: number, asRaw: true, tx?: Transaction): Promise<Uint8Array | null>;
158
158
  select(pk: number, asRaw: false, tx?: Transaction): Promise<string | null>;
159
159
  select(pk: number, asRaw?: boolean, tx?: Transaction): Promise<string | null>;
160
+ /**
161
+ * Selects multiple data by their PKs.
162
+ * @param pks Array of PKs to select
163
+ * @param asRaw Whether to return the selected data as raw
164
+ * @param tx Transaction
165
+ * @returns Array of selected data in the same order as input PKs
166
+ */
167
+ selectMany(pks: number[], asRaw: true, tx?: Transaction): Promise<(Uint8Array | null)[]>;
168
+ selectMany(pks: number[], asRaw: false, tx?: Transaction): Promise<(string | null)[]>;
169
+ selectMany(pks: number[], asRaw?: boolean, tx?: Transaction): Promise<(string | null)[]>;
160
170
  /**
161
171
  * Closes the dataply file.
162
172
  */
@@ -1,5 +1,4 @@
1
1
  import type { IndexPage, MetadataPage, DataplyOptions } from '../types';
2
- import { Ryoiki } from 'ryoiki';
3
2
  import type { Transaction } from './transaction/Transaction';
4
3
  import { PageManagerFactory } from './Page';
5
4
  import { WALManager } from './WALManager';
@@ -17,18 +16,23 @@ export declare class PageFileSystem {
17
16
  protected readonly walManager: WALManager | null;
18
17
  protected readonly pageManagerFactory: PageManagerFactory;
19
18
  protected readonly pageStrategy: PageMVCCStrategy;
20
- protected readonly lock: Ryoiki;
19
+ /** 글로벌 동기화(체크포인트/커밋)를 위한 Mutex */
20
+ private lockPromise;
21
21
  /**
22
22
  * @param pageCacheCapacity 페이지 캐시 크기
23
23
  * @param options 데이터플라이 옵션
24
24
  */
25
25
  constructor(fileHandle: number, pageSize: number, pageCacheCapacity: number, options: Required<DataplyOptions>);
26
+ /**
27
+ * 글로벌 동기화 범위 내에서 작업을 실행합니다.
28
+ * @param task 수행할 비동기 작업
29
+ */
30
+ runGlobalLock<T>(task: () => Promise<T>): Promise<T>;
26
31
  /**
27
32
  * Initializes the page file system.
28
33
  * Performs WAL recovery if necessary.
29
34
  */
30
35
  init(): Promise<void>;
31
- lockCheckpoint(fn: () => Promise<void>): Promise<void>;
32
36
  /**
33
37
  * Returns the page strategy for transaction use.
34
38
  */
@@ -123,6 +123,13 @@ export declare class RowTableEngine {
123
123
  * @returns Raw data of the row
124
124
  */
125
125
  selectByPK(pk: number, tx: Transaction): Promise<Uint8Array | null>;
126
+ /**
127
+ * Selects multiple rows by their PKs in a single B+ Tree traversal.
128
+ * @param pks Array of PKs to look up
129
+ * @param tx Transaction
130
+ * @returns Array of raw data of the rows in the same order as input PKs
131
+ */
132
+ selectMany(pks: number[], tx: Transaction): Promise<(Uint8Array | null)[]>;
126
133
  private fetchRowByRid;
127
134
  /**
128
135
  * Returns the count of rows.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataply",
3
- "version": "0.0.20-alpha.3",
3
+ "version": "0.0.20-alpha.5",
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>",