dataply 0.0.26-alpha.6 → 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
- strategy.write(id, node);
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
- strategy.write(node.id, node);
5060
+ writeBuffer.set(node.id, node);
5034
5061
  },
5035
5062
  deleteNode(node) {
5036
- strategy.delete(node.id);
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
- strategy.writeHead(head);
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
- this._ops = this._createOps();
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
- this._ops,
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
- insertOp(this._ops, this._ctx, key, value, this.comparator);
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
- deleteOp(this._ops, this._ctx, key, this.comparator, value);
5183
+ const { ops, flush } = this._createBufferedOps();
5184
+ deleteOp(ops, this._ctx, key, this.comparator, value);
5185
+ flush();
5139
5186
  }
5140
5187
  batchInsert(entries) {
5141
- batchInsertOp(this._ops, this._ctx, entries, this.comparator);
5188
+ const { ops, flush } = this._createBufferedOps();
5189
+ batchInsertOp(ops, this._ctx, entries, this.comparator);
5190
+ flush();
5142
5191
  }
5143
5192
  bulkLoad(entries) {
5144
- bulkLoadOp(this._ops, this._ctx, entries, this.comparator);
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 head = this._ops.readHead();
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
- this._ops.writeHead({
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
- await strategy.write(id, node);
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
- await strategy.write(node.id, node);
5282
+ writeBuffer.set(node.id, node);
5202
5283
  },
5203
5284
  async deleteNode(node) {
5204
- await strategy.delete(node.id);
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
- await strategy.writeHead(head);
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
- this._ops = this._createOps();
5216
- this._ctx = {
5217
- rootId: "",
5218
- order: this.strategy.order,
5219
- headData: () => this.strategy.head.data
5220
- };
5221
- await initOpAsync(
5222
- this._ops,
5223
- this._ctx,
5224
- this.strategy.order,
5225
- this.strategy.head,
5226
- (head) => {
5227
- this.strategy.head = head;
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
- yield* keysStreamOpAsync(
5254
- this._ops,
5255
- this._ctx.rootId,
5256
- condition,
5257
- this.comparator,
5258
- this._verifierMap,
5259
- this._searchConfigs,
5260
- this._ensureValues,
5261
- options
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
- yield* whereStreamOpAsync(
5266
- this._ops,
5267
- this._ctx.rootId,
5268
- condition,
5269
- this.comparator,
5270
- this._verifierMap,
5271
- this._searchConfigs,
5272
- this._ensureValues,
5273
- options
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
- await insertOpAsync(this._ops, this._ctx, key, value, this.comparator);
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
- await deleteOpAsync(this._ops, this._ctx, key, this.comparator, value);
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
- await batchInsertOpAsync(this._ops, this._ctx, entries, this.comparator);
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
- await bulkLoadOpAsync(this._ops, this._ctx, entries, this.comparator);
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
- const head = await this._ops.readHead();
5311
- if (head === null) throw new Error("Head not found");
5312
- await this._ops.writeHead({ root: head.root, order: head.order, data });
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
- const page = await tx.readPage(pageIndex);
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.writePage(pageIndex, page);
10001
+ await tx.__writePage(pageIndex, page);
9866
10002
  }
9867
10003
  /**
9868
10004
  * Appends and inserts a new page.
@@ -10882,7 +11018,7 @@ var Transaction = class {
10882
11018
  * @param pageId Page ID
10883
11019
  * @returns Page data
10884
11020
  */
10885
- async readPage(pageId) {
11021
+ async __readPage(pageId) {
10886
11022
  const tx = this.ensureMvccTx();
10887
11023
  const data = await tx.read(pageId);
10888
11024
  if (data === null) {
@@ -10897,7 +11033,7 @@ var Transaction = class {
10897
11033
  * @param pageId Page ID
10898
11034
  * @param data Page data
10899
11035
  */
10900
- async writePage(pageId, data) {
11036
+ async __writePage(pageId, data) {
10901
11037
  const tx = this.ensureMvccTx();
10902
11038
  const exists = await tx.exists(pageId);
10903
11039
  if (exists) {
@@ -11079,7 +11215,6 @@ var DataplyAPI = class {
11079
11215
  constructor(file, options) {
11080
11216
  this.file = file;
11081
11217
  this.hook = useHookall(this);
11082
- this.latcher = new Ryoiki3();
11083
11218
  this.options = this.verboseOptions(options);
11084
11219
  this.loggerManager = new LoggerManager(this.options.logLevel);
11085
11220
  this.logger = this.loggerManager.create("DataplyAPI");
@@ -11132,38 +11267,6 @@ var DataplyAPI = class {
11132
11267
  txIdCounter;
11133
11268
  /** Promise-chain mutex for serializing write operations */
11134
11269
  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
11270
  /**
11168
11271
  * Verifies if the page file is a valid Dataply file.
11169
11272
  * The metadata page must be located at the beginning of the Dataply file.
@@ -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.
@@ -59,13 +59,13 @@ export declare class Transaction {
59
59
  * @param pageId Page ID
60
60
  * @returns Page data
61
61
  */
62
- readPage(pageId: number): Promise<Uint8Array>;
62
+ __readPage(pageId: number): Promise<Uint8Array>;
63
63
  /**
64
64
  * Writes a page through the MVCC transaction.
65
65
  * @param pageId Page ID
66
66
  * @param data Page data
67
67
  */
68
- writePage(pageId: number, data: Uint8Array): Promise<void>;
68
+ __writePage(pageId: number, data: Uint8Array): Promise<void>;
69
69
  /**
70
70
  * Acquires a write lock.
71
71
  * @param pageId Page ID
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dataply",
3
- "version": "0.0.26-alpha.6",
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.0"
52
+ "serializable-bptree": "^9.0.1"
53
53
  }
54
- }
54
+ }