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.
- package/README.md +84 -89
- package/dist/cjs/index.cjs +343 -242
- package/dist/esm/index.mjs +343 -233
- package/dist/types/core/async/Transaction.d.ts +10 -2
- package/dist/types/core/async/index.d.ts +0 -1
- package/dist/types/core/base/Transaction.d.ts +42 -9
- package/dist/types/core/base/index.d.ts +0 -1
- package/dist/types/core/sync/Transaction.d.ts +8 -2
- package/dist/types/core/sync/index.d.ts +0 -1
- 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/esm/index.mjs
CHANGED
|
@@ -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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
169
|
-
|
|
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
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
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
|
|
187
|
-
if (
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
this.deletedCache.get(key).push({
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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/
|
|
236
|
-
var
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
592
|
+
return this.writeLock(async () => {
|
|
511
593
|
if (this.committed) throw new Error("Transaction already committed");
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
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
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
this.deletedCache.get(key).push({
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
-
|
|
557
|
-
|
|
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
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
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
|
|
575
|
-
if (
|
|
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
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
this.deletedCache.get(key).push({
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
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.
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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
|
};
|