dataply 0.0.20-alpha.2 → 0.0.20-alpha.4
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,50 @@ 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 snapshot = new Map(this.dirtyPages);
|
|
7652
|
+
const sortedPageIds = Array.from(snapshot.keys()).sort((a, b) => a - b);
|
|
7653
|
+
for (const pageId of sortedPageIds) {
|
|
7654
|
+
const data = snapshot.get(pageId);
|
|
7655
|
+
const position = pageId * this.pageSize;
|
|
7656
|
+
await this._writeToDisk(data, position);
|
|
7657
|
+
this.dirtyPages.delete(pageId);
|
|
7658
|
+
}
|
|
7659
|
+
}
|
|
7660
|
+
/**
|
|
7661
|
+
* 메인 DB 파일의 물리적 동기화를 수행합니다 (fsync).
|
|
7662
|
+
*/
|
|
7663
|
+
async sync() {
|
|
7664
|
+
return new Promise((resolve, reject) => {
|
|
7665
|
+
import_node_fs2.default.fsync(this.fileHandle, (err) => {
|
|
7666
|
+
if (err) return reject(err);
|
|
7667
|
+
resolve();
|
|
7668
|
+
});
|
|
7669
|
+
});
|
|
7670
|
+
}
|
|
7638
7671
|
/**
|
|
7639
7672
|
* 페이지 삭제 (실제로는 캐시에서만 제거)
|
|
7640
7673
|
* 실제 페이지 해제는 상위 레이어(FreeList)에서 관리합니다.
|
|
7641
7674
|
* @param pageId 페이지 ID
|
|
7642
7675
|
*/
|
|
7643
7676
|
async delete(pageId) {
|
|
7677
|
+
this.dirtyPages.delete(pageId);
|
|
7644
7678
|
this.cache.delete(pageId);
|
|
7645
7679
|
}
|
|
7646
7680
|
/**
|
|
@@ -7649,6 +7683,9 @@ var PageMVCCStrategy = class extends AsyncMVCCStrategy2 {
|
|
|
7649
7683
|
* @returns 존재하면 true
|
|
7650
7684
|
*/
|
|
7651
7685
|
async exists(pageId) {
|
|
7686
|
+
if (this.dirtyPages.has(pageId)) {
|
|
7687
|
+
return true;
|
|
7688
|
+
}
|
|
7652
7689
|
const pageStartPos = pageId * this.pageSize;
|
|
7653
7690
|
return pageStartPos < this.fileSize;
|
|
7654
7691
|
}
|
|
@@ -7705,6 +7742,25 @@ var PageFileSystem = class {
|
|
|
7705
7742
|
walManager;
|
|
7706
7743
|
pageManagerFactory;
|
|
7707
7744
|
pageStrategy;
|
|
7745
|
+
/** 글로벌 동기화(체크포인트/커밋)를 위한 Mutex */
|
|
7746
|
+
lockPromise = Promise.resolve();
|
|
7747
|
+
/**
|
|
7748
|
+
* 글로벌 동기화 범위 내에서 작업을 실행합니다.
|
|
7749
|
+
* @param task 수행할 비동기 작업
|
|
7750
|
+
*/
|
|
7751
|
+
async runGlobalLock(task) {
|
|
7752
|
+
const previous = this.lockPromise;
|
|
7753
|
+
let resolveLock;
|
|
7754
|
+
this.lockPromise = new Promise((resolve) => {
|
|
7755
|
+
resolveLock = resolve;
|
|
7756
|
+
});
|
|
7757
|
+
await previous;
|
|
7758
|
+
try {
|
|
7759
|
+
return await task();
|
|
7760
|
+
} finally {
|
|
7761
|
+
resolveLock();
|
|
7762
|
+
}
|
|
7763
|
+
}
|
|
7708
7764
|
/**
|
|
7709
7765
|
* Initializes the page file system.
|
|
7710
7766
|
* Performs WAL recovery if necessary.
|
|
@@ -7714,6 +7770,7 @@ var PageFileSystem = class {
|
|
|
7714
7770
|
await this.walManager.recover(async (pageId, data) => {
|
|
7715
7771
|
await this.pageStrategy.write(pageId, data);
|
|
7716
7772
|
});
|
|
7773
|
+
await this.checkpoint();
|
|
7717
7774
|
}
|
|
7718
7775
|
}
|
|
7719
7776
|
/**
|
|
@@ -8013,15 +8070,30 @@ var PageFileSystem = class {
|
|
|
8013
8070
|
async commitToWAL(dirtyPages) {
|
|
8014
8071
|
if (this.walManager) {
|
|
8015
8072
|
await this.walManager.prepareCommit(dirtyPages);
|
|
8016
|
-
await this.walManager.finalizeCommit(
|
|
8073
|
+
await this.walManager.finalizeCommit(true);
|
|
8017
8074
|
}
|
|
8018
8075
|
}
|
|
8076
|
+
/**
|
|
8077
|
+
* 체크포인트를 수행합니다.
|
|
8078
|
+
* 1. 메모리의 더티 페이지를 DB 파일에 기록 (Flush)
|
|
8079
|
+
* 2. DB 파일 물리적 동기화 (Sync/fsync)
|
|
8080
|
+
* 3. WAL 로그 파일 비우기 (Clear/Truncate)
|
|
8081
|
+
*/
|
|
8082
|
+
async checkpoint() {
|
|
8083
|
+
await this.runGlobalLock(async () => {
|
|
8084
|
+
await this.pageStrategy.flush();
|
|
8085
|
+
await this.pageStrategy.sync();
|
|
8086
|
+
if (this.walManager) {
|
|
8087
|
+
await this.walManager.clear();
|
|
8088
|
+
}
|
|
8089
|
+
});
|
|
8090
|
+
}
|
|
8019
8091
|
/**
|
|
8020
8092
|
* Closes the page file system.
|
|
8021
8093
|
*/
|
|
8022
8094
|
async close() {
|
|
8095
|
+
await this.checkpoint();
|
|
8023
8096
|
if (this.walManager) {
|
|
8024
|
-
await this.walManager.clear();
|
|
8025
8097
|
this.walManager.close();
|
|
8026
8098
|
}
|
|
8027
8099
|
}
|
|
@@ -8847,18 +8919,24 @@ var Transaction = class {
|
|
|
8847
8919
|
await hook();
|
|
8848
8920
|
}
|
|
8849
8921
|
});
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
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();
|
|
8922
|
+
let shouldTriggerCheckpoint = false;
|
|
8923
|
+
await this.pfs.runGlobalLock(async () => {
|
|
8924
|
+
if (this.pfs.wal && this.dirtyPages.size > 0) {
|
|
8925
|
+
await this.pfs.wal.prepareCommit(this.dirtyPages);
|
|
8926
|
+
await this.pfs.wal.writeCommitMarker();
|
|
8861
8927
|
}
|
|
8928
|
+
for (const [pageId, data] of this.dirtyPages) {
|
|
8929
|
+
await this.pageStrategy.write(pageId, data);
|
|
8930
|
+
}
|
|
8931
|
+
if (this.pfs.wal) {
|
|
8932
|
+
this.pfs.wal.incrementWrittenPages(this.dirtyPages.size);
|
|
8933
|
+
if (this.pfs.wal.shouldCheckpoint(this.pfs.options.walCheckpointThreshold)) {
|
|
8934
|
+
shouldTriggerCheckpoint = true;
|
|
8935
|
+
}
|
|
8936
|
+
}
|
|
8937
|
+
});
|
|
8938
|
+
if (shouldTriggerCheckpoint) {
|
|
8939
|
+
await this.pfs.checkpoint();
|
|
8862
8940
|
}
|
|
8863
8941
|
this.dirtyPages.clear();
|
|
8864
8942
|
this.undoPages.clear();
|
|
@@ -16,11 +16,18 @@ export declare class PageFileSystem {
|
|
|
16
16
|
protected readonly walManager: WALManager | null;
|
|
17
17
|
protected readonly pageManagerFactory: PageManagerFactory;
|
|
18
18
|
protected readonly pageStrategy: PageMVCCStrategy;
|
|
19
|
+
/** 글로벌 동기화(체크포인트/커밋)를 위한 Mutex */
|
|
20
|
+
private lockPromise;
|
|
19
21
|
/**
|
|
20
22
|
* @param pageCacheCapacity 페이지 캐시 크기
|
|
21
23
|
* @param options 데이터플라이 옵션
|
|
22
24
|
*/
|
|
23
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>;
|
|
24
31
|
/**
|
|
25
32
|
* Initializes the page file system.
|
|
26
33
|
* Performs WAL recovery if necessary.
|
|
@@ -130,6 +137,13 @@ export declare class PageFileSystem {
|
|
|
130
137
|
* @param dirtyPages 변경된 페이지들
|
|
131
138
|
*/
|
|
132
139
|
commitToWAL(dirtyPages: Map<number, Uint8Array>): Promise<void>;
|
|
140
|
+
/**
|
|
141
|
+
* 체크포인트를 수행합니다.
|
|
142
|
+
* 1. 메모리의 더티 페이지를 DB 파일에 기록 (Flush)
|
|
143
|
+
* 2. DB 파일 물리적 동기화 (Sync/fsync)
|
|
144
|
+
* 3. WAL 로그 파일 비우기 (Clear/Truncate)
|
|
145
|
+
*/
|
|
146
|
+
checkpoint(): Promise<void>;
|
|
133
147
|
/**
|
|
134
148
|
* Closes the page file system.
|
|
135
149
|
*/
|
|
@@ -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)에서 관리합니다.
|