mvcc-api 1.2.10 → 1.3.0

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.
@@ -163,6 +163,47 @@ var MVCCTransaction = class {
163
163
  }
164
164
  return { success: true, created, updated, deleted };
165
165
  }
166
+ /**
167
+ * Cleans up both deletedCache and versionIndex based on minActiveVersion.
168
+ * Root transactions call this after commit to reclaim memory.
169
+ */
170
+ _cleanupDeletedCache() {
171
+ if (this.deletedCache.size === 0 && this.versionIndex.size === 0) return;
172
+ let minActiveVersion = this.version;
173
+ if (this.activeTransactions.size > 0) {
174
+ for (const tx of this.activeTransactions) {
175
+ if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
176
+ minActiveVersion = tx.snapshotVersion;
177
+ }
178
+ }
179
+ }
180
+ if (this.deletedCache.size > 0) {
181
+ for (const [key, cachedList] of this.deletedCache) {
182
+ const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
183
+ if (remaining.length === 0) {
184
+ this.deletedCache.delete(key);
185
+ } else {
186
+ this.deletedCache.set(key, remaining);
187
+ }
188
+ }
189
+ }
190
+ if (this.versionIndex.size > 0) {
191
+ for (const [key, versions] of this.versionIndex) {
192
+ let latestInSnapshotIdx = -1;
193
+ for (let i = versions.length - 1; i >= 0; i--) {
194
+ if (versions[i].version <= minActiveVersion) {
195
+ latestInSnapshotIdx = i;
196
+ break;
197
+ }
198
+ }
199
+ if (latestInSnapshotIdx === versions.length - 1) {
200
+ this.versionIndex.delete(key);
201
+ } else if (latestInSnapshotIdx > 0) {
202
+ versions.splice(0, latestInSnapshotIdx);
203
+ }
204
+ }
205
+ }
206
+ }
166
207
  };
167
208
 
168
209
  // src/core/sync/Strategy.ts
@@ -226,6 +267,25 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
226
267
  if (this.writeBuffer.has(key)) return true;
227
268
  return this.root._diskExists(key, this.snapshotVersion);
228
269
  }
270
+ _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
271
+ if (this.writeBuffer.has(key)) {
272
+ const keyModVersion = this.keyVersions.get(key);
273
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
274
+ return true;
275
+ }
276
+ }
277
+ if (this.deleteBuffer.has(key)) {
278
+ const keyModVersion = this.keyVersions.get(key);
279
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
280
+ return false;
281
+ }
282
+ }
283
+ if (this.parent) {
284
+ return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
285
+ } else {
286
+ return this._diskExists(key, snapshotVersion);
287
+ }
288
+ }
229
289
  _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
230
290
  if (this.writeBuffer.has(key)) {
231
291
  const keyModVersion = this.keyVersions.get(key);
@@ -368,7 +428,6 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
368
428
  this.localVersion = newLocalVersion;
369
429
  this.root.activeTransactions.delete(child);
370
430
  } else {
371
- const newVersion = this.version + 1;
372
431
  if (child !== this) {
373
432
  const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
374
433
  for (const key of modifiedKeys) {
@@ -387,35 +446,70 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
387
446
  }
388
447
  }
389
448
  }
