mvcc-api 1.0.3 → 1.1.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.
@@ -1,73 +1,50 @@
1
- // src/core/base/Manager.ts
2
- var MVCCManager = class {
3
- version;
4
- strategy;
5
- versionIndex;
6
- activeTransactions;
7
- deletedCache;
8
- constructor(strategy) {
9
- this.strategy = strategy;
10
- this.version = 0;
11
- this.activeTransactions = /* @__PURE__ */ new Set();
12
- this.deletedCache = /* @__PURE__ */ new Map();
13
- this.versionIndex = /* @__PURE__ */ new Map();
14
- }
15
- _removeTransaction(tx) {
16
- this.activeTransactions.delete(tx);
17
- this._cleanupDeletedCache();
18
- }
19
- _cleanupDeletedCache() {
20
- let minVersion = this.version;
21
- for (const tx of this.activeTransactions) {
22
- minVersion = Math.min(minVersion, tx.snapshotVersion);
23
- }
24
- for (const [key, versions] of this.versionIndex.entries()) {
25
- const toKeep = [];
26
- let keptOldVersion = false;
27
- let i = versions.length;
28
- while (i--) {
29
- const v = versions[i];
30
- if (v.version > minVersion) {
31
- toKeep.unshift(v);
32
- } else if (!keptOldVersion) {
33
- toKeep.unshift(v);
34
- keptOldVersion = true;
35
- }
36
- }
37
- if (toKeep.length === 0) {
38
- this.versionIndex.delete(key);
39
- } else {
40
- this.versionIndex.set(key, toKeep);
41
- }
42
- }
43
- for (const [key, versions] of this.deletedCache.entries()) {
44
- const filtered = versions.filter((v) => v.deletedAtVersion >= minVersion);
45
- if (filtered.length === 0) {
46
- this.deletedCache.delete(key);
47
- } else {
48
- this.deletedCache.set(key, filtered);
49
- }
50
- }
51
- }
52
- };
53
-
54
1
  // src/core/base/Strategy.ts
55
2
  var MVCCStrategy = class {
56
3
  };
57
4
 
58
5
  // src/core/base/Transaction.ts
