mvcc-api 1.2.1 → 1.2.3

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.
@@ -42,6 +42,8 @@ var MVCCTransaction = class {
42
42
  // create()로 생성된 키 추적
43
43
  deletedValues;
44
44
  // delete 시 삭제 전 값 저장
45
+ originallyExisted;
46
+ // 트랜잭션 시작 시점에 디스크에 존재했던 키 (deleted 결과 필터링용)
45
47
  // Nested Transaction Properties
46
48
  parent;
47
49
  localVersion;
@@ -61,6 +63,7 @@ var MVCCTransaction = class {
61
63
  this.deleteBuffer = /* @__PURE__ */ new Set();
62
64
  this.createdKeys = /* @__PURE__ */ new Set();
63
65
  this.deletedValues = /* @__PURE__ */ new Map();
66
+ this.originallyExisted = /* @__PURE__ */ new Set();
64
67
  this.committed = false;
65
68
  this.parent = parent;
66
69
  this.keyVersions = /* @__PURE__ */ new Map();
@@ -81,12 +84,26 @@ var MVCCTransaction = class {
81
84
  isRoot() {
82
85
  return !this.parent;
83
86
  }
87
+ /**
88
+ * Checks if any ancestor transaction has already been committed.
89
+ * A nested transaction cannot commit if its parent or any higher ancestor is committed.
90
+ * @returns True if at least one ancestor is committed, false otherwise.
91
+ */
92
+ hasCommittedAncestor() {
93
+ let current = this.parent;
94
+ while (current) {
95
+ if (current.committed) return true;
96
+ current = current.parent;
97
+ }
98
+ return false;
99
+ }
84
100
  // --- Internal buffer manipulation helpers ---
85
101
  _bufferCreate(key, value) {
86
102
  this.localVersion++;
87
103
  this.writeBuffer.set(key, value);
88
104
  this.createdKeys.add(key);
89
105
  this.deleteBuffer.delete(key);
106
+ this.originallyExisted.delete(key);
90
107
  this.keyVersions.set(key, this.localVersion);
91
108
  }
92
109
  _bufferWrite(key, value) {
@@ -102,12 +119,7 @@ var MVCCTransaction = class {
102
119
  this.createdKeys.delete(key);
103
120
  this.keyVersions.set(key, this.localVersion);
104
121
  }
105
- /**
106
- * Rolls back the transaction.
107
- * Clears all buffers and marks the transaction as finished.
108
- * @returns The result object with success, created, updated, and deleted keys.
109
- */
110
- rollback() {
122
+ _getResultEntries() {
111
123
  const created = [];
112
124
  const updated = [];
113
125
  for (const [key, data] of this.writeBuffer.entries()) {
@@ -119,15 +131,26 @@ var MVCCTransaction = class {
119
131
  }
120
132
  const deleted = [];
121
133
  for (const key of this.deleteBuffer) {
134
+ if (!this.originallyExisted.has(key)) continue;
122
135
  const data = this.deletedValues.get(key);
123
136
  if (data !== void 0) {
124
137
  deleted.push({ key, data });
125
138
  }
126
139
  }
140
+ return { created, updated, deleted };
141
+ }
142
+ /**
143
+ * Rolls back the transaction.
144
+ * Clears all buffers and marks the transaction as finished.
145
+ * @returns The result object with success, created, updated, and deleted keys.
146
+ */
147
+ rollback() {
148
+ const { created, updated, deleted } = this._getResultEntries();
127
149
  this.writeBuffer.clear();
128
150
  this.deleteBuffer.clear();
129
151
  this.createdKeys.clear();
130
152
  this.deletedValues.clear();
153
+ this.originallyExisted.clear();
131
154
  this.committed = true;
132
155
  if (this.root !== this) {
133
156
  this.root.activeTransactions.delete(this);
@@ -161,8 +184,10 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
161
184
  delete(key) {
162
185
  if (this.committed) throw new Error("Transaction already committed");
163
186
  let valueToDelete = null;
187
+ let wasInWriteBuffer = false;
164
188
  if (this.writeBuffer.has(key)) {
165
189
  valueToDelete = this.writeBuffer.get(key);
190
+ wasInWriteBuffer = true;
166
191
  } else if (!this.deleteBuffer.has(key)) {
167
192
  valueToDelete = this.read(key);
168
193
  }
@@ -170,6 +195,9 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
170
195
  throw new Error(`Key not found: ${key}`);
171
196
  }
172
197
  this.deletedValues.set(key, valueToDelete);
198
+ if (!wasInWriteBuffer || !this.createdKeys.has(key)) {
199
+ this.originallyExisted.add(key);
200
+ }
173
201
  this._bufferDelete(key);
174
202
  return this;
175
203
  }
@@ -212,29 +240,17 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
212
240
  }
213
241
  }
214
242
  commit() {
243
+ const { created, updated, deleted } = this._getResultEntries();
215
244
  if (this.committed) {
216
- return { success: false, error: "Transaction already committed", created: [], updated: [], deleted: [] };
245
+ return { success: false, error: "Transaction already committed", created, updated, deleted };
217
246
  }
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
- }
247
+ if (this.hasCommittedAncestor()) {
248
+ return { success: false, error: "Ancestor transaction already committed", created, updated, deleted };
233
249
  }
234
250
  if (this.parent) {
235
251
  const error = this.parent._merge(this);
236
252
  if (error) {
237
- return { success: false, error, created: [], updated: [], deleted: [] };
253
+ return { success: false, error, created, updated, deleted };
238
254
  }
239
255
  this.committed = true;
240
256
  } else {
@@ -247,6 +263,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
247
263
  this.deleteBuffer.clear();
248
264
  this.createdKeys.clear();
249
265
  this.deletedValues.clear();
266
+ this.originallyExisted.clear();
250
267
  this.keyVersions.clear();
251
268
  this.localVersion = 0;
252
269
  }
@@ -285,6 +302,9 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
285
302
  if (deletedValue !== void 0) {
286
303
  this.deletedValues.set(key, deletedValue);
287
304
  }
305
+ if (child.originallyExisted.has(key)) {
306
+ this.originallyExisted.add(key);
307
+ }
288
308
  }
289
309
  this.localVersion = newLocalVersion;
290
310
  this.root.activeTransactions.delete(child);
@@ -703,8 +723,10 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
703
723
  async delete(key) {
704
724
  if (this.committed) throw new Error("Transaction already committed");
705
725
  let valueToDelete = null;
726
+ let wasInWriteBuffer = false;
706
727
  if (this.writeBuffer.has(key)) {
707
728
  valueToDelete = this.writeBuffer.get(key);
729
+ wasInWriteBuffer = true;
708
730
  } else if (!this.deleteBuffer.has(key)) {
709
731
  valueToDelete = await this.read(key);
710
732
  }
@@ -712,6 +734,9 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
712
734
  throw new Error(`Key not found: ${key}`);
713
735
  }
714
736
  this.deletedValues.set(key, valueToDelete);
737
+ if (!wasInWriteBuffer || !this.createdKeys.has(key)) {
738
+ this.originallyExisted.add(key);
739
+ }
715
740
  this._bufferDelete(key);
716
741
  return this;
717
742
  }
@@ -755,29 +780,17 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
755
780
  }
756
781
  async commit() {
757
782
  return this.writeLock(async () => {
783
+ const { created, updated, deleted } = this._getResultEntries();
758
784
  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
- }
785
+ return { success: false, error: "Transaction already committed", created, updated, deleted };
769
786
  }
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
- }
787
+ if (this.hasCommittedAncestor()) {
788
+ return { success: false, error: "Ancestor transaction already committed", created, updated, deleted };
776
789
  }
777
790
  if (this.parent) {
778
791
  const error = await this.parent._merge(this);
779
792
  if (error) {
780
- return { success: false, error, created: [], updated: [], deleted: [] };
793
+ return { success: false, error, created, updated, deleted };
781
794
  }
782
795
  this.committed = true;
783
796
  } else {
@@ -790,6 +803,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
790
803
  this.deleteBuffer.clear();
791
804
  this.createdKeys.clear();
792
805
  this.deletedValues.clear();
806
+ this.originallyExisted.clear();
793
807
  this.keyVersions.clear();
794
808
  this.localVersion = 0;
795
809
  }
@@ -830,6 +844,9 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
830
844
  if (deletedValue !== void 0) {
831
845
  this.deletedValues.set(key, deletedValue);
832
846
  }
847
+ if (child.originallyExisted.has(key)) {
848
+ this.originallyExisted.add(key);
849
+ }
833
850
  }
834
851
  this.localVersion = newLocalVersion;
835
852
  this.root.activeTransactions.delete(child);
@@ -13,6 +13,8 @@ var MVCCTransaction = class {
13
13
  // create()로 생성된 키 추적
14
14
  deletedValues;
15
15
  // delete 시 삭제 전 값 저장
16
+ originallyExisted;
17
+ // 트랜잭션 시작 시점에 디스크에 존재했던 키 (deleted 결과 필터링용)
16
18
  // Nested Transaction Properties
17
19
  parent;
18
20
  localVersion;
@@ -32,6 +34,7 @@ var MVCCTransaction = class {
32
34
  this.deleteBuffer = /* @__PURE__ */ new Set();
33
35
  this.createdKeys = /* @__PURE__ */ new Set();
34
36
  this.deletedValues = /* @__PURE__ */ new Map();
37
+ this.originallyExisted = /* @__PURE__ */ new Set();
35
38
  this.committed = false;
36
39
  this.parent = parent;
37
40
  this.keyVersions = /* @__PURE__ */ new Map();
@@ -52,12 +55,26 @@ var MVCCTransaction = class {
52
55
  isRoot() {
53
56
  return !this.parent;
54
57
  }
58
+ /**
59
+ * Checks if any ancestor transaction has already been committed.
60
+ * A nested transaction cannot commit if its parent or any higher ancestor is committed.
61
+ * @returns True if at least one ancestor is committed, false otherwise.
62
+ */
63
+ hasCommittedAncestor() {
64
+ let current = this.parent;
65
+ while (current) {
66
+ if (current.committed) return true;
67
+ current = current.parent;
68
+ }
69
+ return false;
70
+ }
55
71
  // --- Internal buffer manipulation helpers ---
56
72
  _bufferCreate(key, value) {
57
73
  this.localVersion++;
58
74
  this.writeBuffer.set(key, value);
59
75
  this.createdKeys.add(key);
60
76
  this.deleteBuffer.delete(key);
77
+ this.originallyExisted.delete(key);
61
78
  this.keyVersions.set(key, this.localVersion);
62
79
  }
63
80
  _bufferWrite(key, value) {
@@ -73,12 +90,7 @@ var MVCCTransaction = class {
73
90
  this.createdKeys.delete(key);
74
91
  this.keyVersions.set(key, this.localVersion);
75
92
  }
76
- /**
77
- * Rolls back the transaction.
78
- * Clears all buffers and marks the transaction as finished.
79
- * @returns The result object with success, created, updated, and deleted keys.
80
- */
81
- rollback() {
93
+ _getResultEntries() {
82
94
  const created = [];
83
95
  const updated = [];
84
96
  for (const [key, data] of this.writeBuffer.entries()) {
@@ -90,15 +102,26 @@ var MVCCTransaction = class {
90
102
  }
91
103
  const deleted = [];
92
104
  for (const key of this.deleteBuffer) {
105
+ if (!this.originallyExisted.has(key)) continue;
93
106
  const data = this.deletedValues.get(key);
94
107
  if (data !== void 0) {
95
108
  deleted.push({ key, data });
96
109
  }
97
110
  }
111
+ return { created, updated, deleted };
112
+ }
113
+ /**
114
+ * Rolls back the transaction.
115
+ * Clears all buffers and marks the transaction as finished.
116
+ * @returns The result object with success, created, updated, and deleted keys.
117
+ */
118
+ rollback() {
119
+ const { created, updated, deleted } = this._getResultEntries();
98
120
  this.writeBuffer.clear();
99
121
  this.deleteBuffer.clear();
100
122
  this.createdKeys.clear();
101
123
  this.deletedValues.clear();
124
+ this.originallyExisted.clear();
102
125
  this.committed = true;
103
126
  if (this.root !== this) {
104
127
  this.root.activeTransactions.delete(this);
@@ -132,8 +155,10 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
132
155
  delete(key) {
133
156
  if (this.committed) throw new Error("Transaction already committed");
134
157
  let valueToDelete = null;
158
+ let wasInWriteBuffer = false;
135
159
  if (this.writeBuffer.has(key)) {
136
160
  valueToDelete = this.writeBuffer.get(key);
161
+ wasInWriteBuffer = true;
137
162
  } else if (!this.deleteBuffer.has(key)) {
138
163
  valueToDelete = this.read(key);
139
164
  }
@@ -141,6 +166,9 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
141
166
  throw new Error(`Key not found: ${key}`);
142
167
  }
143
168
  this.deletedValues.set(key, valueToDelete);
169
+ if (!wasInWriteBuffer || !this.createdKeys.has(key)) {
170
+ this.originallyExisted.add(key);
171
+ }
144
172
  this._bufferDelete(key);
145
173
  return this;
146
174
  }
@@ -183,29 +211,17 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
183
211
  }
184
212
  }
185
213
  commit() {
214
+ const { created, updated, deleted } = this._getResultEntries();
186
215
  if (this.committed) {
187
- return { success: false, error: "Transaction already committed", created: [], updated: [], deleted: [] };
216
+ return { success: false, error: "Transaction already committed", created, updated, deleted };
188
217
  }
189
- const created = [];
190
- const updated = [];
191
- for (const [key, data] of this.writeBuffer.entries()) {
192
- if (this.createdKeys.has(key)) {
193
- created.push({ key, data });
194
- } else {
195
- updated.push({ key, data });
196
- }
197
- }
198
- const deleted = [];
199
- for (const key of this.deleteBuffer) {
200
- const data = this.deletedValues.get(key);
201
- if (data !== void 0) {
202
- deleted.push({ key, data });
203
- }
218
+ if (this.hasCommittedAncestor()) {
219
+ return { success: false, error: "Ancestor transaction already committed", created, updated, deleted };
204
220
  }
205
221
  if (this.parent) {
206
222
  const error = this.parent._merge(this);
207
223
  if (error) {
208
- return { success: false, error, created: [], updated: [], deleted: [] };
224
+ return { success: false, error, created, updated, deleted };
209
225
  }
210
226
  this.committed = true;
211
227
  } else {
@@ -218,6 +234,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
218
234
  this.deleteBuffer.clear();
219
235
  this.createdKeys.clear();
220
236
  this.deletedValues.clear();
237
+ this.originallyExisted.clear();
221
238
  this.keyVersions.clear();
222
239
  this.localVersion = 0;
223
240
  }
@@ -256,6 +273,9 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
256
273
  if (deletedValue !== void 0) {
257
274
  this.deletedValues.set(key, deletedValue);
258
275
  }
276
+ if (child.originallyExisted.has(key)) {
277
+ this.originallyExisted.add(key);
278
+ }
259
279
  }
260
280
  this.localVersion = newLocalVersion;
261
281
  this.root.activeTransactions.delete(child);
@@ -674,8 +694,10 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
674
694
  async delete(key) {
675
695
  if (this.committed) throw new Error("Transaction already committed");
676
696
  let valueToDelete = null;
697
+ let wasInWriteBuffer = false;
677
698
  if (this.writeBuffer.has(key)) {
678
699
  valueToDelete = this.writeBuffer.get(key);
700
+ wasInWriteBuffer = true;
679
701
  } else if (!this.deleteBuffer.has(key)) {
680
702
  valueToDelete = await this.read(key);
681
703
  }
@@ -683,6 +705,9 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
683
705
  throw new Error(`Key not found: ${key}`);
684
706
  }
685
707
  this.deletedValues.set(key, valueToDelete);
708
+ if (!wasInWriteBuffer || !this.createdKeys.has(key)) {
709
+ this.originallyExisted.add(key);
710
+ }
686
711
  this._bufferDelete(key);
687
712
  return this;
688
713
  }
@@ -726,29 +751,17 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
726
751
  }
727
752
  async commit() {
728
753
  return this.writeLock(async () => {
754
+ const { created, updated, deleted } = this._getResultEntries();
729
755
  if (this.committed) {
730
- return { success: false, error: "Transaction already committed", created: [], updated: [], deleted: [] };
731
- }
732
- const created = [];
733
- const updated = [];
734
- for (const [key, data] of this.writeBuffer.entries()) {
735
- if (this.createdKeys.has(key)) {
736
- created.push({ key, data });
737
- } else {
738
- updated.push({ key, data });
739
- }
756
+ return { success: false, error: "Transaction already committed", created, updated, deleted };
740
757
  }
741
- const deleted = [];
742
- for (const key of this.deleteBuffer) {
743
- const data = this.deletedValues.get(key);
744
- if (data !== void 0) {
745
- deleted.push({ key, data });
746
- }
758
+ if (this.hasCommittedAncestor()) {
759
+ return { success: false, error: "Ancestor transaction already committed", created, updated, deleted };
747
760
  }
748
761
  if (this.parent) {
749
762
  const error = await this.parent._merge(this);
750
763
  if (error) {
751
- return { success: false, error, created: [], updated: [], deleted: [] };
764
+ return { success: false, error, created, updated, deleted };
752
765
  }
753
766
  this.committed = true;
754
767
  } else {
@@ -761,6 +774,7 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
761
774
  this.deleteBuffer.clear();
762
775
  this.createdKeys.clear();
763
776
  this.deletedValues.clear();
777
+ this.originallyExisted.clear();
764
778
  this.keyVersions.clear();
765
779
  this.localVersion = 0;
766
780
  }
@@ -801,6 +815,9 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
801
815
  if (deletedValue !== void 0) {
802
816
  this.deletedValues.set(key, deletedValue);
803
817
  }
818
+ if (child.originallyExisted.has(key)) {
819
+ this.originallyExisted.add(key);
820
+ }
804
821
  }
805
822
  this.localVersion = newLocalVersion;
806
823
  this.root.activeTransactions.delete(child);
@@ -1,4 +1,4 @@
1
- import type { Deferred, TransactionResult } from '../../types';
1
+ import type { Deferred, TransactionResult, TransactionEntry } from '../../types';
2
2
  import type { MVCCStrategy } from './Strategy';
3
3
  /**
4
4
  * MVCC Transaction abstract class.
@@ -16,6 +16,7 @@ export declare abstract class MVCCTransaction<S extends MVCCStrategy<K, T>, K, T
16
16
  readonly deleteBuffer: Set<K>;
17
17
  readonly createdKeys: Set<K>;
18
18
  readonly deletedValues: Map<K, T>;
19
+ readonly originallyExisted: Set<K>;
19
20
  readonly parent?: MVCCTransaction<S, K, T>;
20
21
  localVersion: number;
21
22
  readonly keyVersions: Map<K, number>;
@@ -33,6 +34,12 @@ export declare abstract class MVCCTransaction<S extends MVCCStrategy<K, T>, K, T
33
34
  protected activeTransactions: Set<MVCCTransaction<S, K, T>>;
34
35
  constructor(strategy?: S, parent?: MVCCTransaction<S, K, T>, snapshotVersion?: number);
35
36
  isRoot(): boolean;
37
+ /**
38
+ * Checks if any ancestor transaction has already been committed.
39
+ * A nested transaction cannot commit if its parent or any higher ancestor is committed.
40
+ * @returns True if at least one ancestor is committed, false otherwise.
41
+ */
42
+ hasCommittedAncestor(): boolean;
36
43
  /**
37
44
  * Schedules a creation (insert) of a key-value pair.
38
45
  * Throws if the key already exists.
@@ -59,6 +66,11 @@ export declare abstract class MVCCTransaction<S extends MVCCStrategy<K, T>, K, T
59
66
  protected _bufferCreate(key: K, value: T): void;
60
67
  protected _bufferWrite(key: K, value: T): void;
61
68
  protected _bufferDelete(key: K): void;
69
+ protected _getResultEntries(): {
70
+ created: TransactionEntry<K, T>[];
71
+ updated: TransactionEntry<K, T>[];
72
+ deleted: TransactionEntry<K, T>[];
73
+ };
62
74
  /**
63
75
  * Rolls back the transaction.
64
76
  * Clears all buffers and marks the transaction as finished.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mvcc-api",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "Multiversion Concurrency Control (MVCC) API for TypeScript",
5
5
  "license": "MIT",
6
6
  "author": "izure <admin@izure.org>",