390
- }
391
- for (const [key, value] of child.writeBuffer) {
392
- this.writeBuffer.set(key, value);
393
- this.deleteBuffer.delete(key);
394
- if (child.createdKeys.has(key)) {
395
- this.createdKeys.add(key);
449
+ const newVersion = this.version + 1;
450
+ for (const [key, value] of child.writeBuffer) {
451
+ if (this.writeBuffer.has(key)) {
452
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
453
+ this.deletedCache.get(key).push({
454
+ value: this.writeBuffer.get(key),
455
+ deletedAtVersion: newVersion
456
+ });
457
+ } else if (this.strategy.exists(key)) {
458
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
459
+ this.deletedCache.get(key).push({
460
+ value: this.strategy.read(key),
461
+ deletedAtVersion: newVersion
462
+ });
463
+ }
464
+ this.writeBuffer.set(key, value);
465
+ this.deleteBuffer.delete(key);
466
+ if (child.createdKeys.has(key)) {
467
+ this.createdKeys.add(key);
468
+ }
469
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
470
+ this.versionIndex.get(key).push({ version: newVersion, exists: true });
396
471
  }
397
- }
398
- for (const key of child.deleteBuffer) {
399
- this.deleteBuffer.add(key);
400
- this.writeBuffer.delete(key);
401
- this.createdKeys.delete(key);
402
- const deletedValue = child.deletedValues.get(key);
403
- if (deletedValue !== void 0) {
404
- this.deletedValues.set(key, deletedValue);
472
+ for (const key of child.deleteBuffer) {
473
+ if (this.writeBuffer.has(key)) {
474
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
475
+ this.deletedCache.get(key).push({
476
+ value: this.writeBuffer.get(key),
477
+ deletedAtVersion: newVersion
478
+ });
479
+ } else if (this.strategy.exists(key)) {
480
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
481
+ this.deletedCache.get(key).push({
482
+ value: this.strategy.read(key),
483
+ deletedAtVersion: newVersion
484
+ });
485
+ }
486
+ this.deleteBuffer.add(key);
487
+ this.writeBuffer.delete(key);
488
+ this.createdKeys.delete(key);
489
+ const deletedValue = child.deletedValues.get(key);
490
+ if (deletedValue !== void 0) {
491
+ this.deletedValues.set(key, deletedValue);
492
+ }
493
+ if (child.originallyExisted.has(key)) {
494
+ this.originallyExisted.add(key);
495
+ }
496
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
497
+ this.versionIndex.get(key).push({ version: newVersion, exists: false });
405
498
  }
406
- if (child.originallyExisted.has(key)) {
407
- this.originallyExisted.add(key);
499
+ this.version = newVersion;
500
+ this.root.activeTransactions.delete(child);
501
+ this._cleanupDeletedCache();
502
+ } else {
503
+ const newVersion = this.version + 1;
504
+ for (const [key, value] of this.writeBuffer) {
505
+ this._diskWrite(key, value, newVersion);
408
506
  }
507
+ for (const key of this.deleteBuffer) {
508
+ this._diskDelete(key, newVersion);
509
+ }
510
+ this.version = newVersion;
511
+ this._cleanupDeletedCache();
409
512
  }
410
- for (const [key, value] of child.writeBuffer) {
411
- this._diskWrite(key, value, newVersion);
412
- }
413
- for (const key of child.deleteBuffer) {
414
- this._diskDelete(key, newVersion);
415
- }
416
- this.version = newVersion;
417
- this.root.activeTransactions.delete(child);
418
- this._cleanupDeletedCache();
419
513
  }
420
514
  return null;
421
515
  }
@@ -440,6 +534,8 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
440
534
  if (!strategy) throw new Error("Root Transaction missing strategy");
441
535
  const versions = this.versionIndex.get(key);
442
536
  if (!versions) {
537
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
538
+ if (this.deleteBuffer.has(key)) return null;
443
539
  return strategy.exists(key) ? strategy.read(key) : null;
444
540
  }
445
541
  let targetVerObj = null;
@@ -460,10 +556,11 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
460
556
  if (match) return match.value;
461
557
  }
462
558
  }
463
- return null;
559
+ return strategy.exists(key) ? strategy.read(key) : null;
464
560
  }
465
561
  if (!targetVerObj.exists) return null;
466
562
  if (!nextVerObj) {
563
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
467
564
  return strategy.read(key);
468
565
  }
469
566
  const cached = this.deletedCache.get(key);
@@ -478,6 +575,8 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
478
575
  if (!strategy) throw new Error("Root Transaction missing strategy");
479
576
  const versions = this.versionIndex.get(key);
480
577
  if (!versions) {
578
+ if (this.writeBuffer.has(key)) return true;
579
+ if (this.deleteBuffer.has(key)) return false;
481
580
  return strategy.exists(key);
482
581
  }
483
582
  let targetVerObj = null;
@@ -501,30 +600,11 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
501
600
  value: currentVal,
502
601
  deletedAtVersion: snapshotVersion
503
602
  });
603
+ strategy.delete(key);
504
604
  }
505
- strategy.delete(key);
506
605
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
507
606
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
508
607
  }
509
- _cleanupDeletedCache() {
510
- if (this.deletedCache.size === 0) return;
511
- let minActiveVersion = this.version;
512
- if (this.activeTransactions.size > 0) {
513
- for (const tx of this.activeTransactions) {
514
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
515
- minActiveVersion = tx.snapshotVersion;
516
- }
517
- }
518
- }
519
- for (const [key, cachedList] of this.deletedCache) {
520
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
521
- if (remaining.length === 0) {
522
- this.deletedCache.delete(key);
523
- } else {
524
- this.deletedCache.set(key, remaining);
525
- }
526
- }
527
- }
528
608
  };
529
609
 
530
610
  // src/core/async/Strategy.ts
@@ -856,6 +936,25 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
856
936
  if (this.writeBuffer.has(key)) return true;
857
937
  return this.root._diskExists(key, this.snapshotVersion);
858
938
  }
