dataply 0.0.1 → 0.0.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/dist/cjs/index.js +212 -17
- package/dist/types/core/Page.d.ts +43 -0
- package/dist/types/core/PageFileSystem.d.ts +17 -1
- package/package.json +2 -2
- package/readme.md +9 -1
package/dist/cjs/index.js
CHANGED
|
@@ -3776,11 +3776,16 @@ var MetadataPageManager = class _MetadataPageManager extends PageManager {
|
|
|
3776
3776
|
OFFSET_ROOT_INDEX_ORDER: 126,
|
|
3777
3777
|
OFFSET_LAST_INSERT_PAGE_ID: 130,
|
|
3778
3778
|
OFFSET_LAST_ROW_PK: 134,
|
|
3779
|
+
OFFSET_BITMAP_PAGE_ID: 140,
|
|
3780
|
+
OFFSET_FREE_PAGE_ID: 144,
|
|
3779
3781
|
SIZE_PAGE_COUNT: 4,
|
|
3780
3782
|
SIZE_PAGE_SIZE: 4,
|
|
3781
3783
|
SIZE_ROOT_INDEX_PAGE_ID: 4,
|
|
3782
3784
|
SIZE_ROOT_INDEX_ORDER: 4,
|
|
3783
|
-
SIZE_LAST_INSERT_PAGE_ID: 4
|
|
3785
|
+
SIZE_LAST_INSERT_PAGE_ID: 4,
|
|
3786
|
+
SIZE_ROW_PK: 6,
|
|
3787
|
+
SIZE_BITMAP_PAGE_ID: 4,
|
|
3788
|
+
SIZE_FREE_PAGE_ID: 4
|
|
3784
3789
|
};
|
|
3785
3790
|
/**
|
|
3786
3791
|
* Checks if the page type is `MetadataPage`.
|
|
@@ -3882,7 +3887,7 @@ var MetadataPageManager = class _MetadataPageManager extends PageManager {
|
|
|
3882
3887
|
return bytesToNumber(
|
|
3883
3888
|
page,
|
|
3884
3889
|
_MetadataPageManager.CONSTANT.OFFSET_LAST_ROW_PK,
|
|
3885
|
-
|
|
3890
|
+
_MetadataPageManager.CONSTANT.SIZE_ROW_PK
|
|
3886
3891
|
);
|
|
3887
3892
|
}
|
|
3888
3893
|
/**
|
|
@@ -3894,9 +3899,34 @@ var MetadataPageManager = class _MetadataPageManager extends PageManager {
|
|
|
3894
3899
|
return bytesToNumber(
|
|
3895
3900
|
page,
|
|
3896
3901
|
_MetadataPageManager.CONSTANT.OFFSET_ROW_COUNT,
|
|
3897
|
-
|
|
3902
|
+
_MetadataPageManager.CONSTANT.SIZE_ROW_PK
|
|
3903
|
+
);
|
|
3904
|
+
}
|
|
3905
|
+
/**
|
|
3906
|
+
* Returns the ID of the bitmap page.
|
|
3907
|
+
* @param page Page data
|
|
3908
|
+
* @returns Bitmap page ID
|
|
3909
|
+
*/
|
|
3910
|
+
getBitmapPageId(page) {
|
|
3911
|
+
return bytesToNumber(
|
|
3912
|
+
page,
|
|
3913
|
+
_MetadataPageManager.CONSTANT.OFFSET_BITMAP_PAGE_ID,
|
|
3914
|
+
_MetadataPageManager.CONSTANT.SIZE_BITMAP_PAGE_ID
|
|
3898
3915
|
);
|
|
3899
3916
|
}
|
|
3917
|
+
/**
|
|
3918
|
+
* Returns the ID of the free page.
|
|
3919
|
+
* @param page Page data
|
|
3920
|
+
* @returns Free page ID
|
|
3921
|
+
*/
|
|
3922
|
+
getFreePageId(page) {
|
|
3923
|
+
const id = bytesToNumber(
|
|
3924
|
+
page,
|
|
3925
|
+
_MetadataPageManager.CONSTANT.OFFSET_FREE_PAGE_ID,
|
|
3926
|
+
_MetadataPageManager.CONSTANT.SIZE_FREE_PAGE_ID
|
|
3927
|
+
);
|
|
3928
|
+
return id === 4294967295 ? -1 : id;
|
|
3929
|
+
}
|
|
3900
3930
|
/**
|
|
3901
3931
|
* Sets the number of pages stored in the database.
|
|
3902
3932
|
* @param page Page data
|
|
@@ -3997,6 +4027,32 @@ var MetadataPageManager = class _MetadataPageManager extends PageManager {
|
|
|
3997
4027
|
Row.CONSTANT.SIZE_PK
|
|
3998
4028
|
);
|
|
3999
4029
|
}
|
|
4030
|
+
/**
|
|
4031
|
+
* Sets the ID of the bitmap page.
|
|
4032
|
+
* @param page Page data
|
|
4033
|
+
* @param bitmapPageId Bitmap page ID
|
|
4034
|
+
*/
|
|
4035
|
+
setBitmapPageId(page, bitmapPageId) {
|
|
4036
|
+
numberToBytes(
|
|
4037
|
+
bitmapPageId,
|
|
4038
|
+
page,
|
|
4039
|
+
_MetadataPageManager.CONSTANT.OFFSET_BITMAP_PAGE_ID,
|
|
4040
|
+
_MetadataPageManager.CONSTANT.SIZE_BITMAP_PAGE_ID
|
|
4041
|
+
);
|
|
4042
|
+
}
|
|
4043
|
+
/**
|
|
4044
|
+
* Sets the ID of the free page.
|
|
4045
|
+
* @param page Page data
|
|
4046
|
+
* @param pageId Free page ID
|
|
4047
|
+
*/
|
|
4048
|
+
setFreePageId(page, pageId) {
|
|
4049
|
+
numberToBytes(
|
|
4050
|
+
pageId,
|
|
4051
|
+
page,
|
|
4052
|
+
_MetadataPageManager.CONSTANT.OFFSET_FREE_PAGE_ID,
|
|
4053
|
+
_MetadataPageManager.CONSTANT.SIZE_FREE_PAGE_ID
|
|
4054
|
+
);
|
|
4055
|
+
}
|
|
4000
4056
|
};
|
|
4001
4057
|
var BitmapPageManager = class _BitmapPageManager extends PageManager {
|
|
4002
4058
|
get pageType() {
|
|
@@ -4027,6 +4083,31 @@ var BitmapPageManager = class _BitmapPageManager extends PageManager {
|
|
|
4027
4083
|
isEmptyPage(page, index) {
|
|
4028
4084
|
return bytesToNumber(page, index, 1) === 0;
|
|
4029
4085
|
}
|
|
4086
|
+
/**
|
|
4087
|
+
* Gets a bit from the bitmap page.
|
|
4088
|
+
* @param page Page data
|
|
4089
|
+
* @param index Bit index
|
|
4090
|
+
* @returns boolean indicating if the bit is set
|
|
4091
|
+
*/
|
|
4092
|
+
getBit(page, index) {
|
|
4093
|
+
const bitOffset = Math.floor(index / 8);
|
|
4094
|
+
const offset = _BitmapPageManager.CONSTANT.SIZE_PAGE_HEADER + bitOffset;
|
|
4095
|
+
const value = bytesToNumber(page, offset, 1);
|
|
4096
|
+
return getBit(value, index % 8);
|
|
4097
|
+
}
|
|
4098
|
+
/**
|
|
4099
|
+
* Sets a bit in the bitmap page.
|
|
4100
|
+
* @param page Page data
|
|
4101
|
+
* @param index Bit index
|
|
4102
|
+
* @param flag boolean indicating if the bit is set
|
|
4103
|
+
*/
|
|
4104
|
+
setBit(page, index, flag) {
|
|
4105
|
+
const bitOffset = Math.floor(index / 8);
|
|
4106
|
+
const offset = _BitmapPageManager.CONSTANT.SIZE_PAGE_HEADER + bitOffset;
|
|
4107
|
+
const value = bytesToNumber(page, offset, 1);
|
|
4108
|
+
const newValue = setBit(value, index % 8, flag);
|
|
4109
|
+
numberToBytes(newValue, page, offset, 1);
|
|
4110
|
+
}
|
|
4030
4111
|
};
|
|
4031
4112
|
var OverflowPageManager = class _OverflowPageManager extends PageManager {
|
|
4032
4113
|
get pageType() {
|
|
@@ -4448,8 +4529,8 @@ var VirtualFileSystem = class {
|
|
|
4448
4529
|
* @param position Start position in file
|
|
4449
4530
|
*/
|
|
4450
4531
|
_writeAsync(handle, buffer, offset, length, position) {
|
|
4451
|
-
if (position + length >
|
|
4452
|
-
return Promise.reject(new Error(`[Safety Limit] File write exceeds
|
|
4532
|
+
if (position + length > 512 * 1024 * 1024) {
|
|
4533
|
+
return Promise.reject(new Error(`[Safety Limit] File write exceeds 512MB limit at position ${position}`));
|
|
4453
4534
|
}
|
|
4454
4535
|
return new Promise((resolve, reject) => {
|
|
4455
4536
|
import_node_fs2.default.write(handle, buffer, offset, length, position, (err, bytesWritten) => {
|
|
@@ -4627,6 +4708,43 @@ var PageFileSystem = class {
|
|
|
4627
4708
|
pageFactory = new PageManagerFactory();
|
|
4628
4709
|
vfs;
|
|
4629
4710
|
pageManagerFactory;
|
|
4711
|
+
/**
|
|
4712
|
+
* Updates the bitmap status for a specific page.
|
|
4713
|
+
* @param pageId The ID of the page to update
|
|
4714
|
+
* @param isFree True to mark as free, false to mark as used
|
|
4715
|
+
* @param tx Transaction
|
|
4716
|
+
*/
|
|
4717
|
+
async updateBitmap(pageId, isFree, tx) {
|
|
4718
|
+
const metadata = await this.getMetadata(tx);
|
|
4719
|
+
const metadataManager = this.pageFactory.getManager(metadata);
|
|
4720
|
+
const bitmapPageId = metadataManager.getBitmapPageId(metadata);
|
|
4721
|
+
const headerSize = PageManager.CONSTANT.SIZE_PAGE_HEADER;
|
|
4722
|
+
const capacityPerBitmapPage = (this.pageSize - headerSize) * 8;
|
|
4723
|
+
let currentBitmapPageId = bitmapPageId;
|
|
4724
|
+
let targetBitIndex = pageId;
|
|
4725
|
+
while (targetBitIndex >= capacityPerBitmapPage) {
|
|
4726
|
+
const currentBitmapPage = await this.get(currentBitmapPageId, tx);
|
|
4727
|
+
const manager = this.pageFactory.getManager(currentBitmapPage);
|
|
4728
|
+
targetBitIndex -= capacityPerBitmapPage;
|
|
4729
|
+
const nextPageId = manager.getNextPageId(currentBitmapPage);
|
|
4730
|
+
if (nextPageId === -1) {
|
|
4731
|
+
if (!isFree) {
|
|
4732
|
+
throw new Error("Bitmap page not found for reused page");
|
|
4733
|
+
}
|
|
4734
|
+
const newBitmapPageId = await this.appendNewPage(PageManager.CONSTANT.PAGE_TYPE_BITMAP, tx);
|
|
4735
|
+
manager.setNextPageId(currentBitmapPage, newBitmapPageId);
|
|
4736
|
+
await this.setPage(currentBitmapPageId, currentBitmapPage, tx);
|
|
4737
|
+
currentBitmapPageId = newBitmapPageId;
|
|
4738
|
+
} else {
|
|
4739
|
+
currentBitmapPageId = nextPageId;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
await tx.__acquireWriteLock(currentBitmapPageId);
|
|
4743
|
+
const targetBitmapPage = await this.get(currentBitmapPageId, tx);
|
|
4744
|
+
const bitmapManager = this.pageFactory.getManager(targetBitmapPage);
|
|
4745
|
+
bitmapManager.setBit(targetBitmapPage, targetBitIndex, isFree);
|
|
4746
|
+
await this.setPage(currentBitmapPageId, targetBitmapPage, tx);
|
|
4747
|
+
}
|
|
4630
4748
|
/**
|
|
4631
4749
|
* VFS 인스턴스를 반환합니다.
|
|
4632
4750
|
* Transaction 생성 시 사용됩니다.
|
|
@@ -4735,12 +4853,28 @@ var PageFileSystem = class {
|
|
|
4735
4853
|
}
|
|
4736
4854
|
/**
|
|
4737
4855
|
* Appends and inserts a new page.
|
|
4738
|
-
*
|
|
4856
|
+
* If a free page is available in the free list, it reuses it.
|
|
4857
|
+
* Otherwise, it appends a new page to the end of the file.
|
|
4858
|
+
* @returns Created or reused page ID
|
|
4739
4859
|
*/
|
|
4740
4860
|
async appendNewPage(pageType = PageManager.CONSTANT.PAGE_TYPE_EMPTY, tx) {
|
|
4741
4861
|
await tx.__acquireWriteLock(0);
|
|
4742
4862
|
const metadata = await this.getMetadata(tx);
|
|
4743
4863
|
const metadataManager = this.pageFactory.getManager(metadata);
|
|
4864
|
+
const freePageId = metadataManager.getFreePageId(metadata);
|
|
4865
|
+
if (freePageId !== -1) {
|
|
4866
|
+
const reusedPageId = freePageId;
|
|
4867
|
+
const reusedPage = await this.get(reusedPageId, tx);
|
|
4868
|
+
const reusedPageManager = this.pageFactory.getManager(reusedPage);
|
|
4869
|
+
const nextFreePageId = reusedPageManager.getNextPageId(reusedPage);
|
|
4870
|
+
metadataManager.setFreePageId(metadata, nextFreePageId);
|
|
4871
|
+
await this.setPage(0, metadata, tx);
|
|
4872
|
+
await this.updateBitmap(reusedPageId, false, tx);
|
|
4873
|
+
const manager2 = this.pageFactory.getManagerFromType(pageType);
|
|
4874
|
+
const newPage2 = manager2.create(this.pageSize, reusedPageId);
|
|
4875
|
+
await this.setPage(reusedPageId, newPage2, tx);
|
|
4876
|
+
return reusedPageId;
|
|
4877
|
+
}
|
|
4744
4878
|
const pageCount = metadataManager.getPageCount(metadata);
|
|
4745
4879
|
const newPageIndex = pageCount;
|
|
4746
4880
|
const newTotalCount = pageCount + 1;
|
|
@@ -4805,6 +4939,26 @@ var PageFileSystem = class {
|
|
|
4805
4939
|
}
|
|
4806
4940
|
}
|
|
4807
4941
|
}
|
|
4942
|
+
/**
|
|
4943
|
+
* Frees the page and marks it as available in the bitmap.
|
|
4944
|
+
* It also adds the page to the linked list of free pages in metadata.
|
|
4945
|
+
* @param pageId Page ID
|
|
4946
|
+
* @param tx Transaction
|
|
4947
|
+
*/
|
|
4948
|
+
async setFreePage(pageId, tx) {
|
|
4949
|
+
await tx.__acquireWriteLock(0);
|
|
4950
|
+
await tx.__acquireWriteLock(pageId);
|
|
4951
|
+
const metadata = await this.getMetadata(tx);
|
|
4952
|
+
const metadataManager = this.pageFactory.getManager(metadata);
|
|
4953
|
+
const currentHeadFreePageId = metadataManager.getFreePageId(metadata);
|
|
4954
|
+
const emptyPageManager = this.pageFactory.getManagerFromType(PageManager.CONSTANT.PAGE_TYPE_EMPTY);
|
|
4955
|
+
const emptyPage = emptyPageManager.create(this.pageSize, pageId);
|
|
4956
|
+
emptyPageManager.setNextPageId(emptyPage, currentHeadFreePageId);
|
|
4957
|
+
await this.setPage(pageId, emptyPage, tx);
|
|
4958
|
+
await this.updateBitmap(pageId, true, tx);
|
|
4959
|
+
metadataManager.setFreePageId(metadata, pageId);
|
|
4960
|
+
await this.setPage(0, metadata, tx);
|
|
4961
|
+
}
|
|
4808
4962
|
/**
|
|
4809
4963
|
* Closes the page file system.
|
|
4810
4964
|
*/
|
|
@@ -4944,8 +5098,7 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
|
|
|
4944
5098
|
while (true) {
|
|
4945
5099
|
const page = await this.pfs.get(pageId, tx);
|
|
4946
5100
|
const nextPageId = manager.getNextPageId(page);
|
|
4947
|
-
|
|
4948
|
-
manager.setRemainingCapacity(page, this.pfs.pageSize - PageManager.CONSTANT.SIZE_PAGE_HEADER);
|
|
5101
|
+
await this.pfs.setFreePage(pageId, tx);
|
|
4949
5102
|
if (nextPageId === -1) {
|
|
4950
5103
|
break;
|
|
4951
5104
|
}
|
|
@@ -5337,13 +5490,41 @@ var RowTableEngine = class {
|
|
|
5337
5490
|
if (this.rowManager.getDeletedFlag(row)) {
|
|
5338
5491
|
return;
|
|
5339
5492
|
}
|
|
5493
|
+
if (this.rowManager.getOverflowFlag(row)) {
|
|
5494
|
+
let overflowPageId = bytesToNumber(this.rowManager.getBody(row));
|
|
5495
|
+
while (overflowPageId !== -1) {
|
|
5496
|
+
const overflowPage = await this.pfs.get(overflowPageId, tx);
|
|
5497
|
+
const manager = this.factory.getManager(overflowPage);
|
|
5498
|
+
const nextPageId = manager.getNextPageId(overflowPage);
|
|
5499
|
+
await this.pfs.setFreePage(overflowPageId, tx);
|
|
5500
|
+
overflowPageId = nextPageId;
|
|
5501
|
+
}
|
|
5502
|
+
}
|
|
5340
5503
|
this.rowManager.setDeletedFlag(row, true);
|
|
5341
5504
|
await this.pfs.setPage(pageId, page, tx);
|
|
5342
5505
|
if (decrementRowCount) {
|
|
5343
|
-
const
|
|
5344
|
-
const currentRowCount = this.metadataPageManager.getRowCount(
|
|
5345
|
-
this.metadataPageManager.setRowCount(
|
|
5346
|
-
await this.pfs.setMetadata(
|
|
5506
|
+
const metadataPage2 = await this.pfs.getMetadata(tx);
|
|
5507
|
+
const currentRowCount = this.metadataPageManager.getRowCount(metadataPage2);
|
|
5508
|
+
this.metadataPageManager.setRowCount(metadataPage2, currentRowCount - 1);
|
|
5509
|
+
await this.pfs.setMetadata(metadataPage2, tx);
|
|
5510
|
+
}
|
|
5511
|
+
const insertedRowCount = this.dataPageManager.getInsertedRowCount(page);
|
|
5512
|
+
let allDeleted = true;
|
|
5513
|
+
const metadataPage = await this.pfs.getMetadata(tx);
|
|
5514
|
+
const lastInsertPageId = this.metadataPageManager.getLastInsertPageId(metadataPage);
|
|
5515
|
+
if (pageId === lastInsertPageId) {
|
|
5516
|
+
allDeleted = false;
|
|
5517
|
+
} else {
|
|
5518
|
+
for (let i = 0; i < insertedRowCount; i++) {
|
|
5519
|
+
const slotRow = this.dataPageManager.getRow(page, i);
|
|
5520
|
+
if (!this.rowManager.getDeletedFlag(slotRow)) {
|
|
5521
|
+
allDeleted = false;
|
|
5522
|
+
break;
|
|
5523
|
+
}
|
|
5524
|
+
}
|
|
5525
|
+
}
|
|
5526
|
+
if (allDeleted) {
|
|
5527
|
+
await this.pfs.setFreePage(pageId, tx);
|
|
5347
5528
|
}
|
|
5348
5529
|
}
|
|
5349
5530
|
/**
|
|
@@ -5648,6 +5829,7 @@ var DataplyAPI = class {
|
|
|
5648
5829
|
*/
|
|
5649
5830
|
static InitializeFile(fileHandle, options) {
|
|
5650
5831
|
const metadataPageManager = new MetadataPageManager();
|
|
5832
|
+
const bitmapPageManager = new BitmapPageManager();
|
|
5651
5833
|
const dataPageManager = new DataPageManager();
|
|
5652
5834
|
const metadataPage = new Uint8Array(options.pageSize);
|
|
5653
5835
|
const dataPage = new Uint8Array(options.pageSize);
|
|
@@ -5659,19 +5841,32 @@ var DataplyAPI = class {
|
|
|
5659
5841
|
options.pageSize - MetadataPageManager.CONSTANT.SIZE_PAGE_HEADER
|
|
5660
5842
|
);
|
|
5661
5843
|
metadataPageManager.setMagicString(metadataPage);
|
|
5662
|
-
metadataPageManager.setPageCount(metadataPage, 2);
|
|
5663
5844
|
metadataPageManager.setPageSize(metadataPage, options.pageSize);
|
|
5664
5845
|
metadataPageManager.setRootIndexPageId(metadataPage, -1);
|
|
5665
|
-
metadataPageManager.
|
|
5846
|
+
metadataPageManager.setBitmapPageId(metadataPage, 1);
|
|
5847
|
+
metadataPageManager.setLastInsertPageId(metadataPage, 2);
|
|
5848
|
+
metadataPageManager.setPageCount(metadataPage, 3);
|
|
5849
|
+
metadataPageManager.setFreePageId(metadataPage, -1);
|
|
5850
|
+
const bitmapPage = new Uint8Array(options.pageSize);
|
|
5851
|
+
bitmapPageManager.initial(
|
|
5852
|
+
bitmapPage,
|
|
5853
|
+
BitmapPageManager.CONSTANT.PAGE_TYPE_BITMAP,
|
|
5854
|
+
1,
|
|
5855
|
+
-1,
|
|
5856
|
+
options.pageSize - BitmapPageManager.CONSTANT.SIZE_PAGE_HEADER
|
|
5857
|
+
);
|
|
5666
5858
|
dataPageManager.initial(
|
|
5667
5859
|
dataPage,
|
|
5668
5860
|
DataPageManager.CONSTANT.PAGE_TYPE_DATA,
|
|
5669
|
-
|
|
5861
|
+
2,
|
|
5670
5862
|
-1,
|
|
5671
5863
|
options.pageSize - DataPageManager.CONSTANT.SIZE_PAGE_HEADER
|
|
5672
5864
|
);
|
|
5673
|
-
import_node_fs3.default.appendFileSync(fileHandle,
|
|
5674
|
-
|
|
5865
|
+
import_node_fs3.default.appendFileSync(fileHandle, new Uint8Array([
|
|
5866
|
+
...metadataPage,
|
|
5867
|
+
...bitmapPage,
|
|
5868
|
+
...dataPage
|
|
5869
|
+
]));
|
|
5675
5870
|
}
|
|
5676
5871
|
/**
|
|
5677
5872
|
* Opens the database file. If the file does not exist, it initializes it.
|
|
@@ -421,11 +421,16 @@ export declare class MetadataPageManager extends PageManager {
|
|
|
421
421
|
readonly OFFSET_ROOT_INDEX_ORDER: 126;
|
|
422
422
|
readonly OFFSET_LAST_INSERT_PAGE_ID: 130;
|
|
423
423
|
readonly OFFSET_LAST_ROW_PK: 134;
|
|
424
|
+
readonly OFFSET_BITMAP_PAGE_ID: 140;
|
|
425
|
+
readonly OFFSET_FREE_PAGE_ID: 144;
|
|
424
426
|
readonly SIZE_PAGE_COUNT: 4;
|
|
425
427
|
readonly SIZE_PAGE_SIZE: 4;
|
|
426
428
|
readonly SIZE_ROOT_INDEX_PAGE_ID: 4;
|
|
427
429
|
readonly SIZE_ROOT_INDEX_ORDER: 4;
|
|
428
430
|
readonly SIZE_LAST_INSERT_PAGE_ID: 4;
|
|
431
|
+
readonly SIZE_ROW_PK: 6;
|
|
432
|
+
readonly SIZE_BITMAP_PAGE_ID: 4;
|
|
433
|
+
readonly SIZE_FREE_PAGE_ID: 4;
|
|
429
434
|
readonly PAGE_TYPE_UNKNOWN: 0;
|
|
430
435
|
readonly PAGE_TYPE_EMPTY: 1;
|
|
431
436
|
readonly PAGE_TYPE_METADATA: 2;
|
|
@@ -508,6 +513,18 @@ export declare class MetadataPageManager extends PageManager {
|
|
|
508
513
|
* @returns Number of rows
|
|
509
514
|
*/
|
|
510
515
|
getRowCount(page: MetadataPage): number;
|
|
516
|
+
/**
|
|
517
|
+
* Returns the ID of the bitmap page.
|
|
518
|
+
* @param page Page data
|
|
519
|
+
* @returns Bitmap page ID
|
|
520
|
+
*/
|
|
521
|
+
getBitmapPageId(page: MetadataPage): number;
|
|
522
|
+
/**
|
|
523
|
+
* Returns the ID of the free page.
|
|
524
|
+
* @param page Page data
|
|
525
|
+
* @returns Free page ID
|
|
526
|
+
*/
|
|
527
|
+
getFreePageId(page: MetadataPage): number;
|
|
511
528
|
/**
|
|
512
529
|
* Sets the number of pages stored in the database.
|
|
513
530
|
* @param page Page data
|
|
@@ -555,6 +572,18 @@ export declare class MetadataPageManager extends PageManager {
|
|
|
555
572
|
* @param rowCount Number of rows
|
|
556
573
|
*/
|
|
557
574
|
setRowCount(page: MetadataPage, rowCount: number): void;
|
|
575
|
+
/**
|
|
576
|
+
* Sets the ID of the bitmap page.
|
|
577
|
+
* @param page Page data
|
|
578
|
+
* @param bitmapPageId Bitmap page ID
|
|
579
|
+
*/
|
|
580
|
+
setBitmapPageId(page: MetadataPage, bitmapPageId: number): void;
|
|
581
|
+
/**
|
|
582
|
+
* Sets the ID of the free page.
|
|
583
|
+
* @param page Page data
|
|
584
|
+
* @param pageId Free page ID
|
|
585
|
+
*/
|
|
586
|
+
setFreePageId(page: MetadataPage, pageId: number): void;
|
|
558
587
|
}
|
|
559
588
|
/**
|
|
560
589
|
* Represents a bitmap page.
|
|
@@ -581,6 +610,20 @@ export declare class BitmapPageManager extends PageManager {
|
|
|
581
610
|
* @returns boolean indicating if the page is empty
|
|
582
611
|
*/
|
|
583
612
|
isEmptyPage(page: BitmapPage, index: number): boolean;
|
|
613
|
+
/**
|
|
614
|
+
* Gets a bit from the bitmap page.
|
|
615
|
+
* @param page Page data
|
|
616
|
+
* @param index Bit index
|
|
617
|
+
* @returns boolean indicating if the bit is set
|
|
618
|
+
*/
|
|
619
|
+
getBit(page: BitmapPage, index: number): boolean;
|
|
620
|
+
/**
|
|
621
|
+
* Sets a bit in the bitmap page.
|
|
622
|
+
* @param page Page data
|
|
623
|
+
* @param index Bit index
|
|
624
|
+
* @param flag boolean indicating if the bit is set
|
|
625
|
+
*/
|
|
626
|
+
setBit(page: BitmapPage, index: number, flag: boolean): void;
|
|
584
627
|
}
|
|
585
628
|
/**
|
|
586
629
|
* Represents an overflow page.
|
|
@@ -21,6 +21,13 @@ export declare class PageFileSystem {
|
|
|
21
21
|
* @param walPath WAL 파일 경로 (기본값: null)
|
|
22
22
|
*/
|
|
23
23
|
constructor(fileHandle: number, pageSize: number, pageCacheCapacity: number, walPath?: string | undefined | null);
|
|
24
|
+
/**
|
|
25
|
+
* Updates the bitmap status for a specific page.
|
|
26
|
+
* @param pageId The ID of the page to update
|
|
27
|
+
* @param isFree True to mark as free, false to mark as used
|
|
28
|
+
* @param tx Transaction
|
|
29
|
+
*/
|
|
30
|
+
private updateBitmap;
|
|
24
31
|
/**
|
|
25
32
|
* VFS 인스턴스를 반환합니다.
|
|
26
33
|
* Transaction 생성 시 사용됩니다.
|
|
@@ -80,7 +87,9 @@ export declare class PageFileSystem {
|
|
|
80
87
|
setPage(pageIndex: number, page: Uint8Array, tx: Transaction): Promise<void>;
|
|
81
88
|
/**
|
|
82
89
|
* Appends and inserts a new page.
|
|
83
|
-
*
|
|
90
|
+
* If a free page is available in the free list, it reuses it.
|
|
91
|
+
* Otherwise, it appends a new page to the end of the file.
|
|
92
|
+
* @returns Created or reused page ID
|
|
84
93
|
*/
|
|
85
94
|
appendNewPage(pageType: number | undefined, tx: Transaction): Promise<number>;
|
|
86
95
|
/**
|
|
@@ -91,6 +100,13 @@ export declare class PageFileSystem {
|
|
|
91
100
|
* @param tx Transaction
|
|
92
101
|
*/
|
|
93
102
|
writePageContent(pageId: number, data: Uint8Array, offset: number | undefined, tx: Transaction): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Frees the page and marks it as available in the bitmap.
|
|
105
|
+
* It also adds the page to the linked list of free pages in metadata.
|
|
106
|
+
* @param pageId Page ID
|
|
107
|
+
* @param tx Transaction
|
|
108
|
+
*/
|
|
109
|
+
setFreePage(pageId: number, tx: Transaction): Promise<void>;
|
|
94
110
|
/**
|
|
95
111
|
* Closes the page file system.
|
|
96
112
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dataply",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
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>",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"dist/**/*"
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
|
-
"test": "jest
|
|
20
|
+
"test": "jest -i",
|
|
21
21
|
"benchmark": "npx tsx benchmark/benchmark.ts",
|
|
22
22
|
"build": "node build/index.js && tsc"
|
|
23
23
|
},
|
package/readme.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Dataply
|
|
4
4
|
|
|
5
5
|
> [!WARNING]
|
|
6
|
-
> **Dataply is currently in Alpha version.** It is experimental and not yet suitable for production use.
|
|
6
|
+
> **Dataply is currently in Alpha version.** It is experimental and not yet suitable for production use. Internal data structures and file formats are subject to change at any time.
|
|
7
7
|
|
|
8
8
|
**Dataply** is a lightweight, high-performance **Record Store** designed for Node.js. It focuses on storing arbitrary data and providing an auto-generated Primary Key (PK) for ultra-fast retrieval, while supporting core enterprise features like MVCC, WAL, and atomic transactions.
|
|
9
9
|
|
|
@@ -177,9 +177,17 @@ graph TD
|
|
|
177
177
|
```
|
|
178
178
|
|
|
179
179
|
### 2. Page-Based Storage and VFS Caching
|
|
180
|
+
|
|
180
181
|
- **Fixed-size Pages**: All data is managed in fixed-size units (default 8KB) called pages.
|
|
181
182
|
- **VFS Cache**: Minimizes disk I/O by caching frequently accessed pages in memory.
|
|
182
183
|
- **Dirty Page Tracking**: Tracks modified pages (Dirty) to synchronize them with disk efficiently only at the time of commit.
|
|
184
|
+
- **Detailed Structure**: For technical details on the physical layout, see [structure.md](structure.md).
|
|
185
|
+
|
|
186
|
+
#### Page & Row Layout
|
|
187
|
+
Dataply uses a **Slotted Page** architecture to manage records efficiently:
|
|
188
|
+
- **Pages**: Consists of a 100-byte header (containing `type`, `id`, `checksum`, etc.) and a body where rows are stored. Slot offsets are stored at the end of the page to track row positions.
|
|
189
|
+
- **Rows**: Each row has a 9-byte header (`flags`, `size`, `PK`) followed by the actual data. Large records automatically trigger **Overflow Pages** to handle data exceeding page capacity.
|
|
190
|
+
- **Keys & Identifiers**: Uses a 6-byte **Primary Key (PK)** for logical mapping and a 6-byte **Record Identifier (RID)** (Slot + Page ID) for direct physical addressing.
|
|
183
191
|
|
|
184
192
|
### 3. MVCC and Snapshot Isolation
|
|
185
193
|
- **Non-blocking Reads**: Read operations are not blocked by write operations.
|