mvcc-api 1.1.0 → 1.2.1

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.
@@ -38,6 +38,10 @@ var MVCCTransaction = class {
38
38
  snapshotLocalVersion;
39
39
  writeBuffer;
40
40
  deleteBuffer;
41
+ createdKeys;
42
+ // create()로 생성된 키 추적
43
+ deletedValues;
44
+ // delete 시 삭제 전 값 저장
41
45
  // Nested Transaction Properties
42
46
  parent;
43
47
  localVersion;
@@ -55,6 +59,8 @@ var MVCCTransaction = class {
55
59
  this.snapshotVersion = snapshotVersion ?? 0;
56
60
  this.writeBuffer = /* @__PURE__ */ new Map();
57
61
  this.deleteBuffer = /* @__PURE__ */ new Set();
62
+ this.createdKeys = /* @__PURE__ */ new Set();
63
+ this.deletedValues = /* @__PURE__ */ new Map();
58
64
  this.committed = false;
59
65
  this.parent = parent;
60
66
  this.keyVersions = /* @__PURE__ */ new Map();
@@ -75,59 +81,58 @@ var MVCCTransaction = class {
75
81
  isRoot() {
76
82
  return !this.parent;
77
83
  }
78
- /**
79
- * Schedules a creation (insert) of a key-value pair.
80
- * Throws if the transaction is already committed.
81
- * @param key The key to create.
82
- * @param value The value to store.
83
- * @returns The transaction instance for chaining.
84
- */
85
- create(key, value) {
86
- if (this.committed) throw new Error("Transaction already committed");
84
+ // --- Internal buffer manipulation helpers ---
85
+ _bufferCreate(key, value) {
86
+ this.localVersion++;
87
87
  this.writeBuffer.set(key, value);
88
- return this;
88
+ this.createdKeys.add(key);
89
+ this.deleteBuffer.delete(key);
90
+ this.keyVersions.set(key, this.localVersion);
89
91
  }
90
- /**
91
- * Schedules a write (update) of a key-value pair.
92
- * Overwrites any existing value in the buffer.
93
- * @param key The key to write.
94
- * @param value The value to store.
95
- * @returns The transaction instance for chaining.
96
- */
97
- write(key, value) {
98
- if (this.committed) throw new Error("Transaction already committed");
92
+ _bufferWrite(key, value) {
99
93
  this.localVersion++;
100
94
  this.writeBuffer.set(key, value);
101
95
  this.deleteBuffer.delete(key);
102
96
  this.keyVersions.set(key, this.localVersion);
103
- return this;
104
97
  }
105
- /**
106
- * Schedules a deletion of a key.
107
- * @param key The key to delete.
108
- * @returns The transaction instance for chaining.
109
- */
110
- delete(key) {
111
- if (this.committed) throw new Error("Transaction already committed");
98
+ _bufferDelete(key) {
112
99
  this.localVersion++;
113
100
  this.deleteBuffer.add(key);
114
101
  this.writeBuffer.delete(key);
102
+ this.createdKeys.delete(key);
115
103
  this.keyVersions.set(key, this.localVersion);
116
- return this;
117
104
  }
118
105
  /**
119
106
  * Rolls back the transaction.
120
107
  * Clears all buffers and marks the transaction as finished.
121
- * @returns The transaction instance.
108
+ * @returns The result object with success, created, updated, and deleted keys.
122
109
  */
123
110
  rollback() {
111
+ const created = [];
112
+ const updated = [];
113
+ for (const [key, data] of this.writeBuffer.entries()) {
114
+ if (this.createdKeys.has(key)) {
115
+ created.push({ key, data });
116
+ } else {
117
+ updated.push({ key, data });
118
+ }
119
+ }
120
+ const deleted = [];
121
+ for (const key of this.deleteBuffer) {
122
+ const data = this.deletedValues.get(key);
123
+ if (data !== void 0) {
124
+ deleted.push({ key, data });
125
+ }
126
+ }
124
127
  this.writeBuffer.clear();
125
128
  this.deleteBuffer.clear();
129
+ this.createdKeys.clear();
130
+ this.deletedValues.clear();
126
131
  this.committed = true;
127
132
  if (this.root !== this) {
128
133
  this.root.activeTransactions.delete(this);
129
134
  }
130
- return this;
135
+ return { success: true, created, updated, deleted };
131
136
  }
132
137
  };
133
138
 
@@ -137,7 +142,39 @@ var SyncMVCCStrategy = class extends MVCCStrategy {
137
142
 
138
143
  // src/core/sync/Transaction.ts
139
144
  var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
145
+ create(key, value) {
146
+ if (this.committed) throw new Error("Transaction already committed");
147
+ if (this.writeBuffer.has(key) || !this.deleteBuffer.has(key) && this.read(key) !== null) {
148
+ throw new Error(`Key already exists: ${key}`);
149
+ }
150
+ this._bufferCreate(key, value);
151
+ return this;
152
+ }
153
+ write(key, value) {
154
+ if (this.committed) throw new Error("Transaction already committed");
155
+ if (!this.writeBuffer.has(key) && (this.deleteBuffer.has(key) || this.read(key) === null)) {
156
+ throw new Error(`Key not found: ${key}`);
157
+ }
158
+ this._bufferWrite(key, value);
159
+ return this;
160
+ }
161
+ delete(key) {
162
+ if (this.committed) throw new Error("Transaction already committed");
163
+ let valueToDelete = null;
164
+ if (this.writeBuffer.has(key)) {
165
+ valueToDelete = this.writeBuffer.get(key);
166
+ } else if (!this.deleteBuffer.has(key)) {
167
+ valueToDelete = this.read(key);
168
+ }
169
+ if (valueToDelete === null) {
170
+ throw new Error(`Key not found: ${key}`);
171
+ }
172
+ this.deletedValues.set(key, valueToDelete);
173
+ this._bufferDelete(key);
174
+ return this;
175
+ }
140
176
  createNested() {
177
+ if (this.committed) throw new Error("Transaction already committed");
141
178
  const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
142
179
  const child = new _SyncMVCCTransaction(void 0, this, childVersion);
143
180
  this.root.activeTransactions.add(child);
@@ -149,6 +186,12 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
149
186
  if (this.deleteBuffer.has(key)) return null;
150
187
  return this.root._diskRead(key, this.snapshotVersion);
151
188
  }
189
+ exists(key) {
190
+ if (this.committed) throw new Error("Transaction already committed");
191
+ if (this.deleteBuffer.has(key)) return false;
192
+ if (this.writeBuffer.has(key)) return true;
193
+ return this.root._diskExists(key, this.snapshotVersion);
194
+ }
152
195
  _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
153
196
  if (this.writeBuffer.has(key)) {
154
197
  const keyModVersion = this.keyVersions.get(key);
@@ -169,43 +212,79 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
169
212
  }
170
213
  }
171
214
  commit() {
172
- if (this.committed) throw new Error("Transaction already committed");
215
+ if (this.committed) {
216
+ return { success: false, error: "Transaction already committed", created: [], updated: [], deleted: [] };
217
+ }
218
+ const created = [];
219
+ const updated = [];
220
+ for (const [key, data] of this.writeBuffer.entries()) {
221
+ if (this.createdKeys.has(key)) {
222
+ created.push({ key, data });
223
+ } else {
224
+ updated.push({ key, data });
225
+ }
226
+ }
227
+ const deleted = [];
228
+ for (const key of this.deleteBuffer) {
229
+ const data = this.deletedValues.get(key);
230
+ if (data !== void 0) {
231
+ deleted.push({ key, data });
232
+ }
233
+ }
173
234
  if (this.parent) {
174
- this.parent._merge(this);
235
+ const error = this.parent._merge(this);
236
+ if (error) {
237
+ return { success: false, error, created: [], updated: [], deleted: [] };
238
+ }
175
239
  this.committed = true;
176
240
  } else {
177
241
  if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
178
- this._merge(this);
242
+ const error = this._merge(this);
243
+ if (error) {
244
+ return { success: false, error, created: [], updated: [], deleted: [] };
245
+ }
179
246
  this.writeBuffer.clear();
180
247
  this.deleteBuffer.clear();
248
+ this.createdKeys.clear();
249
+ this.deletedValues.clear();
181
250
  this.keyVersions.clear();
182
251
  this.localVersion = 0;
183
252
  }
184
253
  }
185
- return this;
254
+ return { success: true, created, updated, deleted };
186
255
  }
187
256
  _merge(child) {
188
257
  if (this.parent) {
189
258
  for (const key of child.writeBuffer.keys()) {
190
259
  const lastModLocalVer = this.keyVersions.get(key);
191
260
  if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
192
- throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`);
261
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
193
262
  }
194
263
  }
195
264
  for (const key of child.deleteBuffer) {
196
265
  const lastModLocalVer = this.keyVersions.get(key);
197
266
  if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
198
- throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`);
267
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
199
268
  }
200
269
  }
201
270
  const newLocalVersion = this.localVersion + 1;
202
271
  for (const key of child.writeBuffer.keys()) {
203
- this.write(key, child.writeBuffer.get(key));
272
+ this.writeBuffer.set(key, child.writeBuffer.get(key));
273
+ this.deleteBuffer.delete(key);
204
274
  this.keyVersions.set(key, newLocalVersion);
275
+ if (child.createdKeys.has(key)) {
276
+ this.createdKeys.add(key);
277
+ }
205
278
  }
206
279
  for (const key of child.deleteBuffer) {
207
- this.delete(key);
280
+ this.deleteBuffer.add(key);
281
+ this.writeBuffer.delete(key);
282
+ this.createdKeys.delete(key);
208
283
  this.keyVersions.set(key, newLocalVersion);
284
+ const deletedValue = child.deletedValues.get(key);
285
+ if (deletedValue !== void 0) {
286
+ this.deletedValues.set(key, deletedValue);
287
+ }
209
288
  }
210
289
  this.localVersion = newLocalVersion;
211
290
  this.root.activeTransactions.delete(child);
@@ -218,7 +297,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
218
297
  if (versions && versions.length > 0) {
219
298
  const lastVer = versions[versions.length - 1].version;
220
299
  if (lastVer > child.snapshotVersion) {
221
- throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`);
300
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`;
222
301
  }
223
302
  }
224
303
  }
@@ -231,6 +310,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
231
310
  this.version = newVersion;
232
311
  this._cleanupDeletedCache();
233
312
  }
313
+ return null;
234
314
  }
235
315
  // --- Internal IO Helpers (Root Only) ---
236
316
  _diskWrite(key, value, version) {
@@ -276,6 +356,24 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
276
356
  }
277
357
  return null;
278
358
  }