939
+ async _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
940
+ if (this.writeBuffer.has(key)) {
941
+ const keyModVersion = this.keyVersions.get(key);
942
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
943
+ return true;
944
+ }
945
+ }
946
+ if (this.deleteBuffer.has(key)) {
947
+ const keyModVersion = this.keyVersions.get(key);
948
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
949
+ return false;
950
+ }
951
+ }
952
+ if (this.parent) {
953
+ return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
954
+ } else {
955
+ return this._diskExists(key, snapshotVersion);
956
+ }
957
+ }
859
958
  async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
860
959
  if (this.writeBuffer.has(key)) {
861
960
  const keyModVersion = this.keyVersions.get(key);
@@ -1000,7 +1099,6 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1000
1099
  this.root.activeTransactions.delete(child);
1001
1100
  return null;
1002
1101
  } else {
1003
- const newVersion = this.version + 1;
1004
1102
  if (child !== this) {
1005
1103
  const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
1006
1104
  for (const key of modifiedKeys) {
@@ -1019,35 +1117,70 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1019
1117
  }
1020
1118
  }
1021
1119
  }
1022
- }
1023
- for (const [key, value] of child.writeBuffer) {
1024
- this.writeBuffer.set(key, value);
1025
- this.deleteBuffer.delete(key);
1026
- if (child.createdKeys.has(key)) {
1027
- this.createdKeys.add(key);
1120
+ const newVersion = this.version + 1;
1121
+ for (const [key, value] of child.writeBuffer) {
1122
+ if (this.writeBuffer.has(key)) {
1123
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
1124
+ this.deletedCache.get(key).push({
1125
+ value: this.writeBuffer.get(key),
1126
+ deletedAtVersion: newVersion
1127
+ });
1128
+ } else if (await this.strategy.exists(key)) {
1129
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
1130
+ this.deletedCache.get(key).push({
1131
+ value: await this.strategy.read(key),
1132
+ deletedAtVersion: newVersion
1133
+ });
1134
+ }
1135
+ this.writeBuffer.set(key, value);
1136
+ this.deleteBuffer.delete(key);
1137
+ if (child.createdKeys.has(key)) {
1138
+ this.createdKeys.add(key);
1139
+ }
1140
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
1141
+ this.versionIndex.get(key).push({ version: newVersion, exists: true });
1028
1142
  }
1029
- }
1030
- for (const key of child.deleteBuffer) {
1031
- this.deleteBuffer.add(key);
1032
- this.writeBuffer.delete(key);
1033
- this.createdKeys.delete(key);
1034
- const deletedValue = child.deletedValues.get(key);
1035
- if (deletedValue !== void 0) {
1036
- this.deletedValues.set(key, deletedValue);
1143
+ for (const key of child.deleteBuffer) {
1144
+ if (this.writeBuffer.has(key)) {
1145
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
1146
+ this.deletedCache.get(key).push({
1147
+ value: this.writeBuffer.get(key),
1148
+ deletedAtVersion: newVersion
1149
+ });
1150
+ } else if (await this.strategy.exists(key)) {
1151
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
1152
+ this.deletedCache.get(key).push({
1153
+ value: await this.strategy.read(key),
1154
+ deletedAtVersion: newVersion
1155
+ });
1156
+ }
1157
+ this.deleteBuffer.add(key);
1158
+ this.writeBuffer.delete(key);
1159
+ this.createdKeys.delete(key);
1160
+ const deletedValue = child.deletedValues.get(key);
1161
+ if (deletedValue !== void 0) {
1162
+ this.deletedValues.set(key, deletedValue);
1163
+ }
1164
+ if (child.originallyExisted.has(key)) {
1165
+ this.originallyExisted.add(key);
1166
+ }
1167
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
1168
+ this.versionIndex.get(key).push({ version: newVersion, exists: false });
1037
1169
  }
1038
- if (child.originallyExisted.has(key)) {
1039
- this.originallyExisted.add(key);
1170
+ this.version = newVersion;
1171
+ this.root.activeTransactions.delete(child);
1172
+ this._cleanupDeletedCache();
1173
+ } else {
1174
+ const newVersion = this.version + 1;
1175
+ for (const [key, value] of this.writeBuffer) {
1176
+ await this._diskWrite(key, value, newVersion);
1040
1177
  }
1178
+ for (const key of this.deleteBuffer) {
1179
+ await this._diskDelete(key, newVersion);
1180
+ }
1181
+ this.version = newVersion;
1182
+ this._cleanupDeletedCache();
1041
1183
  }
1042
- for (const [key, value] of child.writeBuffer) {
1043
- await this._diskWrite(key, value, newVersion);
1044
- }
1045
- for (const key of child.deleteBuffer) {
1046
- await this._diskDelete(key, newVersion);
1047
- }
1048
- this.version = newVersion;
1049
- this.root.activeTransactions.delete(child);
1050
- this._cleanupDeletedCache();
1051
1184
  return null;
1052
1185
  }
1053
1186
  });
