mvcc-api 1.0.3 → 1.2.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.
- package/README.md +156 -110
- package/dist/cjs/index.cjs +519 -275
- package/dist/esm/index.mjs +519 -266
- package/dist/types/core/async/Transaction.d.ts +15 -3
- package/dist/types/core/async/index.d.ts +0 -1
- package/dist/types/core/base/Transaction.d.ts +59 -19
- package/dist/types/core/base/index.d.ts +0 -1
- package/dist/types/core/sync/Transaction.d.ts +13 -3
- package/dist/types/core/sync/index.d.ts +0 -1
- package/dist/types/types/index.d.ts +11 -0
- package/package.json +2 -2
- package/dist/types/core/async/Manager.d.ts +0 -14
- package/dist/types/core/base/Manager.d.ts +0 -59
- package/dist/types/core/sync/Manager.d.ts +0 -11
package/dist/cjs/index.cjs
CHANGED
|
@@ -20,251 +20,374 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
-
AsyncMVCCManager: () => AsyncMVCCManager,
|
|
24
23
|
AsyncMVCCStrategy: () => AsyncMVCCStrategy,
|
|
25
24
|
AsyncMVCCTransaction: () => AsyncMVCCTransaction,
|
|
26
|
-
SyncMVCCManager: () => SyncMVCCManager,
|
|
27
25
|
SyncMVCCStrategy: () => SyncMVCCStrategy,
|
|
28
26
|
SyncMVCCTransaction: () => SyncMVCCTransaction
|
|
29
27
|
});
|
|
30
28
|
module.exports = __toCommonJS(src_exports);
|
|
31
29
|
|
|
32
|
-
// src/core/base/Manager.ts
|
|
33
|
-
var MVCCManager = class {
|
|
34
|
-
version;
|
|
35
|
-
strategy;
|
|
36
|
-
versionIndex;
|
|
37
|
-
activeTransactions;
|
|
38
|
-
deletedCache;
|
|
39
|
-
constructor(strategy) {
|
|
40
|
-
this.strategy = strategy;
|
|
41
|
-
this.version = 0;
|
|
42
|
-
this.activeTransactions = /* @__PURE__ */ new Set();
|
|
43
|
-
this.deletedCache = /* @__PURE__ */ new Map();
|
|
44
|
-
this.versionIndex = /* @__PURE__ */ new Map();
|
|
45
|
-
}
|
|
46
|
-
_removeTransaction(tx) {
|
|
47
|
-
this.activeTransactions.delete(tx);
|
|
48
|
-
this._cleanupDeletedCache();
|
|
49
|
-
}
|
|
50
|
-
_cleanupDeletedCache() {
|
|
51
|
-
let minVersion = this.version;
|
|
52
|
-
for (const tx of this.activeTransactions) {
|
|
53
|
-
minVersion = Math.min(minVersion, tx.snapshotVersion);
|
|
54
|
-
}
|
|
55
|
-
for (const [key, versions] of this.versionIndex.entries()) {
|
|
56
|
-
const toKeep = [];
|
|
57
|
-
let keptOldVersion = false;
|
|
58
|
-
let i = versions.length;
|
|
59
|
-
while (i--) {
|
|
60
|
-
const v = versions[i];
|
|
61
|
-
if (v.version > minVersion) {
|
|
62
|
-
toKeep.unshift(v);
|
|
63
|
-
} else if (!keptOldVersion) {
|
|
64
|
-
toKeep.unshift(v);
|
|
65
|
-
keptOldVersion = true;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (toKeep.length === 0) {
|
|
69
|
-
this.versionIndex.delete(key);
|
|
70
|
-
} else {
|
|
71
|
-
this.versionIndex.set(key, toKeep);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
for (const [key, versions] of this.deletedCache.entries()) {
|
|
75
|
-
const filtered = versions.filter((v) => v.deletedAtVersion >= minVersion);
|
|
76
|
-
if (filtered.length === 0) {
|
|
77
|
-
this.deletedCache.delete(key);
|
|
78
|
-
} else {
|
|
79
|
-
this.deletedCache.set(key, filtered);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
30
|
// src/core/base/Strategy.ts
|
|
86
31
|
var MVCCStrategy = class {
|
|
87
32
|
};
|
|
88
33
|
|
|
89
34
|
// src/core/base/Transaction.ts
|
|
90
35
|
var MVCCTransaction = class {
|
|
91
|
-
manager;
|
|
92
36
|
committed;
|
|
93
37
|
snapshotVersion;
|
|
38
|
+
snapshotLocalVersion;
|
|
94
39
|
writeBuffer;
|
|
95
40
|
deleteBuffer;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
41
|
+
createdKeys;
|
|
42
|
+
// create()로 생성된 키 추적
|
|
43
|
+
deletedValues;
|
|
44
|
+
// delete 시 삭제 전 값 저장
|
|
45
|
+
// Nested Transaction Properties
|
|
46
|
+
parent;
|
|
47
|
+
localVersion;
|
|
48
|
+
// Local version for Nested Conflict Detection
|
|
49
|
+
keyVersions;
|
|
50
|
+
// Key -> Local Version (When it was modified locally)
|
|
51
|
+
// Root Transaction Properties (Only populated if this is Root)
|
|
52
|
+
root;
|
|
53
|
+
strategy;
|
|
54
|
+
version = 0;
|
|
55
|
+
versionIndex = /* @__PURE__ */ new Map();
|
|
56
|
+
deletedCache = /* @__PURE__ */ new Map();
|
|
57
|
+
activeTransactions = /* @__PURE__ */ new Set();
|
|
58
|
+
constructor(strategy, parent, snapshotVersion) {
|
|
59
|
+
this.snapshotVersion = snapshotVersion ?? 0;
|
|
99
60
|
this.writeBuffer = /* @__PURE__ */ new Map();
|
|
100
61
|
this.deleteBuffer = /* @__PURE__ */ new Set();
|
|
62
|
+
this.createdKeys = /* @__PURE__ */ new Set();
|
|
63
|
+
this.deletedValues = /* @__PURE__ */ new Map();
|
|
101
64
|
this.committed = false;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
65
|
+
this.parent = parent;
|
|
66
|
+
this.keyVersions = /* @__PURE__ */ new Map();
|
|
67
|
+
if (parent) {
|
|
68
|
+
this.localVersion = parent.localVersion;
|
|
69
|
+
this.snapshotLocalVersion = parent.localVersion;
|
|
70
|
+
this.strategy = void 0;
|
|
71
|
+
this.root = parent.root;
|
|
72
|
+
} else {
|
|
73
|
+
if (!strategy) throw new Error("Root Transaction must get Strategy");
|
|
74
|
+
this.strategy = strategy;
|
|
75
|
+
this.version = 0;
|
|
76
|
+
this.localVersion = 0;
|
|
77
|
+
this.snapshotLocalVersion = 0;
|
|
78
|
+
this.root = this;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
isRoot() {
|
|
82
|
+
return !this.parent;
|
|
83
|
+
}
|
|
84
|
+
// --- Internal buffer manipulation helpers ---
|
|
85
|
+
_bufferCreate(key, value) {
|
|
86
|
+
this.localVersion++;
|
|
112
87
|
this.writeBuffer.set(key, value);
|
|
113
|
-
|
|
88
|
+
this.createdKeys.add(key);
|
|
89
|
+
this.deleteBuffer.delete(key);
|
|
90
|
+
this.keyVersions.set(key, this.localVersion);
|
|
114
91
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
* Overwrites any existing value in the buffer.
|
|
118
|
-
* @param key The key to write.
|
|
119
|
-
* @param value The value to store.
|
|
120
|
-
* @returns The transaction instance for chaining.
|
|
121
|
-
*/
|
|
122
|
-
write(key, value) {
|
|
123
|
-
if (this.committed) throw new Error("Transaction already committed");
|
|
92
|
+
_bufferWrite(key, value) {
|
|
93
|
+
this.localVersion++;
|
|
124
94
|
this.writeBuffer.set(key, value);
|
|
125
95
|
this.deleteBuffer.delete(key);
|
|
126
|
-
|
|
96
|
+
this.keyVersions.set(key, this.localVersion);
|
|
127
97
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
* @param key The key to delete.
|
|
131
|
-
* @returns The transaction instance for chaining.
|
|
132
|
-
*/
|
|
133
|
-
delete(key) {
|
|
134
|
-
if (this.committed) throw new Error("Transaction already committed");
|
|
98
|
+
_bufferDelete(key) {
|
|
99
|
+
this.localVersion++;
|
|
135
100
|
this.deleteBuffer.add(key);
|
|
136
101
|
this.writeBuffer.delete(key);
|
|
137
|
-
|
|
102
|
+
this.createdKeys.delete(key);
|
|
103
|
+
this.keyVersions.set(key, this.localVersion);
|
|
138
104
|
}
|
|
139
105
|
/**
|
|
140
106
|
* Rolls back the transaction.
|
|
141
107
|
* Clears all buffers and marks the transaction as finished.
|
|
142
|
-
* @returns The
|
|
108
|
+
* @returns The result object with success, created, updated, and deleted keys.
|
|
143
109
|
*/
|
|
144
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
|
+
}
|
|
145
127
|
this.writeBuffer.clear();
|
|
146
128
|
this.deleteBuffer.clear();
|
|
129
|
+
this.createdKeys.clear();
|
|
130
|
+
this.deletedValues.clear();
|
|
147
131
|
this.committed = true;
|
|
148
|
-
this.
|
|
149
|
-
|
|
132
|
+
if (this.root !== this) {
|
|
133
|
+
this.root.activeTransactions.delete(this);
|
|
134
|
+
}
|
|
135
|
+
return { success: true, created, updated, deleted };
|
|
150
136
|
}
|
|
151
137
|
};
|
|
152
138
|
|
|
139
|
+
// src/core/sync/Strategy.ts
|
|
140
|
+
var SyncMVCCStrategy = class extends MVCCStrategy {
|
|
141
|
+
};
|
|
142
|
+
|
|
153
143
|
// src/core/sync/Transaction.ts
|
|
154
|
-
var SyncMVCCTransaction = class extends MVCCTransaction {
|
|
155
|
-
|
|
144
|
+
var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
145
|
+
create(key, value) {
|
|
156
146
|
if (this.committed) throw new Error("Transaction already committed");
|
|
157
|
-
if (this.writeBuffer.has(key)) {
|
|
158
|
-
|
|
147
|
+
if (this.writeBuffer.has(key) || !this.deleteBuffer.has(key) && this.read(key) !== null) {
|
|
148
|
+
throw new Error(`Key already exists: ${key}`);
|
|
159
149
|
}
|
|
160
|
-
|
|
161
|
-
|
|
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}`);
|
|
162
157
|
}
|
|
163
|
-
|
|
158
|
+
this._bufferWrite(key, value);
|
|
159
|
+
return this;
|
|
164
160
|
}
|
|
165
|
-
|
|
161
|
+
delete(key) {
|
|
166
162
|
if (this.committed) throw new Error("Transaction already committed");
|
|
167
|
-
|
|
168
|
-
this.
|
|
169
|
-
|
|
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);
|
|
170
174
|
return this;
|
|
171
175
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
createNested() {
|
|
177
|
+
if (this.committed) throw new Error("Transaction already committed");
|
|
178
|
+
const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
|
|
179
|
+
const child = new _SyncMVCCTransaction(void 0, this, childVersion);
|
|
180
|
+
this.root.activeTransactions.add(child);
|
|
181
|
+
return child;
|
|
178
182
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
this.
|
|
182
|
-
return
|
|
183
|
+
read(key) {
|
|
184
|
+
if (this.committed) throw new Error("Transaction already committed");
|
|
185
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
186
|
+
if (this.deleteBuffer.has(key)) return null;
|
|
187
|
+
return this.root._diskRead(key, this.snapshotVersion);
|
|
183
188
|
}
|
|
184
|
-
|
|
185
|
-
if (this.
|
|
186
|
-
const
|
|
187
|
-
if (
|
|
188
|
-
this.
|
|
189
|
+
_readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
190
|
+
if (this.writeBuffer.has(key)) {
|
|
191
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
192
|
+
if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
193
|
+
return this.writeBuffer.get(key);
|
|
189
194
|
}
|
|
190
|
-
this.deletedCache.get(key).push({ value: oldValue, deletedAtVersion: version });
|
|
191
195
|
}
|
|
192
|
-
this.
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
if (this.deleteBuffer.has(key)) {
|
|
197
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
198
|
+
if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (this.parent) {
|
|
203
|
+
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
204
|
+
} else {
|
|
205
|
+
return this._diskRead(key, snapshotVersion);
|
|
195
206
|
}
|
|
196
|
-
this.versionIndex.get(key).push({ version, exists: true });
|
|
197
207
|
}
|
|
198
|
-
|
|
199
|
-
if (
|
|
200
|
-
|
|
201
|
-
|
|
208
|
+
commit() {
|
|
209
|
+
if (this.committed) {
|
|
210
|
+
return { success: false, error: "Transaction already committed", created: [], updated: [], deleted: [] };
|
|
211
|
+
}
|
|
212
|
+
const created = [];
|
|
213
|
+
const updated = [];
|
|
214
|
+
for (const [key, data] of this.writeBuffer.entries()) {
|
|
215
|
+
if (this.createdKeys.has(key)) {
|
|
216
|
+
created.push({ key, data });
|
|
217
|
+
} else {
|
|
218
|
+
updated.push({ key, data });
|
|
202
219
|
}
|
|
203
|
-
return null;
|
|
204
220
|
}
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
const
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
221
|
+
const deleted = [];
|
|
222
|
+
for (const key of this.deleteBuffer) {
|
|
223
|
+
const data = this.deletedValues.get(key);
|
|
224
|
+
if (data !== void 0) {
|
|
225
|
+
deleted.push({ key, data });
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (this.parent) {
|
|
229
|
+
const error = this.parent._merge(this);
|
|
230
|
+
if (error) {
|
|
231
|
+
return { success: false, error, created: [], updated: [], deleted: [] };
|
|
232
|
+
}
|
|
233
|
+
this.committed = true;
|
|
234
|
+
} else {
|
|
235
|
+
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
236
|
+
const error = this._merge(this);
|
|
237
|
+
if (error) {
|
|
238
|
+
return { success: false, error, created: [], updated: [], deleted: [] };
|
|
212
239
|
}
|
|
240
|
+
this.writeBuffer.clear();
|
|
241
|
+
this.deleteBuffer.clear();
|
|
242
|
+
this.createdKeys.clear();
|
|
243
|
+
this.deletedValues.clear();
|
|
244
|
+
this.keyVersions.clear();
|
|
245
|
+
this.localVersion = 0;
|
|
213
246
|
}
|
|
214
247
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
248
|
+
return { success: true, created, updated, deleted };
|
|
249
|
+
}
|
|
250
|
+
_merge(child) {
|
|
251
|
+
if (this.parent) {
|
|
252
|
+
for (const key of child.writeBuffer.keys()) {
|
|
253
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
254
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
255
|
+
return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
for (const key of child.deleteBuffer) {
|
|
259
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
260
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
261
|
+
return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const newLocalVersion = this.localVersion + 1;
|
|
265
|
+
for (const key of child.writeBuffer.keys()) {
|
|
266
|
+
this.writeBuffer.set(key, child.writeBuffer.get(key));
|
|
267
|
+
this.deleteBuffer.delete(key);
|
|
268
|
+
this.keyVersions.set(key, newLocalVersion);
|
|
269
|
+
if (child.createdKeys.has(key)) {
|
|
270
|
+
this.createdKeys.add(key);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
for (const key of child.deleteBuffer) {
|
|
274
|
+
this.deleteBuffer.add(key);
|
|
275
|
+
this.writeBuffer.delete(key);
|
|
276
|
+
this.createdKeys.delete(key);
|
|
277
|
+
this.keyVersions.set(key, newLocalVersion);
|
|
278
|
+
const deletedValue = child.deletedValues.get(key);
|
|
279
|
+
if (deletedValue !== void 0) {
|
|
280
|
+
this.deletedValues.set(key, deletedValue);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
this.localVersion = newLocalVersion;
|
|
284
|
+
this.root.activeTransactions.delete(child);
|
|
285
|
+
} else {
|
|
286
|
+
this.root.activeTransactions.delete(child);
|
|
287
|
+
const newVersion = this.version + 1;
|
|
288
|
+
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
289
|
+
for (const key of modifiedKeys) {
|
|
290
|
+
const versions = this.versionIndex.get(key);
|
|
291
|
+
if (versions && versions.length > 0) {
|
|
292
|
+
const lastVer = versions[versions.length - 1].version;
|
|
293
|
+
if (lastVer > child.snapshotVersion) {
|
|
294
|
+
return `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
221
297
|
}
|
|
298
|
+
for (const [key, value] of child.writeBuffer) {
|
|
299
|
+
this._diskWrite(key, value, newVersion);
|
|
300
|
+
}
|
|
301
|
+
for (const key of child.deleteBuffer) {
|
|
302
|
+
this._diskDelete(key, newVersion);
|
|
303
|
+
}
|
|
304
|
+
this.version = newVersion;
|
|
305
|
+
this._cleanupDeletedCache();
|
|
222
306
|
}
|
|
223
307
|
return null;
|
|
224
308
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
309
|
+
// --- Internal IO Helpers (Root Only) ---
|
|
310
|
+
_diskWrite(key, value, version) {
|
|
311
|
+
const strategy = this.strategy;
|
|
312
|
+
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
313
|
+
if (strategy.exists(key)) {
|
|
314
|
+
const currentVal = strategy.read(key);
|
|
315
|
+
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
316
|
+
this.deletedCache.get(key).push({
|
|
317
|
+
value: currentVal,
|
|
318
|
+
deletedAtVersion: version
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
strategy.write(key, value);
|
|
322
|
+
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
323
|
+
this.versionIndex.get(key).push({ version, exists: true });
|
|
324
|
+
}
|
|
325
|
+
_diskRead(key, snapshotVersion) {
|
|
326
|
+
const strategy = this.strategy;
|
|
327
|
+
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
328
|
+
const versions = this.versionIndex.get(key);
|
|
329
|
+
if (!versions) {
|
|
330
|
+
return strategy.exists(key) ? strategy.read(key) : null;
|
|
331
|
+
}
|
|
332
|
+
let targetVerObj = null;
|
|
333
|
+
let nextVerObj = null;
|
|
334
|
+
for (const v of versions) {
|
|
335
|
+
if (v.version <= snapshotVersion) {
|
|
336
|
+
targetVerObj = v;
|
|
337
|
+
} else {
|
|
338
|
+
nextVerObj = v;
|
|
339
|
+
break;
|
|
230
340
|
}
|
|
231
|
-
this.deletedCache.get(key).push({ deletedAtVersion: snapshotVersion, value: data });
|
|
232
|
-
this.strategy.delete(key);
|
|
233
341
|
}
|
|
234
|
-
if (!
|
|
235
|
-
|
|
342
|
+
if (!targetVerObj || !targetVerObj.exists) return null;
|
|
343
|
+
if (!nextVerObj) {
|
|
344
|
+
return strategy.read(key);
|
|
345
|
+
}
|
|
346
|
+
const cached = this.deletedCache.get(key);
|
|
347
|
+
if (cached) {
|
|
348
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
349
|
+
if (match) return match.value;
|
|
236
350
|
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
_diskDelete(key, snapshotVersion) {
|
|
354
|
+
const strategy = this.strategy;
|
|
355
|
+
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
356
|
+
if (strategy.exists(key)) {
|
|
357
|
+
const currentVal = strategy.read(key);
|
|
358
|
+
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
359
|
+
this.deletedCache.get(key).push({
|
|
360
|
+
value: currentVal,
|
|
361
|
+
deletedAtVersion: snapshotVersion
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
strategy.delete(key);
|
|
365
|
+
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
237
366
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
238
367
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
for (const key of affectedKeys) {
|
|
247
|
-
const versions = this.versionIndex.get(key);
|
|
248
|
-
if (versions && versions.length > 0) {
|
|
249
|
-
const latestVersion = versions[versions.length - 1].version;
|
|
250
|
-
if (latestVersion > tx.snapshotVersion) {
|
|
251
|
-
throw new Error(`Commit conflict: file '${key}' was modified by another transaction`);
|
|
252
|
-
}
|
|
368
|
+
_cleanupDeletedCache() {
|
|
369
|
+
if (this.deletedCache.size === 0) return;
|
|
370
|
+
let minActiveVersion = this.version;
|
|
371
|
+
if (this.activeTransactions.size > 0) {
|
|
372
|
+
for (const tx of this.activeTransactions) {
|
|
373
|
+
if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
|
|
374
|
+
minActiveVersion = tx.snapshotVersion;
|
|
253
375
|
}
|
|
254
376
|
}
|
|
255
377
|
}
|
|
256
|
-
this.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
378
|
+
for (const [key, cachedList] of this.deletedCache) {
|
|
379
|
+
const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
|
|
380
|
+
if (remaining.length === 0) {
|
|
381
|
+
this.deletedCache.delete(key);
|
|
382
|
+
} else {
|
|
383
|
+
this.deletedCache.set(key, remaining);
|
|
384
|
+
}
|
|
262
385
|
}
|
|
263
386
|
}
|
|
264
387
|
};
|
|
265
388
|
|
|
266
|
-
// src/core/
|
|
267
|
-
var
|
|
389
|
+
// src/core/async/Strategy.ts
|
|
390
|
+
var AsyncMVCCStrategy = class extends MVCCStrategy {
|
|
268
391
|
};
|
|
269
392
|
|
|
270
393
|
// node_modules/ryoiki/dist/esm/index.mjs
|
|
@@ -526,140 +649,261 @@ var Ryoiki = class _Ryoiki {
|
|
|
526
649
|
};
|
|
527
650
|
|
|
528
651
|
// src/core/async/Transaction.ts
|
|
529
|
-
var AsyncMVCCTransaction = class extends MVCCTransaction {
|
|
652
|
+
var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
653
|
+
lock = new Ryoiki();
|
|
654
|
+
async writeLock(fn) {
|
|
655
|
+
let lockId;
|
|
656
|
+
return this.lock.writeLock(async (_lockId) => {
|
|
657
|
+
lockId = _lockId;
|
|
658
|
+
return fn();
|
|
659
|
+
}).finally(() => {
|
|
660
|
+
this.lock.writeUnlock(lockId);
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
async create(key, value) {
|
|
664
|
+
if (this.committed) throw new Error("Transaction already committed");
|
|
665
|
+
if (this.writeBuffer.has(key) || !this.deleteBuffer.has(key) && await this.read(key) !== null) {
|
|
666
|
+
throw new Error(`Key already exists: ${key}`);
|
|
667
|
+
}
|
|
668
|
+
this._bufferCreate(key, value);
|
|
669
|
+
return this;
|
|
670
|
+
}
|
|
671
|
+
async write(key, value) {
|
|
672
|
+
if (this.committed) throw new Error("Transaction already committed");
|
|
673
|
+
if (!this.writeBuffer.has(key) && (this.deleteBuffer.has(key) || await this.read(key) === null)) {
|
|
674
|
+
throw new Error(`Key not found: ${key}`);
|
|
675
|
+
}
|
|
676
|
+
this._bufferWrite(key, value);
|
|
677
|
+
return this;
|
|
678
|
+
}
|
|
679
|
+
async delete(key) {
|
|
680
|
+
if (this.committed) throw new Error("Transaction already committed");
|
|
681
|
+
let valueToDelete = null;
|
|
682
|
+
if (this.writeBuffer.has(key)) {
|
|
683
|
+
valueToDelete = this.writeBuffer.get(key);
|
|
684
|
+
} else if (!this.deleteBuffer.has(key)) {
|
|
685
|
+
valueToDelete = await this.read(key);
|
|
686
|
+
}
|
|
687
|
+
if (valueToDelete === null) {
|
|
688
|
+
throw new Error(`Key not found: ${key}`);
|
|
689
|
+
}
|
|
690
|
+
this.deletedValues.set(key, valueToDelete);
|
|
691
|
+
this._bufferDelete(key);
|
|
692
|
+
return this;
|
|
693
|
+
}
|
|
694
|
+
createNested() {
|
|
695
|
+
if (this.committed) throw new Error("Transaction already committed");
|
|
696
|
+
const childVersion = this.isRoot() ? this.version : this.snapshotVersion;
|
|
697
|
+
const child = new _AsyncMVCCTransaction(void 0, this, childVersion);
|
|
698
|
+
this.root.activeTransactions.add(child);
|
|
699
|
+
return child;
|
|
700
|
+
}
|
|
530
701
|
async read(key) {
|
|
531
702
|
if (this.committed) throw new Error("Transaction already committed");
|
|
703
|
+
if (this.writeBuffer.has(key)) return this.writeBuffer.get(key);
|
|
704
|
+
if (this.deleteBuffer.has(key)) return null;
|
|
705
|
+
return this.root._diskRead(key, this.snapshotVersion);
|
|
706
|
+
}
|
|
707
|
+
async _readSnapshot(key, snapshotVersion, snapshotLocalVersion) {
|
|
532
708
|
if (this.writeBuffer.has(key)) {
|
|
533
|
-
|
|
709
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
710
|
+
if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
711
|
+
return this.writeBuffer.get(key);
|
|
712
|
+
}
|
|
534
713
|
}
|
|
535
714
|
if (this.deleteBuffer.has(key)) {
|
|
536
|
-
|
|
715
|
+
const keyModVersion = this.keyVersions.get(key);
|
|
716
|
+
if (snapshotLocalVersion === void 0 || keyModVersion === void 0 || keyModVersion <= snapshotLocalVersion) {
|
|
717
|
+
return null;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
if (this.parent) {
|
|
721
|
+
return this.parent._readSnapshot(key, snapshotVersion, this.snapshotLocalVersion);
|
|
722
|
+
} else {
|
|
723
|
+
return this._diskRead(key, snapshotVersion);
|
|
537
724
|
}
|
|
538
|
-
return this.manager._diskRead(key, this.snapshotVersion);
|
|
539
725
|
}
|
|
540
726
|
async commit() {
|
|
541
|
-
return this.
|
|
542
|
-
if (this.committed)
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
727
|
+
return this.writeLock(async () => {
|
|
728
|
+
if (this.committed) {
|
|
729
|
+
return { success: false, error: "Transaction already committed", created: [], updated: [], deleted: [] };
|
|
730
|
+
}
|
|
731
|
+
const created = [];
|
|
732
|
+
const updated = [];
|
|
733
|
+
for (const [key, data] of this.writeBuffer.entries()) {
|
|
734
|
+
if (this.createdKeys.has(key)) {
|
|
735
|
+
created.push({ key, data });
|
|
736
|
+
} else {
|
|
737
|
+
updated.push({ key, data });
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
const deleted = [];
|
|
741
|
+
for (const key of this.deleteBuffer) {
|
|
742
|
+
const data = this.deletedValues.get(key);
|
|
743
|
+
if (data !== void 0) {
|
|
744
|
+
deleted.push({ key, data });
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
if (this.parent) {
|
|
748
|
+
const error = await this.parent._merge(this);
|
|
749
|
+
if (error) {
|
|
750
|
+
return { success: false, error, created: [], updated: [], deleted: [] };
|
|
751
|
+
}
|
|
752
|
+
this.committed = true;
|
|
753
|
+
} else {
|
|
754
|
+
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
755
|
+
const error = await this._merge(this);
|
|
756
|
+
if (error) {
|
|
757
|
+
return { success: false, error, created: [], updated: [], deleted: [] };
|
|
758
|
+
}
|
|
759
|
+
this.writeBuffer.clear();
|
|
760
|
+
this.deleteBuffer.clear();
|
|
761
|
+
this.createdKeys.clear();
|
|
762
|
+
this.deletedValues.clear();
|
|
763
|
+
this.keyVersions.clear();
|
|
764
|
+
this.localVersion = 0;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
return { success: true, created, updated, deleted };
|
|
547
768
|
});
|
|
548
769
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
770
|
+
async _merge(child) {
|
|
771
|
+
return this.writeLock(async () => {
|
|
772
|
+
if (this.parent) {
|
|
773
|
+
for (const key of child.writeBuffer.keys()) {
|
|
774
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
775
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
776
|
+
return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
for (const key of child.deleteBuffer) {
|
|
780
|
+
const lastModLocalVer = this.keyVersions.get(key);
|
|
781
|
+
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
782
|
+
return `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
const newLocalVersion = this.localVersion + 1;
|
|
786
|
+
for (const key of child.writeBuffer.keys()) {
|
|
787
|
+
this.writeBuffer.set(key, child.writeBuffer.get(key));
|
|
788
|
+
this.deleteBuffer.delete(key);
|
|
789
|
+
this.keyVersions.set(key, newLocalVersion);
|
|
790
|
+
if (child.createdKeys.has(key)) {
|
|
791
|
+
this.createdKeys.add(key);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
for (const key of child.deleteBuffer) {
|
|
795
|
+
this.deleteBuffer.add(key);
|
|
796
|
+
this.writeBuffer.delete(key);
|
|
797
|
+
this.createdKeys.delete(key);
|
|
798
|
+
this.keyVersions.set(key, newLocalVersion);
|
|
799
|
+
const deletedValue = child.deletedValues.get(key);
|
|
800
|
+
if (deletedValue !== void 0) {
|
|
801
|
+
this.deletedValues.set(key, deletedValue);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
this.localVersion = newLocalVersion;
|
|
805
|
+
this.root.activeTransactions.delete(child);
|
|
806
|
+
} else {
|
|
807
|
+
this.root.activeTransactions.delete(child);
|
|
808
|
+
const newVersion = this.version + 1;
|
|
809
|
+
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
810
|
+
for (const key of modifiedKeys) {
|
|
811
|
+
const versions = this.versionIndex.get(key);
|
|
812
|
+
if (versions && versions.length > 0) {
|
|
813
|
+
const lastVer = versions[versions.length - 1].version;
|
|
814
|
+
if (lastVer > child.snapshotVersion) {
|
|
815
|
+
return `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
for (const [key, value] of child.writeBuffer) {
|
|
820
|
+
await this._diskWrite(key, value, newVersion);
|
|
821
|
+
}
|
|
822
|
+
for (const key of child.deleteBuffer) {
|
|
823
|
+
await this._diskDelete(key, newVersion);
|
|
824
|
+
}
|
|
825
|
+
this.version = newVersion;
|
|
826
|
+
this._cleanupDeletedCache();
|
|
827
|
+
}
|
|
828
|
+
return null;
|
|
570
829
|
});
|
|
571
830
|
}
|
|
831
|
+
// --- Internal IO Helpers (Root Only) ---
|
|
572
832
|
async _diskWrite(key, value, version) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
this.deletedCache.get(key).push({
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
833
|
+
const strategy = this.strategy;
|
|
834
|
+
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
835
|
+
if (await strategy.exists(key)) {
|
|
836
|
+
const currentVal = await strategy.read(key);
|
|
837
|
+
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
838
|
+
this.deletedCache.get(key).push({
|
|
839
|
+
value: currentVal,
|
|
840
|
+
deletedAtVersion: version
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
await strategy.write(key, value);
|
|
844
|
+
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
584
845
|
this.versionIndex.get(key).push({ version, exists: true });
|
|
585
846
|
}
|
|
586
847
|
async _diskRead(key, snapshotVersion) {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
return this.strategy.read(key);
|
|
590
|
-
}
|
|
591
|
-
return null;
|
|
592
|
-
}
|
|
848
|
+
const strategy = this.strategy;
|
|
849
|
+
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
593
850
|
const versions = this.versionIndex.get(key);
|
|
594
|
-
if (versions
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
851
|
+
if (!versions) {
|
|
852
|
+
return await strategy.exists(key) ? strategy.read(key) : null;
|
|
853
|
+
}
|
|
854
|
+
let targetVerObj = null;
|
|
855
|
+
let nextVerObj = null;
|
|
856
|
+
for (const v of versions) {
|
|
857
|
+
if (v.version <= snapshotVersion) {
|
|
858
|
+
targetVerObj = v;
|
|
859
|
+
} else {
|
|
860
|
+
nextVerObj = v;
|
|
861
|
+
break;
|
|
601
862
|
}
|
|
602
863
|
}
|
|
864
|
+
if (!targetVerObj || !targetVerObj.exists) return null;
|
|
865
|
+
if (!nextVerObj) {
|
|
866
|
+
return strategy.read(key);
|
|
867
|
+
}
|
|
603
868
|
const cached = this.deletedCache.get(key);
|
|
604
869
|
if (cached) {
|
|
605
|
-
const
|
|
606
|
-
if (
|
|
607
|
-
visibleEntries.sort((a, b) => a.deletedAtVersion - b.deletedAtVersion);
|
|
608
|
-
return visibleEntries[0].value;
|
|
609
|
-
}
|
|
870
|
+
const match = cached.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
871
|
+
if (match) return match.value;
|
|
610
872
|
}
|
|
611
873
|
return null;
|
|
612
874
|
}
|
|
613
875
|
async _diskDelete(key, snapshotVersion) {
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
this.deletedCache.get(key).push({
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
876
|
+
const strategy = this.strategy;
|
|
877
|
+
if (!strategy) throw new Error("Root Transaction missing strategy");
|
|
878
|
+
if (await strategy.exists(key)) {
|
|
879
|
+
const currentVal = await strategy.read(key);
|
|
880
|
+
if (!this.deletedCache.has(key)) this.deletedCache.set(key, []);
|
|
881
|
+
this.deletedCache.get(key).push({
|
|
882
|
+
value: currentVal,
|
|
883
|
+
deletedAtVersion: snapshotVersion
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
await strategy.delete(key);
|
|
887
|
+
if (!this.versionIndex.has(key)) this.versionIndex.set(key, []);
|
|
625
888
|
this.versionIndex.get(key).push({ version: snapshotVersion, exists: false });
|
|
626
889
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
for (const key of affectedKeys) {
|
|
635
|
-
const versions = this.versionIndex.get(key);
|
|
636
|
-
if (versions && versions.length > 0) {
|
|
637
|
-
const latestVersion = versions[versions.length - 1].version;
|
|
638
|
-
if (latestVersion > tx.snapshotVersion) {
|
|
639
|
-
throw new Error(`Commit conflict: file '${key}' was modified by another transaction`);
|
|
640
|
-
}
|
|
890
|
+
_cleanupDeletedCache() {
|
|
891
|
+
if (this.deletedCache.size === 0) return;
|
|
892
|
+
let minActiveVersion = this.version;
|
|
893
|
+
if (this.activeTransactions.size > 0) {
|
|
894
|
+
for (const tx of this.activeTransactions) {
|
|
895
|
+
if (!tx.committed && tx.snapshotVersion < minActiveVersion) {
|
|
896
|
+
minActiveVersion = tx.snapshotVersion;
|
|
641
897
|
}
|
|
642
898
|
}
|
|
643
899
|
}
|
|
644
|
-
this.
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
900
|
+
for (const [key, cachedList] of this.deletedCache) {
|
|
901
|
+
const remaining = cachedList.filter((item) => item.deletedAtVersion > minActiveVersion);
|
|
902
|
+
if (remaining.length === 0) {
|
|
903
|
+
this.deletedCache.delete(key);
|
|
904
|
+
} else {
|
|
905
|
+
this.deletedCache.set(key, remaining);
|
|
906
|
+
}
|
|
650
907
|
}
|
|
651
908
|
}
|
|
652
909
|
};
|
|
653
|
-
|
|
654
|
-
// src/core/async/Strategy.ts
|
|
655
|
-
var AsyncMVCCStrategy = class extends MVCCStrategy {
|
|
656
|
-
};
|
|
657
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
658
|
-
0 && (module.exports = {
|
|
659
|
-
AsyncMVCCManager,
|
|
660
|
-
AsyncMVCCStrategy,
|
|
661
|
-
AsyncMVCCTransaction,
|
|
662
|
-
SyncMVCCManager,
|
|
663
|
-
SyncMVCCStrategy,
|
|
664
|
-
SyncMVCCTransaction
|
|
665
|
-
});
|