359
+ _diskExists(key, snapshotVersion) {
360
+ const strategy = this.strategy;
361
+ if (!strategy) throw new Error("Root Transaction missing strategy");
362
+ const versions = this.versionIndex.get(key);
363
+ if (!versions) {
364
+ return strategy.exists(key);
365
+ }
366
+ let targetVerObj = null;
367
+ for (const v of versions) {
368
+ if (v.version <= snapshotVersion) {
369
+ targetVerObj = v;
370
+ } else {
371
+ break;
372
+ }
373
+ }
374
+ if (!targetVerObj) return strategy.exists(key);
375
+ return targetVerObj.exists;
376
+ }
279
377
  _diskDelete(key, snapshotVersion) {
280
378
  const strategy = this.strategy;
281
379
  if (!strategy) throw new Error("Root Transaction missing strategy");
@@ -586,7 +684,39 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
586
684
  this.lock.writeUnlock(lockId);
587
685
  });
588
686
  }
687
+ async create(key, value) {
688
+ if (this.committed) throw new Error("Transaction already committed");
689
+ if (this.writeBuffer.has(key) || !this.deleteBuffer.has(key) && await this.read(key) !== null) {
690
+ throw new Error(`Key already exists: ${key}`);
691
+ }
692
+ this._bufferCreate(key, value);
693
+ return this;
694
+ }
695
+ async write(key, value) {
696
+ if (this.committed) throw new Error("Transaction already committed");
697
+ if (!this.writeBuffer.has(key) && (this.deleteBuffer.has(key) || await this.read(key) === null)) {
698
+ throw new Error(`Key not found: ${key}`);
699
+ }
700
+ this._bufferWrite(key, value);
701
+ return this;
702
+ }
703
+ async delete(key) {
704
+ if (this.committed) throw new Error("Transaction already committed");
705
+ let valueToDelete = null;
706
+ if (this.writeBuffer.has(key)) {
707
+ valueToDelete = this.writeBuffer.get(key);
708
+ } else if (!this.deleteBuffer.has(key)) {
709
+ valueToDelete = await this.read(key);
710
+ }
711
+ if (valueToDelete === null) {
712
+ throw new Error(`Key not found: ${key}`);
713
+ }
714
+ this.deletedValues.set(key, valueToDelete);
715
+ this._bufferDelete(key);
716
+ return this;
717
+ }
589
718
  createNested() {
719
+ if (this.committed) throw new Error("Transaction already committed");
590
720
  const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
591
721
  const child = new _AsyncMVCCTransaction(void 0, this, childVersion);
592
722
  this.root.activeTransactions.add(child);
@@ -598,6 +728,12 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
598
728
  if (this.deleteBuffer.has(key)) return null;
599
729
  return this.root._diskRead(key, this.snapshotVersion);
600
730
  }
