mvcc-api 1.2.3 → 1.2.5
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 +28 -9
- package/dist/cjs/index.cjs +241 -62
- package/dist/esm/index.mjs +241 -62
- package/dist/types/core/async/Transaction.d.ts +3 -3
- package/dist/types/core/base/Transaction.d.ts +4 -3
- package/dist/types/core/sync/Transaction.d.ts +3 -3
- package/dist/types/types/index.d.ts +19 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,12 +34,27 @@ It easily and powerfully solves complex concurrency problems that are difficult
|
|
|
34
34
|
* Business logic and storage logic can be perfectly separated.
|
|
35
35
|
|
|
36
36
|
4. **Improved Development Productivity**
|
|
37
|
-
* No need to write complex synchronization code yourself; write safe code with just intuitive `api.read()`, `api.write()`, and `commit()`.
|
|
37
|
+
* No need to write complex synchronization code yourself; write safe code with just intuitive `api.read()`, `api.write()`, and `api.commit()`.
|
|
38
38
|
|
|
39
39
|
## Installation
|
|
40
40
|
|
|
41
|
+
### Node.js
|
|
42
|
+
|
|
41
43
|
```bash
|
|
42
44
|
npm install mvcc-api
|
|
45
|
+
# or
|
|
46
|
+
npx jsr add @izure/mvcc-api
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Browser
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
import {
|
|
53
|
+
SyncMVCCStrategy,
|
|
54
|
+
SyncMVCCTransaction,
|
|
55
|
+
AsyncMVCCStrategy,
|
|
56
|
+
AsyncMVCCTransaction
|
|
57
|
+
} from 'https://cdn.jsdelivr.net/npm/mvcc-api@1/+esm'
|
|
43
58
|
```
|
|
44
59
|
|
|
45
60
|
## Usage
|
|
@@ -121,8 +136,9 @@ const child = parent.createNested()
|
|
|
121
136
|
parent.write('shared', 'parent') // Parent modifies after child creation
|
|
122
137
|
child.write('shared', 'child') // Child modifies same key
|
|
123
138
|
|
|
124
|
-
const result = child.commit()
|
|
139
|
+
const result = child.commit('It should fail')
|
|
125
140
|
if (!result.success) {
|
|
141
|
+
console.log(result.label) // "It should fail"
|
|
126
142
|
console.log(result.error) // "Commit conflict: Key 'shared' was modified..."
|
|
127
143
|
}
|
|
128
144
|
```
|
|
@@ -174,23 +190,26 @@ const bResult = b.commit()
|
|
|
174
190
|
|
|
175
191
|
| Method | Description | Return Value |
|
|
176
192
|
| :--- | :--- | :--- |
|
|
177
|
-
| `create(key, value)` | Create new key-value | `this` |
|
|
178
|
-
| `write(key, value)` | Update existing key | `this` |
|
|
179
|
-
| `delete(key)` | Delete key | `this` |
|
|
180
|
-
| `read(key)` | Read value | `T \| null` |
|
|
181
|
-
| `exists(key)` | Check if key exists | `boolean` |
|
|
182
|
-
| `commit()` | Apply changes | `TransactionResult<K, T>` |
|
|
183
|
-
| `rollback()` | Discard changes | `TransactionResult<K, T>` |
|
|
193
|
+
| `create(key: K, value: T)` | Create new key-value | `this` |
|
|
194
|
+
| `write(key: K, value: T)` | Update existing key | `this` |
|
|
195
|
+
| `delete(key: K)` | Delete key | `this` |
|
|
196
|
+
| `read(key: K)` | Read value | `T \| null` |
|
|
197
|
+
| `exists(key: K)` | Check if key exists | `boolean` |
|
|
198
|
+
| `commit(label?: string)` | Apply changes | `TransactionResult<K, T>` |
|
|
199
|
+
| `rollback(label?: string)` | Discard changes | `TransactionResult<K, T>` |
|
|
184
200
|
| `createNested()` | Create child transaction | `MVCCTransaction` |
|
|
185
201
|
|
|
186
202
|
### `TransactionResult<K, T>`
|
|
187
203
|
|
|
188
204
|
```typescript
|
|
189
205
|
type TransactionEntry<K, T> = { key: K, data: T }
|
|
206
|
+
type TransactionConflict<K, T> = { key: K, parent: T, child: T }
|
|
190
207
|
|
|
191
208
|
{
|
|
192
209
|
success: boolean // Success status
|
|
210
|
+
label?: string // Label of the transaction
|
|
193
211
|
error?: string // Error message on failure (e.g. conflict)
|
|
212
|
+
conflict?: TransactionConflict<K, T> // Conflict information on failure
|
|
194
213
|
created: TransactionEntry[] // Keys and values created via create()
|
|
195
214
|
updated: TransactionEntry[] // Keys and values updated via write()
|
|
196
215
|
deleted: TransactionEntry[] // Keys deleted via delete() and their previous values
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -239,25 +239,57 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
239
239
|
return this._diskRead(key, snapshotVersion);
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
|
-
commit() {
|
|
242
|
+
commit(label) {
|
|
243
243
|
const { created, updated, deleted } = this._getResultEntries();
|
|
244
244
|
if (this.committed) {
|
|
245
|
-
return {
|
|
245
|
+
return {
|
|
246
|
+
label,
|
|
247
|
+
success: false,
|
|
248
|
+
error: "Transaction already committed",
|
|
249
|
+
conflict: void 0,
|
|
250
|
+
created,
|
|
251
|
+
updated,
|
|
252
|
+
deleted
|
|
253
|
+
};
|
|
246
254
|
}
|
|
247
255
|
if (this.hasCommittedAncestor()) {
|
|
248
|
-
return {
|
|
256
|
+
return {
|
|
257
|
+
label,
|
|
258
|
+
success: false,
|
|
259
|
+
error: "Ancestor transaction already committed",
|
|
260
|
+
conflict: void 0,
|
|
261
|
+
created,
|
|
262
|
+
updated,
|
|
263
|
+
deleted
|
|
264
|
+
};
|
|
249
265
|
}
|
|
250
266
|
if (this.parent) {
|
|
251
|
-
const
|
|
252
|
-
if (
|
|
253
|
-
return {
|
|
267
|
+
const failure = this.parent._merge(this);
|
|
268
|
+
if (failure) {
|
|
269
|
+
return {
|
|
270
|
+
label,
|
|
271
|
+
success: false,
|
|
272
|
+
error: failure.error,
|
|
273
|
+
conflict: failure.conflict,
|
|
274
|
+
created,
|
|
275
|
+
updated,
|
|
276
|
+
deleted
|
|
277
|
+
};
|
|
254
278
|
}
|
|
255
279
|
this.committed = true;
|
|
256
280
|
} else {
|
|
257
281
|
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
258
|
-
const
|
|
259
|
-
if (
|
|
260
|
-
return {
|
|
282
|
+
const failure = this._merge(this);
|
|
283
|
+
if (failure) {
|
|
284
|
+
return {
|
|
285
|
+
label,
|
|
286
|
+
success: false,
|
|
287
|
+
error: failure.error,
|
|
288
|
+
conflict: failure.conflict,
|
|
289
|
+
created: [],
|
|
290
|
+
updated: [],
|
|
291
|
+
deleted: []
|
|
292
|
+
};
|
|
261
293
|
}
|
|
262
294
|
this.writeBuffer.clear();
|
|
263
295
|
this.deleteBuffer.clear();
|
|
@@ -268,20 +300,40 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
268
300
|
this.localVersion = 0;
|
|
269
301
|
}
|
|
270
302
|
}
|
|
271
|
-
return {
|
|
303
|
+
return {
|
|
304
|
+
label,
|
|
305
|
+
success: true,
|
|
306
|
+
created,
|
|
307
|
+
updated,
|
|
308
|
+
deleted
|
|
309
|
+
};
|
|
272
310
|
}
|
|
273
311
|
_merge(child) {
|
|
274
312
|
if (this.parent) {
|
|
275
313
|
for (const key of child.writeBuffer.keys()) {
|
|
276
314
|
const lastModLocalVer = this.keyVersions.get(key);
|
|
277
315
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
278
|
-
return
|
|
316
|
+
return {
|
|
317
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
318
|
+
conflict: {
|
|
319
|
+
key,
|
|
320
|
+
parent: this.read(key),
|
|
321
|
+
child: child.read(key)
|
|
322
|
+
}
|
|
323
|
+
};
|
|
279
324
|
}
|
|
280
325
|
}
|
|
281
326
|
for (const key of child.deleteBuffer) {
|
|
282
327
|
const lastModLocalVer = this.keyVersions.get(key);
|
|
283
328
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
284
|
-
return
|
|
329
|
+
return {
|
|
330
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
331
|
+
conflict: {
|
|
332
|
+
key,
|
|
333
|
+
parent: this.read(key),
|
|
334
|
+
child: child.read(key)
|
|
335
|
+
}
|
|
336
|
+
};
|
|
285
337
|
}
|
|
286
338
|
}
|
|
287
339
|
const newLocalVersion = this.localVersion + 1;
|
|
@@ -309,18 +361,45 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
309
361
|
this.localVersion = newLocalVersion;
|
|
310
362
|
this.root.activeTransactions.delete(child);
|
|
311
363
|
} else {
|
|
312
|
-
this.root.activeTransactions.delete(child);
|
|
313
364
|
const newVersion = this.version + 1;
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
365
|
+
if (child !== this) {
|
|
366
|
+
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
367
|
+
for (const key of modifiedKeys) {
|
|
368
|
+
const versions = this.versionIndex.get(key);
|
|
369
|
+
if (versions && versions.length > 0) {
|
|
370
|
+
const lastVer = versions[versions.length - 1].version;
|
|
371
|
+
if (lastVer > child.snapshotVersion) {
|
|
372
|
+
return {
|
|
373
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
374
|
+
conflict: {
|
|
375
|
+
key,
|
|
376
|
+
parent: this.read(key),
|
|
377
|
+
child: child.read(key)
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
}
|
|
321
381
|
}
|
|
322
382
|
}
|
|
323
383
|
}
|
|
384
|
+
for (const [key, value] of child.writeBuffer) {
|
|
385
|
+
this.writeBuffer.set(key, value);
|
|
386
|
+
this.deleteBuffer.delete(key);
|
|
387
|
+
if (child.createdKeys.has(key)) {
|
|
388
|
+
this.createdKeys.add(key);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
for (const key of child.deleteBuffer) {
|
|
392
|
+
this.deleteBuffer.add(key);
|
|
393
|
+
this.writeBuffer.delete(key);
|
|
394
|
+
this.createdKeys.delete(key);
|
|
395
|
+
const deletedValue = child.deletedValues.get(key);
|
|
396
|
+
if (deletedValue !== void 0) {
|
|
397
|
+
this.deletedValues.set(key, deletedValue);
|
|
398
|
+
}
|
|
399
|
+
if (child.originallyExisted.has(key)) {
|
|
400
|
+
this.originallyExisted.add(key);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
324
403
|
for (const [key, value] of child.writeBuffer) {
|
|
325
404
|
this._diskWrite(key, value, newVersion);
|
|
326
405
|
}
|
|
@@ -328,6 +407,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
328
407
|
this._diskDelete(key, newVersion);
|
|
329
408
|
}
|
|
330
409
|
this.version = newVersion;
|
|
410
|
+
this.root.activeTransactions.delete(child);
|
|
331
411
|
this._cleanupDeletedCache();
|
|
332
412
|
}
|
|
333
413
|
return null;
|
|
@@ -365,7 +445,17 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
365
445
|
break;
|
|
366
446
|
}
|
|
367
447
|
}
|
|
368
|
-
if (!targetVerObj
|
|
448
|
+
if (!targetVerObj) {
|
|
449
|
+
if (nextVerObj) {
|
|
450
|
+
const cached2 = this.deletedCache.get(key);
|
|
451
|
+
if (cached2) {
|
|
452
|
+
const match = cached2.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
453
|
+
if (match) return match.value;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return null;
|
|
457
|
+
}
|
|
458
|
+
if (!targetVerObj.exists) return null;
|
|
369
459
|
if (!nextVerObj) {
|
|
370
460
|
return strategy.read(key);
|
|
371
461
|
}
|
|
@@ -778,38 +868,74 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
778
868
|
return this._diskRead(key, snapshotVersion);
|
|
779
869
|
}
|
|
780
870
|
}
|
|
781
|
-
async commit() {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
871
|
+
async commit(label) {
|
|
872
|
+
const { created, updated, deleted } = this._getResultEntries();
|
|
873
|
+
if (this.committed) {
|
|
874
|
+
return {
|
|
875
|
+
label,
|
|
876
|
+
success: false,
|
|
877
|
+
error: "Transaction already committed",
|
|
878
|
+
conflict: void 0,
|
|
879
|
+
created,
|
|
880
|
+
updated,
|
|
881
|
+
deleted
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
if (this.hasCommittedAncestor()) {
|
|
885
|
+
return {
|
|
886
|
+
label,
|
|
887
|
+
success: false,
|
|
888
|
+
error: "Ancestor transaction already committed",
|
|
889
|
+
conflict: void 0,
|
|
890
|
+
created,
|
|
891
|
+
updated,
|
|
892
|
+
deleted
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
if (this.parent) {
|
|
896
|
+
const failure = await this.parent._merge(this);
|
|
897
|
+
if (failure) {
|
|
898
|
+
return {
|
|
899
|
+
label,
|
|
900
|
+
success: false,
|
|
901
|
+
error: failure.error,
|
|
902
|
+
conflict: failure.conflict,
|
|
903
|
+
created,
|
|
904
|
+
updated,
|
|
905
|
+
deleted
|
|
906
|
+
};
|
|
789
907
|
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
this.createdKeys.clear();
|
|
805
|
-
this.deletedValues.clear();
|
|
806
|
-
this.originallyExisted.clear();
|
|
807
|
-
this.keyVersions.clear();
|
|
808
|
-
this.localVersion = 0;
|
|
908
|
+
this.committed = true;
|
|
909
|
+
} else {
|
|
910
|
+
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
911
|
+
const failure = await this._merge(this);
|
|
912
|
+
if (failure) {
|
|
913
|
+
return {
|
|
914
|
+
label,
|
|
915
|
+
success: false,
|
|
916
|
+
error: failure.error,
|
|
917
|
+
conflict: failure.conflict,
|
|
918
|
+
created: [],
|
|
919
|
+
updated: [],
|
|
920
|
+
deleted: []
|
|
921
|
+
};
|
|
809
922
|
}
|
|
923
|
+
this.writeBuffer.clear();
|
|
924
|
+
this.deleteBuffer.clear();
|
|
925
|
+
this.createdKeys.clear();
|
|
926
|
+
this.deletedValues.clear();
|
|
927
|
+
this.originallyExisted.clear();
|
|
928
|
+
this.keyVersions.clear();
|
|
929
|
+
this.localVersion = 0;
|
|
810
930
|
}
|
|
811
|
-
|
|
812
|
-
|
|
931
|
+
}
|
|
932
|
+
return {
|
|
933
|
+
label,
|
|
934
|
+
success: true,
|
|
935
|
+
created,
|
|
936
|
+
updated,
|
|
937
|
+
deleted
|
|
938
|
+
};
|
|
813
939
|
}
|
|
814
940
|
async _merge(child) {
|
|
815
941
|
return this.writeLock(async () => {
|
|
@@ -817,13 +943,27 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
817
943
|
for (const key of child.writeBuffer.keys()) {
|
|
818
944
|
const lastModLocalVer = this.keyVersions.get(key);
|
|
819
945
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
820
|
-
return
|
|
946
|
+
return {
|
|
947
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
948
|
+
conflict: {
|
|
949
|
+
key,
|
|
950
|
+
parent: await this.read(key),
|
|
951
|
+
child: await child.read(key)
|
|
952
|
+
}
|
|
953
|
+
};
|
|
821
954
|
}
|
|
822
955
|
}
|
|
823
956
|
for (const key of child.deleteBuffer) {
|
|
824
957
|
const lastModLocalVer = this.keyVersions.get(key);
|
|
825
958
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
826
|
-
return
|
|
959
|
+
return {
|
|
960
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
961
|
+
conflict: {
|
|
962
|
+
key,
|
|
963
|
+
parent: await this.read(key),
|
|
964
|
+
child: await child.read(key)
|
|
965
|
+
}
|
|
966
|
+
};
|
|
827
967
|
}
|
|
828
968
|
}
|
|
829
969
|
const newLocalVersion = this.localVersion + 1;
|
|
@@ -850,19 +990,47 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
850
990
|
}
|
|
851
991
|
this.localVersion = newLocalVersion;
|
|
852
992
|
this.root.activeTransactions.delete(child);
|
|
993
|
+
return null;
|
|
853
994
|
} else {
|
|
854
|
-
this.root.activeTransactions.delete(child);
|
|
855
995
|
const newVersion = this.version + 1;
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
const
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
996
|
+
if (child !== this) {
|
|
997
|
+
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
998
|
+
for (const key of modifiedKeys) {
|
|
999
|
+
const versions = this.versionIndex.get(key);
|
|
1000
|
+
if (versions && versions.length > 0) {
|
|
1001
|
+
const lastVer = versions[versions.length - 1].version;
|
|
1002
|
+
if (lastVer > child.snapshotVersion) {
|
|
1003
|
+
return {
|
|
1004
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
1005
|
+
conflict: {
|
|
1006
|
+
key,
|
|
1007
|
+
parent: await this.read(key),
|
|
1008
|
+
child: await child.read(key)
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
863
1012
|
}
|
|
864
1013
|
}
|
|
865
1014
|
}
|
|
1015
|
+
for (const [key, value] of child.writeBuffer) {
|
|
1016
|
+
this.writeBuffer.set(key, value);
|
|
1017
|
+
this.deleteBuffer.delete(key);
|
|
1018
|
+
if (child.createdKeys.has(key)) {
|
|
1019
|
+
this.createdKeys.add(key);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
for (const key of child.deleteBuffer) {
|
|
1023
|
+
this.deleteBuffer.add(key);
|
|
1024
|
+
this.writeBuffer.delete(key);
|
|
1025
|
+
this.createdKeys.delete(key);
|
|
1026
|
+
const deletedValue = child.deletedValues.get(key);
|
|
1027
|
+
if (deletedValue !== void 0) {
|
|
1028
|
+
this.deletedValues.set(key, deletedValue);
|
|
1029
|
+
}
|
|
1030
|
+
if (child.originallyExisted.has(key)) {
|
|
1031
|
+
this.originallyExisted.add(key);
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
866
1034
|
for (const [key, value] of child.writeBuffer) {
|
|
867
1035
|
await this._diskWrite(key, value, newVersion);
|
|
868
1036
|
}
|
|
@@ -870,9 +1038,10 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
870
1038
|
await this._diskDelete(key, newVersion);
|
|
871
1039
|
}
|
|
872
1040
|
this.version = newVersion;
|
|
1041
|
+
this.root.activeTransactions.delete(child);
|
|
873
1042
|
this._cleanupDeletedCache();
|
|
1043
|
+
return null;
|
|
874
1044
|
}
|
|
875
|
-
return null;
|
|
876
1045
|
});
|
|
877
1046
|
}
|
|
878
1047
|
// --- Internal IO Helpers (Root Only) ---
|
|
@@ -908,7 +1077,17 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
908
1077
|
break;
|
|
909
1078
|
}
|
|
910
1079
|
}
|
|
911
|
-
if (!targetVerObj
|
|
1080
|
+
if (!targetVerObj) {
|
|
1081
|
+
if (nextVerObj) {
|
|
1082
|
+
const cached2 = this.deletedCache.get(key);
|
|
1083
|
+
if (cached2) {
|
|
1084
|
+
const match = cached2.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
1085
|
+
if (match) return match.value;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
return null;
|
|
1089
|
+
}
|
|
1090
|
+
if (!targetVerObj.exists) return null;
|
|
912
1091
|
if (!nextVerObj) {
|
|
913
1092
|
return strategy.read(key);
|
|
914
1093
|
}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -210,25 +210,57 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
210
210
|
return this._diskRead(key, snapshotVersion);
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
-
commit() {
|
|
213
|
+
commit(label) {
|
|
214
214
|
const { created, updated, deleted } = this._getResultEntries();
|
|
215
215
|
if (this.committed) {
|
|
216
|
-
return {
|
|
216
|
+
return {
|
|
217
|
+
label,
|
|
218
|
+
success: false,
|
|
219
|
+
error: "Transaction already committed",
|
|
220
|
+
conflict: void 0,
|
|
221
|
+
created,
|
|
222
|
+
updated,
|
|
223
|
+
deleted
|
|
224
|
+
};
|
|
217
225
|
}
|
|
218
226
|
if (this.hasCommittedAncestor()) {
|
|
219
|
-
return {
|
|
227
|
+
return {
|
|
228
|
+
label,
|
|
229
|
+
success: false,
|
|
230
|
+
error: "Ancestor transaction already committed",
|
|
231
|
+
conflict: void 0,
|
|
232
|
+
created,
|
|
233
|
+
updated,
|
|
234
|
+
deleted
|
|
235
|
+
};
|
|
220
236
|
}
|
|
221
237
|
if (this.parent) {
|
|
222
|
-
const
|
|
223
|
-
if (
|
|
224
|
-
return {
|
|
238
|
+
const failure = this.parent._merge(this);
|
|
239
|
+
if (failure) {
|
|
240
|
+
return {
|
|
241
|
+
label,
|
|
242
|
+
success: false,
|
|
243
|
+
error: failure.error,
|
|
244
|
+
conflict: failure.conflict,
|
|
245
|
+
created,
|
|
246
|
+
updated,
|
|
247
|
+
deleted
|
|
248
|
+
};
|
|
225
249
|
}
|
|
226
250
|
this.committed = true;
|
|
227
251
|
} else {
|
|
228
252
|
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
229
|
-
const
|
|
230
|
-
if (
|
|
231
|
-
return {
|
|
253
|
+
const failure = this._merge(this);
|
|
254
|
+
if (failure) {
|
|
255
|
+
return {
|
|
256
|
+
label,
|
|
257
|
+
success: false,
|
|
258
|
+
error: failure.error,
|
|
259
|
+
conflict: failure.conflict,
|
|
260
|
+
created: [],
|
|
261
|
+
updated: [],
|
|
262
|
+
deleted: []
|
|
263
|
+
};
|
|
232
264
|
}
|
|
233
265
|
this.writeBuffer.clear();
|
|
234
266
|
this.deleteBuffer.clear();
|
|
@@ -239,20 +271,40 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
239
271
|
this.localVersion = 0;
|
|
240
272
|
}
|
|
241
273
|
}
|
|
242
|
-
return {
|
|
274
|
+
return {
|
|
275
|
+
label,
|
|
276
|
+
success: true,
|
|
277
|
+
created,
|
|
278
|
+
updated,
|
|
279
|
+
deleted
|
|
280
|
+
};
|
|
243
281
|
}
|
|
244
282
|
_merge(child) {
|
|
245
283
|
if (this.parent) {
|
|
246
284
|
for (const key of child.writeBuffer.keys()) {
|
|
247
285
|
const lastModLocalVer = this.keyVersions.get(key);
|
|
248
286
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
249
|
-
return
|
|
287
|
+
return {
|
|
288
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
289
|
+
conflict: {
|
|
290
|
+
key,
|
|
291
|
+
parent: this.read(key),
|
|
292
|
+
child: child.read(key)
|
|
293
|
+
}
|
|
294
|
+
};
|
|
250
295
|
}
|
|
251
296
|
}
|
|
252
297
|
for (const key of child.deleteBuffer) {
|
|
253
298
|
const lastModLocalVer = this.keyVersions.get(key);
|
|
254
299
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
255
|
-
return
|
|
300
|
+
return {
|
|
301
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
302
|
+
conflict: {
|
|
303
|
+
key,
|
|
304
|
+
parent: this.read(key),
|
|
305
|
+
child: child.read(key)
|
|
306
|
+
}
|
|
307
|
+
};
|
|
256
308
|
}
|
|
257
309
|
}
|
|
258
310
|
const newLocalVersion = this.localVersion + 1;
|
|
@@ -280,18 +332,45 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
280
332
|
this.localVersion = newLocalVersion;
|
|
281
333
|
this.root.activeTransactions.delete(child);
|
|
282
334
|
} else {
|
|
283
|
-
this.root.activeTransactions.delete(child);
|
|
284
335
|
const newVersion = this.version + 1;
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
336
|
+
if (child !== this) {
|
|
337
|
+
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
338
|
+
for (const key of modifiedKeys) {
|
|
339
|
+
const versions = this.versionIndex.get(key);
|
|
340
|
+
if (versions && versions.length > 0) {
|
|
341
|
+
const lastVer = versions[versions.length - 1].version;
|
|
342
|
+
if (lastVer > child.snapshotVersion) {
|
|
343
|
+
return {
|
|
344
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
345
|
+
conflict: {
|
|
346
|
+
key,
|
|
347
|
+
parent: this.read(key),
|
|
348
|
+
child: child.read(key)
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
}
|
|
292
352
|
}
|
|
293
353
|
}
|
|
294
354
|
}
|
|
355
|
+
for (const [key, value] of child.writeBuffer) {
|
|
356
|
+
this.writeBuffer.set(key, value);
|
|
357
|
+
this.deleteBuffer.delete(key);
|
|
358
|
+
if (child.createdKeys.has(key)) {
|
|
359
|
+
this.createdKeys.add(key);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
for (const key of child.deleteBuffer) {
|
|
363
|
+
this.deleteBuffer.add(key);
|
|
364
|
+
this.writeBuffer.delete(key);
|
|
365
|
+
this.createdKeys.delete(key);
|
|
366
|
+
const deletedValue = child.deletedValues.get(key);
|
|
367
|
+
if (deletedValue !== void 0) {
|
|
368
|
+
this.deletedValues.set(key, deletedValue);
|
|
369
|
+
}
|
|
370
|
+
if (child.originallyExisted.has(key)) {
|
|
371
|
+
this.originallyExisted.add(key);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
295
374
|
for (const [key, value] of child.writeBuffer) {
|
|
296
375
|
this._diskWrite(key, value, newVersion);
|
|
297
376
|
}
|
|
@@ -299,6 +378,7 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
299
378
|
this._diskDelete(key, newVersion);
|
|
300
379
|
}
|
|
301
380
|
this.version = newVersion;
|
|
381
|
+
this.root.activeTransactions.delete(child);
|
|
302
382
|
this._cleanupDeletedCache();
|
|
303
383
|
}
|
|
304
384
|
return null;
|
|
@@ -336,7 +416,17 @@ var SyncMVCCTransaction = class _SyncMVCCTransaction extends MVCCTransaction {
|
|
|
336
416
|
break;
|
|
337
417
|
}
|
|
338
418
|
}
|
|
339
|
-
if (!targetVerObj
|
|
419
|
+
if (!targetVerObj) {
|
|
420
|
+
if (nextVerObj) {
|
|
421
|
+
const cached2 = this.deletedCache.get(key);
|
|
422
|
+
if (cached2) {
|
|
423
|
+
const match = cached2.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
424
|
+
if (match) return match.value;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
if (!targetVerObj.exists) return null;
|
|
340
430
|
if (!nextVerObj) {
|
|
341
431
|
return strategy.read(key);
|
|
342
432
|
}
|
|
@@ -749,38 +839,74 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
749
839
|
return this._diskRead(key, snapshotVersion);
|
|
750
840
|
}
|
|
751
841
|
}
|
|
752
|
-
async commit() {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
842
|
+
async commit(label) {
|
|
843
|
+
const { created, updated, deleted } = this._getResultEntries();
|
|
844
|
+
if (this.committed) {
|
|
845
|
+
return {
|
|
846
|
+
label,
|
|
847
|
+
success: false,
|
|
848
|
+
error: "Transaction already committed",
|
|
849
|
+
conflict: void 0,
|
|
850
|
+
created,
|
|
851
|
+
updated,
|
|
852
|
+
deleted
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
if (this.hasCommittedAncestor()) {
|
|
856
|
+
return {
|
|
857
|
+
label,
|
|
858
|
+
success: false,
|
|
859
|
+
error: "Ancestor transaction already committed",
|
|
860
|
+
conflict: void 0,
|
|
861
|
+
created,
|
|
862
|
+
updated,
|
|
863
|
+
deleted
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
if (this.parent) {
|
|
867
|
+
const failure = await this.parent._merge(this);
|
|
868
|
+
if (failure) {
|
|
869
|
+
return {
|
|
870
|
+
label,
|
|
871
|
+
success: false,
|
|
872
|
+
error: failure.error,
|
|
873
|
+
conflict: failure.conflict,
|
|
874
|
+
created,
|
|
875
|
+
updated,
|
|
876
|
+
deleted
|
|
877
|
+
};
|
|
760
878
|
}
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
this.createdKeys.clear();
|
|
776
|
-
this.deletedValues.clear();
|
|
777
|
-
this.originallyExisted.clear();
|
|
778
|
-
this.keyVersions.clear();
|
|
779
|
-
this.localVersion = 0;
|
|
879
|
+
this.committed = true;
|
|
880
|
+
} else {
|
|
881
|
+
if (this.writeBuffer.size > 0 || this.deleteBuffer.size > 0) {
|
|
882
|
+
const failure = await this._merge(this);
|
|
883
|
+
if (failure) {
|
|
884
|
+
return {
|
|
885
|
+
label,
|
|
886
|
+
success: false,
|
|
887
|
+
error: failure.error,
|
|
888
|
+
conflict: failure.conflict,
|
|
889
|
+
created: [],
|
|
890
|
+
updated: [],
|
|
891
|
+
deleted: []
|
|
892
|
+
};
|
|
780
893
|
}
|
|
894
|
+
this.writeBuffer.clear();
|
|
895
|
+
this.deleteBuffer.clear();
|
|
896
|
+
this.createdKeys.clear();
|
|
897
|
+
this.deletedValues.clear();
|
|
898
|
+
this.originallyExisted.clear();
|
|
899
|
+
this.keyVersions.clear();
|
|
900
|
+
this.localVersion = 0;
|
|
781
901
|
}
|
|
782
|
-
|
|
783
|
-
|
|
902
|
+
}
|
|
903
|
+
return {
|
|
904
|
+
label,
|
|
905
|
+
success: true,
|
|
906
|
+
created,
|
|
907
|
+
updated,
|
|
908
|
+
deleted
|
|
909
|
+
};
|
|
784
910
|
}
|
|
785
911
|
async _merge(child) {
|
|
786
912
|
return this.writeLock(async () => {
|
|
@@ -788,13 +914,27 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
788
914
|
for (const key of child.writeBuffer.keys()) {
|
|
789
915
|
const lastModLocalVer = this.keyVersions.get(key);
|
|
790
916
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
791
|
-
return
|
|
917
|
+
return {
|
|
918
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
919
|
+
conflict: {
|
|
920
|
+
key,
|
|
921
|
+
parent: await this.read(key),
|
|
922
|
+
child: await child.read(key)
|
|
923
|
+
}
|
|
924
|
+
};
|
|
792
925
|
}
|
|
793
926
|
}
|
|
794
927
|
for (const key of child.deleteBuffer) {
|
|
795
928
|
const lastModLocalVer = this.keyVersions.get(key);
|
|
796
929
|
if (lastModLocalVer !== void 0 && lastModLocalVer > child.snapshotLocalVersion) {
|
|
797
|
-
return
|
|
930
|
+
return {
|
|
931
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (Local v${lastModLocalVer})`,
|
|
932
|
+
conflict: {
|
|
933
|
+
key,
|
|
934
|
+
parent: await this.read(key),
|
|
935
|
+
child: await child.read(key)
|
|
936
|
+
}
|
|
937
|
+
};
|
|
798
938
|
}
|
|
799
939
|
}
|
|
800
940
|
const newLocalVersion = this.localVersion + 1;
|
|
@@ -821,19 +961,47 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
821
961
|
}
|
|
822
962
|
this.localVersion = newLocalVersion;
|
|
823
963
|
this.root.activeTransactions.delete(child);
|
|
964
|
+
return null;
|
|
824
965
|
} else {
|
|
825
|
-
this.root.activeTransactions.delete(child);
|
|
826
966
|
const newVersion = this.version + 1;
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
const
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
967
|
+
if (child !== this) {
|
|
968
|
+
const modifiedKeys = /* @__PURE__ */ new Set([...child.writeBuffer.keys(), ...child.deleteBuffer]);
|
|
969
|
+
for (const key of modifiedKeys) {
|
|
970
|
+
const versions = this.versionIndex.get(key);
|
|
971
|
+
if (versions && versions.length > 0) {
|
|
972
|
+
const lastVer = versions[versions.length - 1].version;
|
|
973
|
+
if (lastVer > child.snapshotVersion) {
|
|
974
|
+
return {
|
|
975
|
+
error: `Commit conflict: Key '${key}' was modified by a newer transaction (v${lastVer})`,
|
|
976
|
+
conflict: {
|
|
977
|
+
key,
|
|
978
|
+
parent: await this.read(key),
|
|
979
|
+
child: await child.read(key)
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
}
|
|
834
983
|
}
|
|
835
984
|
}
|
|
836
985
|
}
|
|
986
|
+
for (const [key, value] of child.writeBuffer) {
|
|
987
|
+
this.writeBuffer.set(key, value);
|
|
988
|
+
this.deleteBuffer.delete(key);
|
|
989
|
+
if (child.createdKeys.has(key)) {
|
|
990
|
+
this.createdKeys.add(key);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
for (const key of child.deleteBuffer) {
|
|
994
|
+
this.deleteBuffer.add(key);
|
|
995
|
+
this.writeBuffer.delete(key);
|
|
996
|
+
this.createdKeys.delete(key);
|
|
997
|
+
const deletedValue = child.deletedValues.get(key);
|
|
998
|
+
if (deletedValue !== void 0) {
|
|
999
|
+
this.deletedValues.set(key, deletedValue);
|
|
1000
|
+
}
|
|
1001
|
+
if (child.originallyExisted.has(key)) {
|
|
1002
|
+
this.originallyExisted.add(key);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
837
1005
|
for (const [key, value] of child.writeBuffer) {
|
|
838
1006
|
await this._diskWrite(key, value, newVersion);
|
|
839
1007
|
}
|
|
@@ -841,9 +1009,10 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
841
1009
|
await this._diskDelete(key, newVersion);
|
|
842
1010
|
}
|
|
843
1011
|
this.version = newVersion;
|
|
1012
|
+
this.root.activeTransactions.delete(child);
|
|
844
1013
|
this._cleanupDeletedCache();
|
|
1014
|
+
return null;
|
|
845
1015
|
}
|
|
846
|
-
return null;
|
|
847
1016
|
});
|
|
848
1017
|
}
|
|
849
1018
|
// --- Internal IO Helpers (Root Only) ---
|
|
@@ -879,7 +1048,17 @@ var AsyncMVCCTransaction = class _AsyncMVCCTransaction extends MVCCTransaction {
|
|
|
879
1048
|
break;
|
|
880
1049
|
}
|
|
881
1050
|
}
|
|
882
|
-
if (!targetVerObj
|
|
1051
|
+
if (!targetVerObj) {
|
|
1052
|
+
if (nextVerObj) {
|
|
1053
|
+
const cached2 = this.deletedCache.get(key);
|
|
1054
|
+
if (cached2) {
|
|
1055
|
+
const match = cached2.find((c) => c.deletedAtVersion === nextVerObj.version);
|
|
1056
|
+
if (match) return match.value;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
return null;
|
|
1060
|
+
}
|
|
1061
|
+
if (!targetVerObj.exists) return null;
|
|
883
1062
|
if (!nextVerObj) {
|
|
884
1063
|
return strategy.read(key);
|
|
885
1064
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AsyncMVCCStrategy } from './Strategy';
|
|
2
|
-
import type { TransactionResult } from '../../types';
|
|
2
|
+
import type { TransactionMergeFailure, TransactionResult } from '../../types';
|
|
3
3
|
import { MVCCTransaction } from '../base';
|
|
4
4
|
export declare class AsyncMVCCTransaction<S extends AsyncMVCCStrategy<K, T>, K, T> extends MVCCTransaction<S, K, T> {
|
|
5
5
|
private lock;
|
|
@@ -11,8 +11,8 @@ export declare class AsyncMVCCTransaction<S extends AsyncMVCCStrategy<K, T>, K,
|
|
|
11
11
|
read(key: K): Promise<T | null>;
|
|
12
12
|
exists(key: K): Promise<boolean>;
|
|
13
13
|
_readSnapshot(key: K, snapshotVersion: number, snapshotLocalVersion?: number): Promise<T | null>;
|
|
14
|
-
commit(): Promise<TransactionResult<K, T>>;
|
|
15
|
-
_merge(child: MVCCTransaction<S, K, T>): Promise<
|
|
14
|
+
commit(label?: string): Promise<TransactionResult<K, T>>;
|
|
15
|
+
_merge(child: MVCCTransaction<S, K, T>): Promise<TransactionMergeFailure<K, T> | null>;
|
|
16
16
|
_diskWrite(key: K, value: T, version: number): Promise<void>;
|
|
17
17
|
_diskRead(key: K, snapshotVersion: number): Promise<T | null>;
|
|
18
18
|
_diskExists(key: K, snapshotVersion: number): Promise<boolean>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Deferred, TransactionResult, TransactionEntry } from '../../types';
|
|
1
|
+
import type { Deferred, TransactionResult, TransactionEntry, TransactionMergeFailure } from '../../types';
|
|
2
2
|
import type { MVCCStrategy } from './Strategy';
|
|
3
3
|
/**
|
|
4
4
|
* MVCC Transaction abstract class.
|
|
@@ -93,9 +93,10 @@ export declare abstract class MVCCTransaction<S extends MVCCStrategy<K, T>, K, T
|
|
|
93
93
|
* Commits the transaction.
|
|
94
94
|
* If root, persists to storage.
|
|
95
95
|
* If nested, merges to parent.
|
|
96
|
+
* @param label The label for the commit.
|
|
96
97
|
* @returns The result object with success, created, and obsolete keys.
|
|
97
98
|
*/
|
|
98
|
-
abstract commit(): Deferred<TransactionResult<K, T>>;
|
|
99
|
+
abstract commit(label?: string): Deferred<TransactionResult<K, T>>;
|
|
99
100
|
/**
|
|
100
101
|
* Creates a nested transaction (child) from this transaction.
|
|
101
102
|
* @returns A new nested transaction instance.
|
|
@@ -106,7 +107,7 @@ export declare abstract class MVCCTransaction<S extends MVCCStrategy<K, T>, K, T
|
|
|
106
107
|
* @param child The committed child transaction.
|
|
107
108
|
* @returns Error message if conflict, null if success.
|
|
108
109
|
*/
|
|
109
|
-
abstract _merge(child: MVCCTransaction<S, K, T>): Deferred<
|
|
110
|
+
abstract _merge(child: MVCCTransaction<S, K, T>): Deferred<TransactionMergeFailure<K, T> | null>;
|
|
110
111
|
/**
|
|
111
112
|
* Reads a value at a specific snapshot version.
|
|
112
113
|
* Used by child transactions to read from parent respecting the child's snapshot.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SyncMVCCStrategy } from './Strategy';
|
|
2
|
-
import type { TransactionResult } from '../../types';
|
|
2
|
+
import type { TransactionResult, TransactionMergeFailure } from '../../types';
|
|
3
3
|
import { MVCCTransaction } from '../base';
|
|
4
4
|
export declare class SyncMVCCTransaction<S extends SyncMVCCStrategy<K, T>, K, T> extends MVCCTransaction<S, K, T> {
|
|
5
5
|
create(key: K, value: T): this;
|
|
@@ -9,8 +9,8 @@ export declare class SyncMVCCTransaction<S extends SyncMVCCStrategy<K, T>, K, T>
|
|
|
9
9
|
read(key: K): T | null;
|
|
10
10
|
exists(key: K): boolean;
|
|
11
11
|
_readSnapshot(key: K, snapshotVersion: number, snapshotLocalVersion?: number): T | null;
|
|
12
|
-
commit(): TransactionResult<K, T>;
|
|
13
|
-
_merge(child:
|
|
12
|
+
commit(label?: string): TransactionResult<K, T>;
|
|
13
|
+
_merge(child: SyncMVCCTransaction<S, K, T>): TransactionMergeFailure<K, T> | null;
|
|
14
14
|
_diskWrite(key: K, value: T, version: number): void;
|
|
15
15
|
_diskRead(key: K, snapshotVersion: number): T | null;
|
|
16
16
|
_diskExists(key: K, snapshotVersion: number): boolean;
|
|
@@ -7,9 +7,26 @@ export type TransactionEntry<K, T> = {
|
|
|
7
7
|
key: K;
|
|
8
8
|
data: T;
|
|
9
9
|
};
|
|
10
|
+
export type TransactionConflict<K, T> = {
|
|
11
|
+
key: K;
|
|
12
|
+
parent: T;
|
|
13
|
+
child: T;
|
|
14
|
+
};
|
|
15
|
+
export type TransactionMergeFailure<K, T> = {
|
|
16
|
+
error: string;
|
|
17
|
+
conflict: TransactionConflict<K, T>;
|
|
18
|
+
};
|
|
10
19
|
export type TransactionResult<K, T> = {
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
label?: string;
|
|
21
|
+
success: true;
|
|
22
|
+
created: TransactionEntry<K, T>[];
|
|
23
|
+
updated: TransactionEntry<K, T>[];
|
|
24
|
+
deleted: TransactionEntry<K, T>[];
|
|
25
|
+
} | {
|
|
26
|
+
label?: string;
|
|
27
|
+
success: false;
|
|
28
|
+
error: string;
|
|
29
|
+
conflict?: TransactionConflict<K, T>;
|
|
13
30
|
created: TransactionEntry<K, T>[];
|
|
14
31
|
updated: TransactionEntry<K, T>[];
|
|
15
32
|
deleted: TransactionEntry<K, T>[];
|