59
6
  var MVCCTransaction = class {
60
- manager;
61
7
  committed;
62
8
  snapshotVersion;
9
+ snapshotLocalVersion;
63
10
  writeBuffer;
64
11
  deleteBuffer;
65
- constructor(manager, snapshotVersion) {
66
- this.manager = manager;
67
- this.snapshotVersion = snapshotVersion;
12
+ // Nested Transaction Properties
13
+ parent;
14
+ localVersion;
15
+ // Local version for Nested Conflict Detection
16
+ keyVersions;
17
+ // Key -> Local Version (When it was modified locally)
18
+ // Root Transaction Properties (Only populated if this is Root)
19
+ root;
20
+ strategy;
21
+ version = 0;
22
+ versionIndex = /* @__PURE__ */ new Map();
23
+ deletedCache = /* @__PURE__ */ new Map();
24
+ activeTransactions = /* @__PURE__ */ new Set();
25
+ constructor(strategy, parent, snapshotVersion) {
26
+ this.snapshotVersion = snapshotVersion ?? 0;
68
27
  this.writeBuffer = /* @__PURE__ */ new Map();
69
28
  this.deleteBuffer = /* @__PURE__ */ new Set();
70
29
  this.committed = false;
30
+ this.parent = parent;
31
+ this.keyVersions = /* @__PURE__ */ new Map();
32
+ if (parent) {
33
+ this.localVersion = parent.localVersion;
34
+ this.snapshotLocalVersion = parent.localVersion;
35
+ this.strategy = void 0;
36
+ this.root = parent.root;
37
+ } else {
38
+ if (!strategy) throw new Error("Root Transaction must get Strategy");
39
+ this.strategy = strategy;
40
+ this.version = 0;
41
+ this.localVersion = 0;
42
+ this.snapshotLocalVersion = 0;
43
+ this.root = this;
44
+ }
45
+ }
46
+ isRoot() {
47
+ return !this.parent;
71
48
  }
72
49
  /**
73
50
  * Schedules a creation (insert) of a key-value pair.
@@ -90,8 +67,10 @@ var MVCCTransaction = class {
90
67
  */
91
68
  write(key, value) {
92
69
  if (this.committed) throw new Error("Transaction already committed");
70
+ this.localVersion++;
93
71
  this.writeBuffer.set(key, value);
94
72
  this.deleteBuffer.delete(key);
73
+ this.keyVersions.set(key, this.localVersion);
95
74
  return this;
96
75
  }
97
76
  /**
@@ -101,8 +80,10 @@ var MVCCTransaction = class {
101
80
  */
102
81
  delete(key) {
103
82
  if (this.committed) throw new Error("Transaction already committed");
83
+ this.localVersion++;
104
84
  this.deleteBuffer.add(key);
105
85
  this.writeBuffer.delete(key);
86
+ this.keyVersions.set(key, this.localVersion);
106
87
  return this;
107
88
  }
108
89
  /**
@@ -114,126 +95,196 @@ var MVCCTransaction = class {
114
95
  this.writeBuffer.clear();
115
96
  this.deleteBuffer.clear();
116
97
  this.committed = true;
117
- this.manager._removeTransaction(this);
98
+ if (this.root !== this) {
99
+ this.root.activeTransactions.delete(this);
100
+ }
118
101
  return this;
119
102
  }
120
103
  };
121
104
 
105
+ // src/core/sync/Strategy.ts
106
+ var SyncMVCCStrategy = class extends MVCCStrategy {
107
+ };
108
+
122
109
  // src/core/sync/Transaction.ts
123
- var SyncMVCCTransaction = class extends MVCCTransaction {
110
+ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
111
+ createNested() {
112
+ const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
113
+ const child = new _SyncMVCCTransaction(void 0, this, childVersion);
114
+ this.root.activeTransactions.add(child);
115
+ return child;
116
+ }
124
117
  read(key) {
125
118
  if (this.committed) throw new Error("Transaction already committed");
119
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
120
+ if (this.deleteBuffer.has(key)) return null;
121
+ return this.root._diskRead(key, this.snapshotVersion);
122
+ }
123
+ _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
126
124
  if (this.writeBuffer.has(key)) {
127
- return this.writeBuffer.get(key);
125
+ const keyModVersion = this.keyVersions.get(key);
126
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
127
+ return this.writeBuffer.get(key);
128
+ }
128
129
  }
129
130
  if (this.deleteBuffer.has(key)) {
130
- return null;
131
+ const keyModVersion = this.keyVersions.get(key);
132
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
133
+ return null;
134
+ }
135
+ }
136
+ if (this.parent) {
137
+ return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
138
+ } else {
139
+ return this._diskRead(key, snapshotVersion);
131
140
  }
132
- return this.manager._diskRead(key, this.snapshotVersion);
133
141
  }
134
142
  commit() {
135
143
  if (this.committed) throw new Error("Transaction already committed");
136
- this.manager._commit(this);
137
- this.committed = true;
138
- this.manager._removeTransaction(this);
144
+ if (this.parent) {
145
+ this.parent._merge(this);
146
+ this.committed = true;
147
+ } else {
148
+ if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
149
+ this._merge(this);
150
+ this.writeBuffer.clear();
151
+ this.deleteBuffer.clear();
152
+ this.keyVersions.clear();
153
+ this.localVersion = 0;
154
+ }
155
+ }
139
156
  return this;
140
157
  }
141
- };
142
-
143
- // src/core/sync/Manager.ts
144
- var SyncMVCCManager = class extends MVCCManager {
145
- constructor(strategy) {
146
- super(strategy);
147
- }
148
- createTransaction() {
149
- const tx = new SyncMVCCTransaction(this, this.version);
150
- this.activeTransactions.add(tx);
151
- return tx;
152
- }
153
- _diskWrite(key, value, version) {
154
- if (this.strategy.exists(key)) {
155
- const oldValue = this.strategy.read(key);
156
- if (!this.deletedCache.has(key)) {
157
- this.deletedCache.set(key, []);
158
+ _merge(child) {
159
+ if (this.parent) {
160
+ for (const key of child.writeBuffer.keys()) {
161
+ const lastModLocalVer = this.keyVersions.get(key);
162
+ if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
163
+ throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`);
164
+ }
158
165
  }
159
- this.deletedCache.get(key).push({ value: oldValue, deletedAtVersion: version });
160
- }
161
- this.strategy.write(key, value);
162
- if (!this.versionIndex.has(key)) {
163
- this.versionIndex.set(key, []);
166
+ for (const key of child.deleteBuffer) {
167
+ const lastModLocalVer = this.keyVersions.get(key);
168
+ if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
169
+ throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`);
170
+ }
171
+ }
172
+ const newLocalVersion = this.localVersion + 1;
173
+ for (const key of child.writeBuffer.keys()) {
174
+ this.write(key, child.writeBuffer.get(key));
175
+ this.keyVersions.set(key, newLocalVersion);
176
+ }
177
+ for (const key of child.deleteBuffer) {
178
+ this.delete(key);
179
+ this.keyVersions.set(key, newLocalVersion);
180
+ }
181
+ this.localVersion = newLocalVersion;
182
+ this.root.activeTransactions.delete(child);
183
+ } else {
184
+ this.root.activeTransactions.delete(child);
185
+ const newVersion = this.version + 1;
186
+ const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
187
+ for (const key of modifiedKeys) {
188
+ const versions = this.versionIndex.get(key);
189
+ if (versions && versions.length > 0) {
190
+ const lastVer = versions[versions.length - 1].version;
191
+ if (lastVer > child.snapshotVersion) {
192
+ throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`);
193
+ }
194
+ }
195
+ }
196
+ for (const [key, value] of child.writeBuffer) {
197
+ this._diskWrite(key, value, newVersion);
198
+ }
199
+ for (const key of child.deleteBuffer) {
200
+ this._diskDelete(key, newVersion);
201
+ }
202
+ this.version = newVersion;
203
+ this._cleanupDeletedCache();
164
204
  }
205
+ }
206
+ // --- Internal IO Helpers (Root Only) ---
207
+ _diskWrite(key, value, version) {
208
+ const strategy = this.strategy;
209
+ if (!strategy) throw new Error("Root Transaction missing strategy");
210
+ if (strategy.exists(key)) {
211
+ const currentVal = strategy.read(key);
212
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
213
+ this.deletedCache.get(key).push({
214
+ value: currentVal,
215
+ deletedAtVersion: version
216
+ });
217
+ }
218
+ strategy.write(key, value);
219
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
165
220
  this.versionIndex.get(key).push({ version, exists: true });
166
221
  }
167
222
  _diskRead(key, snapshotVersion) {
168
- if (!this.versionIndex.has(key) && !this.deletedCache.has(key)) {
169
- if (this.strategy.exists(key)) {
170
- return this.strategy.read(key);
171
- }
172
- return null;
173
- }
223
+ const strategy = this.strategy;
224
+ if (!strategy) throw new Error("Root Transaction missing strategy");
174
225
  const versions = this.versionIndex.get(key);
175
- if (versions && versions.length > 0) {
176
- const visibleVersions = versions.filter((v) => v.version <= snapshotVersion && v.exists);
177
- if (visibleVersions.length > 0) {
178
- const newerVersions = versions.filter((v) => v.version > snapshotVersion);
179
- if (newerVersions.length === 0) {
180
- return this.strategy.read(key);
181
- }
226
+ if (!versions) {
227
+ return strategy.exists(key) ? strategy.read(key) : null;
228
+ }
229
+ let targetVerObj = null;
230
+ let nextVerObj = null;
231
+ for (const v of versions) {
232
+ if (v.version <= snapshotVersion) {
233
+ targetVerObj = v;
234
+ } else {
235
+ nextVerObj = v;
236
+ break;
182
237
  }
183
238
  }
239
+ if (!targetVerObj || !targetVerObj.exists) return null;
240
+ if (!nextVerObj) {
241
+ return strategy.read(key);
242
+ }
184
243
  const cached = this.deletedCache.get(key);
185
244
  if (cached) {
186
- const visibleEntries = cached.filter((v) => v.deletedAtVersion > snapshotVersion);
187
- if (visibleEntries.length > 0) {
188
- visibleEntries.sort((a, b) => a.deletedAtVersion - b.deletedAtVersion);
189
- return visibleEntries[0].value;
190
- }
245
+ const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
246
+ if (match) return match.value;
191
247
  }
192
248
  return null;
193
249
  }
194
250
  _diskDelete(key, snapshotVersion) {
195
- if (this.strategy.exists(key)) {
196
- const data = this.strategy.read(key);
197
- if (!this.deletedCache.has(key)) {
198
- this.deletedCache.set(key, []);
199
- }
200
- this.deletedCache.get(key).push({ deletedAtVersion: snapshotVersion, value: data });
201
- this.strategy.delete(key);
202
- }
203
- if (!this.versionIndex.has(key)) {
204
- this.versionIndex.set(key, []);
205
- }
251
+ const strategy = this.strategy;
252
+ if (!strategy) throw new Error("Root Transaction missing strategy");
253
+ if (strategy.exists(key)) {
254
+ const currentVal = strategy.read(key);
255
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
256
+ this.deletedCache.get(key).push({
257
+ value: currentVal,
258
+ deletedAtVersion: snapshotVersion
259
+ });
260
+ }
261
+ strategy.delete(key);
262
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
206
263
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
207
264
  }
208
- _commit(tx) {
209
- const isReadOnly = tx.writeBuffer.size === 0 && tx.deleteBuffer.size === 0;
210
- if (!isReadOnly && this.version > tx.snapshotVersion) {
211
- const affectedKeys = /* @__PURE__ */ new Set([
212
- ...tx.writeBuffer.keys(),
213
- ...tx.deleteBuffer
214
- ]);
215
- for (const key of affectedKeys) {
216
- const versions = this.versionIndex.get(key);
217
- if (versions && versions.length > 0) {
218
- const latestVersion = versions[versions.length - 1].version;
219
- if (latestVersion > tx.snapshotVersion) {
220
- throw new Error(`Commit conflict: file '${key}' was modified by another transaction`);
221
- }
265
+ _cleanupDeletedCache() {
266
+ if (this.deletedCache.size === 0) return;
267
+ let minActiveVersion = this.version;
268
+ if (this.activeTransactions.size > 0) {
269
+ for (const tx of this.activeTransactions) {
270
+ if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
271
+ minActiveVersion = tx.snapshotVersion;
222
272
  }
223
273
  }
224
274
  }
225
- this.version++;
226
- for (const key of tx.deleteBuffer) {
227
- this._diskDelete(key, this.version);
228
- }
229
- for (const [key, value] of tx.writeBuffer) {
230
- this._diskWrite(key, value, this.version);
275
+ for (const [key, cachedList] of this.deletedCache) {
276
+ const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
277
+ if (remaining.length === 0) {
278
+ this.deletedCache.delete(key);
279
+ } else {
280
+ this.deletedCache.set(key, remaining);
281
+ }
231
282
  }
232
283
  }
233
284
  };
234
285
 
235
- // src/core/sync/Strategy.ts
236
- var SyncMVCCStrategy = class extends MVCCStrategy {
286
+ // src/core/async/Strategy.ts
287
+ var AsyncMVCCStrategy = class extends MVCCStrategy {
237
288
  };
238
289
 
239
290
  // node_modules/ryoiki/dist/esm/index.mjs
@@ -495,139 +546,198 @@ var Ryoiki = class _Ryoiki {
495
546
  };
496
547
 
497
548
  // src/core/async/Transaction.ts
498
- var AsyncMVCCTransaction = class extends MVCCTransaction {
549
+ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
550
+ lock = new Ryoiki();
551
+ async writeLock(fn) {
552
+ let lockId;
553
+ return this.lock.writeLock(async (_lockId) => {
554
+ lockId = _lockId;
555
+ return fn();
556
+ }).finally(() => {
557
+ this.lock.writeUnlock(lockId);
558
+ });
559
+ }
560
+ createNested() {
561
+ const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
562
+ const child = new _AsyncMVCCTransaction(void 0, this, childVersion);
563
+ this.root.activeTransactions.add(child);
564
+ return child;
565
+ }
499
566
  async read(key) {
500
567
  if (this.committed) throw new Error("Transaction already committed");
568
+ if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
569
+ if (this.deleteBuffer.has(key)) return null;
570
+ return this.root._diskRead(key, this.snapshotVersion);
571
+ }
572
+ async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
501
573
  if (this.writeBuffer.has(key)) {
502
- return this.writeBuffer.get(key);
574
+ const keyModVersion = this.keyVersions.get(key);
575
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
576
+ return this.writeBuffer.get(key);
577
+ }
503
578
  }
504
579
  if (this.deleteBuffer.has(key)) {
505
- return null;
580
+ const keyModVersion = this.keyVersions.get(key);
581
+ if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
582
+ return null;
583
+ }
584
+ }
585
+ if (this.parent) {
586
+ return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
587
+ } else {
588
+ return this._diskRead(key, snapshotVersion);
506
589
  }
507
- return this.manager._diskRead(key, this.snapshotVersion);
508
590
  }
509
591
  async commit() {
510
- return this.manager.writeLock(async () => {
592
+ return this.writeLock(async () => {
511
593
  if (this.committed) throw new Error("Transaction already committed");
512
- await this.manager._commit(this);
513
- this.committed = true;
514
- this.manager._removeTransaction(this);
594
+ if (this.parent) {
595
+ await this.parent._merge(this);
596
+ this.committed = true;
597
+ } else {
598
+ if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
599
+ await this._merge(this);
600
+ this.writeBuffer.clear();
601
+ this.deleteBuffer.clear();
602
+ this.keyVersions.clear();
603
+ this.localVersion = 0;
604
+ }
605
+ }
515
606
  return this;
516
607
  });
517
608
  }
518
- };
519
-
520
- // src/core/async/Manager.ts
521
- var AsyncMVCCManager = class extends MVCCManager {
522
- lock;
523
- constructor(strategy) {
524
- super(strategy);
525
- this.lock = new Ryoiki();
526
- }
527
- createTransaction() {
528
- const tx = new AsyncMVCCTransaction(this, this.version);
529
- this.activeTransactions.add(tx);
530
- return tx;
531
- }
532
- async writeLock(fn) {
533
- let lockId;
534
- return this.lock.writeLock(async (_lockId) => {
535
- lockId = _lockId;
536
- return fn();
537
- }).finally(() => {
538
- this.lock.writeUnlock(lockId);
609
+ async _merge(child) {
610
+ return this.writeLock(async () => {
611
+ if (this.parent) {
612
+ for (const key of child.writeBuffer.keys()) {
613
+ const lastModLocalVer = this.keyVersions.get(key);
614
+ if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
615
+ throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`);
616
+ }
617
+ }
618
+ for (const key of child.deleteBuffer) {
619
+ const lastModLocalVer = this.keyVersions.get(key);
620
+ if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
621
+ throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`);
622
+ }
623
+ }
624
+ const newLocalVersion = this.localVersion + 1;
625
+ for (const key of child.writeBuffer.keys()) {
626
+ this.write(key, child.writeBuffer.get(key));
627
+ this.keyVersions.set(key, newLocalVersion);
628
+ }
629
+ for (const key of child.deleteBuffer) {
630
+ this.delete(key);
631
+ this.keyVersions.set(key, newLocalVersion);
632
+ }
633
+ this.localVersion = newLocalVersion;
634
+ this.root.activeTransactions.delete(child);
635
+ } else {
636
+ this.root.activeTransactions.delete(child);
637
+ const newVersion = this.version + 1;
638
+ const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
639
+ for (const key of modifiedKeys) {
640
+ const versions = this.versionIndex.get(key);
641
+ if (versions && versions.length > 0) {
642
+ const lastVer = versions[versions.length - 1].version;
643
+ if (lastVer > child.snapshotVersion) {
644
+ throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`);
645
+ }
646
+ }
647
+ }
648
+ for (const [key, value] of child.writeBuffer) {
649
+ await this._diskWrite(key, value, newVersion);
650
+ }
651
+ for (const key of child.deleteBuffer) {
652
+ await this._diskDelete(key, newVersion);
653
+ }
654
+ this.version = newVersion;
655
+ this._cleanupDeletedCache();
656
+ }
539
657
  });
540
658
  }
659
+ // --- Internal IO Helpers (Root Only) ---
541
660
  async _diskWrite(key, value, version) {
542
- if (await this.strategy.exists(key)) {
543
- const oldValue = await this.strategy.read(key);
544
- if (!this.deletedCache.has(key)) {
545
- this.deletedCache.set(key, []);
546
- }
547
- this.deletedCache.get(key).push({ value: oldValue, deletedAtVersion: version });
548
- }
549
- await this.strategy.write(key, value);
550
- if (!this.versionIndex.has(key)) {
551
- this.versionIndex.set(key, []);
552
- }
661
+ const strategy = this.strategy;
662
+ if (!strategy) throw new Error("Root Transaction missing strategy");
663
+ if (await strategy.exists(key)) {
664
+ const currentVal = await strategy.read(key);
665
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
666
+ this.deletedCache.get(key).push({
667
+ value: currentVal,
668
+ deletedAtVersion: version
669
+ });
670
+ }
671
+ await strategy.write(key, value);
672
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
553
673
  this.versionIndex.get(key).push({ version, exists: true });
554
674
  }
555
675
  async _diskRead(key, snapshotVersion) {
556
- if (!this.versionIndex.has(key) && !this.deletedCache.has(key)) {
557
- if (await this.strategy.exists(key)) {
558
- return this.strategy.read(key);
559
- }
560
- return null;
561
- }
676
+ const strategy = this.strategy;
677
+ if (!strategy) throw new Error("Root Transaction missing strategy");
562
678
  const versions = this.versionIndex.get(key);
563
- if (versions && versions.length > 0) {
564
- const visibleVersions = versions.filter((v) => v.version <= snapshotVersion && v.exists);
565
- if (visibleVersions.length > 0) {
566
- const newerVersions = versions.filter((v) => v.version > snapshotVersion);
567
- if (newerVersions.length === 0) {
568
- return this.strategy.read(key);
569
- }
679
+ if (!versions) {
680
+ return await strategy.exists(key) ? strategy.read(key) : null;
681
+ }
682
+ let targetVerObj = null;
683
+ let nextVerObj = null;
684
+ for (const v of versions) {
685
+ if (v.version <= snapshotVersion) {
686
+ targetVerObj = v;
687
+ } else {
688
+ nextVerObj = v;
689
+ break;
570
690
  }
571
691
  }
692
+ if (!targetVerObj || !targetVerObj.exists) return null;
693
+ if (!nextVerObj) {
694
+ return strategy.read(key);
695
+ }
572
696
  const cached = this.deletedCache.get(key);
573
697
  if (cached) {
574
- const visibleEntries = cached.filter((v) => v.deletedAtVersion > snapshotVersion);
575
- if (visibleEntries.length > 0) {
576
- visibleEntries.sort((a, b) => a.deletedAtVersion - b.deletedAtVersion);
577
- return visibleEntries[0].value;
578
- }
698
+ const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
699
+ if (match) return match.value;
579
700
  }
580
701
  return null;
581
702
  }
582
703
  async _diskDelete(key, snapshotVersion) {
583
- if (await this.strategy.exists(key)) {
584
- const data = await this.strategy.read(key);
585
- if (!this.deletedCache.has(key)) {
586
- this.deletedCache.set(key, []);
587
- }
588
- this.deletedCache.get(key).push({ deletedAtVersion: snapshotVersion, value: data });
589
- await this.strategy.delete(key);
590
- }
591
- if (!this.versionIndex.has(key)) {
592
- this.versionIndex.set(key, []);
593
- }
704
+ const strategy = this.strategy;
705
+ if (!strategy) throw new Error("Root Transaction missing strategy");
706
+ if (await strategy.exists(key)) {
707
+ const currentVal = await strategy.read(key);
708
+ if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
709
+ this.deletedCache.get(key).push({
710
+ value: currentVal,
711
+ deletedAtVersion: snapshotVersion
712
+ });
713
+ }
714
+ await strategy.delete(key);
715
+ if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
594
716
  this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
595
717
  }
596
- async _commit(tx) {
597
- const isReadOnly = tx.writeBuffer.size === 0 && tx.deleteBuffer.size === 0;
598
- if (!isReadOnly && this.version > tx.snapshotVersion) {
599
- const affectedKeys = /* @__PURE__ */ new Set([
600
- ...tx.writeBuffer.keys(),
601
- ...tx.deleteBuffer
602
- ]);
603
- for (const key of affectedKeys) {
604
- const versions = this.versionIndex.get(key);
605
- if (versions && versions.length > 0) {
606
- const latestVersion = versions[versions.length - 1].version;
607
- if (latestVersion > tx.snapshotVersion) {
608
- throw new Error(`Commit conflict: file '${key}' was modified by another transaction`);
609
- }
718
+ _cleanupDeletedCache() {
719
+ if (this.deletedCache.size === 0) return;
720
+ let minActiveVersion = this.version;
721
+ if (this.activeTransactions.size > 0) {
722
+ for (const tx of this.activeTransactions) {
723
+ if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
724
+ minActiveVersion = tx.snapshotVersion;
610
725
  }
611
726
  }
612
727
  }
613
- this.version++;
614
- for (const key of tx.deleteBuffer) {
615
- await this._diskDelete(key, this.version);
616
- }
617
- for (const [key, value] of tx.writeBuffer) {
618
- await this._diskWrite(key, value, this.version);
728
+ for (const [key, cachedList] of this.deletedCache) {
729
+ const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
730
+ if (remaining.length === 0) {
731
+ this.deletedCache.delete(key);
732
+ } else {
733
+ this.deletedCache.set(key, remaining);
734
+ }
619
735
  }
620
736
  }
621
737
  };
622
-
623
- // src/core/async/Strategy.ts
624
- var AsyncMVCCStrategy = class extends MVCCStrategy {
625
- };
626
738
  export {
627
- AsyncMVCCManager,
628
739
  AsyncMVCCStrategy,
629
740
  AsyncMVCCTransaction,
630
- SyncMVCCManager,
631
741
  SyncMVCCStrategy,
632
742
  SyncMVCCTransaction
633
743
  };