@@ -1073,6 +1206,8 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1073
1206
  if (!strategy) throw new Error("Root Transaction missing strategy");
1074
1207
  const versions = this.versionIndex.get(key);
1075
1208
  if (!versions) {
1209
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
1210
+ if (this.deleteBuffer.has(key)) return null;
1076
1211
  return await strategy.exists(key) ? strategy.read(key) : null;
1077
1212
  }
1078
1213
  let targetVerObj = null;
@@ -1093,10 +1228,11 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1093
1228
  if (match) return match.value;
1094
1229
  }
1095
1230
  }
1096
- return null;
1231
+ return await strategy.exists(key) ? strategy.read(key) : null;
1097
1232
  }
1098
1233
  if (!targetVerObj.exists) return null;
1099
1234
  if (!nextVerObj) {
1235
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
1100
1236
  return strategy.read(key);
1101
1237
  }
1102
1238
  const cached = this.deletedCache.get(key);
@@ -1111,6 +1247,8 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1111
1247
  if (!strategy) throw new Error("Root Transaction missing strategy");
1112
1248
  const versions = this.versionIndex.get(key);
1113
1249
  if (!versions) {
1250
+ if (this.writeBuffer.has(key)) return true;
1251
+ if (this.deleteBuffer.has(key)) return false;
1114
1252
  return strategy.exists(key);
1115
1253
  }
1116
1254
  let targetVerObj = null;
@@ -1134,28 +1272,9 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1134
1272
  value: currentVal,
1135
1273
  deletedAtVersion: snapshotVersion
1136
1274
  });
1275
+ await strategy.delete(key);
1137
1276
  }
1138
- await strategy.delete(key);
1139
1277
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
1140
1278
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
1141
1279
  }
1142
- _cleanupDeletedCache() {
1143
- if (this.deletedCache.size === 0) return;
1144
- let minActiveVersion = this.version;
1145
- if (this.activeTransactions.size > 0) {
1146
- for (const tx of this.activeTransactions) {
1147
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
1148
- minActiveVersion = tx.snapshotVersion;
1149
- }
1150
- }
1151
- }
1152
- for (const [key, cachedList] of this.deletedCache) {
1153
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
1154
- if (remaining.length === 0) {
1155
- this.deletedCache.delete(key);
1156
- } else {
1157
- this.deletedCache.set(key, remaining);
1158
- }
1159
- }
1160
- }
1161
1280
  };
@@ -132,6 +132,47 @@ var MVCCTransaction = class {
132
132
  }
133
133
  return { success: true, created, updated, deleted };
134
134
  }
135
+ /**
136
+ * Cleans up both deletedCache and versionIndex based on minActiveVersion.
137
+ * Root transactions call this after commit to reclaim memory.
138
+ */
139
+ _cleanupDeletedCache() {
140
+ if (this.deletedCache.size === 0 && this.versionIndex.size === 0) return;
141
+ let minActiveVersion = this.version;
142
+ if (this.activeTransactions.size > 0) {
143
+ for (const tx of this.activeTransactions) {
144
+ if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
145
+ minActiveVersion = tx.snapshotVersion;
146
+ }
147
+ }
148
+ }
149
+ if (this.deletedCache.size > 0) {
150
+ for (const [key, cachedList] of this.deletedCache) {
151
+ const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
152
+ if (remaining.length === 0) {
153
+ this.deletedCache.delete(key);
154
+ } else {
155
+ this.deletedCache.set(key, remaining);
156
+ }
157
+ }
158
+ }
159
+ if (this.versionIndex.size > 0) {
160
+ for (const [key, versions] of this.versionIndex) {
161
+ let latestInSnapshotIdx = -1;
162
+ for (let i = versions.length - 1; i >= 0; i--) {
163
+ if (versions[i].version <= minActiveVersion) {
164
+ latestInSnapshotIdx = i;
165
+ break;
166
+ }
167
+ }
168
+ if (latestInSnapshotIdx === versions.length - 1) {
169
+ this.versionIndex.delete(key);
170
+ } else if (latestInSnapshotIdx > 0) {
171
+ versions.splice(0, latestInSnapshotIdx);
172
+ }
173
+ }
174
+ }
175
+ }
135
176
  };
136
177
 
137
178
  // src/core/sync/Strategy.ts
