@harperfast/rocksdb-js 0.1.12 → 0.1.13
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 +47 -8
- package/dist/index.cjs +101 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -2
- package/dist/index.d.mts +15 -2
- package/dist/index.mjs +101 -45
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -9
package/README.md
CHANGED
|
@@ -396,7 +396,7 @@ Synchronous version of `remove()`.
|
|
|
396
396
|
|
|
397
397
|
## Transactions
|
|
398
398
|
|
|
399
|
-
### `db.transaction(
|
|
399
|
+
### `db.transaction<T>(callback: TransactionCallback<T>, options?: DBTransactionOptions): Promise<T>`
|
|
400
400
|
|
|
401
401
|
Executes all database operations within the specified callback within a single transaction. If the
|
|
402
402
|
callback completes without error, the database operations are automatically committed. However, if
|
|
@@ -427,7 +427,7 @@ const isBar = await db.transaction(async (txn: Transaction) => {
|
|
|
427
427
|
console.log(isBar ? 'Foo is bar' : 'Foo is not bar');
|
|
428
428
|
```
|
|
429
429
|
|
|
430
|
-
### `db.transactionSync(
|
|
430
|
+
### `db.transactionSync<T>(callback: TransactionCallback<T>, options?: TransactionOptions): T`
|
|
431
431
|
|
|
432
432
|
Executes a transaction callback and commits synchronously. Once the transaction callback returns,
|
|
433
433
|
the commit is executed synchronously and blocks the current thread until finished.
|
|
@@ -441,17 +441,56 @@ db.transactionSync((txn: Transaction) => {
|
|
|
441
441
|
});
|
|
442
442
|
```
|
|
443
443
|
|
|
444
|
+
### `TransactionCallback<T>`
|
|
445
|
+
|
|
446
|
+
`(txn: Transaction, attempt: number) => T | PromiseLike<T>`
|
|
447
|
+
|
|
448
|
+
A sync or async function to encapsulate all of the transaction operations. Once the function is
|
|
449
|
+
executed, the transaction is automatically committed. If the function returns a value, it will be
|
|
450
|
+
returned from the transaction call.
|
|
451
|
+
|
|
452
|
+
The `txn` parameter is a `Transaction`. See the [Transaction](#class-transaction) section for more
|
|
453
|
+
details.
|
|
454
|
+
|
|
455
|
+
The `attempt` parameter is the number of times the transaction has been retried.
|
|
456
|
+
|
|
457
|
+
### `TransactionOptions`
|
|
458
|
+
|
|
459
|
+
- `disableSnapshot?: boolean` Whether to disable snapshots. Defaults to `false`.
|
|
460
|
+
- `maxRetries?: number` The maximum number of times to retry the transaction. Defaults to `3`.
|
|
461
|
+
- `retryOnBusy?: boolean` Whether to retry the transaction if the commit fails with `IsBusy`.
|
|
462
|
+
Defaults to `true` when the transaction is bound to a transaction log, otherwise `false`.
|
|
463
|
+
|
|
464
|
+
### Transaction Retry Logic
|
|
465
|
+
|
|
466
|
+
The retry mechanism will only be active when the `retryOnBusy` option is `true` or when
|
|
467
|
+
`retryOnBusy` is `undefined` and the transaction is bound to a transaction log. The attempts starts
|
|
468
|
+
at `1` and ends at `maxRetries`.
|
|
469
|
+
|
|
470
|
+
When using a transaction log and the commit fails with `ERR_BUSY`, the transaction log will be in
|
|
471
|
+
a bad state and the transaction will need to be retried. If the max retries is reached or the
|
|
472
|
+
transaction is not retried, a `ERR_TRANSACTION_ABANDONED` error will be thrown.
|
|
473
|
+
|
|
474
|
+
Users should use the `attempt` transaction callback parameter to ensure duplicate transaction log
|
|
475
|
+
entries are not added.
|
|
476
|
+
|
|
444
477
|
### Class: `Transaction`
|
|
445
478
|
|
|
446
479
|
The transaction callback is passed in a `Transaction` instance which contains all of the same data
|
|
447
480
|
operations methods as the `RocksDatabase` instance plus:
|
|
448
481
|
|
|
449
|
-
- `txn.abort()`
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
- `txn.
|
|
453
|
-
- `txn.
|
|
454
|
-
- `txn.
|
|
482
|
+
- `txn.abort()` Rolls back and closes the transaction. This method is automatically called after the
|
|
483
|
+
transaction callback returns, so you shouldn't need to call it, but it's ok to do so. Once called,
|
|
484
|
+
no further transaction operations are permitted. Calling this method multiple times has no effect.
|
|
485
|
+
- `txn.commit(): Promise<void>` Asynchronously commits the transaction and closes the transaction.
|
|
486
|
+
- `txn.commitSync()` Synchronously commits and closes the transaction.
|
|
487
|
+
- `txn.getTimestamp(): number` Retrieves the transaction start timestamp in seconds as a decimal. It
|
|
488
|
+
defaults to the time at which the transaction was created.
|
|
489
|
+
- `txn.id: number` The readonly transaction ID. Transaction IDs are unique to the RocksDB database
|
|
490
|
+
path, regardless the database name/column family.
|
|
491
|
+
- `txn.setTimestamp(ts?: number): void` Overrides the transaction start timestamp. If called without
|
|
492
|
+
a timestamp, it will set the timestamp to the current time. The value must be in seconds with
|
|
493
|
+
higher precision in the decimal.
|
|
455
494
|
|
|
456
495
|
#### `txn.abort(): void`
|
|
457
496
|
|
package/dist/index.cjs
CHANGED
|
@@ -1011,6 +1011,29 @@ function getKeyParam(keyBuffer) {
|
|
|
1011
1011
|
|
|
1012
1012
|
//#endregion
|
|
1013
1013
|
//#region src/transaction.ts
|
|
1014
|
+
var TransactionAlreadyAbortedError = class extends Error {
|
|
1015
|
+
code = "ERR_ALREADY_ABORTED";
|
|
1016
|
+
};
|
|
1017
|
+
var TransactionIsBusyError = class extends Error {
|
|
1018
|
+
code = "ERR_BUSY";
|
|
1019
|
+
hasLog;
|
|
1020
|
+
txn;
|
|
1021
|
+
constructor(error, txn) {
|
|
1022
|
+
super(error.message);
|
|
1023
|
+
this.hasLog = error.hasLog ?? false;
|
|
1024
|
+
this.txn = txn;
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
var TransactionAbandonedError = class extends Error {
|
|
1028
|
+
code = "ERR_TRANSACTION_ABANDONED";
|
|
1029
|
+
hasLog;
|
|
1030
|
+
txn;
|
|
1031
|
+
constructor(error, txn) {
|
|
1032
|
+
super(error.message);
|
|
1033
|
+
this.hasLog = error.hasLog ?? false;
|
|
1034
|
+
this.txn = txn;
|
|
1035
|
+
}
|
|
1036
|
+
};
|
|
1014
1037
|
/**
|
|
1015
1038
|
* Provides transaction level operations to a transaction callback.
|
|
1016
1039
|
*/
|
|
@@ -1031,7 +1054,12 @@ var Transaction = class extends DBI {
|
|
|
1031
1054
|
* Abort the transaction.
|
|
1032
1055
|
*/
|
|
1033
1056
|
abort() {
|
|
1034
|
-
|
|
1057
|
+
try {
|
|
1058
|
+
this.#txn.abort();
|
|
1059
|
+
} catch (err) {
|
|
1060
|
+
if (err instanceof Error && "code" in err && err.code === "ERR_TRANSACTION_ABANDONED") throw new TransactionAbandonedError(err, this);
|
|
1061
|
+
throw err;
|
|
1062
|
+
}
|
|
1035
1063
|
}
|
|
1036
1064
|
/**
|
|
1037
1065
|
* Commit the transaction.
|
|
@@ -1042,6 +1070,8 @@ var Transaction = class extends DBI {
|
|
|
1042
1070
|
this.notify("beforecommit");
|
|
1043
1071
|
this.#txn.commit(resolve, reject);
|
|
1044
1072
|
});
|
|
1073
|
+
} catch (err) {
|
|
1074
|
+
throw this.#handleCommitError(err);
|
|
1045
1075
|
} finally {
|
|
1046
1076
|
this.notify("aftercommit", {
|
|
1047
1077
|
next: null,
|
|
@@ -1057,6 +1087,8 @@ var Transaction = class extends DBI {
|
|
|
1057
1087
|
try {
|
|
1058
1088
|
this.notify("beforecommit");
|
|
1059
1089
|
this.#txn.commitSync();
|
|
1090
|
+
} catch (err) {
|
|
1091
|
+
throw this.#handleCommitError(err);
|
|
1060
1092
|
} finally {
|
|
1061
1093
|
this.notify("aftercommit", {
|
|
1062
1094
|
next: null,
|
|
@@ -1066,6 +1098,19 @@ var Transaction = class extends DBI {
|
|
|
1066
1098
|
}
|
|
1067
1099
|
}
|
|
1068
1100
|
/**
|
|
1101
|
+
* Detect if error is an already aborted or busy error and return the appropriate error class.
|
|
1102
|
+
*
|
|
1103
|
+
* @param err - The error to check.
|
|
1104
|
+
* @returns The specialized error.
|
|
1105
|
+
*/
|
|
1106
|
+
#handleCommitError(err) {
|
|
1107
|
+
if (err instanceof Error && "code" in err) {
|
|
1108
|
+
if (err.code === "ERR_ALREADY_ABORTED") return new TransactionAlreadyAbortedError(err.message);
|
|
1109
|
+
if (err.code === "ERR_BUSY") return new TransactionIsBusyError(err, this);
|
|
1110
|
+
}
|
|
1111
|
+
return err;
|
|
1112
|
+
}
|
|
1113
|
+
/**
|
|
1069
1114
|
* Returns the transaction start timestamp in seconds. Defaults to the time at which
|
|
1070
1115
|
* the transaction was created.
|
|
1071
1116
|
*
|
|
@@ -1472,30 +1517,24 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1472
1517
|
*/
|
|
1473
1518
|
async transaction(callback, options) {
|
|
1474
1519
|
if (typeof callback !== "function") throw new TypeError("Callback must be a function");
|
|
1520
|
+
const maxRetries = options?.maxRetries ?? 3;
|
|
1475
1521
|
const txn = new Transaction(this.store, options);
|
|
1476
1522
|
let result;
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
result = await callback(txn);
|
|
1480
|
-
} catch (err) {
|
|
1523
|
+
this.notify("begin-transaction");
|
|
1524
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1481
1525
|
try {
|
|
1482
|
-
txn
|
|
1483
|
-
} catch (
|
|
1484
|
-
|
|
1526
|
+
result = await callback(txn, attempt);
|
|
1527
|
+
} catch (callbackErr) {
|
|
1528
|
+
return this.#abortTransaction(txn, callbackErr);
|
|
1485
1529
|
}
|
|
1486
|
-
throw err;
|
|
1487
|
-
}
|
|
1488
|
-
try {
|
|
1489
|
-
await txn.commit();
|
|
1490
|
-
return result;
|
|
1491
|
-
} catch (err) {
|
|
1492
|
-
if (err instanceof Error && "code" in err && err.code === "ERR_ALREADY_ABORTED") return;
|
|
1493
1530
|
try {
|
|
1494
|
-
txn.
|
|
1495
|
-
|
|
1496
|
-
|
|
1531
|
+
await txn.commit();
|
|
1532
|
+
return result;
|
|
1533
|
+
} catch (commitErr) {
|
|
1534
|
+
if (commitErr instanceof TransactionAlreadyAbortedError) return;
|
|
1535
|
+
if (commitErr instanceof TransactionIsBusyError && (options?.retryOnBusy ?? commitErr.hasLog) && attempt <= maxRetries) continue;
|
|
1536
|
+
this.#abandonTransaction(txn, commitErr);
|
|
1497
1537
|
}
|
|
1498
|
-
throw err;
|
|
1499
1538
|
}
|
|
1500
1539
|
}
|
|
1501
1540
|
/**
|
|
@@ -1517,38 +1556,55 @@ var RocksDatabase = class RocksDatabase extends DBI {
|
|
|
1517
1556
|
*/
|
|
1518
1557
|
transactionSync(callback, options) {
|
|
1519
1558
|
if (typeof callback !== "function") throw new TypeError("Callback must be a function");
|
|
1520
|
-
const
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1559
|
+
const maxRetries = options?.maxRetries ?? 3;
|
|
1560
|
+
const isRetryable = (err, attempt) => {
|
|
1561
|
+
return err instanceof TransactionIsBusyError && (options?.retryOnBusy ?? err.hasLog) && attempt <= maxRetries;
|
|
1562
|
+
};
|
|
1563
|
+
const runAttempt = (attempt) => {
|
|
1564
|
+
const txn = new Transaction(this.store, options);
|
|
1565
|
+
let result;
|
|
1526
1566
|
try {
|
|
1527
|
-
txn
|
|
1528
|
-
} catch (
|
|
1529
|
-
|
|
1567
|
+
result = callback(txn, attempt);
|
|
1568
|
+
} catch (callbackErr) {
|
|
1569
|
+
return this.#abortTransaction(txn, callbackErr);
|
|
1530
1570
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1571
|
+
if (typeof result?.then === "function") return result.then((value) => {
|
|
1572
|
+
try {
|
|
1573
|
+
txn.commitSync();
|
|
1574
|
+
return value;
|
|
1575
|
+
} catch (commitErr) {
|
|
1576
|
+
if (commitErr instanceof TransactionAlreadyAbortedError) return;
|
|
1577
|
+
if (isRetryable(commitErr, attempt)) return runAttempt(attempt + 1);
|
|
1578
|
+
this.#abandonTransaction(txn, commitErr);
|
|
1579
|
+
}
|
|
1580
|
+
});
|
|
1534
1581
|
try {
|
|
1535
1582
|
txn.commitSync();
|
|
1536
|
-
return
|
|
1537
|
-
} catch (
|
|
1538
|
-
if (
|
|
1539
|
-
|
|
1583
|
+
return result;
|
|
1584
|
+
} catch (commitErr) {
|
|
1585
|
+
if (commitErr instanceof TransactionAlreadyAbortedError) return;
|
|
1586
|
+
if (isRetryable(commitErr, attempt)) return runAttempt(attempt + 1);
|
|
1587
|
+
this.#abandonTransaction(txn, commitErr);
|
|
1540
1588
|
}
|
|
1541
|
-
}
|
|
1589
|
+
};
|
|
1590
|
+
this.notify("begin-transaction");
|
|
1591
|
+
return runAttempt(1);
|
|
1592
|
+
}
|
|
1593
|
+
#abortTransaction(txn, callbackErr) {
|
|
1542
1594
|
try {
|
|
1543
|
-
txn.
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1595
|
+
txn.abort();
|
|
1596
|
+
} catch (abortErr) {
|
|
1597
|
+
if (abortErr instanceof TransactionAlreadyAbortedError) return;
|
|
1598
|
+
}
|
|
1599
|
+
throw callbackErr;
|
|
1600
|
+
}
|
|
1601
|
+
#abandonTransaction(txn, commitErr) {
|
|
1602
|
+
try {
|
|
1603
|
+
txn.abort();
|
|
1604
|
+
} catch (abortErr) {
|
|
1605
|
+
if (abortErr instanceof TransactionAbandonedError) throw abortErr;
|
|
1551
1606
|
}
|
|
1607
|
+
throw commitErr;
|
|
1552
1608
|
}
|
|
1553
1609
|
/**
|
|
1554
1610
|
* Attempts to acquire a lock for a given key. If the lock is available,
|
|
@@ -1852,7 +1908,7 @@ function loadLastPosition(transactionLog, readUncommitted) {
|
|
|
1852
1908
|
//#region src/index.ts
|
|
1853
1909
|
const versions = {
|
|
1854
1910
|
rocksdb: version,
|
|
1855
|
-
"rocksdb-js": "0.1.
|
|
1911
|
+
"rocksdb-js": "0.1.13"
|
|
1856
1912
|
};
|
|
1857
1913
|
|
|
1858
1914
|
//#endregion
|