dataply 0.0.24-alpha.5 → 0.0.24-alpha.6
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/dist/cjs/index.js
CHANGED
|
@@ -9465,8 +9465,6 @@ var Transaction = class {
|
|
|
9465
9465
|
pageLocks = /* @__PURE__ */ new Map();
|
|
9466
9466
|
/** Dirty Pages modified by the transaction: PageID -> Modified Page Buffer */
|
|
9467
9467
|
dirtyPages = /* @__PURE__ */ new Map();
|
|
9468
|
-
/** Promise chain for serializing operations on this transaction */
|
|
9469
|
-
serialPromise = Promise.resolve();
|
|
9470
9468
|
/** Undo pages: PageID -> Original Page Buffer (Snapshot) */
|
|
9471
9469
|
undoPages = /* @__PURE__ */ new Map();
|
|
9472
9470
|
/** BPTree Transaction instance */
|
|
@@ -9477,6 +9475,8 @@ var Transaction = class {
|
|
|
9477
9475
|
commitHooks = [];
|
|
9478
9476
|
/** Page MVCC Strategy for disk access */
|
|
9479
9477
|
pageStrategy;
|
|
9478
|
+
/** Release function for global write lock, set by DataplyAPI */
|
|
9479
|
+
_writeLockRelease = null;
|
|
9480
9480
|
/**
|
|
9481
9481
|
* Sets the BPTree transaction.
|
|
9482
9482
|
* @param tx BPTree transaction
|
|
@@ -9508,28 +9508,21 @@ var Transaction = class {
|
|
|
9508
9508
|
* Registers a commit hook.
|
|
9509
9509
|
* @param hook Function to execute
|
|
9510
9510
|
*/
|
|
9511
|
+
onCommit(hook) {
|
|
9512
|
+
this.commitHooks.push(hook);
|
|
9513
|
+
}
|
|
9511
9514
|
/**
|
|
9512
|
-
*
|
|
9513
|
-
*
|
|
9514
|
-
* @param fn The async function to serialize
|
|
9515
|
-
* @returns The result of the function
|
|
9515
|
+
* Sets the global write lock release function.
|
|
9516
|
+
* Called by DataplyAPI.runWithDefaultWrite when acquiring the lock.
|
|
9516
9517
|
*/
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
let resolveLock;
|
|
9520
|
-
this.serialPromise = new Promise((resolve) => {
|
|
9521
|
-
resolveLock = resolve;
|
|
9522
|
-
});
|
|
9523
|
-
return previous.then(async () => {
|
|
9524
|
-
try {
|
|
9525
|
-
return await fn();
|
|
9526
|
-
} finally {
|
|
9527
|
-
resolveLock();
|
|
9528
|
-
}
|
|
9529
|
-
});
|
|
9518
|
+
__setWriteLockRelease(release) {
|
|
9519
|
+
this._writeLockRelease = release;
|
|
9530
9520
|
}
|
|
9531
|
-
|
|
9532
|
-
|
|
9521
|
+
/**
|
|
9522
|
+
* Returns whether this transaction already has a write lock.
|
|
9523
|
+
*/
|
|
9524
|
+
__hasWriteLockRelease() {
|
|
9525
|
+
return this._writeLockRelease !== null;
|
|
9533
9526
|
}
|
|
9534
9527
|
/**
|
|
9535
9528
|
* Reads a page. Uses dirty buffer if available, otherwise disk.
|
|
@@ -9576,7 +9569,7 @@ var Transaction = class {
|
|
|
9576
9569
|
* Commits the transaction.
|
|
9577
9570
|
*/
|
|
9578
9571
|
async commit() {
|
|
9579
|
-
|
|
9572
|
+
try {
|
|
9580
9573
|
await this.context.run(this, async () => {
|
|
9581
9574
|
for (const hook of this.commitHooks) {
|
|
9582
9575
|
await hook();
|
|
@@ -9604,20 +9597,30 @@ var Transaction = class {
|
|
|
9604
9597
|
this.dirtyPages.clear();
|
|
9605
9598
|
this.undoPages.clear();
|
|
9606
9599
|
this.releaseAllLocks();
|
|
9607
|
-
}
|
|
9600
|
+
} finally {
|
|
9601
|
+
if (this._writeLockRelease) {
|
|
9602
|
+
this._writeLockRelease();
|
|
9603
|
+
this._writeLockRelease = null;
|
|
9604
|
+
}
|
|
9605
|
+
}
|
|
9608
9606
|
}
|
|
9609
9607
|
/**
|
|
9610
9608
|
* Rolls back the transaction.
|
|
9611
9609
|
*/
|
|
9612
9610
|
async rollback() {
|
|
9613
|
-
|
|
9611
|
+
try {
|
|
9614
9612
|
if (this.bptreeTx) {
|
|
9615
9613
|
this.bptreeTx.rollback();
|
|
9616
9614
|
}
|
|
9617
9615
|
this.dirtyPages.clear();
|
|
9618
9616
|
this.undoPages.clear();
|
|
9619
9617
|
this.releaseAllLocks();
|
|
9620
|
-
}
|
|
9618
|
+
} finally {
|
|
9619
|
+
if (this._writeLockRelease) {
|
|
9620
|
+
this._writeLockRelease();
|
|
9621
|
+
this._writeLockRelease = null;
|
|
9622
|
+
}
|
|
9623
|
+
}
|
|
9621
9624
|
}
|
|
9622
9625
|
/**
|
|
9623
9626
|
* Returns the dirty pages map.
|
|
@@ -9698,6 +9701,8 @@ var DataplyAPI = class {
|
|
|
9698
9701
|
/** Whether the database was created this time. */
|
|
9699
9702
|
isNewlyCreated;
|
|
9700
9703
|
txIdCounter;
|
|
9704
|
+
/** Promise-chain mutex for serializing write operations */
|
|
9705
|
+
writeQueue = Promise.resolve();
|
|
9701
9706
|
/**
|
|
9702
9707
|
* Verifies if the page file is a valid Dataply file.
|
|
9703
9708
|
* The metadata page must be located at the beginning of the Dataply file.
|
|
@@ -9855,13 +9860,58 @@ var DataplyAPI = class {
|
|
|
9855
9860
|
* @param tx The transaction to use. If not provided, a new transaction is created.
|
|
9856
9861
|
* @returns The result of the callback function.
|
|
9857
9862
|
*/
|
|
9863
|
+
/**
|
|
9864
|
+
* Acquires the global write lock.
|
|
9865
|
+
* Returns a release function that MUST be called to unlock.
|
|
9866
|
+
* Used internally by runWithDefaultWrite.
|
|
9867
|
+
* @returns A release function
|
|
9868
|
+
*/
|
|
9869
|
+
acquireWriteLock() {
|
|
9870
|
+
const previous = this.writeQueue;
|
|
9871
|
+
let release;
|
|
9872
|
+
this.writeQueue = new Promise((resolve) => {
|
|
9873
|
+
release = resolve;
|
|
9874
|
+
});
|
|
9875
|
+
return previous.then(() => release);
|
|
9876
|
+
}
|
|
9877
|
+
/**
|
|
9878
|
+
* Runs a write callback within a transaction context with global write serialization.
|
|
9879
|
+
* If no transaction is provided, a new transaction is created, committed on success, rolled back on error.
|
|
9880
|
+
* If a transaction is provided (external), the write lock is acquired on first call and held until commit/rollback.
|
|
9881
|
+
* Subclasses MUST use this method for all write operations instead of runWithDefault.
|
|
9882
|
+
* @param callback The callback function to run.
|
|
9883
|
+
* @param tx Optional external transaction.
|
|
9884
|
+
* @returns The result of the callback.
|
|
9885
|
+
*/
|
|
9886
|
+
async runWithDefaultWrite(callback, tx) {
|
|
9887
|
+
if (!tx) {
|
|
9888
|
+
const release = await this.acquireWriteLock();
|
|
9889
|
+
const internalTx = this.createTransaction();
|
|
9890
|
+
internalTx.__setWriteLockRelease(release);
|
|
9891
|
+
const [error2, result2] = await catchPromise(this.txContext.run(internalTx, () => callback(internalTx)));
|
|
9892
|
+
if (error2) {
|
|
9893
|
+
await internalTx.rollback();
|
|
9894
|
+
throw error2;
|
|
9895
|
+
}
|
|
9896
|
+
await internalTx.commit();
|
|
9897
|
+
return result2;
|
|
9898
|
+
}
|
|
9899
|
+
if (!tx.__hasWriteLockRelease()) {
|
|
9900
|
+
const release = await this.acquireWriteLock();
|
|
9901
|
+
tx.__setWriteLockRelease(release);
|
|
9902
|
+
}
|
|
9903
|
+
const [error, result] = await catchPromise(this.txContext.run(tx, () => callback(tx)));
|
|
9904
|
+
if (error) {
|
|
9905
|
+
throw error;
|
|
9906
|
+
}
|
|
9907
|
+
return result;
|
|
9908
|
+
}
|
|
9858
9909
|
async runWithDefault(callback, tx) {
|
|
9859
9910
|
const isInternalTx = !tx;
|
|
9860
9911
|
if (!tx) {
|
|
9861
9912
|
tx = this.createTransaction();
|
|
9862
9913
|
}
|
|
9863
|
-
const
|
|
9864
|
-
const [error, result] = isInternalTx ? await run() : await tx.serialize(run);
|
|
9914
|
+
const [error, result] = await catchPromise(this.txContext.run(tx, () => callback(tx)));
|
|
9865
9915
|
if (error) {
|
|
9866
9916
|
if (isInternalTx) {
|
|
9867
9917
|
await tx.rollback();
|
|
@@ -9889,20 +9939,6 @@ var DataplyAPI = class {
|
|
|
9889
9939
|
tx = this.createTransaction();
|
|
9890
9940
|
}
|
|
9891
9941
|
let hasError = false;
|
|
9892
|
-
if (!isInternalTx) {
|
|
9893
|
-
const values = await tx.serialize(async () => {
|
|
9894
|
-
const collected = [];
|
|
9895
|
-
const generator = this.txContext.stream(tx, () => callback(tx));
|
|
9896
|
-
for await (const value of generator) {
|
|
9897
|
-
collected.push(value);
|
|
9898
|
-
}
|
|
9899
|
-
return collected;
|
|
9900
|
-
});
|
|
9901
|
-
for (const value of values) {
|
|
9902
|
-
yield value;
|
|
9903
|
-
}
|
|
9904
|
-
return;
|
|
9905
|
-
}
|
|
9906
9942
|
try {
|
|
9907
9943
|
const generator = this.txContext.stream(tx, () => callback(tx));
|
|
9908
9944
|
for await (const value of generator) {
|
|
@@ -9910,10 +9946,12 @@ var DataplyAPI = class {
|
|
|
9910
9946
|
}
|
|
9911
9947
|
} catch (error) {
|
|
9912
9948
|
hasError = true;
|
|
9913
|
-
|
|
9949
|
+
if (isInternalTx) {
|
|
9950
|
+
await tx.rollback();
|
|
9951
|
+
}
|
|
9914
9952
|
throw error;
|
|
9915
9953
|
} finally {
|
|
9916
|
-
if (!hasError) {
|
|
9954
|
+
if (!hasError && isInternalTx) {
|
|
9917
9955
|
await tx.commit();
|
|
9918
9956
|
}
|
|
9919
9957
|
}
|
|
@@ -9939,7 +9977,7 @@ var DataplyAPI = class {
|
|
|
9939
9977
|
if (!this.initialized) {
|
|
9940
9978
|
throw new Error("Dataply instance is not initialized");
|
|
9941
9979
|
}
|
|
9942
|
-
return this.
|
|
9980
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
9943
9981
|
incrementRowCount = incrementRowCount ?? true;
|
|
9944
9982
|
if (typeof data === "string") {
|
|
9945
9983
|
data = this.textCodec.encode(data);
|
|
@@ -9959,7 +9997,7 @@ var DataplyAPI = class {
|
|
|
9959
9997
|
if (!this.initialized) {
|
|
9960
9998
|
throw new Error("Dataply instance is not initialized");
|
|
9961
9999
|
}
|
|
9962
|
-
return this.
|
|
10000
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
9963
10001
|
incrementRowCount = incrementRowCount ?? true;
|
|
9964
10002
|
if (typeof data === "string") {
|
|
9965
10003
|
data = this.textCodec.encode(data);
|
|
@@ -9980,7 +10018,7 @@ var DataplyAPI = class {
|
|
|
9980
10018
|
if (!this.initialized) {
|
|
9981
10019
|
throw new Error("Dataply instance is not initialized");
|
|
9982
10020
|
}
|
|
9983
|
-
return this.
|
|
10021
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
9984
10022
|
incrementRowCount = incrementRowCount ?? true;
|
|
9985
10023
|
const encodedList = dataList.map(
|
|
9986
10024
|
(data) => typeof data === "string" ? this.textCodec.encode(data) : data
|
|
@@ -9998,7 +10036,7 @@ var DataplyAPI = class {
|
|
|
9998
10036
|
if (!this.initialized) {
|
|
9999
10037
|
throw new Error("Dataply instance is not initialized");
|
|
10000
10038
|
}
|
|
10001
|
-
return this.
|
|
10039
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
10002
10040
|
if (typeof data === "string") {
|
|
10003
10041
|
data = this.textCodec.encode(data);
|
|
10004
10042
|
}
|
|
@@ -10015,7 +10053,7 @@ var DataplyAPI = class {
|
|
|
10015
10053
|
if (!this.initialized) {
|
|
10016
10054
|
throw new Error("Dataply instance is not initialized");
|
|
10017
10055
|
}
|
|
10018
|
-
return this.
|
|
10056
|
+
return this.runWithDefaultWrite(async (tx2) => {
|
|
10019
10057
|
decrementRowCount = decrementRowCount ?? true;
|
|
10020
10058
|
await this.rowTableEngine.delete(pk, decrementRowCount, tx2);
|
|
10021
10059
|
}, tx);
|
|
@@ -40,6 +40,8 @@ export declare class DataplyAPI {
|
|
|
40
40
|
/** Whether the database was created this time. */
|
|
41
41
|
private readonly isNewlyCreated;
|
|
42
42
|
private txIdCounter;
|
|
43
|
+
/** Promise-chain mutex for serializing write operations */
|
|
44
|
+
private writeQueue;
|
|
43
45
|
constructor(file: string, options: DataplyOptions);
|
|
44
46
|
/**
|
|
45
47
|
* Verifies if the page file is a valid Dataply file.
|
|
@@ -91,6 +93,23 @@ export declare class DataplyAPI {
|
|
|
91
93
|
* @param tx The transaction to use. If not provided, a new transaction is created.
|
|
92
94
|
* @returns The result of the callback function.
|
|
93
95
|
*/
|
|
96
|
+
/**
|
|
97
|
+
* Acquires the global write lock.
|
|
98
|
+
* Returns a release function that MUST be called to unlock.
|
|
99
|
+
* Used internally by runWithDefaultWrite.
|
|
100
|
+
* @returns A release function
|
|
101
|
+
*/
|
|
102
|
+
protected acquireWriteLock(): Promise<() => void>;
|
|
103
|
+
/**
|
|
104
|
+
* Runs a write callback within a transaction context with global write serialization.
|
|
105
|
+
* If no transaction is provided, a new transaction is created, committed on success, rolled back on error.
|
|
106
|
+
* If a transaction is provided (external), the write lock is acquired on first call and held until commit/rollback.
|
|
107
|
+
* Subclasses MUST use this method for all write operations instead of runWithDefault.
|
|
108
|
+
* @param callback The callback function to run.
|
|
109
|
+
* @param tx Optional external transaction.
|
|
110
|
+
* @returns The result of the callback.
|
|
111
|
+
*/
|
|
112
|
+
protected runWithDefaultWrite<T>(callback: (tx: Transaction) => Promise<T>, tx?: Transaction): Promise<T>;
|
|
94
113
|
protected runWithDefault<T>(callback: (tx: Transaction) => Promise<T>, tx?: Transaction): Promise<T>;
|
|
95
114
|
/**
|
|
96
115
|
* Runs a generator callback function within a transaction context.
|
|
@@ -19,8 +19,6 @@ export declare class Transaction {
|
|
|
19
19
|
private pageLocks;
|
|
20
20
|
/** Dirty Pages modified by the transaction: PageID -> Modified Page Buffer */
|
|
21
21
|
private dirtyPages;
|
|
22
|
-
/** Promise chain for serializing operations on this transaction */
|
|
23
|
-
private serialPromise;
|
|
24
22
|
/** Undo pages: PageID -> Original Page Buffer (Snapshot) */
|
|
25
23
|
private undoPages;
|
|
26
24
|
/** BPTree Transaction instance */
|
|
@@ -31,6 +29,8 @@ export declare class Transaction {
|
|
|
31
29
|
private commitHooks;
|
|
32
30
|
/** Page MVCC Strategy for disk access */
|
|
33
31
|
private readonly pageStrategy;
|
|
32
|
+
/** Release function for global write lock, set by DataplyAPI */
|
|
33
|
+
private _writeLockRelease;
|
|
34
34
|
/**
|
|
35
35
|
* @param id Transaction ID
|
|
36
36
|
* @param context Transaction context
|
|
@@ -62,14 +62,16 @@ export declare class Transaction {
|
|
|
62
62
|
* Registers a commit hook.
|
|
63
63
|
* @param hook Function to execute
|
|
64
64
|
*/
|
|
65
|
+
onCommit(hook: () => Promise<void>): void;
|
|
65
66
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* @param fn The async function to serialize
|
|
69
|
-
* @returns The result of the function
|
|
67
|
+
* Sets the global write lock release function.
|
|
68
|
+
* Called by DataplyAPI.runWithDefaultWrite when acquiring the lock.
|
|
70
69
|
*/
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
__setWriteLockRelease(release: () => void): void;
|
|
71
|
+
/**
|
|
72
|
+
* Returns whether this transaction already has a write lock.
|
|
73
|
+
*/
|
|
74
|
+
__hasWriteLockRelease(): boolean;
|
|
73
75
|
/**
|
|
74
76
|
* Reads a page. Uses dirty buffer if available, otherwise disk.
|
|
75
77
|
* @param pageId Page ID
|