@@ -195,6 +236,25 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
195
236
  if (this.writeBuffer.has(key)) return true;
196
237
  return this.root._diskExists(key, this.snapshotVersion);
197
238
  }
239
+ _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
240
+ if (this.writeBuffer.has(key)) {
241
+ const keyModVersion = this.keyVersions.get(key);
242
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
243
+ return true;
244
+ }
245
+ }
246
+ if (this.deleteBuffer.has(key)) {
247
+ const keyModVersion = this.keyVersions.get(key);
248
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
249
+ return false;
250
+ }
251
+ }
252
+ if (this.parent) {
253
+ return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
254
+ } else {
255
+ return this._diskExists(key, snapshotVersion);
256
+ }
257
+ }
198
258
  _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
199
259
  if (this.writeBuffer.has(key)) {
200
260
  const keyModVersion = this.keyVersions.get(key);
@@ -337,7 +397,6 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
337
397
  this.localVersion = newLocalVersion;
338
398
  this.root.activeTransactions.delete(child);
339
399
  } else {
340
- const newVersion = this.version + 1;
341
400
  if (child !== this) {
342
401
  const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
343
402
  for (const key of modifiedKeys) {
@@ -356,35 +415,70 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
356
415
  }
357
416
  }
358
417
  }
359
- }
360
- for (const [key, value] of child.writeBuffer) {
361
- this.writeBuffer.set(key, value);
362
- this.deleteBuffer.delete(key);
363
- if (child.createdKeys.has(key)) {
364
- this.createdKeys.add(key);
418
+ const newVersion = this.version + 1;
419
+ for (const [key, value] of child.writeBuffer) {
420
+ if (this.writeBuffer.has(key)) {
421
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
422
+ this.deletedCache.get(key).push({
423
+ value: this.writeBuffer.get(key),
424
+ deletedAtVersion: newVersion
425
+ });
426
+ } else if (this.strategy.exists(key)) {
427
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
428
+ this.deletedCache.get(key).push({
429
+ value: this.strategy.read(key),
430
+ deletedAtVersion: newVersion
431
+ });
432
+ }
433
+ this.writeBuffer.set(key, value);
434
+ this.deleteBuffer.delete(key);
435
+ if (child.createdKeys.has(key)) {
436
+ this.createdKeys.add(key);
437
+ }
438
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
439
+ this.versionIndex.get(key).push({ version: newVersion, exists: true });
365
440
  }
366
- }
367
- for (const key of child.deleteBuffer) {
368
- this.deleteBuffer.add(key);
369
- this.writeBuffer.delete(key);
370
- this.createdKeys.delete(key);
371
- const deletedValue = child.deletedValues.get(key);
372
- if (deletedValue !== void 0) {
373
- this.deletedValues.set(key, deletedValue);
441
+ for (const key of child.deleteBuffer) {
442
+ if (this.writeBuffer.has(key)) {
443
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
444
+ this.deletedCache.get(key).push({
445
+ value: this.writeBuffer.get(key),
446
+ deletedAtVersion: newVersion
447
+ });
448
+ } else if (this.strategy.exists(key)) {
449
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
450
+ this.deletedCache.get(key).push({
451
+ value: this.strategy.read(key),
452
+ deletedAtVersion: newVersion
453
+ });
454
+ }
455
+ this.deleteBuffer.add(key);
456
+ this.writeBuffer.delete(key);
457
+ this.createdKeys.delete(key);
458
+ const deletedValue = child.deletedValues.get(key);
459
+ if (deletedValue !== void 0) {
460
+ this.deletedValues.set(key, deletedValue);
461
+ }
462
+ if (child.originallyExisted.has(key)) {
463
+ this.originallyExisted.add(key);
464
+ }
465
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
466
+ this.versionIndex.get(key).push({ version: newVersion, exists: false });
374
467
  }
375
- if (child.originallyExisted.has(key)) {
376
- this.originallyExisted.add(key);
468
+ this.version = newVersion;
469
+ this.root.activeTransactions.delete(child);
470
+ this._cleanupDeletedCache();
471
+ } else {
472
+ const newVersion = this.version + 1;
473
+ for (const [key, value] of this.writeBuffer) {
474
+ this._diskWrite(key, value, newVersion);
377
475
  }
476
+ for (const key of this.deleteBuffer) {
477
+ this._diskDelete(key, newVersion);
478
+ }
479
+ this.version = newVersion;
480
+ this._cleanupDeletedCache();
378
481
  }
379
- for (const [key, value] of child.writeBuffer) {
380
- this._diskWrite(key, value, newVersion);
381
- }
382
- for (const key of child.deleteBuffer) {
383
- this._diskDelete(key, newVersion);
384
- }
385
- this.version = newVersion;
386
- this.root.activeTransactions.delete(child);
387
- this._cleanupDeletedCache();
388
482
  }
389
483
  return null;
390
484
  }
@@ -409,6 +503,8 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
409
503
  if (!strategy) throw new Error("Root Transaction missing strategy");
410
504
  const versions = this.versionIndex.get(key);
411
505
  if (!versions) {
506
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
507
+ if (this.deleteBuffer.has(key)) return null;
412
508
  return strategy.exists(key) ? strategy.read(key) : null;
413
509
  }
414
510
  let targetVerObj = null;
@@ -429,10 +525,11 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
429
525
  if (match) return match.value;
430
526
  }
431
527
  }
432
- return null;
528
+ return strategy.exists(key) ? strategy.read(key) : null;
433
529
  }
434
530
  if (!targetVerObj.exists) return null;
435
531
  if (!nextVerObj) {
532
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
436
533
  return strategy.read(key);
437
534
  }
438
535
  const cached = this.deletedCache.get(key);
@@ -447,6 +544,8 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
447
544
  if (!strategy) throw new Error("Root Transaction missing strategy");
448
545
  const versions = this.versionIndex.get(key);
449
546
  if (!versions) {
547
+ if (this.writeBuffer.has(key)) return true;
548
+ if (this.deleteBuffer.has(key)) return false;
450
549
  return strategy.exists(key);
451
550
  }
452
551
  let targetVerObj = null;
@@ -470,30 +569,11 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
470
569
  value: currentVal,
471
570
  deletedAtVersion: snapshotVersion
472
571
  });
572
+ strategy.delete(key);
473
573
  }
474
- strategy.delete(key);
475
574
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
476
575
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
477
576
  }
478
- _cleanupDeletedCache() {
479
- if (this.deletedCache.size === 0) return;
480
- let minActiveVersion = this.version;
481
- if (this.activeTransactions.size > 0) {
482
- for (const tx of this.activeTransactions) {
483
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
484
- minActiveVersion = tx.snapshotVersion;
485
- }
486
- }
487
- }
488
- for (const [key, cachedList] of this.deletedCache) {
489
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
490
- if (remaining.length === 0) {
491
- this.deletedCache.delete(key);
492
- } else {
493
- this.deletedCache.set(key, remaining);
494
- }
495
- }
496
- }
497
577
  };
498
578
 
499
579
  // src/core/async/Strategy.ts
@@ -825,6 +905,25 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
825
905
  if (this.writeBuffer.has(key)) return true;
826
906
  return this.root._diskExists(key, this.snapshotVersion);
827
907
  }
908
+ async _existsSnapshot(key, snapshotVersion, snapshotLocalVersion) {
909
+ if (this.writeBuffer.has(key)) {
910
+ const keyModVersion = this.keyVersions.get(key);
911
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
912
+ return true;
913
+ }
914
+ }
915
+ if (this.deleteBuffer.has(key)) {
916
+ const keyModVersion = this.keyVersions.get(key);
917
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
918
+ return false;
919
+ }
920
+ }
921
+ if (this.parent) {
922
+ return this.parent._existsSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
923
+ } else {
924
+ return this._diskExists(key, snapshotVersion);
925
+ }
926
+ }
828
927
  async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
829
928
  if (this.writeBuffer.has(key)) {
830
929
  const keyModVersion = this.keyVersions.get(key);
@@ -969,7 +1068,6 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
969
1068
  this.root.activeTransactions.delete(child);
970
1069
  return null;
971
1070
  } else {
972
- const newVersion = this.version + 1;
973
1071
  if (child !== this) {
974
1072
  const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
975
1073
  for (const key of modifiedKeys) {
@@ -988,35 +1086,70 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
988
1086
  }
989
1087
  }
990
1088
  }
