Skip to content

db.transaction() retry on ERR_BUSY#470

Merged
cb1kenobi merged 10 commits intomainfrom
db-transaction-retry
Mar 19, 2026
Merged

db.transaction() retry on ERR_BUSY#470
cb1kenobi merged 10 commits intomainfrom
db-transaction-retry

Conversation

@cb1kenobi
Copy link
Copy Markdown
Contributor

When using db.transaction() or db.transactionSync() and a transaction log, if the transaction fails due to a ERR_BUSY error, the log and the data in RocksDB will be inconsistent. The transaction should be retried.

Currently, db.transaction() will automatically abort the transaction if the commit fails leaving no opportunity for the transaction to be retried.

This PR adds a retry mechanism that will re-run the transaction. If the commit fails, it will retry up to maxRetries (default 3). If it still can't commit, or the retryOnBusy option is false, then it throws the ERR_TRANSACTION_ABANDONED error.

The transaction callback function now accepts a 2nd argument: attempt. This value starts at 1 and goes to maxRetries. Users will need to use attempts === 1 to ensure addEntry() is only called once.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 17, 2026

📊 Benchmark Results

get-sync.bench.ts

getSync() > random keys - small key size (100 records)

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 lmdb 1 23.32K ops/sec 42.88 41.40 632.549 0.110 116,608
🥈 rocksdb 2 11.93K ops/sec 83.86 78.86 26,773.715 1.05 59,626

getSync() > sequential keys - small key size (100 records)

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 lmdb 1 27.24K ops/sec 36.72 35.59 412.422 0.106 136,179
🥈 rocksdb 2 12.95K ops/sec 77.21 75.66 824.312 0.052 64,756

ranges.bench.ts

getRange() > small range (100 records, 50 range)

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 lmdb 1 25.89K ops/sec 38.62 36.45 783.398 0.163 129,461
🥈 rocksdb 2 3.44K ops/sec 290.977 258.7 1,864.507 0.512 17,184

realistic-load.bench.ts

Realistic write load with workers > write variable records with transaction log

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 rocksdb 1 186.14 ops/sec 5,372.361 68.22 136,973.435 34.10 373
🥈 lmdb 2 26.33 ops/sec 37,978.499 303.305 1,190,490.235 136.381 64.00

transaction-log.bench.ts

Transaction log > read 100 iterators while write log with 100 byte records

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 rocksdb 1 35.31K ops/sec 28.32 13.21 14,390.407 0.602 176,544
🥈 lmdb 2 438.65 ops/sec 2,279.735 130.67 8,507.805 1.14 2,194

Transaction log > read one entry from random position from log with 1000 100 byte records

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 rocksdb 1 732.83K ops/sec 1.36 1.19 3,063.433 0.155 3,664,170
🥈 lmdb 2 458.92K ops/sec 2.18 1.18 3,339.803 0.326 2,294,580

worker-put-sync.bench.ts

putSync() > random keys - small key size (100 records, 10 workers)

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 rocksdb 1 857.24 ops/sec 1,166.531 1,017.281 2,412.526 0.286 1,715
🥈 lmdb 2 1.01 ops/sec 988,406.739 862,432.136 1,057,570.349 3.82 10.00

worker-transaction-log.bench.ts

Transaction log with workers > write log with 100 byte records

Implementation Rank Operations/sec Mean (ms) Min (ms) Max (ms) RME (%) Samples
🥇 rocksdb 1 18.63K ops/sec 53.68 30.38 505.35 0.490 37,261
🥈 lmdb 2 833.34 ops/sec 1,199.987 216.697 9,995.603 4.91 1,667

Results from commit 6f046e7

@cb1kenobi cb1kenobi requested a review from a team March 17, 2026 23:08
Copy link
Copy Markdown
Member

@kriszyp kriszyp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, I think this is a solid way to handle this.
(I think I am still a little skeptical that throwing an error is the best way to communicate that something should be retried, but its fine, this works).
Good job getting this together.

@cb1kenobi cb1kenobi merged commit 233d80a into main Mar 19, 2026
20 checks passed
@cb1kenobi cb1kenobi deleted the db-transaction-retry branch March 19, 2026 04:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants