dataply 0.0.26-alpha.6 → 0.0.26-alpha.8
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 +274 -143
- package/dist/types/core/Dataply.d.ts +13 -1
- package/dist/types/core/DataplyAPI.d.ts +4 -30
- package/dist/types/core/transaction/GlobalTransaction.d.ts +12 -3
- package/dist/types/core/transaction/Transaction.d.ts +3 -9
- package/package.json +2 -2
- package/readme.md +20 -43
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;
|
|
@@ -9767,11 +9907,7 @@ var PageFileSystem = class {
|
|
|
9767
9907
|
* @returns 페이지 버퍼
|
|
9768
9908
|
*/
|
|
9769
9909
|
async get(pageIndex, tx) {
|
|
9770
|
-
|
|
9771
|
-
if (page === null) {
|
|
9772
|
-
return new Uint8Array(this.pageSize);
|
|
9773
|
-
}
|
|
9774
|
-
return page;
|
|
9910
|
+
return tx.__readPage(pageIndex);
|
|
9775
9911
|
}
|
|
9776
9912
|
/**
|
|
9777
9913
|
* Reads the page header.
|
|
@@ -9862,7 +9998,7 @@ var PageFileSystem = class {
|
|
|
9862
9998
|
const manager = this.pageFactory.getManager(page);
|
|
9863
9999
|
manager.updateChecksum(page);
|
|
9864
10000
|
await tx.__acquireWriteLock(pageIndex);
|
|
9865
|
-
await tx.
|
|
10001
|
+
await tx.__writePage(pageIndex, page);
|
|
9866
10002
|
}
|
|
9867
10003
|
/**
|
|
9868
10004
|
* Appends and inserts a new page.
|
|
@@ -10831,6 +10967,7 @@ var Transaction = class {
|
|
|
10831
10967
|
this.pfs = pfs;
|
|
10832
10968
|
this.id = id;
|
|
10833
10969
|
this.rootTx = rootTx;
|
|
10970
|
+
this.mvccTx = rootTx.createNested();
|
|
10834
10971
|
}
|
|
10835
10972
|
/** Transaction ID */
|
|
10836
10973
|
id;
|
|
@@ -10841,22 +10978,11 @@ var Transaction = class {
|
|
|
10841
10978
|
/** List of callbacks to execute on commit */
|
|
10842
10979
|
commitHooks = [];
|
|
10843
10980
|
/** Nested MVCC Transaction for snapshot isolation (lazy init) */
|
|
10844
|
-
mvccTx
|
|
10981
|
+
mvccTx;
|
|
10845
10982
|
/** Root MVCC Transaction reference */
|
|
10846
10983
|
rootTx;
|
|
10847
10984
|
/** Release function for global write lock, set by DataplyAPI */
|
|
10848
10985
|
_writeLockRelease = null;
|
|
10849
|
-
/**
|
|
10850
|
-
* Lazily initializes the nested MVCC transaction.
|
|
10851
|
-
* This ensures the snapshot is taken at the time of first access,
|
|
10852
|
-
* picking up the latest committed root version.
|
|
10853
|
-
*/
|
|
10854
|
-
ensureMvccTx() {
|
|
10855
|
-
if (!this.mvccTx) {
|
|
10856
|
-
this.mvccTx = this.rootTx.createNested();
|
|
10857
|
-
}
|
|
10858
|
-
return this.mvccTx;
|
|
10859
|
-
}
|
|
10860
10986
|
/**
|
|
10861
10987
|
* Registers a commit hook.
|
|
10862
10988
|
* @param hook Function to execute
|
|
@@ -10882,9 +11008,8 @@ var Transaction = class {
|
|
|
10882
11008
|
* @param pageId Page ID
|
|
10883
11009
|
* @returns Page data
|
|
10884
11010
|
*/
|
|
10885
|
-
async
|
|
10886
|
-
const
|
|
10887
|
-
const data = await tx.read(pageId);
|
|
11011
|
+
async __readPage(pageId) {
|
|
11012
|
+
const data = await this.mvccTx.read(pageId);
|
|
10888
11013
|
if (data === null) {
|
|
10889
11014
|
return new Uint8Array(this.pfs.pageSize);
|
|
10890
11015
|
}
|
|
@@ -10897,15 +11022,14 @@ var Transaction = class {
|
|
|
10897
11022
|
* @param pageId Page ID
|
|
10898
11023
|
* @param data Page data
|
|
10899
11024
|
*/
|
|
10900
|
-
async
|
|
10901
|
-
const
|
|
10902
|
-
const exists = await tx.exists(pageId);
|
|
11025
|
+
async __writePage(pageId, data) {
|
|
11026
|
+
const exists = await this.mvccTx.exists(pageId);
|
|
10903
11027
|
if (exists) {
|
|
10904
11028
|
const copy = new Uint8Array(data.length);
|
|
10905
11029
|
copy.set(data);
|
|
10906
|
-
await
|
|
11030
|
+
await this.mvccTx.write(pageId, copy);
|
|
10907
11031
|
} else {
|
|
10908
|
-
await
|
|
11032
|
+
await this.mvccTx.create(pageId, data);
|
|
10909
11033
|
}
|
|
10910
11034
|
}
|
|
10911
11035
|
/**
|
|
@@ -10933,10 +11057,9 @@ var Transaction = class {
|
|
|
10933
11057
|
await hook();
|
|
10934
11058
|
}
|
|
10935
11059
|
});
|
|
10936
|
-
const tx = this.ensureMvccTx();
|
|
10937
11060
|
let shouldTriggerCheckpoint = false;
|
|
10938
11061
|
await this.pfs.runGlobalLock(async () => {
|
|
10939
|
-
const entries =
|
|
11062
|
+
const entries = this.mvccTx.getResultEntries();
|
|
10940
11063
|
const dirtyPages = /* @__PURE__ */ new Map();
|
|
10941
11064
|
for (const entry of [...entries.created, ...entries.updated]) {
|
|
10942
11065
|
dirtyPages.set(entry.key, entry.data);
|
|
@@ -10946,7 +11069,7 @@ var Transaction = class {
|
|
|
10946
11069
|
await this.pfs.wal.prepareCommit(dirtyPages);
|
|
10947
11070
|
await this.pfs.wal.writeCommitMarker();
|
|
10948
11071
|
}
|
|
10949
|
-
await
|
|
11072
|
+
await this.mvccTx.commit();
|
|
10950
11073
|
if (hasDirtyPages) {
|
|
10951
11074
|
await this.rootTx.commit();
|
|
10952
11075
|
}
|
|
@@ -11079,7 +11202,6 @@ var DataplyAPI = class {
|
|
|
11079
11202
|
constructor(file, options) {
|
|
11080
11203
|
this.file = file;
|
|
11081
11204
|
this.hook = useHookall(this);
|
|
11082
|
-
this.latcher = new Ryoiki3();
|
|
11083
11205
|
this.options = this.verboseOptions(options);
|
|
11084
11206
|
this.loggerManager = new LoggerManager(this.options.logLevel);
|
|
11085
11207
|
this.logger = this.loggerManager.create("DataplyAPI");
|
|
@@ -11132,38 +11254,6 @@ var DataplyAPI = class {
|
|
|
11132
11254
|
txIdCounter;
|
|
11133
11255
|
/** Promise-chain mutex for serializing write operations */
|
|
11134
11256
|
writeQueue = Promise.resolve();
|
|
11135
|
-
/** Lock manager. Used for managing transactions */
|
|
11136
|
-
latcher;
|
|
11137
|
-
/**
|
|
11138
|
-
* Acquire a read lock on the given page ID and execute the given function.
|
|
11139
|
-
* @param pageId Page ID to acquire a read lock on
|
|
11140
|
-
* @param fn Function to execute while holding the read lock
|
|
11141
|
-
* @returns The result of the given function
|
|
11142
|
-
*/
|
|
11143
|
-
async latchReadLock(pageId, fn) {
|
|
11144
|
-
let lockId;
|
|
11145
|
-
return this.latcher.readLock(this.latcher.range(pageId, 1), async (_lockId) => {
|
|
11146
|
-
lockId = _lockId;
|
|
11147
|
-
return fn();
|
|
11148
|
-
}).finally(() => {
|
|
11149
|
-
this.latcher.readUnlock(lockId);
|
|
11150
|
-
});
|
|
11151
|
-
}
|
|
11152
|
-
/**
|
|
11153
|
-
* Acquire a write lock on the given page ID and execute the given function.
|
|
11154
|
-
* @param pageId Page ID to acquire a write lock on
|
|
11155
|
-
* @param fn Function to execute while holding the write lock
|
|
11156
|
-
* @returns The result of the given function
|
|
11157
|
-
*/
|
|
11158
|
-
async latchWriteLock(pageId, fn) {
|
|
11159
|
-
let lockId;
|
|
11160
|
-
return this.latcher.writeLock(this.latcher.range(pageId, 1), async (_lockId) => {
|
|
11161
|
-
lockId = _lockId;
|
|
11162
|
-
return fn();
|
|
11163
|
-
}).finally(() => {
|
|
11164
|
-
this.latcher.writeUnlock(lockId);
|
|
11165
|
-
});
|
|
11166
|
-
}
|
|
11167
11257
|
/**
|
|
11168
11258
|
* Verifies if the page file is a valid Dataply file.
|
|
11169
11259
|
* The metadata page must be located at the beginning of the Dataply file.
|
|
@@ -11293,7 +11383,7 @@ var DataplyAPI = class {
|
|
|
11293
11383
|
if (this.initialized) {
|
|
11294
11384
|
return;
|
|
11295
11385
|
}
|
|
11296
|
-
await this.
|
|
11386
|
+
await this.withReadTransaction(async (tx) => {
|
|
11297
11387
|
await this.hook.trigger("init", tx, async (tx2) => {
|
|
11298
11388
|
await this.pfs.init();
|
|
11299
11389
|
await this.rowTableEngine.init();
|
|
@@ -11318,15 +11408,6 @@ var DataplyAPI = class {
|
|
|
11318
11408
|
this.pfs
|
|
11319
11409
|
);
|
|
11320
11410
|
}
|
|
11321
|
-
/**
|
|
11322
|
-
* Runs a callback function within a transaction context.
|
|
11323
|
-
* If no transaction is provided, a new transaction is created.
|
|
11324
|
-
* The transaction is committed if the callback completes successfully,
|
|
11325
|
-
* or rolled back if an error occurs.
|
|
11326
|
-
* @param callback The callback function to run within the transaction context.
|
|
11327
|
-
* @param tx The transaction to use. If not provided, a new transaction is created.
|
|
11328
|
-
* @returns The result of the callback function.
|
|
11329
|
-
*/
|
|
11330
11411
|
/**
|
|
11331
11412
|
* Acquires the global write lock.
|
|
11332
11413
|
* Returns a release function that MUST be called to unlock.
|
|
@@ -11351,7 +11432,7 @@ var DataplyAPI = class {
|
|
|
11351
11432
|
* @param tx Optional external transaction.
|
|
11352
11433
|
* @returns The result of the callback.
|
|
11353
11434
|
*/
|
|
11354
|
-
async
|
|
11435
|
+
async withWriteTransaction(callback, tx) {
|
|
11355
11436
|
this.logger.debug("Running with default write transaction");
|
|
11356
11437
|
if (!tx) {
|
|
11357
11438
|
const release = await this.acquireWriteLock();
|
|
@@ -11375,7 +11456,7 @@ var DataplyAPI = class {
|
|
|
11375
11456
|
}
|
|
11376
11457
|
return result;
|
|
11377
11458
|
}
|
|
11378
|
-
async
|
|
11459
|
+
async withReadTransaction(callback, tx) {
|
|
11379
11460
|
this.logger.debug("Running with default transaction");
|
|
11380
11461
|
const isInternalTx = !tx;
|
|
11381
11462
|
if (!tx) {
|
|
@@ -11403,7 +11484,7 @@ var DataplyAPI = class {
|
|
|
11403
11484
|
* @param tx The transaction to use. If not provided, a new transaction is created.
|
|
11404
11485
|
* @returns An AsyncGenerator that yields values from the callback.
|
|
11405
11486
|
*/
|
|
11406
|
-
async *
|
|
11487
|
+
async *withReadStreamTransaction(callback, tx) {
|
|
11407
11488
|
this.logger.debug("Streaming with default transaction");
|
|
11408
11489
|
const isInternalTx = !tx;
|
|
11409
11490
|
if (!tx) {
|
|
@@ -11436,7 +11517,7 @@ var DataplyAPI = class {
|
|
|
11436
11517
|
if (!this.initialized) {
|
|
11437
11518
|
throw new Error("Dataply instance is not initialized");
|
|
11438
11519
|
}
|
|
11439
|
-
return this.
|
|
11520
|
+
return this.withReadTransaction((tx2) => this.rowTableEngine.getMetadata(tx2), tx);
|
|
11440
11521
|
}
|
|
11441
11522
|
/**
|
|
11442
11523
|
* Inserts data. Returns the PK of the added row.
|
|
@@ -11450,7 +11531,7 @@ var DataplyAPI = class {
|
|
|
11450
11531
|
if (!this.initialized) {
|
|
11451
11532
|
throw new Error("Dataply instance is not initialized");
|
|
11452
11533
|
}
|
|
11453
|
-
return this.
|
|
11534
|
+
return this.withWriteTransaction(async (tx2) => {
|
|
11454
11535
|
incrementRowCount = incrementRowCount ?? true;
|
|
11455
11536
|
if (typeof data === "string") {
|
|
11456
11537
|
data = this.textCodec.encode(data);
|
|
@@ -11471,7 +11552,7 @@ var DataplyAPI = class {
|
|
|
11471
11552
|
if (!this.initialized) {
|
|
11472
11553
|
throw new Error("Dataply instance is not initialized");
|
|
11473
11554
|
}
|
|
11474
|
-
return this.
|
|
11555
|
+
return this.withWriteTransaction(async (tx2) => {
|
|
11475
11556
|
incrementRowCount = incrementRowCount ?? true;
|
|
11476
11557
|
if (typeof data === "string") {
|
|
11477
11558
|
data = this.textCodec.encode(data);
|
|
@@ -11493,7 +11574,7 @@ var DataplyAPI = class {
|
|
|
11493
11574
|
if (!this.initialized) {
|
|
11494
11575
|
throw new Error("Dataply instance is not initialized");
|
|
11495
11576
|
}
|
|
11496
|
-
return this.
|
|
11577
|
+
return this.withWriteTransaction(async (tx2) => {
|
|
11497
11578
|
incrementRowCount = incrementRowCount ?? true;
|
|
11498
11579
|
const encodedList = dataList.map(
|
|
11499
11580
|
(data) => typeof data === "string" ? this.textCodec.encode(data) : data
|
|
@@ -11512,7 +11593,7 @@ var DataplyAPI = class {
|
|
|
11512
11593
|
if (!this.initialized) {
|
|
11513
11594
|
throw new Error("Dataply instance is not initialized");
|
|
11514
11595
|
}
|
|
11515
|
-
return this.
|
|
11596
|
+
return this.withWriteTransaction(async (tx2) => {
|
|
11516
11597
|
if (typeof data === "string") {
|
|
11517
11598
|
data = this.textCodec.encode(data);
|
|
11518
11599
|
}
|
|
@@ -11530,7 +11611,7 @@ var DataplyAPI = class {
|
|
|
11530
11611
|
if (!this.initialized) {
|
|
11531
11612
|
throw new Error("Dataply instance is not initialized");
|
|
11532
11613
|
}
|
|
11533
|
-
return this.
|
|
11614
|
+
return this.withWriteTransaction(async (tx2) => {
|
|
11534
11615
|
decrementRowCount = decrementRowCount ?? true;
|
|
11535
11616
|
await this.rowTableEngine.delete(pk, decrementRowCount, tx2);
|
|
11536
11617
|
}, tx);
|
|
@@ -11540,7 +11621,7 @@ var DataplyAPI = class {
|
|
|
11540
11621
|
if (!this.initialized) {
|
|
11541
11622
|
throw new Error("Dataply instance is not initialized");
|
|
11542
11623
|
}
|
|
11543
|
-
return this.
|
|
11624
|
+
return this.withReadTransaction(async (tx2) => {
|
|
11544
11625
|
const data = await this.rowTableEngine.selectByPK(pk, tx2);
|
|
11545
11626
|
if (data === null) return null;
|
|
11546
11627
|
if (asRaw) return data;
|
|
@@ -11552,7 +11633,7 @@ var DataplyAPI = class {
|
|
|
11552
11633
|
if (!this.initialized) {
|
|
11553
11634
|
throw new Error("Dataply instance is not initialized");
|
|
11554
11635
|
}
|
|
11555
|
-
return this.
|
|
11636
|
+
return this.withReadTransaction(async (tx2) => {
|
|
11556
11637
|
const results = await this.rowTableEngine.selectMany(pks, tx2);
|
|
11557
11638
|
return results.map((data) => {
|
|
11558
11639
|
if (data === null) return null;
|
|
@@ -11569,7 +11650,7 @@ var DataplyAPI = class {
|
|
|
11569
11650
|
if (!this.initialized) {
|
|
11570
11651
|
throw new Error("Dataply instance is not initialized");
|
|
11571
11652
|
}
|
|
11572
|
-
return this.
|
|
11653
|
+
return this.withWriteTransaction(() => {
|
|
11573
11654
|
return this.hook.trigger("close", void 0, async () => {
|
|
11574
11655
|
await this.pfs.close();
|
|
11575
11656
|
import_node_fs3.default.closeSync(this.fileHandle);
|
|
@@ -11600,6 +11681,24 @@ var Dataply = class {
|
|
|
11600
11681
|
createTransaction() {
|
|
11601
11682
|
return this.api.createTransaction();
|
|
11602
11683
|
}
|
|
11684
|
+
/**
|
|
11685
|
+
* Runs a write callback within a transaction context.
|
|
11686
|
+
*/
|
|
11687
|
+
async withWriteTransaction(callback, tx) {
|
|
11688
|
+
return this.api.withWriteTransaction(callback, tx);
|
|
11689
|
+
}
|
|
11690
|
+
/**
|
|
11691
|
+
* Runs a read callback within a transaction context.
|
|
11692
|
+
*/
|
|
11693
|
+
async withReadTransaction(callback, tx) {
|
|
11694
|
+
return this.api.withReadTransaction(callback, tx);
|
|
11695
|
+
}
|
|
11696
|
+
/**
|
|
11697
|
+
* Runs a generator callback function within a transaction context.
|
|
11698
|
+
*/
|
|
11699
|
+
async *withReadStreamTransaction(callback, tx) {
|
|
11700
|
+
return this.api.withReadStreamTransaction(callback, tx);
|
|
11701
|
+
}
|
|
11603
11702
|
/**
|
|
11604
11703
|
* Initializes the dataply instance.
|
|
11605
11704
|
* Must be called before using the dataply instance.
|
|
@@ -11666,10 +11765,42 @@ var Dataply = class {
|
|
|
11666
11765
|
};
|
|
11667
11766
|
|
|
11668
11767
|
// src/core/transaction/GlobalTransaction.ts
|
|
11669
|
-
var GlobalTransaction = class {
|
|
11768
|
+
var GlobalTransaction = class _GlobalTransaction {
|
|
11670
11769
|
transactions = [];
|
|
11671
11770
|
isCommitted = false;
|
|
11672
11771
|
isRolledBack = false;
|
|
11772
|
+
/**
|
|
11773
|
+
* Executes a global transaction across multiple Dataply instances using a callback.
|
|
11774
|
+
* Locks are acquired in the order instances are provided.
|
|
11775
|
+
* @param dbs Array of Dataply instances
|
|
11776
|
+
* @param callback Function to execute with the array of Transactions
|
|
11777
|
+
*/
|
|
11778
|
+
static async Run(dbs, callback) {
|
|
11779
|
+
const globalTx = new _GlobalTransaction();
|
|
11780
|
+
const txs = [];
|
|
11781
|
+
const releases = [];
|
|
11782
|
+
try {
|
|
11783
|
+
for (const db of dbs) {
|
|
11784
|
+
const release = await db.api.acquireWriteLock();
|
|
11785
|
+
releases.push(release);
|
|
11786
|
+
const tx = db.api.createTransaction();
|
|
11787
|
+
tx.__setWriteLockRelease(release);
|
|
11788
|
+
txs.push(tx);
|
|
11789
|
+
globalTx.add(tx);
|
|
11790
|
+
}
|
|
11791
|
+
const result = await callback(txs);
|
|
11792
|
+
await globalTx.commit();
|
|
11793
|
+
return result;
|
|
11794
|
+
} catch (e) {
|
|
11795
|
+
await globalTx.rollback();
|
|
11796
|
+
for (let i = txs.length; i < releases.length; i++) {
|
|
11797
|
+
releases[i]();
|
|
11798
|
+
}
|
|
11799
|
+
throw e;
|
|
11800
|
+
}
|
|
11801
|
+
}
|
|
11802
|
+
constructor() {
|
|
11803
|
+
}
|
|
11673
11804
|
/**
|
|
11674
11805
|
* Adds a transaction to the global transaction.
|
|
11675
11806
|
* @param tx Transaction to add
|
|
@@ -18,7 +18,19 @@ export declare class Dataply {
|
|
|
18
18
|
* A transaction must be terminated by calling either `commit` or `rollback`.
|
|
19
19
|
* @returns Transaction object
|
|
20
20
|
*/
|
|
21
|
-
createTransaction(): Transaction;
|
|
21
|
+
protected createTransaction(): Transaction;
|
|
22
|
+
/**
|
|
23
|
+
* Runs a write callback within a transaction context.
|
|
24
|
+
*/
|
|
25
|
+
withWriteTransaction<T>(callback: (tx: Transaction) => Promise<T>, tx?: Transaction): Promise<T>;
|
|
26
|
+
/**
|
|
27
|
+
* Runs a read callback within a transaction context.
|
|
28
|
+
*/
|
|
29
|
+
withReadTransaction<T>(callback: (tx: Transaction) => Promise<T>, tx?: Transaction): Promise<T>;
|
|
30
|
+
/**
|
|
31
|
+
* Runs a generator callback function within a transaction context.
|
|
32
|
+
*/
|
|
33
|
+
withReadStreamTransaction<T>(callback: (tx: Transaction) => AsyncGenerator<T>, tx?: Transaction): AsyncGenerator<T>;
|
|
22
34
|
/**
|
|
23
35
|
* Initializes the dataply instance.
|
|
24
36
|
* Must be called before using the dataply instance.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type DataplyOptions, type DataplyMetadata } from '../types';
|
|
2
2
|
import { type IHookall } from 'hookall';
|
|
3
|
-
import { Ryoiki } from 'ryoiki';
|
|
4
3
|
import { PageFileSystem } from './PageFileSystem';
|
|
5
4
|
import { RowTableEngine } from './RowTableEngine';
|
|
6
5
|
import { TextCodec } from '../utils/TextCodec';
|
|
@@ -48,23 +47,7 @@ export declare class DataplyAPI {
|
|
|
48
47
|
private txIdCounter;
|
|
49
48
|
/** Promise-chain mutex for serializing write operations */
|
|
50
49
|
private writeQueue;
|
|
51
|
-
/** Lock manager. Used for managing transactions */
|
|
52
|
-
protected readonly latcher: Ryoiki;
|
|
53
50
|
constructor(file: string, options: DataplyOptions);
|
|
54
|
-
/**
|
|
55
|
-
* Acquire a read lock on the given page ID and execute the given function.
|
|
56
|
-
* @param pageId Page ID to acquire a read lock on
|
|
57
|
-
* @param fn Function to execute while holding the read lock
|
|
58
|
-
* @returns The result of the given function
|
|
59
|
-
*/
|
|
60
|
-
latchReadLock<T>(pageId: number, fn: () => Promise<T>): Promise<T>;
|
|
61
|
-
/**
|
|
62
|
-
* Acquire a write lock on the given page ID and execute the given function.
|
|
63
|
-
* @param pageId Page ID to acquire a write lock on
|
|
64
|
-
* @param fn Function to execute while holding the write lock
|
|
65
|
-
* @returns The result of the given function
|
|
66
|
-
*/
|
|
67
|
-
latchWriteLock<T>(pageId: number, fn: () => Promise<T>): Promise<T>;
|
|
68
51
|
/**
|
|
69
52
|
* Verifies if the page file is a valid Dataply file.
|
|
70
53
|
* The metadata page must be located at the beginning of the Dataply file.
|
|
@@ -106,22 +89,13 @@ export declare class DataplyAPI {
|
|
|
106
89
|
* @returns Transaction object
|
|
107
90
|
*/
|
|
108
91
|
createTransaction(): Transaction;
|
|
109
|
-
/**
|
|
110
|
-
* Runs a callback function within a transaction context.
|
|
111
|
-
* If no transaction is provided, a new transaction is created.
|
|
112
|
-
* The transaction is committed if the callback completes successfully,
|
|
113
|
-
* or rolled back if an error occurs.
|
|
114
|
-
* @param callback The callback function to run within the transaction context.
|
|
115
|
-
* @param tx The transaction to use. If not provided, a new transaction is created.
|
|
116
|
-
* @returns The result of the callback function.
|
|
117
|
-
*/
|
|
118
92
|
/**
|
|
119
93
|
* Acquires the global write lock.
|
|
120
94
|
* Returns a release function that MUST be called to unlock.
|
|
121
95
|
* Used internally by runWithDefaultWrite.
|
|
122
96
|
* @returns A release function
|
|
123
97
|
*/
|
|
124
|
-
|
|
98
|
+
acquireWriteLock(): Promise<() => void>;
|
|
125
99
|
/**
|
|
126
100
|
* Runs a write callback within a transaction context with global write serialization.
|
|
127
101
|
* If no transaction is provided, a new transaction is created, committed on success, rolled back on error.
|
|
@@ -131,8 +105,8 @@ export declare class DataplyAPI {
|
|
|
131
105
|
* @param tx Optional external transaction.
|
|
132
106
|
* @returns The result of the callback.
|
|
133
107
|
*/
|
|
134
|
-
|
|
135
|
-
|
|
108
|
+
withWriteTransaction<T>(callback: (tx: Transaction) => Promise<T>, tx?: Transaction): Promise<T>;
|
|
109
|
+
withReadTransaction<T>(callback: (tx: Transaction) => Promise<T>, tx?: Transaction): Promise<T>;
|
|
136
110
|
/**
|
|
137
111
|
* Runs a generator callback function within a transaction context.
|
|
138
112
|
* Similar to runWithDefault but allows yielding values from an AsyncGenerator.
|
|
@@ -143,7 +117,7 @@ export declare class DataplyAPI {
|
|
|
143
117
|
* @param tx The transaction to use. If not provided, a new transaction is created.
|
|
144
118
|
* @returns An AsyncGenerator that yields values from the callback.
|
|
145
119
|
*/
|
|
146
|
-
|
|
120
|
+
withReadStreamTransaction<T>(callback: (tx: Transaction) => AsyncGenerator<T>, tx?: Transaction): AsyncGenerator<T>;
|
|
147
121
|
/**
|
|
148
122
|
* Retrieves metadata from the dataply.
|
|
149
123
|
* @returns Metadata of the dataply.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Transaction } from './Transaction';
|
|
2
|
+
import { Dataply } from '../Dataply';
|
|
2
3
|
/**
|
|
3
4
|
* Global Transaction Manager.
|
|
4
5
|
* Coordinates transactions across multiple instances (shards).
|
|
@@ -9,19 +10,27 @@ export declare class GlobalTransaction {
|
|
|
9
10
|
private transactions;
|
|
10
11
|
private isCommitted;
|
|
11
12
|
private isRolledBack;
|
|
13
|
+
/**
|
|
14
|
+
* Executes a global transaction across multiple Dataply instances using a callback.
|
|
15
|
+
* Locks are acquired in the order instances are provided.
|
|
16
|
+
* @param dbs Array of Dataply instances
|
|
17
|
+
* @param callback Function to execute with the array of Transactions
|
|
18
|
+
*/
|
|
19
|
+
static Run<T>(dbs: Dataply[], callback: (txs: Transaction[]) => Promise<T>): Promise<T>;
|
|
20
|
+
protected constructor();
|
|
12
21
|
/**
|
|
13
22
|
* Adds a transaction to the global transaction.
|
|
14
23
|
* @param tx Transaction to add
|
|
15
24
|
*/
|
|
16
|
-
add(tx: Transaction): void;
|
|
25
|
+
protected add(tx: Transaction): void;
|
|
17
26
|
/**
|
|
18
27
|
* Commits all transactions.
|
|
19
28
|
* Note: This is now a single-phase commit. For true atomicity across shards,
|
|
20
29
|
* each instance's WAL provides durability, but cross-shard atomicity is best-effort.
|
|
21
30
|
*/
|
|
22
|
-
commit(): Promise<void>;
|
|
31
|
+
protected commit(): Promise<void>;
|
|
23
32
|
/**
|
|
24
33
|
* Rolls back all transactions.
|
|
25
34
|
*/
|
|
26
|
-
rollback(): Promise<void>;
|
|
35
|
+
protected rollback(): Promise<void>;
|
|
27
36
|
}
|
|
@@ -21,7 +21,7 @@ export declare class Transaction {
|
|
|
21
21
|
/** List of callbacks to execute on commit */
|
|
22
22
|
private commitHooks;
|
|
23
23
|
/** Nested MVCC Transaction for snapshot isolation (lazy init) */
|
|
24
|
-
private mvccTx;
|
|
24
|
+
private readonly mvccTx;
|
|
25
25
|
/** Root MVCC Transaction reference */
|
|
26
26
|
private readonly rootTx;
|
|
27
27
|
/** Release function for global write lock, set by DataplyAPI */
|
|
@@ -34,12 +34,6 @@ export declare class Transaction {
|
|
|
34
34
|
* @param pfs Page File System
|
|
35
35
|
*/
|
|
36
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;
|
|
43
37
|
/**
|
|
44
38
|
* Registers a commit hook.
|
|
45
39
|
* @param hook Function to execute
|
|
@@ -59,13 +53,13 @@ export declare class Transaction {
|
|
|
59
53
|
* @param pageId Page ID
|
|
60
54
|
* @returns Page data
|
|
61
55
|
*/
|
|
62
|
-
|
|
56
|
+
__readPage(pageId: number): Promise<Uint8Array>;
|
|
63
57
|
/**
|
|
64
58
|
* Writes a page through the MVCC transaction.
|
|
65
59
|
* @param pageId Page ID
|
|
66
60
|
* @param data Page data
|
|
67
61
|
*/
|
|
68
|
-
|
|
62
|
+
__writePage(pageId: number, data: Uint8Array): Promise<void>;
|
|
69
63
|
/**
|
|
70
64
|
* Acquires a write lock.
|
|
71
65
|
* @param pageId Page ID
|
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.8",
|
|
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
|
}
|
package/readme.md
CHANGED
|
@@ -105,23 +105,17 @@ db.init().then(() => {
|
|
|
105
105
|
## Transaction Management
|
|
106
106
|
|
|
107
107
|
### Explicit Transactions
|
|
108
|
-
You can group multiple operations into a single unit of work to ensure atomicity.
|
|
108
|
+
You can group multiple operations into a single unit of work to ensure atomicity. The `withWriteTransaction` method handles the transaction lifecycle automatically, committing on success and rolling back on failure.
|
|
109
109
|
|
|
110
110
|
```typescript
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
await dataply.insert('Data 1', tx)
|
|
111
|
+
await dataply.withWriteTransaction(async (tx) => {
|
|
112
|
+
const pk = await dataply.insert('Data 1', tx)
|
|
115
113
|
await dataply.update(pk, 'Updated Data', tx)
|
|
116
|
-
|
|
117
|
-
await tx.commit() // Persist changes to disk and clear WAL on success
|
|
118
|
-
} catch (error) {
|
|
119
|
-
await tx.rollback() // Revert all changes on failure (Undo)
|
|
120
|
-
}
|
|
114
|
+
}) // Persists changes automatically on success or rolls back on failure
|
|
121
115
|
```
|
|
122
116
|
|
|
123
117
|
### Global Transactions
|
|
124
|
-
You can perform atomic operations across multiple `Dataply` instances using the `GlobalTransaction` class. This
|
|
118
|
+
You can perform atomic operations across multiple `Dataply` instances using the `GlobalTransaction` class. This safely acquires write locks on all instances sequentially and manages the transaction lifecycle to ensure either all instances commit successfully or all are rolled back.
|
|
125
119
|
|
|
126
120
|
```typescript
|
|
127
121
|
import { Dataply, GlobalTransaction } from 'dataply'
|
|
@@ -132,22 +126,13 @@ const db2 = new Dataply('./db2.db', { wal: './db2.wal' })
|
|
|
132
126
|
await db1.init()
|
|
133
127
|
await db2.init()
|
|
134
128
|
|
|
135
|
-
const tx1 = db1.createTransaction()
|
|
136
|
-
const tx2 = db2.createTransaction()
|
|
137
|
-
|
|
138
|
-
const globalTx = new GlobalTransaction()
|
|
139
|
-
globalTx.add(tx1)
|
|
140
|
-
globalTx.add(tx2)
|
|
141
|
-
|
|
142
129
|
try {
|
|
143
|
-
await
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
// Note: This is a best-effort atomic commit.
|
|
148
|
-
await globalTx.commit()
|
|
130
|
+
await GlobalTransaction.Run([db1, db2], async ([tx1, tx2]) => {
|
|
131
|
+
await db1.insert('Data for DB1', tx1)
|
|
132
|
+
await db2.insert('Data for DB2', tx2)
|
|
133
|
+
})
|
|
149
134
|
} catch (error) {
|
|
150
|
-
|
|
135
|
+
console.error('Global transaction failed and rolled back.', error)
|
|
151
136
|
}
|
|
152
137
|
```
|
|
153
138
|
|
|
@@ -196,30 +181,22 @@ Marks data as deleted.
|
|
|
196
181
|
#### `async getMetadata(tx?: Transaction): Promise<DataplyMetadata>`
|
|
197
182
|
Returns the current metadata of the dataply, including `pageSize`, `pageCount`, and `rowCount`.
|
|
198
183
|
|
|
199
|
-
#### `
|
|
200
|
-
|
|
184
|
+
#### `async withWriteTransaction<T>(callback: (tx: Transaction) => Promise<T>, tx?: Transaction): Promise<T>`
|
|
185
|
+
Executes write operations within a serialized write-lock transaction. Automatically commits on success and rolls back on failure if creating a new internal transaction.
|
|
201
186
|
|
|
202
|
-
#### `async
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
### Transaction Class
|
|
187
|
+
#### `async withReadTransaction<T>(callback: (tx: Transaction) => Promise<T>, tx?: Transaction): Promise<T>`
|
|
188
|
+
Executes read operations within a transaction context.
|
|
206
189
|
|
|
207
|
-
#### `async
|
|
208
|
-
|
|
190
|
+
#### `async *withReadStreamTransaction<T>(callback: (tx: Transaction) => AsyncGenerator<T>, tx?: Transaction): AsyncGenerator<T>`
|
|
191
|
+
Executes streaming read operations using an async generator in a transaction.
|
|
209
192
|
|
|
210
|
-
#### `async
|
|
211
|
-
|
|
193
|
+
#### `async close(): Promise<void>`
|
|
194
|
+
Closes the file handles and shuts down safely.
|
|
212
195
|
|
|
213
196
|
### GlobalTransaction Class
|
|
214
197
|
|
|
215
|
-
#### `
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
#### `async commit(): Promise<void>`
|
|
219
|
-
Executes a coordinated commit across all registered transactions. Note that without a prepare phase, this is a best-effort atomic commit.
|
|
220
|
-
|
|
221
|
-
#### `async rollback(): Promise<void>`
|
|
222
|
-
Rolls back all registered transactions simultaneously.
|
|
198
|
+
#### `static async Run<T>(dbs: Dataply[], callback: (txs: Transaction[]) => Promise<T>): Promise<T>`
|
|
199
|
+
Executes a callback-based global transaction across multiple Dataply instances sequentially locking them to prevent deadlocks, providing true atomicity within the Dataply nodes. Automatically commits on success and rolls back on failure.
|
|
223
200
|
|
|
224
201
|
## Extending Dataply
|
|
225
202
|
|