991
- }
992
- for (const [key, value] of child.writeBuffer) {
993
- this.writeBuffer.set(key, value);
994
- this.deleteBuffer.delete(key);
995
- if (child.createdKeys.has(key)) {
996
- this.createdKeys.add(key);
1089
+ const newVersion = this.version + 1;
1090
+ for (const [key, value] of child.writeBuffer) {
1091
+ if (this.writeBuffer.has(key)) {
1092
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
1093
+ this.deletedCache.get(key).push({
1094
+ value: this.writeBuffer.get(key),
1095
+ deletedAtVersion: newVersion
1096
+ });
1097
+ } else if (await this.strategy.exists(key)) {
1098
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
1099
+ this.deletedCache.get(key).push({
1100
+ value: await this.strategy.read(key),
1101
+ deletedAtVersion: newVersion
1102
+ });
1103
+ }
1104
+ this.writeBuffer.set(key, value);
1105
+ this.deleteBuffer.delete(key);
1106
+ if (child.createdKeys.has(key)) {
1107
+ this.createdKeys.add(key);
1108
+ }
1109
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
1110
+ this.versionIndex.get(key).push({ version: newVersion, exists: true });
997
1111
  }
998
- }
999
- for (const key of child.deleteBuffer) {
1000
- this.deleteBuffer.add(key);
1001
- this.writeBuffer.delete(key);
1002
- this.createdKeys.delete(key);
1003
- const deletedValue = child.deletedValues.get(key);
1004
- if (deletedValue !== void 0) {
1005
- this.deletedValues.set(key, deletedValue);
1112
+ for (const key of child.deleteBuffer) {
1113
+ if (this.writeBuffer.has(key)) {
1114
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
1115
+ this.deletedCache.get(key).push({
1116
+ value: this.writeBuffer.get(key),
1117
+ deletedAtVersion: newVersion
1118
+ });
1119
+ } else if (await this.strategy.exists(key)) {
1120
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
1121
+ this.deletedCache.get(key).push({
1122
+ value: await this.strategy.read(key),
1123
+ deletedAtVersion: newVersion
1124
+ });
1125
+ }
1126
+ this.deleteBuffer.add(key);
1127
+ this.writeBuffer.delete(key);
1128
+ this.createdKeys.delete(key);
1129
+ const deletedValue = child.deletedValues.get(key);
1130
+ if (deletedValue !== void 0) {
1131
+ this.deletedValues.set(key, deletedValue);
1132
+ }
1133
+ if (child.originallyExisted.has(key)) {
1134
+ this.originallyExisted.add(key);
1135
+ }
1136
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
1137
+ this.versionIndex.get(key).push({ version: newVersion, exists: false });
1006
1138
  }
1007
- if (child.originallyExisted.has(key)) {
1008
- this.originallyExisted.add(key);
1139
+ this.version = newVersion;
1140
+ this.root.activeTransactions.delete(child);
1141
+ this._cleanupDeletedCache();
1142
+ } else {
1143
+ const newVersion = this.version + 1;
1144
+ for (const [key, value] of this.writeBuffer) {
1145
+ await this._diskWrite(key, value, newVersion);
1009
1146
  }
1147
+ for (const key of this.deleteBuffer) {
1148
+ await this._diskDelete(key, newVersion);
1149
+ }
1150
+ this.version = newVersion;
1151
+ this._cleanupDeletedCache();
1010
1152
  }
1011
- for (const [key, value] of child.writeBuffer) {
1012
- await this._diskWrite(key, value, newVersion);
1013
- }
1014
- for (const key of child.deleteBuffer) {
1015
- await this._diskDelete(key, newVersion);
1016
- }
1017
- this.version = newVersion;
1018
- this.root.activeTransactions.delete(child);
1019
- this._cleanupDeletedCache();
1020
1153
  return null;
1021
1154
  }
1022
1155
  });
@@ -1042,6 +1175,8 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1042
1175
  if (!strategy) throw new Error("Root Transaction missing strategy");
1043
1176
  const versions = this.versionIndex.get(key);
1044
1177
  if (!versions) {
1178
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
1179
+ if (this.deleteBuffer.has(key)) return null;
1045
1180
  return await strategy.exists(key) ? strategy.read(key) : null;
1046
1181
  }
1047
1182
  let targetVerObj = null;
@@ -1062,10 +1197,11 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1062
1197
  if (match) return match.value;
1063
1198
  }
1064
1199
  }
1065
- return null;
1200
+ return await strategy.exists(key) ? strategy.read(key) : null;
1066
1201
  }
1067
1202
  if (!targetVerObj.exists) return null;
1068
1203
  if (!nextVerObj) {
1204
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
1069
1205
  return strategy.read(key);
1070
1206
  }
1071
1207
  const cached = this.deletedCache.get(key);
@@ -1080,6 +1216,8 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1080
1216
  if (!strategy) throw new Error("Root Transaction missing strategy");
1081
1217
  const versions = this.versionIndex.get(key);
1082
1218
  if (!versions) {
1219
+ if (this.writeBuffer.has(key)) return true;
1220
+ if (this.deleteBuffer.has(key)) return false;
1083
1221
  return strategy.exists(key);
1084
1222
  }
1085
1223
  let targetVerObj = null;
@@ -1103,30 +1241,11 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
1103
1241
  value: currentVal,
1104
1242
  deletedAtVersion: snapshotVersion
1105
1243
  });
