serializable-bptree 9.0.0 → 9.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4990,8 +4990,6 @@ var BPTreePureSync = class {
4990
4990
  comparator;
4991
4991
  option;
4992
4992
  _cachedRegexp = /* @__PURE__ */ new Map();
4993
- _ctx;
4994
- _ops;
4995
4993
  _verifierMap;
4996
4994
  _searchConfigs;
4997
4995
  constructor(strategy, comparator, option) {
@@ -5005,7 +5003,7 @@ var BPTreePureSync = class {
5005
5003
  _ensureValues(v) {
5006
5004
  return Array.isArray(v) ? v : [v];
5007
5005
  }
5008
- _createOps() {
5006
+ _createReadOps() {
5009
5007
  const strategy = this.strategy;
5010
5008
  return {
5011
5009
  getNode(id) {
@@ -5014,51 +5012,112 @@ var BPTreePureSync = class {
5014
5012
  createNode(leaf, keys, values, parent = null, next = null, prev = null) {
5015
5013
  const id = strategy.id(leaf);
5016
5014
  const node = { id, keys, values, leaf, parent, next, prev };
5017
- strategy.write(id, node);
5015
+ return node;
5016
+ },
5017
+ updateNode() {
5018
+ },
5019
+ deleteNode() {
5020
+ },
5021
+ readHead() {
5022
+ return strategy.readHead();
5023
+ },
5024
+ writeHead() {
5025
+ }
5026
+ };
5027
+ }
5028
+ _createBufferedOps() {
5029
+ const strategy = this.strategy;
5030
+ const writeBuffer = /* @__PURE__ */ new Map();
5031
+ const deleteBuffer = /* @__PURE__ */ new Set();
5032
+ let headBuffer = null;
5033
+ const ops = {
5034
+ getNode(id) {
5035
+ const buffered = writeBuffer.get(id);
5036
+ if (buffered) return buffered;
5037
+ return strategy.read(id);
5038
+ },
5039
+ createNode(leaf, keys, values, parent = null, next = null, prev = null) {
5040
+ const id = strategy.id(leaf);
5041
+ const node = { id, keys, values, leaf, parent, next, prev };
5042
+ writeBuffer.set(id, node);
5018
5043
  return node;
5019
5044
  },
5020
5045
  updateNode(node) {
5021
- strategy.write(node.id, node);
5046
+ writeBuffer.set(node.id, node);
5022
5047
  },
5023
5048
  deleteNode(node) {
5024
- strategy.delete(node.id);
5049
+ deleteBuffer.add(node.id);
5050
+ writeBuffer.delete(node.id);
5025
5051
  },
5026
5052
  readHead() {
5053
+ if (headBuffer) return headBuffer;
5027
5054
  return strategy.readHead();
5028
5055
  },
5029
5056
  writeHead(head) {
5030
- strategy.writeHead(head);
5057
+ headBuffer = head;
5031
5058
  }
5032
5059
  };
5060
+ function flush() {
5061
+ for (const id of deleteBuffer) {
5062
+ strategy.delete(id);
5063
+ }
5064
+ for (const [id, node] of writeBuffer) {
5065
+ strategy.write(id, node);
5066
+ }
5067
+ if (headBuffer) {
5068
+ strategy.writeHead(headBuffer);
5069
+ }
5070
+ }
5071
+ return { ops, flush };
5072
+ }
5073
+ _readCtx() {
5074
+ const head = this.strategy.readHead();
5075
+ if (head === null) {
5076
+ throw new Error("Tree not initialized. Call init() first.");
5077
+ }
5078
+ return { rootId: head.root, order: head.order };
5079
+ }
5080
+ _createCtx() {
5081
+ const strategy = this.strategy;
5082
+ const head = strategy.readHead();
5083
+ if (head === null) {
5084
+ throw new Error("Tree not initialized. Call init() first.");
5085
+ }
5086
+ return {
5087
+ rootId: head.root,
5088
+ order: head.order,
5089
+ headData: () => strategy.head.data
5090
+ };
5033
5091
  }
5034
5092
  init() {
5035
- this._ops = this._createOps();
5036
- this._ctx = {
5093
+ const { ops, flush } = this._createBufferedOps();
5094
+ const ctx = {
5037
5095
  rootId: "",
5038
5096
  order: this.strategy.order,
5039
5097
  headData: () => this.strategy.head.data
5040
5098
  };
5041
5099
  initOp(
5042
- this._ops,
5043
- this._ctx,
5100
+ ops,
5101
+ ctx,
5044
5102
  this.strategy.order,
5045
5103
  this.strategy.head,
5046
5104
  (head) => {
5047
5105
  this.strategy.head = head;
5048
5106
  }
5049
5107
  );
5108
+ flush();
5050
5109
  }
5051
5110
  /**
5052
5111
  * Returns the ID of the root node.
5053
5112
  */
5054
5113
  getRootId() {
5055
- return this._ctx.rootId;
5114
+ return this._readCtx().rootId;
5056
5115
  }
5057
5116
  /**
5058
5117
  * Returns the order of the B+Tree.
5059
5118
  */
5060
5119
  getOrder() {
5061
- return this._ctx.order;
5120
+ return this._readCtx().order;
5062
5121
  }
5063
5122
  /**
5064
5123
  * Verified if the value satisfies the condition.
@@ -5075,15 +5134,18 @@ var BPTreePureSync = class {
5075
5134
  }
5076
5135
  // ─── Query ───────────────────────────────────────────────────────
5077
5136
  get(key) {
5078
- return getOp(this._ops, this._ctx.rootId, key);
5137
+ const { rootId } = this._readCtx();
5138
+ return getOp(this._createReadOps(), rootId, key);
5079
5139
  }
5080
5140
  exists(key, value) {
5081
- return existsOp(this._ops, this._ctx.rootId, key, value, this.comparator);
5141
+ const { rootId } = this._readCtx();
5142
+ return existsOp(this._createReadOps(), rootId, key, value, this.comparator);
5082
5143
  }
5083
5144
  *keysStream(condition, options) {
5145
+ const { rootId } = this._readCtx();
5084
5146
  yield* keysStreamOp(
5085
- this._ops,
5086
- this._ctx.rootId,
5147
+ this._createReadOps(),
5148
+ rootId,
5087
5149
  condition,
5088
5150
  this.comparator,
5089
5151
  this._verifierMap,
@@ -5093,9 +5155,10 @@ var BPTreePureSync = class {
5093
5155
  );
5094
5156
  }
5095
5157
  *whereStream(condition, options) {
5158
+ const { rootId } = this._readCtx();
5096
5159
  yield* whereStreamOp(
5097
- this._ops,
5098
- this._ctx.rootId,
5160
+ this._createReadOps(),
5161
+ rootId,
5099
5162
  condition,
5100
5163
  this.comparator,
5101
5164
  this._verifierMap,
@@ -5120,35 +5183,49 @@ var BPTreePureSync = class {
5120
5183
  }
5121
5184
  // ─── Mutation ────────────────────────────────────────────────────
5122
5185
  insert(key, value) {
5123
- insertOp(this._ops, this._ctx, key, value, this.comparator);
5186
+ const { ops, flush } = this._createBufferedOps();
5187
+ const ctx = this._createCtx();
5188
+ insertOp(ops, ctx, key, value, this.comparator);
5189
+ flush();
5124
5190
  }
5125
5191
  delete(key, value) {
5126
- deleteOp(this._ops, this._ctx, key, this.comparator, value);
5192
+ const { ops, flush } = this._createBufferedOps();
5193
+ const ctx = this._createCtx();
5194
+ deleteOp(ops, ctx, key, this.comparator, value);
5195
+ flush();
5127
5196
  }
5128
5197
  batchInsert(entries) {
5129
- batchInsertOp(this._ops, this._ctx, entries, this.comparator);
5198
+ const { ops, flush } = this._createBufferedOps();
5199
+ const ctx = this._createCtx();
5200
+ batchInsertOp(ops, ctx, entries, this.comparator);
5201
+ flush();
5130
5202
  }
5131
5203
  bulkLoad(entries) {
5132
- bulkLoadOp(this._ops, this._ctx, entries, this.comparator);
5204
+ const { ops, flush } = this._createBufferedOps();
5205
+ const ctx = this._createCtx();
5206
+ bulkLoadOp(ops, ctx, entries, this.comparator);
5207
+ flush();
5133
5208
  }
5134
5209
  // ─── Head Data ───────────────────────────────────────────────────
5135
5210
  getHeadData() {
5136
- const head = this._ops.readHead();
5211
+ const head = this.strategy.readHead();
5137
5212
  if (head === null) {
5138
5213
  throw new Error("Head not found");
5139
5214
  }
5140
5215
  return head.data;
5141
5216
  }
5142
5217
  setHeadData(data) {
5143
- const head = this._ops.readHead();
5218
+ const { ops, flush } = this._createBufferedOps();
5219
+ const head = ops.readHead();
5144
5220
  if (head === null) {
5145
5221
  throw new Error("Head not found");
5146
5222
  }
5147
- this._ops.writeHead({
5223
+ ops.writeHead({
5148
5224
  root: head.root,
5149
5225
  order: head.order,
5150
5226
  data
5151
5227
  });
5228
+ flush();
5152
5229
  }
5153
5230
  // ─── Static utilities ────────────────────────────────────────────
5154
5231
  static ChooseDriver = BPTreeTransaction.ChooseDriver;
@@ -5159,9 +5236,8 @@ var BPTreePureAsync = class {
5159
5236
  strategy;
5160
5237
  comparator;
5161
5238
  option;
5239
+ lock = new Ryoiki2();
5162
5240
  _cachedRegexp = /* @__PURE__ */ new Map();
5163
- _ctx;
5164
- _ops;
5165
5241
  _verifierMap;
5166
5242
  _searchConfigs;
5167
5243
  constructor(strategy, comparator, option) {
@@ -5175,7 +5251,7 @@ var BPTreePureAsync = class {
5175
5251
  _ensureValues(v) {
5176
5252
  return Array.isArray(v) ? v : [v];
5177
5253
  }
5178
- _createOps() {
5254
+ _createReadOps() {
5179
5255
  const strategy = this.strategy;
5180
5256
  return {
5181
5257
  async getNode(id) {
@@ -5184,45 +5260,117 @@ var BPTreePureAsync = class {
5184
5260
  async createNode(leaf, keys, values, parent = null, next = null, prev = null) {
5185
5261
  const id = await strategy.id(leaf);
5186
5262
  const node = { id, keys, values, leaf, parent, next, prev };
5187
- await strategy.write(id, node);
5263
+ return node;
5264
+ },
5265
+ async updateNode() {
5266
+ },
5267
+ async deleteNode() {
5268
+ },
5269
+ async readHead() {
5270
+ return await strategy.readHead();
5271
+ },
5272
+ async writeHead() {
5273
+ }
5274
+ };
5275
+ }
5276
+ _createBufferedOps() {
5277
+ const strategy = this.strategy;
5278
+ const writeBuffer = /* @__PURE__ */ new Map();
5279
+ const deleteBuffer = /* @__PURE__ */ new Set();
5280
+ let headBuffer = null;
5281
+ const ops = {
5282
+ async getNode(id) {
5283
+ const buffered = writeBuffer.get(id);
5284
+ if (buffered) return buffered;
5285
+ return await strategy.read(id);
5286
+ },
5287
+ async createNode(leaf, keys, values, parent = null, next = null, prev = null) {
5288
+ const id = await strategy.id(leaf);
5289
+ const node = { id, keys, values, leaf, parent, next, prev };
5290
+ writeBuffer.set(id, node);
5188
5291
  return node;
5189
5292
  },
5190
5293
  async updateNode(node) {
5191
- await strategy.write(node.id, node);
5294
+ writeBuffer.set(node.id, node);
5192
5295
  },
5193
5296
  async deleteNode(node) {
5194
- await strategy.delete(node.id);
5297
+ deleteBuffer.add(node.id);
5298
+ writeBuffer.delete(node.id);
5195
5299
  },
5196
5300
  async readHead() {
5301
+ if (headBuffer) return headBuffer;
5197
5302
  return await strategy.readHead();
5198
5303
  },
5199
5304
  async writeHead(head) {
5200
- await strategy.writeHead(head);
5305
+ headBuffer = head;
5201
5306
  }
5202
5307
  };
5308
+ async function flush() {
5309
+ for (const id of deleteBuffer) {
5310
+ await strategy.delete(id);
5311
+ }
5312
+ for (const [id, node] of writeBuffer) {
5313
+ await strategy.write(id, node);
5314
+ }
5315
+ if (headBuffer) {
5316
+ await strategy.writeHead(headBuffer);
5317
+ }
5318
+ }
5319
+ return { ops, flush };
5203
5320
  }
5204
- async init() {
5205
- this._ops = this._createOps();
5206
- this._ctx = {
5207
- rootId: "",
5208
- order: this.strategy.order,
5209
- headData: () => this.strategy.head.data
5321
+ async _readCtx() {
5322
+ const head = await this.strategy.readHead();
5323
+ if (head === null) {
5324
+ throw new Error("Tree not initialized. Call init() first.");
5325
+ }
5326
+ return { rootId: head.root, order: head.order };
5327
+ }
5328
+ async _createCtx() {
5329
+ const strategy = this.strategy;
5330
+ const head = await strategy.readHead();
5331
+ if (head === null) {
5332
+ throw new Error("Tree not initialized. Call init() first.");
5333
+ }
5334
+ return {
5335
+ rootId: head.root,
5336
+ order: head.order,
5337
+ headData: () => strategy.head.data
5210
5338
  };
5211
- await initOpAsync(
5212
- this._ops,
5213
- this._ctx,
5214
- this.strategy.order,
5215
- this.strategy.head,
5216
- (head) => {
5217
- this.strategy.head = head;
5218
- }
5219
- );
5220
5339
  }
5221
- getRootId() {
5222
- return this._ctx.rootId;
5340
+ async writeLock(fn) {
5341
+ let lockId;
5342
+ return this.lock.writeLock(async (_lockId) => {
5343
+ lockId = _lockId;
5344
+ return fn();
5345
+ }).finally(() => {
5346
+ this.lock.writeUnlock(lockId);
5347
+ });
5223
5348
  }
5224
- getOrder() {
5225
- return this._ctx.order;
5349
+ async init() {
5350
+ return this.writeLock(async () => {
5351
+ const { ops, flush } = this._createBufferedOps();
5352
+ const ctx = {
5353
+ rootId: "",
5354
+ order: this.strategy.order,
5355
+ headData: () => this.strategy.head.data
5356
+ };
5357
+ await initOpAsync(
5358
+ ops,
5359
+ ctx,
5360
+ this.strategy.order,
5361
+ this.strategy.head,
5362
+ (head) => {
5363
+ this.strategy.head = head;
5364
+ }
5365
+ );
5366
+ await flush();
5367
+ });
5368
+ }
5369
+ async getRootId() {
5370
+ return (await this._readCtx()).rootId;
5371
+ }
5372
+ async getOrder() {
5373
+ return (await this._readCtx()).order;
5226
5374
  }
5227
5375
  verify(nodeValue, condition) {
5228
5376
  for (const key in condition) {
@@ -5234,34 +5382,50 @@ var BPTreePureAsync = class {
5234
5382
  }
5235
5383
  // ─── Query ───────────────────────────────────────────────────────
5236
5384
  async get(key) {
5237
- return getOpAsync(this._ops, this._ctx.rootId, key);
5385
+ const { rootId } = await this._readCtx();
5386
+ return getOpAsync(this._createReadOps(), rootId, key);
5238
5387
  }
5239
5388
  async exists(key, value) {
5240
- return existsOpAsync(this._ops, this._ctx.rootId, key, value, this.comparator);
5389
+ const { rootId } = await this._readCtx();
5390
+ return existsOpAsync(this._createReadOps(), rootId, key, value, this.comparator);
5241
5391
  }
5242
5392
  async *keysStream(condition, options) {
5243
- yield* keysStreamOpAsync(
5244
- this._ops,
5245
- this._ctx.rootId,
5246
- condition,
5247
- this.comparator,
5248
- this._verifierMap,
5249
- this._searchConfigs,
5250
- this._ensureValues,
5251
- options
5252
- );
5393
+ let lockId;
5394
+ try {
5395
+ lockId = await this.lock.readLock([0, 0.1], async (id) => id);
5396
+ const { rootId } = await this._readCtx();
5397
+ yield* keysStreamOpAsync(
5398
+ this._createReadOps(),
5399
+ rootId,
5400
+ condition,
5401
+ this.comparator,
5402
+ this._verifierMap,
5403
+ this._searchConfigs,
5404
+ this._ensureValues,
5405
+ options
5406
+ );
5407
+ } finally {
5408
+ if (lockId) this.lock.readUnlock(lockId);
5409
+ }
5253
5410
  }
5254
5411
  async *whereStream(condition, options) {
5255
- yield* whereStreamOpAsync(
5256
- this._ops,
5257
- this._ctx.rootId,
5258
- condition,
5259
- this.comparator,
5260
- this._verifierMap,
5261
- this._searchConfigs,
5262
- this._ensureValues,
5263
- options
5264
- );
5412
+ let lockId;
5413
+ try {
5414
+ lockId = await this.lock.readLock([0, 0.1], async (id) => id);
5415
+ const { rootId } = await this._readCtx();
5416
+ yield* whereStreamOpAsync(
5417
+ this._createReadOps(),
5418
+ rootId,
5419
+ condition,
5420
+ this.comparator,
5421
+ this._verifierMap,
5422
+ this._searchConfigs,
5423
+ this._ensureValues,
5424
+ options
5425
+ );
5426
+ } finally {
5427
+ if (lockId) this.lock.readUnlock(lockId);
5428
+ }
5265
5429
  }
5266
5430
  async keys(condition, options) {
5267
5431
  const set = /* @__PURE__ */ new Set();
@@ -5279,27 +5443,51 @@ var BPTreePureAsync = class {
5279
5443
  }
5280
5444
  // ─── Mutation ────────────────────────────────────────────────────
5281
5445
  async insert(key, value) {
5282
- await insertOpAsync(this._ops, this._ctx, key, value, this.comparator);
5446
+ return this.writeLock(async () => {
5447
+ const { ops, flush } = this._createBufferedOps();
5448
+ const ctx = await this._createCtx();
5449
+ await insertOpAsync(ops, ctx, key, value, this.comparator);
5450
+ await flush();
5451
+ });
5283
5452
  }
5284
5453
  async delete(key, value) {
5285
- await deleteOpAsync(this._ops, this._ctx, key, this.comparator, value);
5454
+ return this.writeLock(async () => {
5455
+ const { ops, flush } = this._createBufferedOps();
5456
+ const ctx = await this._createCtx();
5457
+ await deleteOpAsync(ops, ctx, key, this.comparator, value);
5458
+ await flush();
5459
+ });
5286
5460
  }
5287
5461
  async batchInsert(entries) {
5288
- await batchInsertOpAsync(this._ops, this._ctx, entries, this.comparator);
5462
+ return this.writeLock(async () => {
5463
+ const { ops, flush } = this._createBufferedOps();
5464
+ const ctx = await this._createCtx();
5465
+ await batchInsertOpAsync(ops, ctx, entries, this.comparator);
5466
+ await flush();
5467
+ });
5289
5468
  }
5290
5469
  async bulkLoad(entries) {
5291
- await bulkLoadOpAsync(this._ops, this._ctx, entries, this.comparator);
5470
+ return this.writeLock(async () => {
5471
+ const { ops, flush } = this._createBufferedOps();
5472
+ const ctx = await this._createCtx();
5473
+ await bulkLoadOpAsync(ops, ctx, entries, this.comparator);
5474
+ await flush();
5475
+ });
5292
5476
  }
5293
5477
  // ─── Head Data ───────────────────────────────────────────────────
5294
5478
  async getHeadData() {
5295
- const head = await this._ops.readHead();
5479
+ const head = await this.strategy.readHead();
5296
5480
  if (head === null) throw new Error("Head not found");
5297
5481
  return head.data;
5298
5482
  }
5299
5483
  async setHeadData(data) {
5300
- const head = await this._ops.readHead();
5301
- if (head === null) throw new Error("Head not found");
5302
- await this._ops.writeHead({ root: head.root, order: head.order, data });
5484
+ return this.writeLock(async () => {
5485
+ const { ops, flush } = this._createBufferedOps();
5486
+ const head = await ops.readHead();
5487
+ if (head === null) throw new Error("Head not found");
5488
+ await ops.writeHead({ root: head.root, order: head.order, data });
5489
+ await flush();
5490
+ });
5303
5491
  }
5304
5492
  // ─── Static utilities ────────────────────────────────────────────
5305
5493
  static ChooseDriver = BPTreeTransaction.ChooseDriver;
@@ -4952,8 +4952,6 @@ var BPTreePureSync = class {
4952
4952
  comparator;
4953
4953
  option;
4954
4954
  _cachedRegexp = /* @__PURE__ */ new Map();
4955
- _ctx;
4956
- _ops;
4957
4955
  _verifierMap;
4958
4956
  _searchConfigs;
4959
4957
  constructor(strategy, comparator, option) {
@@ -4967,7 +4965,7 @@ var BPTreePureSync = class {
4967
4965
  _ensureValues(v) {
4968
4966
  return Array.isArray(v) ? v : [v];
4969
4967
  }
4970
- _createOps() {
4968
+ _createReadOps() {
4971
4969
  const strategy = this.strategy;
4972
4970
  return {
4973
4971
  getNode(id) {
@@ -4976,51 +4974,112 @@ var BPTreePureSync = class {
4976
4974
  createNode(leaf, keys, values, parent = null, next = null, prev = null) {
4977
4975
  const id = strategy.id(leaf);
4978
4976
  const node = { id, keys, values, leaf, parent, next, prev };
4979
- strategy.write(id, node);
4977
+ return node;
4978
+ },
4979
+ updateNode() {
4980
+ },
4981
+ deleteNode() {
4982
+ },
4983
+ readHead() {
4984
+ return strategy.readHead();
4985
+ },
4986
+ writeHead() {
4987
+ }
4988
+ };
4989
+ }
4990
+ _createBufferedOps() {
4991
+ const strategy = this.strategy;
4992
+ const writeBuffer = /* @__PURE__ */ new Map();
4993
+ const deleteBuffer = /* @__PURE__ */ new Set();
4994
+ let headBuffer = null;
4995
+ const ops = {
4996
+ getNode(id) {
4997
+ const buffered = writeBuffer.get(id);
4998
+ if (buffered) return buffered;
4999
+ return strategy.read(id);
5000
+ },
5001
+ createNode(leaf, keys, values, parent = null, next = null, prev = null) {
5002
+ const id = strategy.id(leaf);
5003
+ const node = { id, keys, values, leaf, parent, next, prev };
5004
+ writeBuffer.set(id, node);
4980
5005
  return node;
4981
5006
  },
4982
5007
  updateNode(node) {
4983
- strategy.write(node.id, node);
5008
+ writeBuffer.set(node.id, node);
4984
5009
  },
4985
5010
  deleteNode(node) {
4986
- strategy.delete(node.id);
5011
+ deleteBuffer.add(node.id);
5012
+ writeBuffer.delete(node.id);
4987
5013
  },
4988
5014
  readHead() {
5015
+ if (headBuffer) return headBuffer;
4989
5016
  return strategy.readHead();
4990
5017
  },
4991
5018
  writeHead(head) {
4992
- strategy.writeHead(head);
5019
+ headBuffer = head;
4993
5020
  }
4994
5021
  };
5022
+ function flush() {
5023
+ for (const id of deleteBuffer) {
5024
+ strategy.delete(id);
5025
+ }
5026
+ for (const [id, node] of writeBuffer) {
5027
+ strategy.write(id, node);
5028
+ }
5029
+ if (headBuffer) {
5030
+ strategy.writeHead(headBuffer);
5031
+ }
5032
+ }
5033
+ return { ops, flush };
5034
+ }
5035
+ _readCtx() {
5036
+ const head = this.strategy.readHead();
5037
+ if (head === null) {
5038
+ throw new Error("Tree not initialized. Call init() first.");
5039
+ }
5040
+ return { rootId: head.root, order: head.order };
5041
+ }
5042
+ _createCtx() {
5043
+ const strategy = this.strategy;
5044
+ const head = strategy.readHead();
5045
+ if (head === null) {
5046
+ throw new Error("Tree not initialized. Call init() first.");
5047
+ }
5048
+ return {
5049
+ rootId: head.root,
5050
+ order: head.order,
5051
+ headData: () => strategy.head.data
5052
+ };
4995
5053
  }
4996
5054
  init() {
4997
- this._ops = this._createOps();
4998
- this._ctx = {
5055
+ const { ops, flush } = this._createBufferedOps();
5056
+ const ctx = {
4999
5057
  rootId: "",
5000
5058
  order: this.strategy.order,
5001
5059
  headData: () => this.strategy.head.data
5002
5060
  };
5003
5061
  initOp(
5004
- this._ops,
5005
- this._ctx,
5062
+ ops,
5063
+ ctx,
5006
5064
  this.strategy.order,
5007
5065
  this.strategy.head,
5008
5066
  (head) => {
5009
5067
  this.strategy.head = head;
5010
5068
  }
5011
5069
  );
5070
+ flush();
5012
5071
  }
5013
5072
  /**
5014
5073
  * Returns the ID of the root node.
5015
5074
  */
5016
5075
  getRootId() {
5017
- return this._ctx.rootId;
5076
+ return this._readCtx().rootId;
5018
5077
  }
5019
5078
  /**
5020
5079
  * Returns the order of the B+Tree.
5021
5080
  */
5022
5081
  getOrder() {
5023
- return this._ctx.order;
5082
+ return this._readCtx().order;
5024
5083
  }
5025
5084
  /**
5026
5085
  * Verified if the value satisfies the condition.
@@ -5037,15 +5096,18 @@ var BPTreePureSync = class {
5037
5096
  }
5038
5097
  // ─── Query ───────────────────────────────────────────────────────
5039
5098
  get(key) {
5040
- return getOp(this._ops, this._ctx.rootId, key);
5099
+ const { rootId } = this._readCtx();
5100
+ return getOp(this._createReadOps(), rootId, key);
5041
5101
  }
5042
5102
  exists(key, value) {
5043
- return existsOp(this._ops, this._ctx.rootId, key, value, this.comparator);
5103
+ const { rootId } = this._readCtx();
5104
+ return existsOp(this._createReadOps(), rootId, key, value, this.comparator);
5044
5105
  }
5045
5106
  *keysStream(condition, options) {
5107
+ const { rootId } = this._readCtx();
5046
5108
  yield* keysStreamOp(
5047
- this._ops,
5048
- this._ctx.rootId,
5109
+ this._createReadOps(),
5110
+ rootId,
5049
5111
  condition,
5050
5112
  this.comparator,
5051
5113
  this._verifierMap,
@@ -5055,9 +5117,10 @@ var BPTreePureSync = class {
5055
5117
  );
5056
5118
  }
5057
5119
  *whereStream(condition, options) {
5120
+ const { rootId } = this._readCtx();
5058
5121
  yield* whereStreamOp(
5059
- this._ops,
5060
- this._ctx.rootId,
5122
+ this._createReadOps(),
5123
+ rootId,
5061
5124
  condition,
5062
5125
  this.comparator,
5063
5126
  this._verifierMap,
@@ -5082,35 +5145,49 @@ var BPTreePureSync = class {
5082
5145
  }
5083
5146
  // ─── Mutation ────────────────────────────────────────────────────
5084
5147
  insert(key, value) {
5085
- insertOp(this._ops, this._ctx, key, value, this.comparator);
5148
+ const { ops, flush } = this._createBufferedOps();
5149
+ const ctx = this._createCtx();
5150
+ insertOp(ops, ctx, key, value, this.comparator);
5151
+ flush();
5086
5152
  }
5087
5153
  delete(key, value) {
5088
- deleteOp(this._ops, this._ctx, key, this.comparator, value);
5154
+ const { ops, flush } = this._createBufferedOps();
5155
+ const ctx = this._createCtx();
5156
+ deleteOp(ops, ctx, key, this.comparator, value);
5157
+ flush();
5089
5158
  }
5090
5159
  batchInsert(entries) {
5091
- batchInsertOp(this._ops, this._ctx, entries, this.comparator);
5160
+ const { ops, flush } = this._createBufferedOps();
5161
+ const ctx = this._createCtx();
5162
+ batchInsertOp(ops, ctx, entries, this.comparator);
5163
+ flush();
5092
5164
  }
5093
5165
  bulkLoad(entries) {
5094
- bulkLoadOp(this._ops, this._ctx, entries, this.comparator);
5166
+ const { ops, flush } = this._createBufferedOps();
5167
+ const ctx = this._createCtx();
5168
+ bulkLoadOp(ops, ctx, entries, this.comparator);
5169
+ flush();
5095
5170
  }
5096
5171
  // ─── Head Data ───────────────────────────────────────────────────
5097
5172
  getHeadData() {
5098
- const head = this._ops.readHead();
5173
+ const head = this.strategy.readHead();
5099
5174
  if (head === null) {
5100
5175
  throw new Error("Head not found");
5101
5176
  }
5102
5177
  return head.data;
5103
5178
  }
5104
5179
  setHeadData(data) {
5105
- const head = this._ops.readHead();
5180
+ const { ops, flush } = this._createBufferedOps();
5181
+ const head = ops.readHead();
5106
5182
  if (head === null) {
5107
5183
  throw new Error("Head not found");
5108
5184
  }
5109
- this._ops.writeHead({
5185
+ ops.writeHead({
5110
5186
  root: head.root,
5111
5187
  order: head.order,
5112
5188
  data
5113
5189
  });
5190
+ flush();
5114
5191
  }
5115
5192
  // ─── Static utilities ────────────────────────────────────────────
5116
5193
  static ChooseDriver = BPTreeTransaction.ChooseDriver;
@@ -5121,9 +5198,8 @@ var BPTreePureAsync = class {
5121
5198
  strategy;
5122
5199
  comparator;
5123
5200
  option;
5201
+ lock = new Ryoiki2();
5124
5202
  _cachedRegexp = /* @__PURE__ */ new Map();
5125
- _ctx;
5126
- _ops;
5127
5203
  _verifierMap;
5128
5204
  _searchConfigs;
5129
5205
  constructor(strategy, comparator, option) {
@@ -5137,7 +5213,7 @@ var BPTreePureAsync = class {
5137
5213
  _ensureValues(v) {
5138
5214
  return Array.isArray(v) ? v : [v];
5139
5215
  }
5140
- _createOps() {
5216
+ _createReadOps() {
5141
5217
  const strategy = this.strategy;
5142
5218
  return {
5143
5219
  async getNode(id) {
@@ -5146,45 +5222,117 @@ var BPTreePureAsync = class {
5146
5222
  async createNode(leaf, keys, values, parent = null, next = null, prev = null) {
5147
5223
  const id = await strategy.id(leaf);
5148
5224
  const node = { id, keys, values, leaf, parent, next, prev };
5149
- await strategy.write(id, node);
5225
+ return node;
5226
+ },
5227
+ async updateNode() {
5228
+ },
5229
+ async deleteNode() {
5230
+ },
5231
+ async readHead() {
5232
+ return await strategy.readHead();
5233
+ },
5234
+ async writeHead() {
5235
+ }
5236
+ };
5237
+ }
5238
+ _createBufferedOps() {
5239
+ const strategy = this.strategy;
5240
+ const writeBuffer = /* @__PURE__ */ new Map();
5241
+ const deleteBuffer = /* @__PURE__ */ new Set();
5242
+ let headBuffer = null;
5243
+ const ops = {
5244
+ async getNode(id) {
5245
+ const buffered = writeBuffer.get(id);
5246
+ if (buffered) return buffered;
5247
+ return await strategy.read(id);
5248
+ },
5249
+ async createNode(leaf, keys, values, parent = null, next = null, prev = null) {
5250
+ const id = await strategy.id(leaf);
5251
+ const node = { id, keys, values, leaf, parent, next, prev };
5252
+ writeBuffer.set(id, node);
5150
5253
  return node;
5151
5254
  },
5152
5255
  async updateNode(node) {
5153
- await strategy.write(node.id, node);
5256
+ writeBuffer.set(node.id, node);
5154
5257
  },
5155
5258
  async deleteNode(node) {
5156
- await strategy.delete(node.id);
5259
+ deleteBuffer.add(node.id);
5260
+ writeBuffer.delete(node.id);
5157
5261
  },
5158
5262
  async readHead() {
5263
+ if (headBuffer) return headBuffer;
5159
5264
  return await strategy.readHead();
5160
5265
  },
5161
5266
  async writeHead(head) {
5162
- await strategy.writeHead(head);
5267
+ headBuffer = head;
5163
5268
  }
5164
5269
  };
5270
+ async function flush() {
5271
+ for (const id of deleteBuffer) {
5272
+ await strategy.delete(id);
5273
+ }
5274
+ for (const [id, node] of writeBuffer) {
5275
+ await strategy.write(id, node);
5276
+ }
5277
+ if (headBuffer) {
5278
+ await strategy.writeHead(headBuffer);
5279
+ }
5280
+ }
5281
+ return { ops, flush };
5165
5282
  }
5166
- async init() {
5167
- this._ops = this._createOps();
5168
- this._ctx = {
5169
- rootId: "",
5170
- order: this.strategy.order,
5171
- headData: () => this.strategy.head.data
5283
+ async _readCtx() {
5284
+ const head = await this.strategy.readHead();
5285
+ if (head === null) {
5286
+ throw new Error("Tree not initialized. Call init() first.");
5287
+ }
5288
+ return { rootId: head.root, order: head.order };
5289
+ }
5290
+ async _createCtx() {
5291
+ const strategy = this.strategy;
5292
+ const head = await strategy.readHead();
5293
+ if (head === null) {
5294
+ throw new Error("Tree not initialized. Call init() first.");
5295
+ }
5296
+ return {
5297
+ rootId: head.root,
5298
+ order: head.order,
5299
+ headData: () => strategy.head.data
5172
5300
  };
5173
- await initOpAsync(
5174
- this._ops,
5175
- this._ctx,
5176
- this.strategy.order,
5177
- this.strategy.head,
5178
- (head) => {
5179
- this.strategy.head = head;
5180
- }
5181
- );
5182
5301
  }
5183
- getRootId() {
5184
- return this._ctx.rootId;
5302
+ async writeLock(fn) {
5303
+ let lockId;
5304
+ return this.lock.writeLock(async (_lockId) => {
5305
+ lockId = _lockId;
5306
+ return fn();
5307
+ }).finally(() => {
5308
+ this.lock.writeUnlock(lockId);
5309
+ });
5185
5310
  }
5186
- getOrder() {
5187
- return this._ctx.order;
5311
+ async init() {
5312
+ return this.writeLock(async () => {
5313
+ const { ops, flush } = this._createBufferedOps();
5314
+ const ctx = {
5315
+ rootId: "",
5316
+ order: this.strategy.order,
5317
+ headData: () => this.strategy.head.data
5318
+ };
5319
+ await initOpAsync(
5320
+ ops,
5321
+ ctx,
5322
+ this.strategy.order,
5323
+ this.strategy.head,
5324
+ (head) => {
5325
+ this.strategy.head = head;
5326
+ }
5327
+ );
5328
+ await flush();
5329
+ });
5330
+ }
5331
+ async getRootId() {
5332
+ return (await this._readCtx()).rootId;
5333
+ }
5334
+ async getOrder() {
5335
+ return (await this._readCtx()).order;
5188
5336
  }
5189
5337
  verify(nodeValue, condition) {
5190
5338
  for (const key in condition) {
@@ -5196,34 +5344,50 @@ var BPTreePureAsync = class {
5196
5344
  }
5197
5345
  // ─── Query ───────────────────────────────────────────────────────
5198
5346
  async get(key) {
5199
- return getOpAsync(this._ops, this._ctx.rootId, key);
5347
+ const { rootId } = await this._readCtx();
5348
+ return getOpAsync(this._createReadOps(), rootId, key);
5200
5349
  }
5201
5350
  async exists(key, value) {
5202
- return existsOpAsync(this._ops, this._ctx.rootId, key, value, this.comparator);
5351
+ const { rootId } = await this._readCtx();
5352
+ return existsOpAsync(this._createReadOps(), rootId, key, value, this.comparator);
5203
5353
  }
5204
5354
  async *keysStream(condition, options) {
5205
- yield* keysStreamOpAsync(
5206
- this._ops,
5207
- this._ctx.rootId,
5208
- condition,
5209
- this.comparator,
5210
- this._verifierMap,
5211
- this._searchConfigs,
5212
- this._ensureValues,
5213
- options
5214
- );
5355
+ let lockId;
5356
+ try {
5357
+ lockId = await this.lock.readLock([0, 0.1], async (id) => id);
5358
+ const { rootId } = await this._readCtx();
5359
+ yield* keysStreamOpAsync(
5360
+ this._createReadOps(),
5361
+ rootId,
5362
+ condition,
5363
+ this.comparator,
5364
+ this._verifierMap,
5365
+ this._searchConfigs,
5366
+ this._ensureValues,
5367
+ options
5368
+ );
5369
+ } finally {
5370
+ if (lockId) this.lock.readUnlock(lockId);
5371
+ }
5215
5372
  }
5216
5373
  async *whereStream(condition, options) {
5217
- yield* whereStreamOpAsync(
5218
- this._ops,
5219
- this._ctx.rootId,
5220
- condition,
5221
- this.comparator,
5222
- this._verifierMap,
5223
- this._searchConfigs,
5224
- this._ensureValues,
5225
- options
5226
- );
5374
+ let lockId;
5375
+ try {
5376
+ lockId = await this.lock.readLock([0, 0.1], async (id) => id);
5377
+ const { rootId } = await this._readCtx();
5378
+ yield* whereStreamOpAsync(
5379
+ this._createReadOps(),
5380
+ rootId,
5381
+ condition,
5382
+ this.comparator,
5383
+ this._verifierMap,
5384
+ this._searchConfigs,
5385
+ this._ensureValues,
5386
+ options
5387
+ );
5388
+ } finally {
5389
+ if (lockId) this.lock.readUnlock(lockId);
5390
+ }
5227
5391
  }
5228
5392
  async keys(condition, options) {
5229
5393
  const set = /* @__PURE__ */ new Set();
@@ -5241,27 +5405,51 @@ var BPTreePureAsync = class {
5241
5405
  }
5242
5406
  // ─── Mutation ────────────────────────────────────────────────────
5243
5407
  async insert(key, value) {
5244
- await insertOpAsync(this._ops, this._ctx, key, value, this.comparator);
5408
+ return this.writeLock(async () => {
5409
+ const { ops, flush } = this._createBufferedOps();
5410
+ const ctx = await this._createCtx();
5411
+ await insertOpAsync(ops, ctx, key, value, this.comparator);
5412
+ await flush();
5413
+ });
5245
5414
  }
5246
5415
  async delete(key, value) {
5247
- await deleteOpAsync(this._ops, this._ctx, key, this.comparator, value);
5416
+ return this.writeLock(async () => {
5417
+ const { ops, flush } = this._createBufferedOps();
5418
+ const ctx = await this._createCtx();
5419
+ await deleteOpAsync(ops, ctx, key, this.comparator, value);
5420
+ await flush();
5421
+ });
5248
5422
  }
5249
5423
  async batchInsert(entries) {
5250
- await batchInsertOpAsync(this._ops, this._ctx, entries, this.comparator);
5424
+ return this.writeLock(async () => {
5425
+ const { ops, flush } = this._createBufferedOps();
5426
+ const ctx = await this._createCtx();
5427
+ await batchInsertOpAsync(ops, ctx, entries, this.comparator);
5428
+ await flush();
5429
+ });
5251
5430
  }
5252
5431
  async bulkLoad(entries) {
5253
- await bulkLoadOpAsync(this._ops, this._ctx, entries, this.comparator);
5432
+ return this.writeLock(async () => {
5433
+ const { ops, flush } = this._createBufferedOps();
5434
+ const ctx = await this._createCtx();
5435
+ await bulkLoadOpAsync(ops, ctx, entries, this.comparator);
5436
+ await flush();
5437
+ });
5254
5438
  }
5255
5439
  // ─── Head Data ───────────────────────────────────────────────────
5256
5440
  async getHeadData() {
5257
- const head = await this._ops.readHead();
5441
+ const head = await this.strategy.readHead();
5258
5442
  if (head === null) throw new Error("Head not found");
5259
5443
  return head.data;
5260
5444
  }
5261
5445
  async setHeadData(data) {
5262
- const head = await this._ops.readHead();
5263
- if (head === null) throw new Error("Head not found");
5264
- 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
+ });
5265
5453
  }
5266
5454
  // ─── Static utilities ────────────────────────────────────────────
5267
5455
  static ChooseDriver = BPTreeTransaction.ChooseDriver;
@@ -2,21 +2,25 @@ import type { BPTreeCondition, BPTreeConstructorOption, BPTreePair, BPTreeSearch
2
2
  import { SerializeStrategyAsync } from './SerializeStrategyAsync';
3
3
  import { ValueComparator } from './base/ValueComparator';
4
4
  import { BPTreeTransaction } from './base/BPTreeTransaction';
5
+ import { Ryoiki } from 'ryoiki';
5
6
  export declare class BPTreePureAsync<K, V> {
6
7
  protected readonly strategy: SerializeStrategyAsync<K, V>;
7
8
  protected readonly comparator: ValueComparator<V>;
8
9
  protected readonly option: BPTreeConstructorOption;
10
+ protected readonly lock: Ryoiki;
9
11
  private readonly _cachedRegexp;
10
- private _ctx;
11
- private _ops;
12
12
  private readonly _verifierMap;
13
13
  private readonly _searchConfigs;
14
14
  constructor(strategy: SerializeStrategyAsync<K, V>, comparator: ValueComparator<V>, option?: BPTreeConstructorOption);
15
15
  private _ensureValues;
16
- private _createOps;
16
+ private _createReadOps;
17
+ private _createBufferedOps;
18
+ private _readCtx;
19
+ private _createCtx;
20
+ protected writeLock<T>(fn: () => Promise<T>): Promise<T>;
17
21
  init(): Promise<void>;
18
- getRootId(): string;
19
- getOrder(): number;
22
+ getRootId(): Promise<string>;
23
+ getOrder(): Promise<number>;
20
24
  verify(nodeValue: V, condition: BPTreeCondition<V>): boolean;
21
25
  get(key: K): Promise<V | undefined>;
22
26
  exists(key: K, value: V): Promise<boolean>;
@@ -7,13 +7,14 @@ export declare class BPTreePureSync<K, V> {
7
7
  protected readonly comparator: ValueComparator<V>;
8
8
  protected readonly option: BPTreeConstructorOption;
9
9
  private readonly _cachedRegexp;
10
- private _ctx;
11
- private _ops;
12
10
  private readonly _verifierMap;
13
11
  private readonly _searchConfigs;
14
12
  constructor(strategy: SerializeStrategySync<K, V>, comparator: ValueComparator<V>, option?: BPTreeConstructorOption);
15
13
  private _ensureValues;
16
- private _createOps;
14
+ private _createReadOps;
15
+ private _createBufferedOps;
16
+ private _readCtx;
17
+ private _createCtx;
17
18
  init(): void;
18
19
  /**
19
20
  * Returns the ID of the root node.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serializable-bptree",
3
- "version": "9.0.0",
3
+ "version": "9.0.2",
4
4
  "description": "Store the B+tree flexibly, not only in-memory.",
5
5
  "types": "./dist/types/index.d.ts",
6
6
  "main": "./dist/cjs/index.cjs",