731
+ async exists(key) {
732
+ if (this.committed) throw new Error("Transaction already committed");
733
+ if (this.deleteBuffer.has(key)) return false;
734
+ if (this.writeBuffer.has(key)) return true;
735
+ return this.root._diskExists(key, this.snapshotVersion);
736
+ }
601
737
  async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
602
738
  if (this.writeBuffer.has(key)) {
603
739
  const keyModVersion = this.keyVersions.get(key);
@@ -619,20 +755,46 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
619
755
  }
620
756
  async commit() {
621
757
  return this.writeLock(async () => {
622
- if (this.committed) throw new Error("Transaction already committed");
758
+ if (this.committed) {
759
+ return { success: false, error: "Transaction already committed", created: [], updated: [], deleted: [] };
760
+ }
761
+ const created = [];
762
+ const updated = [];
763
+ for (const [key, data] of this.writeBuffer.entries()) {
764
+ if (this.createdKeys.has(key)) {
765
+ created.push({ key, data });
766
+ } else {
767
+ updated.push({ key, data });
768
+ }
769
+ }
770
+ const deleted = [];
771
+ for (const key of this.deleteBuffer) {
772
+ const data = this.deletedValues.get(key);
773
+ if (data !== void 0) {
774
+ deleted.push({ key, data });
775
+ }
776
+ }
623
777
  if (this.parent) {
624
- await this.parent._merge(this);
778
+ const error = await this.parent._merge(this);
779
+ if (error) {
780
+ return { success: false, error, created: [], updated: [], deleted: [] };
781
+ }
625
782
  this.committed = true;
626
783
  } else {
627
784
  if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
628
- await this._merge(this);
785
+ const error = await this._merge(this);
786
+ if (error) {
787
+ return { success: false, error, created: [], updated: [], deleted: [] };
788
+ }
629
789
  this.writeBuffer.clear();
630
790
  this.deleteBuffer.clear();
791
+ this.createdKeys.clear();
792
+ this.deletedValues.clear();
631
793
  this.keyVersions.clear();
632
794
  this.localVersion = 0;
633
795
  }
634
796
  }
635
- return this;
797
+ return { success: true, created, updated, deleted };
636
798
  });