1244
+ await strategy.delete(key);
1106
1245
  }
1107
- await strategy.delete(key);
1108
1246
  if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
1109
1247
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
1110
1248
  }
1111
- _cleanupDeletedCache() {
1112
- if (this.deletedCache.size === 0) return;
1113
- let minActiveVersion = this.version;
1114
- if (this.activeTransactions.size > 0) {
1115
- for (const tx of this.activeTransactions) {
1116
- if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
1117
- minActiveVersion = tx.snapshotVersion;
1118
- }
1119
- }
1120
- }
1121
- for (const [key, cachedList] of this.deletedCache) {
1122
- const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
1123
- if (remaining.length === 0) {
1124
- this.deletedCache.delete(key);
1125
- } else {
1126
- this.deletedCache.set(key, remaining);
1127
- }
1128
- }
1129
- }
1130
1249
  };
1131
1250
  export {
1132
1251
  AsyncMVCCStrategy,
@@ -10,6 +10,7 @@ export declare class AsyncMVCCTransaction<S extends AsyncMVCCStrategy<K, T>, K,
10
10
  createNested(): this;
11
11
  read(key: K): Promise<T | null>;
12
12
  exists(key: K): Promise<boolean>;
13
+ _existsSnapshot(key: K, snapshotVersion: number, snapshotLocalVersion?: number): Promise<boolean>;
13
14
  _readSnapshot(key: K, snapshotVersion: number, snapshotLocalVersion?: number): Promise<T | null>;
14
15
  commit(label?: string): Promise<TransactionResult<K, T>>;
15
16
  _merge(child: AsyncMVCCTransaction<S, K, T>): Promise<TransactionMergeFailure<K, T> | null>;
@@ -17,5 +18,4 @@ export declare class AsyncMVCCTransaction<S extends AsyncMVCCStrategy<K, T>, K,
17
18
  _diskRead(key: K, snapshotVersion: number): Promise<T | null>;
18
19
  _diskExists(key: K, snapshotVersion: number): Promise<boolean>;
19
20
  _diskDelete(key: K, snapshotVersion: number): Promise<void>;
20
- _cleanupDeletedCache(): void;
21
21
  }
@@ -120,4 +120,17 @@ export declare abstract class MVCCTransaction<S extends MVCCStrategy<K, T>, K, T
120
120
  * @param snapshotLocalVersion The local version within the parent's buffer to read at.
121
121
  */
122
122
  abstract _readSnapshot(key: K, snapshotVersion: number, snapshotLocalVersion?: number): Deferred<T | null>;
123
+ /**
124
+ * Checks if a key exists at a specific snapshot version.
125
+ * Used by child transactions to check existence from parent respecting the child's snapshot.
126
+ * @param key The key to check.
127
+ * @param snapshotVersion The global version to check at.
128
+ * @param snapshotLocalVersion The local version within the parent's buffer to check at.
129
+ */
130
+ abstract _existsSnapshot(key: K, snapshotVersion: number, snapshotLocalVersion?: number): Deferred<boolean>;
131
+ /**
132
+ * Cleans up both deletedCache and versionIndex based on minActiveVersion.
133
+ * Root transactions call this after commit to reclaim memory.
134
+ */
135
+ protected _cleanupDeletedCache(): void;
123
136
  }
@@ -8,6 +8,7 @@ export declare class SyncMVCCTransaction<S extends SyncMVCCStrategy<K, T>, K, T>
8
8
  createNested(): this;
9
9
  read(key: K): T | null;
10
10
  exists(key: K): boolean;
11
+ _existsSnapshot(key: K, snapshotVersion: number, snapshotLocalVersion?: number): boolean;
11
12
  _readSnapshot(key: K, snapshotVersion: number, snapshotLocalVersion?: number): T | null;
12
13
  commit(label?: string): TransactionResult<K, T>;
13
14
  _merge(child: SyncMVCCTransaction<S, K, T>): TransactionMergeFailure<K, T> | null;
@@ -15,5 +16,4 @@ export declare class SyncMVCCTransaction<S extends SyncMVCCStrategy<K, T>, K, T>
15
16
  _diskRead(key: K, snapshotVersion: number): T | null;
16
17
  _diskExists(key: K, snapshotVersion: number): boolean;
17
18
  _diskDelete(key: K, snapshotVersion: number): void;
18
- _cleanupDeletedCache(): void;
19
19
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mvcc-api",
3
- "version": "1.2.10",
3
+ "version": "1.3.0",
4
4
  "description": "Multiversion Concurrency Control (MVCC) API for TypeScript",
5
5
  "license": "MIT",
6
6
  "author": "izure <admin@izure.org>",