dataply 0.0.26-alpha.5 → 0.0.26-alpha.7
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
|
@@ -5026,32 +5026,73 @@ var BPTreePureSync = class {
|
|
|
5026
5026
|
createNode(leaf, keys, values, parent = null, next = null, prev = null) {
|
|
5027
5027
|
const id = strategy.id(leaf);
|
|
5028
5028
|
const node = { id, keys, values, leaf, parent, next, prev };
|
|
5029
|
-
|
|
5029
|
+
return node;
|
|
5030
|
+
},
|
|
5031
|
+
updateNode() {
|
|
5032
|
+
},
|
|
5033
|
+
deleteNode() {
|
|
5034
|
+
},
|
|
5035
|
+
readHead() {
|
|
5036
|
+
return strategy.readHead();
|
|
5037
|
+
},
|
|
5038
|
+
writeHead() {
|
|
5039
|
+
}
|
|
5040
|
+
};
|
|
5041
|
+
}
|
|
5042
|
+
_createBufferedOps() {
|
|
5043
|
+
const strategy = this.strategy;
|
|
5044
|
+
const writeBuffer = /* @__PURE__ */ new Map();
|
|
5045
|
+
const deleteBuffer = /* @__PURE__ */ new Set();
|
|
5046
|
+
let headBuffer = null;
|
|
5047
|
+
const ops = {
|
|
5048
|
+
getNode(id) {
|
|
5049
|
+
const buffered = writeBuffer.get(id);
|
|
5050
|
+
if (buffered) return buffered;
|
|
5051
|
+
return strategy.read(id);
|
|
5052
|
+
},
|
|
5053
|
+
createNode(leaf, keys, values, parent = null, next = null, prev = null) {
|
|
5054
|
+
const id = strategy.id(leaf);
|
|
5055
|
+
const node = { id, keys, values, leaf, parent, next, prev };
|
|
5056
|
+
writeBuffer.set(id, node);
|
|
5030
5057
|
return node;
|
|
5031
5058
|
},
|
|
5032
5059
|
updateNode(node) {
|
|
5033
|
-
|
|
5060
|
+
writeBuffer.set(node.id, node);
|
|
5034
5061
|
},
|
|
5035
5062
|
deleteNode(node) {
|
|
5036
|
-
|
|
5063
|
+
deleteBuffer.add(node.id);
|
|
5064
|
+
writeBuffer.delete(node.id);
|
|
5037
5065
|
},
|
|
5038
5066
|
readHead() {
|
|
5067
|
+
if (headBuffer) return headBuffer;
|
|
5039
5068
|
return strategy.readHead();
|
|
5040
5069
|
},
|
|
5041
5070
|
writeHead(head) {
|
|
5042
|
-
|
|
5071
|
+
headBuffer = head;
|
|
5043
5072
|
}
|
|
5044
5073
|
};
|
|
5074
|
+
function flush() {
|
|
5075
|
+
for (const id of deleteBuffer) {
|
|
5076
|
+
strategy.delete(id);
|
|
5077
|
+
}
|
|
5078
|
+
for (const [id, node] of writeBuffer) {
|
|
5079
|
+
strategy.write(id, node);
|
|
5080
|
+
}
|
|
5081
|
+
if (headBuffer) {
|
|
5082
|
+
strategy.writeHead(headBuffer);
|
|
5083
|
+
}
|
|
5084
|
+
}
|
|
5085
|
+
return { ops, flush };
|
|
5045
5086
|
}
|
|
5046
5087
|
init() {
|
|
5047
|
-
|
|
5088
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5048
5089
|
this._ctx = {
|
|
5049
5090
|
rootId: "",
|
|
5050
5091
|
order: this.strategy.order,
|
|
5051
5092
|
headData: () => this.strategy.head.data
|
|
5052
5093
|
};
|
|
5053
5094
|
initOp(
|
|
5054
|
-
|
|
5095
|
+
ops,
|
|
5055
5096
|
this._ctx,
|
|
5056
5097
|
this.strategy.order,
|
|
5057
5098
|
this.strategy.head,
|
|
@@ -5059,6 +5100,8 @@ var BPTreePureSync = class {
|
|
|
5059
5100
|
this.strategy.head = head;
|
|
5060
5101
|
}
|
|
5061
5102
|
);
|
|
5103
|
+
flush();
|
|
5104
|
+
this._ops = this._createOps();
|
|
5062
5105
|
}
|
|
5063
5106
|
/**
|
|
5064
5107
|
* Returns the ID of the root node.
|
|
@@ -5132,16 +5175,24 @@ var BPTreePureSync = class {
|
|
|
5132
5175
|
}
|
|
5133
5176
|
// ─── Mutation ────────────────────────────────────────────────────
|
|
5134
5177
|
insert(key, value) {
|
|
5135
|
-
|
|
5178
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5179
|
+
insertOp(ops, this._ctx, key, value, this.comparator);
|
|
5180
|
+
flush();
|
|
5136
5181
|
}
|
|
5137
5182
|
delete(key, value) {
|
|
5138
|
-
|
|
5183
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5184
|
+
deleteOp(ops, this._ctx, key, this.comparator, value);
|
|
5185
|
+
flush();
|
|
5139
5186
|
}
|
|
5140
5187
|
batchInsert(entries) {
|
|
5141
|
-
|
|
5188
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5189
|
+
batchInsertOp(ops, this._ctx, entries, this.comparator);
|
|
5190
|
+
flush();
|
|
5142
5191
|
}
|
|
5143
5192
|
bulkLoad(entries) {
|
|
5144
|
-
|
|
5193
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5194
|
+
bulkLoadOp(ops, this._ctx, entries, this.comparator);
|
|
5195
|
+
flush();
|
|
5145
5196
|
}
|
|
5146
5197
|
// ─── Head Data ───────────────────────────────────────────────────
|
|
5147
5198
|
getHeadData() {
|
|
@@ -5152,15 +5203,17 @@ var BPTreePureSync = class {
|
|
|
5152
5203
|
return head.data;
|
|
5153
5204
|
}
|
|
5154
5205
|
setHeadData(data) {
|
|
5155
|
-
const
|
|
5206
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5207
|
+
const head = ops.readHead();
|
|
5156
5208
|
if (head === null) {
|
|
5157
5209
|
throw new Error("Head not found");
|
|
5158
5210
|
}
|
|
5159
|
-
|
|
5211
|
+
ops.writeHead({
|
|
5160
5212
|
root: head.root,
|
|
5161
5213
|
order: head.order,
|
|
5162
5214
|
data
|
|
5163
5215
|
});
|
|
5216
|
+
flush();
|
|
5164
5217
|
}
|
|
5165
5218
|
// ─── Static utilities ────────────────────────────────────────────
|
|
5166
5219
|
static ChooseDriver = BPTreeTransaction.ChooseDriver;
|
|
@@ -5169,6 +5222,7 @@ var BPTreePureAsync = class {
|
|
|
5169
5222
|
strategy;
|
|
5170
5223
|
comparator;
|
|
5171
5224
|
option;
|
|
5225
|
+
lock = new Ryoiki2();
|
|
5172
5226
|
_cachedRegexp = /* @__PURE__ */ new Map();
|
|
5173
5227
|
_ctx;
|
|
5174
5228
|
_ops;
|
|
@@ -5194,39 +5248,93 @@ var BPTreePureAsync = class {
|
|
|
5194
5248
|
async createNode(leaf, keys, values, parent = null, next = null, prev = null) {
|
|
5195
5249
|
const id = await strategy.id(leaf);
|
|
5196
5250
|
const node = { id, keys, values, leaf, parent, next, prev };
|
|
5197
|
-
|
|
5251
|
+
return node;
|
|
5252
|
+
},
|
|
5253
|
+
async updateNode() {
|
|
5254
|
+
},
|
|
5255
|
+
async deleteNode() {
|
|
5256
|
+
},
|
|
5257
|
+
async readHead() {
|
|
5258
|
+
return await strategy.readHead();
|
|
5259
|
+
},
|
|
5260
|
+
async writeHead() {
|
|
5261
|
+
}
|
|
5262
|
+
};
|
|
5263
|
+
}
|
|
5264
|
+
_createBufferedOps() {
|
|
5265
|
+
const strategy = this.strategy;
|
|
5266
|
+
const writeBuffer = /* @__PURE__ */ new Map();
|
|
5267
|
+
const deleteBuffer = /* @__PURE__ */ new Set();
|
|
5268
|
+
let headBuffer = null;
|
|
5269
|
+
const ops = {
|
|
5270
|
+
async getNode(id) {
|
|
5271
|
+
const buffered = writeBuffer.get(id);
|
|
5272
|
+
if (buffered) return buffered;
|
|
5273
|
+
return await strategy.read(id);
|
|
5274
|
+
},
|
|
5275
|
+
async createNode(leaf, keys, values, parent = null, next = null, prev = null) {
|
|
5276
|
+
const id = await strategy.id(leaf);
|
|
5277
|
+
const node = { id, keys, values, leaf, parent, next, prev };
|
|
5278
|
+
writeBuffer.set(id, node);
|
|
5198
5279
|
return node;
|
|
5199
5280
|
},
|
|
5200
5281
|
async updateNode(node) {
|
|
5201
|
-
|
|
5282
|
+
writeBuffer.set(node.id, node);
|
|
5202
5283
|
},
|
|
5203
5284
|
async deleteNode(node) {
|
|
5204
|
-
|
|
5285
|
+
deleteBuffer.add(node.id);
|
|
5286
|
+
writeBuffer.delete(node.id);
|
|
5205
5287
|
},
|
|
5206
5288
|
async readHead() {
|
|
5289
|
+
if (headBuffer) return headBuffer;
|
|
5207
5290
|
return await strategy.readHead();
|
|
5208
5291
|
},
|
|
5209
5292
|
async writeHead(head) {
|
|
5210
|
-
|
|
5293
|
+
headBuffer = head;
|
|
5211
5294
|
}
|
|
5212
5295
|
};
|
|
5296
|
+
async function flush() {
|
|
5297
|
+
for (const id of deleteBuffer) {
|
|
5298
|
+
await strategy.delete(id);
|
|
5299
|
+
}
|
|
5300
|
+
for (const [id, node] of writeBuffer) {
|
|
5301
|
+
await strategy.write(id, node);
|
|
5302
|
+
}
|
|
5303
|
+
if (headBuffer) {
|
|
5304
|
+
await strategy.writeHead(headBuffer);
|
|
5305
|
+
}
|
|
5306
|
+
}
|
|
5307
|
+
return { ops, flush };
|
|
5308
|
+
}
|
|
5309
|
+
async writeLock(fn) {
|
|
5310
|
+
let lockId;
|
|
5311
|
+
return this.lock.writeLock(async (_lockId) => {
|
|
5312
|
+
lockId = _lockId;
|
|
5313
|
+
return fn();
|
|
5314
|
+
}).finally(() => {
|
|
5315
|
+
this.lock.writeUnlock(lockId);
|
|
5316
|
+
});
|
|
5213
5317
|
}
|
|
5214
5318
|
async init() {
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5319
|
+
return this.writeLock(async () => {
|
|
5320
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5321
|
+
this._ctx = {
|
|
5322
|
+
rootId: "",
|
|
5323
|
+
order: this.strategy.order,
|
|
5324
|
+
headData: () => this.strategy.head.data
|
|
5325
|
+
};
|
|
5326
|
+
await initOpAsync(
|
|
5327
|
+
ops,
|
|
5328
|
+
this._ctx,
|
|
5329
|
+
this.strategy.order,
|
|
5330
|
+
this.strategy.head,
|
|
5331
|
+
(head) => {
|
|
5332
|
+
this.strategy.head = head;
|
|
5333
|
+
}
|
|
5334
|
+
);
|
|
5335
|
+
await flush();
|
|
5336
|
+
this._ops = this._createOps();
|
|
5337
|
+
});
|
|
5230
5338
|
}
|
|
5231
5339
|
getRootId() {
|
|
5232
5340
|
return this._ctx.rootId;
|
|
@@ -5250,28 +5358,40 @@ var BPTreePureAsync = class {
|
|
|
5250
5358
|
return existsOpAsync(this._ops, this._ctx.rootId, key, value, this.comparator);
|
|
5251
5359
|
}
|
|
5252
5360
|
async *keysStream(condition, options) {
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
this.
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5361
|
+
let lockId;
|
|
5362
|
+
try {
|
|
5363
|
+
lockId = await this.lock.readLock([0, 0.1], async (id) => id);
|
|
5364
|
+
yield* keysStreamOpAsync(
|
|
5365
|
+
this._ops,
|
|
5366
|
+
this._ctx.rootId,
|
|
5367
|
+
condition,
|
|
5368
|
+
this.comparator,
|
|
5369
|
+
this._verifierMap,
|
|
5370
|
+
this._searchConfigs,
|
|
5371
|
+
this._ensureValues,
|
|
5372
|
+
options
|
|
5373
|
+
);
|
|
5374
|
+
} finally {
|
|
5375
|
+
if (lockId) this.lock.readUnlock(lockId);
|
|
5376
|
+
}
|
|
5263
5377
|
}
|
|
5264
5378
|
async *whereStream(condition, options) {
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
this.
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5379
|
+
let lockId;
|
|
5380
|
+
try {
|
|
5381
|
+
lockId = await this.lock.readLock([0, 0.1], async (id) => id);
|
|
5382
|
+
yield* whereStreamOpAsync(
|
|
5383
|
+
this._ops,
|
|
5384
|
+
this._ctx.rootId,
|
|
5385
|
+
condition,
|
|
5386
|
+
this.comparator,
|
|
5387
|
+
this._verifierMap,
|
|
5388
|
+
this._searchConfigs,
|
|
5389
|
+
this._ensureValues,
|
|
5390
|
+
options
|
|
5391
|
+
);
|
|
5392
|
+
} finally {
|
|
5393
|
+
if (lockId) this.lock.readUnlock(lockId);
|
|
5394
|
+
}
|
|
5275
5395
|
}
|
|
5276
5396
|
async keys(condition, options) {
|
|
5277
5397
|
const set = /* @__PURE__ */ new Set();
|
|
@@ -5289,16 +5409,32 @@ var BPTreePureAsync = class {
|
|
|
5289
5409
|
}
|
|
5290
5410
|
// ─── Mutation ────────────────────────────────────────────────────
|
|
5291
5411
|
async insert(key, value) {
|
|
5292
|
-
|
|
5412
|
+
return this.writeLock(async () => {
|
|
5413
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5414
|
+
await insertOpAsync(ops, this._ctx, key, value, this.comparator);
|
|
5415
|
+
await flush();
|
|
5416
|
+
});
|
|
5293
5417
|
}
|
|
5294
5418
|
async delete(key, value) {
|
|
5295
|
-
|
|
5419
|
+
return this.writeLock(async () => {
|
|
5420
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5421
|
+
await deleteOpAsync(ops, this._ctx, key, this.comparator, value);
|
|
5422
|
+
await flush();
|
|
5423
|
+
});
|
|
5296
5424
|
}
|
|
5297
5425
|
async batchInsert(entries) {
|
|
5298
|
-
|
|
5426
|
+
return this.writeLock(async () => {
|
|
5427
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5428
|
+
await batchInsertOpAsync(ops, this._ctx, entries, this.comparator);
|
|
5429
|
+
await flush();
|
|
5430
|
+
});
|
|
5299
5431
|
}
|
|
5300
5432
|
async bulkLoad(entries) {
|
|
5301
|
-
|
|
5433
|
+
return this.writeLock(async () => {
|
|
5434
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5435
|
+
await bulkLoadOpAsync(ops, this._ctx, entries, this.comparator);
|
|
5436
|
+
await flush();
|
|
5437
|
+
});
|
|
5302
5438
|
}
|
|
5303
5439
|
// ─── Head Data ───────────────────────────────────────────────────
|
|
5304
5440
|
async getHeadData() {
|
|
@@ -5307,9 +5443,13 @@ var BPTreePureAsync = class {
|
|
|
5307
5443
|
return head.data;
|
|
5308
5444
|
}
|
|
5309
5445
|
async setHeadData(data) {
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5446
|
+
return this.writeLock(async () => {
|
|
5447
|
+
const { ops, flush } = this._createBufferedOps();
|
|
5448
|
+
const head = await ops.readHead();
|
|
5449
|
+
if (head === null) throw new Error("Head not found");
|
|
5450
|
+
await ops.writeHead({ root: head.root, order: head.order, data });
|
|
5451
|
+
await flush();
|
|
5452
|
+
});
|
|
5313
5453
|
}
|
|
5314
5454
|
// ─── Static utilities ────────────────────────────────────────────
|
|
5315
5455
|
static ChooseDriver = BPTreeTransaction.ChooseDriver;
|
|
@@ -9553,45 +9693,27 @@ var WALManager = class {
|
|
|
9553
9693
|
|
|
9554
9694
|
// src/core/PageMVCCStrategy.ts
|
|
9555
9695
|
var import_node_fs2 = __toESM(require("node:fs"));
|
|
9556
|
-
var PageMVCCStrategy = class {
|
|
9557
|
-
constructor(fileHandle, pageSize
|
|
9696
|
+
var PageMVCCStrategy = class extends AsyncMVCCStrategy2 {
|
|
9697
|
+
constructor(fileHandle, pageSize) {
|
|
9698
|
+
super();
|
|
9558
9699
|
this.fileHandle = fileHandle;
|
|
9559
9700
|
this.pageSize = pageSize;
|
|
9560
|
-
this.cache = new LRUMap2(cacheCapacity);
|
|
9561
9701
|
this.fileSize = import_node_fs2.default.fstatSync(fileHandle).size;
|
|
9562
9702
|
}
|
|
9563
|
-
/** LRU 캐시 (페이지 ID -> 페이지 데이터) */
|
|
9564
|
-
cache;
|
|
9565
|
-
/** 디스크에 기록되지 않은 변경된 페이지들 (페이지 ID -> 페이지 데이터) */
|
|
9566
|
-
dirtyPages = /* @__PURE__ */ new Map();
|
|
9567
9703
|
/** 파일 크기 (논리적) */
|
|
9568
9704
|
fileSize;
|
|
9569
9705
|
/**
|
|
9570
9706
|
* 디스크에서 페이지를 읽습니다.
|
|
9571
|
-
* 캐시에 있으면 캐시에서 반환합니다.
|
|
9572
9707
|
* @param pageId 페이지 ID
|
|
9573
9708
|
* @returns 페이지 데이터
|
|
9574
9709
|
*/
|
|
9575
9710
|
async read(pageId) {
|
|
9576
|
-
const dirty = this.dirtyPages.get(pageId);
|
|
9577
|
-
if (dirty) {
|
|
9578
|
-
const copy = new Uint8Array(this.pageSize);
|
|
9579
|
-
copy.set(dirty);
|
|
9580
|
-
return copy;
|
|
9581
|
-
}
|
|
9582
|
-
const cached = this.cache.get(pageId);
|
|
9583
|
-
if (cached) {
|
|
9584
|
-
return cached;
|
|
9585
|
-
}
|
|
9586
9711
|
const buffer = new Uint8Array(this.pageSize);
|
|
9587
9712
|
const pageStartPos = pageId * this.pageSize;
|
|
9588
9713
|
if (pageStartPos >= this.fileSize) {
|
|
9589
9714
|
return buffer;
|
|
9590
9715
|
}
|
|
9591
9716
|
await this._readFromDisk(buffer, pageStartPos);
|
|
9592
|
-
const cacheCopy = new Uint8Array(this.pageSize);
|
|
9593
|
-
cacheCopy.set(buffer);
|
|
9594
|
-
this.cache.set(pageId, cacheCopy);
|
|
9595
9717
|
return buffer;
|
|
9596
9718
|
}
|
|
9597
9719
|
/**
|
|
@@ -9601,47 +9723,27 @@ var PageMVCCStrategy = class {
|
|
|
9601
9723
|
*/
|
|
9602
9724
|
async write(pageId, data) {
|
|
9603
9725
|
const pageStartPos = pageId * this.pageSize;
|
|
9604
|
-
|
|
9605
|
-
dataCopy.set(data);
|
|
9606
|
-
this.dirtyPages.set(pageId, dataCopy);
|
|
9607
|
-
this.cache.set(pageId, dataCopy);
|
|
9726
|
+
await this._writeToDisk(data, pageStartPos);
|
|
9608
9727
|
const endPosition = pageStartPos + this.pageSize;
|
|
9609
9728
|
if (endPosition > this.fileSize) {
|
|
9610
9729
|
this.fileSize = endPosition;
|
|
9611
9730
|
}
|
|
9612
9731
|
}
|
|
9613
9732
|
/**
|
|
9614
|
-
*
|
|
9615
|
-
*
|
|
9733
|
+
* 페이지 삭제.
|
|
9734
|
+
* 실제 페이지 해제는 상위 레이어(FreeList)에서 관리합니다.
|
|
9735
|
+
* @param pageId 페이지 ID
|
|
9616
9736
|
*/
|
|
9617
|
-
async
|
|
9618
|
-
if (this.dirtyPages.size === 0) {
|
|
9619
|
-
return;
|
|
9620
|
-
}
|
|
9621
|
-
const snapshot = new Map(this.dirtyPages);
|
|
9622
|
-
const sortedPageIds = Array.from(snapshot.keys()).sort((a, b) => a - b);
|
|
9623
|
-
for (const pageId of sortedPageIds) {
|
|
9624
|
-
const data = snapshot.get(pageId);
|
|
9625
|
-
const position = pageId * this.pageSize;
|
|
9626
|
-
await this._writeToDisk(data, position);
|
|
9627
|
-
this.dirtyPages.delete(pageId);
|
|
9628
|
-
}
|
|
9737
|
+
async delete(pageId) {
|
|
9629
9738
|
}
|
|
9630
9739
|
/**
|
|
9631
|
-
*
|
|
9632
|
-
*
|
|
9633
|
-
* @
|
|
9740
|
+
* 페이지 존재 여부 확인
|
|
9741
|
+
* @param pageId 페이지 ID
|
|
9742
|
+
* @returns 존재하면 true
|
|
9634
9743
|
*/
|
|
9635
|
-
async
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
}
|
|
9639
|
-
const sortedPageIds = Array.from(pages.keys()).sort((a, b) => a - b);
|
|
9640
|
-
for (const pageId of sortedPageIds) {
|
|
9641
|
-
const data = pages.get(pageId);
|
|
9642
|
-
const position = pageId * this.pageSize;
|
|
9643
|
-
await this._writeToDisk(data, position);
|
|
9644
|
-
}
|
|
9744
|
+
async exists(pageId) {
|
|
9745
|
+
const pageStartPos = pageId * this.pageSize;
|
|
9746
|
+
return pageStartPos < this.fileSize;
|
|
9645
9747
|
}
|
|
9646
9748
|
/**
|
|
9647
9749
|
* 메인 DB 파일의 물리적 동기화를 수행합니다 (fsync).
|
|
@@ -9654,39 +9756,12 @@ var PageMVCCStrategy = class {
|
|
|
9654
9756
|
});
|
|
9655
9757
|
});
|
|
9656
9758
|
}
|
|
9657
|
-
/**
|
|
9658
|
-
* 페이지 삭제 (실제로는 캐시에서만 제거)
|
|
9659
|
-
* 실제 페이지 해제는 상위 레이어(FreeList)에서 관리합니다.
|
|
9660
|
-
* @param pageId 페이지 ID
|
|
9661
|
-
*/
|
|
9662
|
-
async delete(pageId) {
|
|
9663
|
-
this.dirtyPages.delete(pageId);
|
|
9664
|
-
this.cache.delete(pageId);
|
|
9665
|
-
}
|
|
9666
|
-
/**
|
|
9667
|
-
* 페이지 존재 여부 확인
|
|
9668
|
-
* @param pageId 페이지 ID
|
|
9669
|
-
* @returns 존재하면 true
|
|
9670
|
-
*/
|
|
9671
|
-
async exists(pageId) {
|
|
9672
|
-
if (this.dirtyPages.has(pageId)) {
|
|
9673
|
-
return true;
|
|
9674
|
-
}
|
|
9675
|
-
const pageStartPos = pageId * this.pageSize;
|
|
9676
|
-
return pageStartPos < this.fileSize;
|
|
9677
|
-
}
|
|
9678
9759
|
/**
|
|
9679
9760
|
* 현재 파일 크기 반환
|
|
9680
9761
|
*/
|
|
9681
9762
|
getFileSize() {
|
|
9682
9763
|
return this.fileSize;
|
|
9683
9764
|
}
|
|
9684
|
-
/**
|
|
9685
|
-
* 캐시 초기화
|
|
9686
|
-
*/
|
|
9687
|
-
clearCache() {
|
|
9688
|
-
this.cache.clear();
|
|
9689
|
-
}
|
|
9690
9765
|
// ─────────────────────────────────────────────────────────────
|
|
9691
9766
|
// Private helpers
|
|
9692
9767
|
// ─────────────────────────────────────────────────────────────
|
|
@@ -9722,13 +9797,15 @@ var PageFileSystem = class {
|
|
|
9722
9797
|
const walPath = options.wal;
|
|
9723
9798
|
this.walManager = walPath && walLogger ? new WALManager(walPath, pageSize, walLogger) : null;
|
|
9724
9799
|
this.pageManagerFactory = new PageManagerFactory();
|
|
9725
|
-
this.pageStrategy = new PageMVCCStrategy(fileHandle, pageSize
|
|
9800
|
+
this.pageStrategy = new PageMVCCStrategy(fileHandle, pageSize);
|
|
9801
|
+
this.rootTransaction = new AsyncMVCCTransaction2(this.pageStrategy, { cacheCapacity: pageCacheCapacity });
|
|
9726
9802
|
this.logger = logger;
|
|
9727
9803
|
}
|
|
9728
9804
|
pageFactory = new PageManagerFactory();
|
|
9729
9805
|
walManager;
|
|
9730
9806
|
pageManagerFactory;
|
|
9731
9807
|
pageStrategy;
|
|
9808
|
+
rootTransaction;
|
|
9732
9809
|
logger;
|
|
9733
9810
|
/** 글로벌 동기화(체크포인트/커밋)를 위한 Mutex */
|
|
9734
9811
|
lockPromise = Promise.resolve();
|
|
@@ -9769,6 +9846,12 @@ var PageFileSystem = class {
|
|
|
9769
9846
|
getPageStrategy() {
|
|
9770
9847
|
return this.pageStrategy;
|
|
9771
9848
|
}
|
|
9849
|
+
/**
|
|
9850
|
+
* Returns the root MVCC transaction.
|
|
9851
|
+
*/
|
|
9852
|
+
getRootTransaction() {
|
|
9853
|
+
return this.rootTransaction;
|
|
9854
|
+
}
|
|
9772
9855
|
/**
|
|
9773
9856
|
* Updates the bitmap status for a specific page.
|
|
9774
9857
|
* @param pageId The ID of the page to update
|
|
@@ -9824,11 +9907,7 @@ var PageFileSystem = class {
|
|
|
9824
9907
|
* @returns 페이지 버퍼
|
|
9825
9908
|
*/
|
|
9826
9909
|
async get(pageIndex, tx) {
|
|
9827
|
-
|
|
9828
|
-
if (page === null) {
|
|
9829
|
-
return new Uint8Array(this.pageSize);
|
|
9830
|
-
}
|
|
9831
|
-
return page;
|
|
9910
|
+
return tx.__readPage(pageIndex);
|
|
9832
9911
|
}
|
|
9833
9912
|
/**
|
|
9834
9913
|
* Reads the page header.
|
|
@@ -9919,7 +9998,7 @@ var PageFileSystem = class {
|
|
|
9919
9998
|
const manager = this.pageFactory.getManager(page);
|
|
9920
9999
|
manager.updateChecksum(page);
|
|
9921
10000
|
await tx.__acquireWriteLock(pageIndex);
|
|
9922
|
-
await tx.
|
|
10001
|
+
await tx.__writePage(pageIndex, page);
|
|
9923
10002
|
}
|
|
9924
10003
|
/**
|
|
9925
10004
|
* Appends and inserts a new page.
|
|
@@ -10066,27 +10145,14 @@ var PageFileSystem = class {
|
|
|
10066
10145
|
metadataManager.setFreePageId(metadata, pageId);
|
|
10067
10146
|
await this.setPage(0, metadata, tx);
|
|
10068
10147
|
}
|
|
10069
|
-
/**
|
|
10070
|
-
* WAL에 커밋합니다.
|
|
10071
|
-
* @param dirtyPages 변경된 페이지들
|
|
10072
|
-
*/
|
|
10073
|
-
async commitToWAL(dirtyPages) {
|
|
10074
|
-
if (this.walManager) {
|
|
10075
|
-
this.logger.debug(`Committing ${dirtyPages.size} pages to WAL`);
|
|
10076
|
-
await this.walManager.prepareCommit(dirtyPages);
|
|
10077
|
-
await this.walManager.finalizeCommit(true);
|
|
10078
|
-
}
|
|
10079
|
-
}
|
|
10080
10148
|
/**
|
|
10081
10149
|
* 체크포인트를 수행합니다.
|
|
10082
|
-
* 1.
|
|
10083
|
-
* 2.
|
|
10084
|
-
* 3. WAL 로그 파일 비우기 (Clear/Truncate)
|
|
10150
|
+
* 1. DB 파일 물리적 동기화 (Sync/fsync)
|
|
10151
|
+
* 2. WAL 로그 파일 비우기 (Clear/Truncate)
|
|
10085
10152
|
*/
|
|
10086
10153
|
async checkpoint() {
|
|
10087
10154
|
this.logger.info("Starting checkpoint");
|
|
10088
10155
|
await this.runGlobalLock(async () => {
|
|
10089
|
-
await this.pageStrategy.flush();
|
|
10090
10156
|
await this.pageStrategy.sync();
|
|
10091
10157
|
if (this.walManager) {
|
|
10092
10158
|
await this.walManager.clear();
|
|
@@ -10891,17 +10957,16 @@ var Transaction = class {
|
|
|
10891
10957
|
/**
|
|
10892
10958
|
* @param id Transaction ID
|
|
10893
10959
|
* @param context Transaction context
|
|
10894
|
-
* @param
|
|
10960
|
+
* @param rootTx Root MVCC Transaction
|
|
10895
10961
|
* @param lockManager LockManager instance
|
|
10896
10962
|
* @param pfs Page File System
|
|
10897
|
-
* @param reloadBPTree Callback to reload BPTree cache on rollback
|
|
10898
10963
|
*/
|
|
10899
|
-
constructor(id, context,
|
|
10964
|
+
constructor(id, context, rootTx, lockManager, pfs) {
|
|
10900
10965
|
this.context = context;
|
|
10901
10966
|
this.lockManager = lockManager;
|
|
10902
10967
|
this.pfs = pfs;
|
|
10903
10968
|
this.id = id;
|
|
10904
|
-
this.
|
|
10969
|
+
this.rootTx = rootTx;
|
|
10905
10970
|
}
|
|
10906
10971
|
/** Transaction ID */
|
|
10907
10972
|
id;
|
|
@@ -10909,16 +10974,25 @@ var Transaction = class {
|
|
|
10909
10974
|
heldLocks = /* @__PURE__ */ new Set();
|
|
10910
10975
|
/** Held page locks (PageID -> LockID) */
|
|
10911
10976
|
pageLocks = /* @__PURE__ */ new Map();
|
|
10912
|
-
/** Dirty Pages modified by the transaction: PageID -> Modified Page Buffer */
|
|
10913
|
-
dirtyPages = /* @__PURE__ */ new Map();
|
|
10914
|
-
/** Undo pages: PageID -> Original Page Buffer (Snapshot) */
|
|
10915
|
-
undoPages = /* @__PURE__ */ new Map();
|
|
10916
10977
|
/** List of callbacks to execute on commit */
|
|
10917
10978
|
commitHooks = [];
|
|
10918
|
-
/**
|
|
10919
|
-
|
|
10979
|
+
/** Nested MVCC Transaction for snapshot isolation (lazy init) */
|
|
10980
|
+
mvccTx = null;
|
|
10981
|
+
/** Root MVCC Transaction reference */
|
|
10982
|
+
rootTx;
|
|
10920
10983
|
/** Release function for global write lock, set by DataplyAPI */
|
|
10921
10984
|
_writeLockRelease = null;
|
|
10985
|
+
/**
|
|
10986
|
+
* Lazily initializes the nested MVCC transaction.
|
|
10987
|
+
* This ensures the snapshot is taken at the time of first access,
|
|
10988
|
+
* picking up the latest committed root version.
|
|
10989
|
+
*/
|
|
10990
|
+
ensureMvccTx() {
|
|
10991
|
+
if (!this.mvccTx) {
|
|
10992
|
+
this.mvccTx = this.rootTx.createNested();
|
|
10993
|
+
}
|
|
10994
|
+
return this.mvccTx;
|
|
10995
|
+
}
|
|
10922
10996
|
/**
|
|
10923
10997
|
* Registers a commit hook.
|
|
10924
10998
|
* @param hook Function to execute
|
|
@@ -10940,30 +11014,35 @@ var Transaction = class {
|
|
|
10940
11014
|
return this._writeLockRelease !== null;
|
|
10941
11015
|
}
|
|
10942
11016
|
/**
|
|
10943
|
-
* Reads a page
|
|
11017
|
+
* Reads a page through the MVCC transaction.
|
|
10944
11018
|
* @param pageId Page ID
|
|
10945
11019
|
* @returns Page data
|
|
10946
11020
|
*/
|
|
10947
|
-
async
|
|
10948
|
-
const
|
|
10949
|
-
|
|
10950
|
-
|
|
11021
|
+
async __readPage(pageId) {
|
|
11022
|
+
const tx = this.ensureMvccTx();
|
|
11023
|
+
const data = await tx.read(pageId);
|
|
11024
|
+
if (data === null) {
|
|
11025
|
+
return new Uint8Array(this.pfs.pageSize);
|
|
10951
11026
|
}
|
|
10952
|
-
|
|
11027
|
+
const copy = new Uint8Array(data.length);
|
|
11028
|
+
copy.set(data);
|
|
11029
|
+
return copy;
|
|
10953
11030
|
}
|
|
10954
11031
|
/**
|
|
10955
|
-
* Writes a page
|
|
11032
|
+
* Writes a page through the MVCC transaction.
|
|
10956
11033
|
* @param pageId Page ID
|
|
10957
11034
|
* @param data Page data
|
|
10958
11035
|
*/
|
|
10959
|
-
async
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10963
|
-
|
|
10964
|
-
|
|
11036
|
+
async __writePage(pageId, data) {
|
|
11037
|
+
const tx = this.ensureMvccTx();
|
|
11038
|
+
const exists = await tx.exists(pageId);
|
|
11039
|
+
if (exists) {
|
|
11040
|
+
const copy = new Uint8Array(data.length);
|
|
11041
|
+
copy.set(data);
|
|
11042
|
+
await tx.write(pageId, copy);
|
|
11043
|
+
} else {
|
|
11044
|
+
await tx.create(pageId, data);
|
|
10965
11045
|
}
|
|
10966
|
-
this.dirtyPages.set(pageId, data);
|
|
10967
11046
|
}
|
|
10968
11047
|
/**
|
|
10969
11048
|
* Acquires a write lock.
|
|
@@ -10990,21 +11069,31 @@ var Transaction = class {
|
|
|
10990
11069
|
await hook();
|
|
10991
11070
|
}
|
|
10992
11071
|
});
|
|
11072
|
+
const tx = this.ensureMvccTx();
|
|
10993
11073
|
let shouldTriggerCheckpoint = false;
|
|
10994
11074
|
await this.pfs.runGlobalLock(async () => {
|
|
10995
|
-
|
|
10996
|
-
|
|
11075
|
+
const entries = tx.getResultEntries();
|
|
11076
|
+
const dirtyPages = /* @__PURE__ */ new Map();
|
|
11077
|
+
for (const entry of [...entries.created, ...entries.updated]) {
|
|
11078
|
+
dirtyPages.set(entry.key, entry.data);
|
|
11079
|
+
}
|
|
11080
|
+
const hasDirtyPages = dirtyPages.size > 0;
|
|
11081
|
+
if (this.pfs.wal && hasDirtyPages) {
|
|
11082
|
+
await this.pfs.wal.prepareCommit(dirtyPages);
|
|
10997
11083
|
await this.pfs.wal.writeCommitMarker();
|
|
10998
11084
|
}
|
|
10999
|
-
|
|
11000
|
-
|
|
11085
|
+
await tx.commit();
|
|
11086
|
+
if (hasDirtyPages) {
|
|
11087
|
+
await this.rootTx.commit();
|
|
11001
11088
|
}
|
|
11002
|
-
if (
|
|
11003
|
-
|
|
11004
|
-
|
|
11005
|
-
|
|
11006
|
-
|
|
11007
|
-
|
|
11089
|
+
if (hasDirtyPages) {
|
|
11090
|
+
if (!this.pfs.wal) {
|
|
11091
|
+
await this.pfs.strategy.sync();
|
|
11092
|
+
} else {
|
|
11093
|
+
this.pfs.wal.incrementWrittenPages(dirtyPages.size);
|
|
11094
|
+
if (this.pfs.wal.shouldCheckpoint(this.pfs.options.walCheckpointThreshold)) {
|
|
11095
|
+
shouldTriggerCheckpoint = true;
|
|
11096
|
+
}
|
|
11008
11097
|
}
|
|
11009
11098
|
}
|
|
11010
11099
|
});
|
|
@@ -11012,8 +11101,6 @@ var Transaction = class {
|
|
|
11012
11101
|
await this.pfs.checkpoint();
|
|
11013
11102
|
}
|
|
11014
11103
|
} finally {
|
|
11015
|
-
this.dirtyPages.clear();
|
|
11016
|
-
this.undoPages.clear();
|
|
11017
11104
|
this.releaseAllLocks();
|
|
11018
11105
|
if (this._writeLockRelease) {
|
|
11019
11106
|
this._writeLockRelease();
|
|
@@ -11026,8 +11113,9 @@ var Transaction = class {
|
|
|
11026
11113
|
*/
|
|
11027
11114
|
async rollback() {
|
|
11028
11115
|
try {
|
|
11029
|
-
this.
|
|
11030
|
-
|
|
11116
|
+
if (this.mvccTx) {
|
|
11117
|
+
this.mvccTx.rollback();
|
|
11118
|
+
}
|
|
11031
11119
|
this.releaseAllLocks();
|
|
11032
11120
|
} finally {
|
|
11033
11121
|
if (this._writeLockRelease) {
|
|
@@ -11036,12 +11124,6 @@ var Transaction = class {
|
|
|
11036
11124
|
}
|
|
11037
11125
|
}
|
|
11038
11126
|
}
|
|
11039
|
-
/**
|
|
11040
|
-
* Returns the dirty pages map.
|
|
11041
|
-
*/
|
|
11042
|
-
__getDirtyPages() {
|
|
11043
|
-
return this.dirtyPages;
|
|
11044
|
-
}
|
|
11045
11127
|
/**
|
|
11046
11128
|
* Releases all locks.
|
|
11047
11129
|
*/
|
|
@@ -11213,7 +11295,7 @@ var DataplyAPI = class {
|
|
|
11213
11295
|
pagePreallocationCount: 1e3,
|
|
11214
11296
|
wal: null,
|
|
11215
11297
|
walCheckpointThreshold: 1e3,
|
|
11216
|
-
logLevel:
|
|
11298
|
+
logLevel: 0 /* None */
|
|
11217
11299
|
}, options);
|
|
11218
11300
|
}
|
|
11219
11301
|
/**
|
|
@@ -11334,7 +11416,7 @@ var DataplyAPI = class {
|
|
|
11334
11416
|
return new Transaction(
|
|
11335
11417
|
++this.txIdCounter,
|
|
11336
11418
|
this.txContext,
|
|
11337
|
-
this.pfs.
|
|
11419
|
+
this.pfs.getRootTransaction(),
|
|
11338
11420
|
this.lockManager,
|
|
11339
11421
|
this.pfs
|
|
11340
11422
|
);
|
|
@@ -11354,7 +11436,7 @@ var DataplyAPI = class {
|
|
|
11354
11436
|
* Used internally by runWithDefaultWrite.
|
|
11355
11437
|
* @returns A release function
|
|
11356
11438
|
*/
|
|
11357
|
-
acquireWriteLock() {
|
|
11439
|
+
async acquireWriteLock() {
|
|
11358
11440
|
this.logger.debug("Acquiring write lock");
|
|
11359
11441
|
const previous = this.writeQueue;
|
|
11360
11442
|
let release;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { IndexPage, MetadataPage, DataplyOptions } from '../types';
|
|
2
2
|
import type { Transaction } from './transaction/Transaction';
|
|
3
|
+
import { AsyncMVCCTransaction } from 'mvcc-api';
|
|
3
4
|
import { PageManagerFactory } from './Page';
|
|
4
5
|
import { WALManager } from './WALManager';
|
|
5
6
|
import { PageMVCCStrategy } from './PageMVCCStrategy';
|
|
@@ -17,6 +18,7 @@ export declare class PageFileSystem {
|
|
|
17
18
|
protected readonly walManager: WALManager | null;
|
|
18
19
|
protected readonly pageManagerFactory: PageManagerFactory;
|
|
19
20
|
protected readonly pageStrategy: PageMVCCStrategy;
|
|
21
|
+
protected readonly rootTransaction: AsyncMVCCTransaction<PageMVCCStrategy, number, Uint8Array>;
|
|
20
22
|
protected readonly logger: Logger;
|
|
21
23
|
/** 글로벌 동기화(체크포인트/커밋)를 위한 Mutex */
|
|
22
24
|
private lockPromise;
|
|
@@ -39,6 +41,10 @@ export declare class PageFileSystem {
|
|
|
39
41
|
* Returns the page strategy for transaction use.
|
|
40
42
|
*/
|
|
41
43
|
getPageStrategy(): PageMVCCStrategy;
|
|
44
|
+
/**
|
|
45
|
+
* Returns the root MVCC transaction.
|
|
46
|
+
*/
|
|
47
|
+
getRootTransaction(): AsyncMVCCTransaction<PageMVCCStrategy, number, Uint8Array>;
|
|
42
48
|
/**
|
|
43
49
|
* Updates the bitmap status for a specific page.
|
|
44
50
|
* @param pageId The ID of the page to update
|
|
@@ -134,16 +140,10 @@ export declare class PageFileSystem {
|
|
|
134
140
|
* @param tx Transaction
|
|
135
141
|
*/
|
|
136
142
|
setFreePage(pageId: number, tx: Transaction): Promise<void>;
|
|
137
|
-
/**
|
|
138
|
-
* WAL에 커밋합니다.
|
|
139
|
-
* @param dirtyPages 변경된 페이지들
|
|
140
|
-
*/
|
|
141
|
-
commitToWAL(dirtyPages: Map<number, Uint8Array>): Promise<void>;
|
|
142
143
|
/**
|
|
143
144
|
* 체크포인트를 수행합니다.
|
|
144
|
-
* 1.
|
|
145
|
-
* 2.
|
|
146
|
-
* 3. WAL 로그 파일 비우기 (Clear/Truncate)
|
|
145
|
+
* 1. DB 파일 물리적 동기화 (Sync/fsync)
|
|
146
|
+
* 2. WAL 로그 파일 비우기 (Clear/Truncate)
|
|
147
147
|
*/
|
|
148
148
|
checkpoint(): Promise<void>;
|
|
149
149
|
/**
|
|
@@ -1,23 +1,20 @@
|
|
|
1
|
+
import { AsyncMVCCStrategy } from 'mvcc-api';
|
|
1
2
|
/**
|
|
2
3
|
* 페이지 수준 MVCC Strategy.
|
|
3
4
|
* mvcc-api의 AsyncMVCCStrategy를 상속하여 디스크 I/O를 담당합니다.
|
|
5
|
+
* 캐시 및 버퍼 관리는 mvcc-api의 AsyncMVCCTransaction이 담당합니다.
|
|
4
6
|
*
|
|
5
7
|
* 키: 페이지 ID (number)
|
|
6
8
|
* 값: 페이지 데이터 (Uint8Array)
|
|
7
9
|
*/
|
|
8
|
-
export declare class PageMVCCStrategy {
|
|
10
|
+
export declare class PageMVCCStrategy extends AsyncMVCCStrategy<number, Uint8Array> {
|
|
9
11
|
private readonly fileHandle;
|
|
10
12
|
private readonly pageSize;
|
|
11
|
-
/** LRU 캐시 (페이지 ID -> 페이지 데이터) */
|
|
12
|
-
private readonly cache;
|
|
13
|
-
/** 디스크에 기록되지 않은 변경된 페이지들 (페이지 ID -> 페이지 데이터) */
|
|
14
|
-
private readonly dirtyPages;
|
|
15
13
|
/** 파일 크기 (논리적) */
|
|
16
14
|
private fileSize;
|
|
17
|
-
constructor(fileHandle: number, pageSize: number
|
|
15
|
+
constructor(fileHandle: number, pageSize: number);
|
|
18
16
|
/**
|
|
19
17
|
* 디스크에서 페이지를 읽습니다.
|
|
20
|
-
* 캐시에 있으면 캐시에서 반환합니다.
|
|
21
18
|
* @param pageId 페이지 ID
|
|
22
19
|
* @returns 페이지 데이터
|
|
23
20
|
*/
|
|
@@ -29,22 +26,7 @@ export declare class PageMVCCStrategy {
|
|
|
29
26
|
*/
|
|
30
27
|
write(pageId: number, data: Uint8Array): Promise<void>;
|
|
31
28
|
/**
|
|
32
|
-
*
|
|
33
|
-
* WAL 체크포인트 시점에 호출되어야 합니다.
|
|
34
|
-
*/
|
|
35
|
-
flush(): Promise<void>;
|
|
36
|
-
/**
|
|
37
|
-
* 지정된 페이지들만 디스크에 기록합니다.
|
|
38
|
-
* WAL 없이 트랜잭션 커밋 시, 해당 트랜잭션의 dirty pages만 선택적으로 flush합니다.
|
|
39
|
-
* @param pages 기록할 페이지 맵 (PageID -> PageData)
|
|
40
|
-
*/
|
|
41
|
-
flushPages(pages: Map<number, Uint8Array>): Promise<void>;
|
|
42
|
-
/**
|
|
43
|
-
* 메인 DB 파일의 물리적 동기화를 수행합니다 (fsync).
|
|
44
|
-
*/
|
|
45
|
-
sync(): Promise<void>;
|
|
46
|
-
/**
|
|
47
|
-
* 페이지 삭제 (실제로는 캐시에서만 제거)
|
|
29
|
+
* 페이지 삭제.
|
|
48
30
|
* 실제 페이지 해제는 상위 레이어(FreeList)에서 관리합니다.
|
|
49
31
|
* @param pageId 페이지 ID
|
|
50
32
|
*/
|
|
@@ -56,13 +38,13 @@ export declare class PageMVCCStrategy {
|
|
|
56
38
|
*/
|
|
57
39
|
exists(pageId: number): Promise<boolean>;
|
|
58
40
|
/**
|
|
59
|
-
*
|
|
41
|
+
* 메인 DB 파일의 물리적 동기화를 수행합니다 (fsync).
|
|
60
42
|
*/
|
|
61
|
-
|
|
43
|
+
sync(): Promise<void>;
|
|
62
44
|
/**
|
|
63
|
-
*
|
|
45
|
+
* 현재 파일 크기 반환
|
|
64
46
|
*/
|
|
65
|
-
|
|
47
|
+
getFileSize(): number;
|
|
66
48
|
private _readFromDisk;
|
|
67
49
|
private _writeToDisk;
|
|
68
50
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { PageFileSystem } from '../PageFileSystem';
|
|
2
|
+
import type { AsyncMVCCTransaction } from 'mvcc-api';
|
|
3
|
+
import type { PageMVCCStrategy } from '../PageMVCCStrategy';
|
|
2
4
|
import { LockManager } from './LockManager';
|
|
3
5
|
import { TransactionContext } from './TxContext';
|
|
4
|
-
import { PageMVCCStrategy } from '../PageMVCCStrategy';
|
|
5
6
|
/**
|
|
6
7
|
* Transaction class.
|
|
7
8
|
* Manages the lifecycle and resources of a database transaction.
|
|
9
|
+
* Internally wraps a nested AsyncMVCCTransaction for snapshot isolation.
|
|
8
10
|
*/
|
|
9
11
|
export declare class Transaction {
|
|
10
12
|
readonly context: TransactionContext;
|
|
@@ -16,25 +18,28 @@ export declare class Transaction {
|
|
|
16
18
|
private heldLocks;
|
|
17
19
|
/** Held page locks (PageID -> LockID) */
|
|
18
20
|
private pageLocks;
|
|
19
|
-
/** Dirty Pages modified by the transaction: PageID -> Modified Page Buffer */
|
|
20
|
-
private dirtyPages;
|
|
21
|
-
/** Undo pages: PageID -> Original Page Buffer (Snapshot) */
|
|
22
|
-
private undoPages;
|
|
23
21
|
/** List of callbacks to execute on commit */
|
|
24
22
|
private commitHooks;
|
|
25
|
-
/**
|
|
26
|
-
private
|
|
23
|
+
/** Nested MVCC Transaction for snapshot isolation (lazy init) */
|
|
24
|
+
private mvccTx;
|
|
25
|
+
/** Root MVCC Transaction reference */
|
|
26
|
+
private readonly rootTx;
|
|
27
27
|
/** Release function for global write lock, set by DataplyAPI */
|
|
28
28
|
private _writeLockRelease;
|
|
29
29
|
/**
|
|
30
30
|
* @param id Transaction ID
|
|
31
31
|
* @param context Transaction context
|
|
32
|
-
* @param
|
|
32
|
+
* @param rootTx Root MVCC Transaction
|
|
33
33
|
* @param lockManager LockManager instance
|
|
34
34
|
* @param pfs Page File System
|
|
35
|
-
* @param reloadBPTree Callback to reload BPTree cache on rollback
|
|
36
35
|
*/
|
|
37
|
-
constructor(id: number, context: TransactionContext,
|
|
36
|
+
constructor(id: number, context: TransactionContext, rootTx: AsyncMVCCTransaction<PageMVCCStrategy, number, Uint8Array>, lockManager: LockManager, pfs: PageFileSystem);
|
|
37
|
+
/**
|
|
38
|
+
* Lazily initializes the nested MVCC transaction.
|
|
39
|
+
* This ensures the snapshot is taken at the time of first access,
|
|
40
|
+
* picking up the latest committed root version.
|
|
41
|
+
*/
|
|
42
|
+
private ensureMvccTx;
|
|
38
43
|
/**
|
|
39
44
|
* Registers a commit hook.
|
|
40
45
|
* @param hook Function to execute
|
|
@@ -50,17 +55,17 @@ export declare class Transaction {
|
|
|
50
55
|
*/
|
|
51
56
|
__hasWriteLockRelease(): boolean;
|
|
52
57
|
/**
|
|
53
|
-
* Reads a page
|
|
58
|
+
* Reads a page through the MVCC transaction.
|
|
54
59
|
* @param pageId Page ID
|
|
55
60
|
* @returns Page data
|
|
56
61
|
*/
|
|
57
|
-
|
|
62
|
+
__readPage(pageId: number): Promise<Uint8Array>;
|
|
58
63
|
/**
|
|
59
|
-
* Writes a page
|
|
64
|
+
* Writes a page through the MVCC transaction.
|
|
60
65
|
* @param pageId Page ID
|
|
61
66
|
* @param data Page data
|
|
62
67
|
*/
|
|
63
|
-
|
|
68
|
+
__writePage(pageId: number, data: Uint8Array): Promise<void>;
|
|
64
69
|
/**
|
|
65
70
|
* Acquires a write lock.
|
|
66
71
|
* @param pageId Page ID
|
|
@@ -74,10 +79,6 @@ export declare class Transaction {
|
|
|
74
79
|
* Rolls back the transaction.
|
|
75
80
|
*/
|
|
76
81
|
rollback(): Promise<void>;
|
|
77
|
-
/**
|
|
78
|
-
* Returns the dirty pages map.
|
|
79
|
-
*/
|
|
80
|
-
__getDirtyPages(): Map<number, Uint8Array>;
|
|
81
82
|
/**
|
|
82
83
|
* Releases all locks.
|
|
83
84
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dataply",
|
|
3
|
-
"version": "0.0.26-alpha.
|
|
3
|
+
"version": "0.0.26-alpha.7",
|
|
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>",
|
|
@@ -49,6 +49,6 @@
|
|
|
49
49
|
"hookall": "^2.2.0",
|
|
50
50
|
"mvcc-api": "^1.3.7",
|
|
51
51
|
"ryoiki": "^1.2.0",
|
|
52
|
-
"serializable-bptree": "^9.0.
|
|
52
|
+
"serializable-bptree": "^9.0.1"
|
|
53
53
|
}
|
|
54
|
-
}
|
|
54
|
+
}
|