637
799
  }
638
800
  async _merge(child) {
@@ -641,23 +803,33 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
641
803
  for (const key of child.writeBuffer.keys()) {
642
804
  const lastModLocalVer = this.keyVersions.get(key);
643
805
  if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
644
- throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`);
806
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
645
807
  }
646
808
  }
647
809
  for (const key of child.deleteBuffer) {
648
810
  const lastModLocalVer = this.keyVersions.get(key);
649
811
  if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
650
- throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`);
812
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
651
813
  }
652
814
  }
653
815
  const newLocalVersion = this.localVersion + 1;
654
816
  for (const key of child.writeBuffer.keys()) {
655
- this.write(key, child.writeBuffer.get(key));
817
+ this.writeBuffer.set(key, child.writeBuffer.get(key));
818
+ this.deleteBuffer.delete(key);
656
819
  this.keyVersions.set(key, newLocalVersion);
820
+ if (child.createdKeys.has(key)) {
821
+ this.createdKeys.add(key);
822
+ }
657
823
  }
658
824
  for (const key of child.deleteBuffer) {
659
- this.delete(key);
825
+ this.deleteBuffer.add(key);
826
+ this.writeBuffer.delete(key);
827
+ this.createdKeys.delete(key);
660
828
  this.keyVersions.set(key, newLocalVersion);
829
+ const deletedValue = child.deletedValues.get(key);
830
+ if (deletedValue !== void 0) {
831
+ this.deletedValues.set(key, deletedValue);
832
+ }
661
833
  }
662
834
  this.localVersion = newLocalVersion;
663
835
  this.root.activeTransactions.delete(child);
@@ -670,7 +842,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
670
842
  if (versions && versions.length > 0) {
671
843
  const lastVer = versions[versions.length - 1].version;
672
844
  if (lastVer > child.snapshotVersion) {
673
- throw new Error(`Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`);
845
+ return `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`;
674
846
  }
675
847
  }
676
848
  }
@@ -683,6 +855,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
683
855
  this.version = newVersion;
684
856
  this._cleanupDeletedCache();
685
857
  }
858
+ return null;
686
859
  });
687
860
  }
688
861
  // --- Internal IO Helpers (Root Only) ---
@@ -729,6 +902,24 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
729
902
  }
730
903
  return null;
731
904
  }
905
+ async _diskExists(key, snapshotVersion) {
906
+ const strategy = this.strategy;
907
+ if (!strategy) throw new Error("Root Transaction missing strategy");
908
+ const versions = this.versionIndex.get(key);
909
+ if (!versions) {
910
+ return strategy.exists(key);
911
+ }
912
+ let targetVerObj = null;
913
+ for (const v of versions) {
914
+ if (v.version <= snapshotVersion) {
915
+ targetVerObj = v;
916
+ } else {
917
+ break;
918
+ }
919
+ }
920
+ if (!targetVerObj) return strategy.exists(key);
921
+ return targetVerObj.exists;
922
+ }
732
923
  async _diskDelete(key, snapshotVersion) {
733
924
  const strategy = this.strategy;
734
925
  if (!strategy) throw new Error("Root Transaction missing strategy");