dataply 0.0.20-alpha.2 → 0.0.20-alpha.3
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
|
@@ -7427,9 +7427,6 @@ var WALManager = class {
|
|
|
7427
7427
|
promises.push(writePage(pageId, data));
|
|
7428
7428
|
}
|
|
7429
7429
|
await Promise.all(promises);
|
|
7430
|
-
if (restoredPages.size > 0) {
|
|
7431
|
-
await this.clear();
|
|
7432
|
-
}
|
|
7433
7430
|
}
|
|
7434
7431
|
/**
|
|
7435
7432
|
* WAL에 페이지 데이터를 기록합니다 (Phase 1: Prepare).
|
|
@@ -7590,6 +7587,8 @@ var PageMVCCStrategy = class extends AsyncMVCCStrategy2 {
|
|
|
7590
7587
|
}
|
|
7591
7588
|
/** LRU 캐시 (페이지 ID -> 페이지 데이터) */
|
|
7592
7589
|
cache;
|
|
7590
|
+
/** 디스크에 기록되지 않은 변경된 페이지들 (페이지 ID -> 페이지 데이터) */
|
|
7591
|
+
dirtyPages = /* @__PURE__ */ new Map();
|
|
7593
7592
|
/** 파일 크기 (논리적) */
|
|
7594
7593
|
fileSize;
|
|
7595
7594
|
/**
|
|
@@ -7599,6 +7598,12 @@ var PageMVCCStrategy = class extends AsyncMVCCStrategy2 {
|
|
|
7599
7598
|
* @returns 페이지 데이터
|
|
7600
7599
|
*/
|
|
7601
7600
|
async read(pageId) {
|
|
7601
|
+
const dirty = this.dirtyPages.get(pageId);
|
|
7602
|
+
if (dirty) {
|
|
7603
|
+
const copy = new Uint8Array(this.pageSize);
|
|
7604
|
+
copy.set(dirty);
|
|
7605
|
+
return copy;
|
|
7606
|
+
}
|
|
7602
7607
|
const cached = this.cache.get(pageId);
|
|
7603
7608
|
if (cached) {
|
|
7604
7609
|
const copy = new Uint8Array(this.pageSize);
|
|
@@ -7626,21 +7631,49 @@ var PageMVCCStrategy = class extends AsyncMVCCStrategy2 {
|
|
|
7626
7631
|
if (pageStartPos + this.pageSize > 512 * 1024 * 1024) {
|
|
7627
7632
|
throw new Error(`[Safety Limit] File write exceeds 512MB limit at position ${pageStartPos}`);
|
|
7628
7633
|
}
|
|
7629
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
this.cache.set(pageId,
|
|
7634
|
+
const dataCopy = new Uint8Array(this.pageSize);
|
|
7635
|
+
dataCopy.set(data);
|
|
7636
|
+
this.dirtyPages.set(pageId, dataCopy);
|
|
7637
|
+
this.cache.set(pageId, dataCopy);
|
|
7633
7638
|
const endPosition = pageStartPos + this.pageSize;
|
|
7634
7639
|
if (endPosition > this.fileSize) {
|
|
7635
7640
|
this.fileSize = endPosition;
|
|
7636
7641
|
}
|
|
7637
7642
|
}
|
|
7643
|
+
/**
|
|
7644
|
+
* 더티 페이지들을 메인 디스크 파일에 일괄 기록합니다.
|
|
7645
|
+
* WAL 체크포인트 시점에 호출되어야 합니다.
|
|
7646
|
+
*/
|
|
7647
|
+
async flush() {
|
|
7648
|
+
if (this.dirtyPages.size === 0) {
|
|
7649
|
+
return;
|
|
7650
|
+
}
|
|
7651
|
+
const sortedPageIds = Array.from(this.dirtyPages.keys()).sort((a, b) => a - b);
|
|
7652
|
+
for (const pageId of sortedPageIds) {
|
|
7653
|
+
const data = this.dirtyPages.get(pageId);
|
|
7654
|
+
const position = pageId * this.pageSize;
|
|
7655
|
+
await this._writeToDisk(data, position);
|
|
7656
|
+
}
|
|
7657
|
+
this.dirtyPages.clear();
|
|
7658
|
+
}
|
|
7659
|
+
/**
|
|
7660
|
+
* 메인 DB 파일의 물리적 동기화를 수행합니다 (fsync).
|
|
7661
|
+
*/
|
|
7662
|
+
async sync() {
|
|
7663
|
+
return new Promise((resolve, reject) => {
|
|
7664
|
+
import_node_fs2.default.fsync(this.fileHandle, (err) => {
|
|
7665
|
+
if (err) return reject(err);
|
|
7666
|
+
resolve();
|
|
7667
|
+
});
|
|
7668
|
+
});
|
|
7669
|
+
}
|
|
7638
7670
|
/**
|
|
7639
7671
|
* 페이지 삭제 (실제로는 캐시에서만 제거)
|
|
7640
7672
|
* 실제 페이지 해제는 상위 레이어(FreeList)에서 관리합니다.
|
|
7641
7673
|
* @param pageId 페이지 ID
|
|
7642
7674
|
*/
|
|
7643
7675
|
async delete(pageId) {
|
|
7676
|
+
this.dirtyPages.delete(pageId);
|
|
7644
7677
|
this.cache.delete(pageId);
|
|
7645
7678
|
}
|
|
7646
7679
|
/**
|
|
@@ -7649,6 +7682,9 @@ var PageMVCCStrategy = class extends AsyncMVCCStrategy2 {
|
|
|
7649
7682
|
* @returns 존재하면 true
|
|
7650
7683
|
*/
|
|
7651
7684
|
async exists(pageId) {
|
|
7685
|
+
if (this.dirtyPages.has(pageId)) {
|
|
7686
|
+
return true;
|
|
7687
|
+
}
|
|
7652
7688
|
const pageStartPos = pageId * this.pageSize;
|
|
7653
7689
|
return pageStartPos < this.fileSize;
|
|
7654
7690
|
}
|
|
@@ -7700,11 +7736,13 @@ var PageFileSystem = class {
|
|
|
7700
7736
|
this.walManager = walPath ? new WALManager(walPath, pageSize) : null;
|
|
7701
7737
|
this.pageManagerFactory = new PageManagerFactory();
|
|
7702
7738
|
this.pageStrategy = new PageMVCCStrategy(fileHandle, pageSize, pageCacheCapacity);
|
|
7739
|
+
this.lock = new Ryoiki3();
|
|
7703
7740
|
}
|
|
7704
7741
|
pageFactory = new PageManagerFactory();
|
|
7705
7742
|
walManager;
|
|
7706
7743
|
pageManagerFactory;
|
|
7707
7744
|
pageStrategy;
|
|
7745
|
+
lock;
|
|
7708
7746
|
/**
|
|
7709
7747
|
* Initializes the page file system.
|
|
7710
7748
|
* Performs WAL recovery if necessary.
|
|
@@ -7714,8 +7752,18 @@ var PageFileSystem = class {
|
|
|
7714
7752
|
await this.walManager.recover(async (pageId, data) => {
|
|
7715
7753
|
await this.pageStrategy.write(pageId, data);
|
|
7716
7754
|
});
|
|
7755
|
+
await this.checkpoint();
|
|
7717
7756
|
}
|
|
7718
7757
|
}
|
|
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
|
+
}
|
|
7719
7767
|
/**
|
|
7720
7768
|
* Returns the page strategy for transaction use.
|
|
7721
7769
|
*/
|
|
@@ -8013,15 +8061,30 @@ var PageFileSystem = class {
|
|
|
8013
8061
|
async commitToWAL(dirtyPages) {
|
|
8014
8062
|
if (this.walManager) {
|
|
8015
8063
|
await this.walManager.prepareCommit(dirtyPages);
|
|
8016
|
-
await this.walManager.finalizeCommit(
|
|
8064
|
+
await this.walManager.finalizeCommit(true);
|
|
8017
8065
|
}
|
|
8018
8066
|
}
|
|
8067
|
+
/**
|
|
8068
|
+
* 체크포인트를 수행합니다.
|
|
8069
|
+
* 1. 메모리의 더티 페이지를 DB 파일에 기록 (Flush)
|
|
8070
|
+
* 2. DB 파일 물리적 동기화 (Sync/fsync)
|
|
8071
|
+
* 3. WAL 로그 파일 비우기 (Clear/Truncate)
|
|
8072
|
+
*/
|
|
8073
|
+
async checkpoint() {
|
|
8074
|
+
return this.lockCheckpoint(async () => {
|
|
8075
|
+
await this.pageStrategy.flush();
|
|
8076
|
+
await this.pageStrategy.sync();
|
|
8077
|
+
if (this.walManager) {
|
|
8078
|
+
await this.walManager.clear();
|
|
8079
|
+
}
|
|
8080
|
+
});
|
|
8081
|
+
}
|
|
8019
8082
|
/**
|
|
8020
8083
|
* Closes the page file system.
|
|
8021
8084
|
*/
|
|
8022
8085
|
async close() {
|
|
8086
|
+
await this.checkpoint();
|
|
8023
8087
|
if (this.walManager) {
|
|
8024
|
-
await this.walManager.clear();
|
|
8025
8088
|
this.walManager.close();
|
|
8026
8089
|
}
|
|
8027
8090
|
}
|
|
@@ -8842,27 +8905,29 @@ var Transaction = class {
|
|
|
8842
8905
|
* Commits the transaction.
|
|
8843
8906
|
*/
|
|
8844
8907
|
async commit() {
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
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
|
+
});
|
|
8914
|
+
if (this.pfs.wal && this.dirtyPages.size > 0) {
|
|
8915
|
+
await this.pfs.wal.prepareCommit(this.dirtyPages);
|
|
8916
|
+
await this.pfs.wal.writeCommitMarker();
|
|
8848
8917
|
}
|
|
8849
|
-
|
|
8850
|
-
|
|
8851
|
-
await this.pfs.wal.prepareCommit(this.dirtyPages);
|
|
8852
|
-
await this.pfs.wal.writeCommitMarker();
|
|
8853
|
-
}
|
|
8854
|
-
for (const [pageId, data] of this.dirtyPages) {
|
|
8855
|
-
await this.pageStrategy.write(pageId, data);
|
|
8856
|
-
}
|
|
8857
|
-
if (this.pfs.wal) {
|
|
8858
|
-
this.pfs.wal.incrementWrittenPages(this.dirtyPages.size);
|
|
8859
|
-
if (this.pfs.wal.shouldCheckpoint(this.pfs.options.walCheckpointThreshold)) {
|
|
8860
|
-
await this.pfs.wal.clear();
|
|
8918
|
+
for (const [pageId, data] of this.dirtyPages) {
|
|
8919
|
+
await this.pageStrategy.write(pageId, data);
|
|
8861
8920
|
}
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
|
|
8865
|
-
|
|
8921
|
+
if (this.pfs.wal) {
|
|
8922
|
+
this.pfs.wal.incrementWrittenPages(this.dirtyPages.size);
|
|
8923
|
+
if (this.pfs.wal.shouldCheckpoint(this.pfs.options.walCheckpointThreshold)) {
|
|
8924
|
+
await this.pfs.checkpoint();
|
|
8925
|
+
}
|
|
8926
|
+
}
|
|
8927
|
+
this.dirtyPages.clear();
|
|
8928
|
+
this.undoPages.clear();
|
|
8929
|
+
this.releaseAllLocks();
|
|
8930
|
+
});
|
|
8866
8931
|
}
|
|
8867
8932
|
/**
|
|
8868
8933
|
* Rolls back the transaction.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IndexPage, MetadataPage, DataplyOptions } from '../types';
|
|
2
|
+
import { Ryoiki } from 'ryoiki';
|
|
2
3
|
import type { Transaction } from './transaction/Transaction';
|
|
3
4
|
import { PageManagerFactory } from './Page';
|
|
4
5
|
import { WALManager } from './WALManager';
|
|
@@ -16,6 +17,7 @@ export declare class PageFileSystem {
|
|
|
16
17
|
protected readonly walManager: WALManager | null;
|
|
17
18
|
protected readonly pageManagerFactory: PageManagerFactory;
|
|
18
19
|
protected readonly pageStrategy: PageMVCCStrategy;
|
|
20
|
+
protected readonly lock: Ryoiki;
|
|
19
21
|
/**
|
|
20
22
|
* @param pageCacheCapacity 페이지 캐시 크기
|
|
21
23
|
* @param options 데이터플라이 옵션
|
|
@@ -26,6 +28,7 @@ export declare class PageFileSystem {
|
|
|
26
28
|
* Performs WAL recovery if necessary.
|
|
27
29
|
*/
|
|
28
30
|
init(): Promise<void>;
|
|
31
|
+
lockCheckpoint(fn: () => Promise<void>): Promise<void>;
|
|
29
32
|
/**
|
|
30
33
|
* Returns the page strategy for transaction use.
|
|
31
34
|
*/
|
|
@@ -130,6 +133,13 @@ export declare class PageFileSystem {
|
|
|
130
133
|
* @param dirtyPages 변경된 페이지들
|
|
131
134
|
*/
|
|
132
135
|
commitToWAL(dirtyPages: Map<number, Uint8Array>): Promise<void>;
|
|
136
|
+
/**
|
|
137
|
+
* 체크포인트를 수행합니다.
|
|
138
|
+
* 1. 메모리의 더티 페이지를 DB 파일에 기록 (Flush)
|
|
139
|
+
* 2. DB 파일 물리적 동기화 (Sync/fsync)
|
|
140
|
+
* 3. WAL 로그 파일 비우기 (Clear/Truncate)
|
|
141
|
+
*/
|
|
142
|
+
checkpoint(): Promise<void>;
|
|
133
143
|
/**
|
|
134
144
|
* Closes the page file system.
|
|
135
145
|
*/
|
|
@@ -11,6 +11,8 @@ export declare class PageMVCCStrategy extends AsyncMVCCStrategy<number, Uint8Arr
|
|
|
11
11
|
private readonly pageSize;
|
|
12
12
|
/** LRU 캐시 (페이지 ID -> 페이지 데이터) */
|
|
13
13
|
private readonly cache;
|
|
14
|
+
/** 디스크에 기록되지 않은 변경된 페이지들 (페이지 ID -> 페이지 데이터) */
|
|
15
|
+
private readonly dirtyPages;
|
|
14
16
|
/** 파일 크기 (논리적) */
|
|
15
17
|
private fileSize;
|
|
16
18
|
constructor(fileHandle: number, pageSize: number, cacheCapacity: number);
|
|
@@ -27,6 +29,15 @@ export declare class PageMVCCStrategy extends AsyncMVCCStrategy<number, Uint8Arr
|
|
|
27
29
|
* @param data 페이지 데이터
|
|
28
30
|
*/
|
|
29
31
|
write(pageId: number, data: Uint8Array): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* 더티 페이지들을 메인 디스크 파일에 일괄 기록합니다.
|
|
34
|
+
* WAL 체크포인트 시점에 호출되어야 합니다.
|
|
35
|
+
*/
|
|
36
|
+
flush(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* 메인 DB 파일의 물리적 동기화를 수행합니다 (fsync).
|
|
39
|
+
*/
|
|
40
|
+
sync(): Promise<void>;
|
|
30
41
|
/**
|
|
31
42
|
* 페이지 삭제 (실제로는 캐시에서만 제거)
|
|
32
43
|
* 실제 페이지 해제는 상위 레이어(FreeList)에서 관리합니다.
|