fix: serialize layered DB write transaxtions - #138
Conversation
Security Scan - CodeSeverity: HIGH, CRITICAL No vulnerabilities found View scan results |
|
Claude finished @kstoykov's task in 4m 7s —— View job PR Review: fix: serialize layered DB write transactions
Overall AssessmentThis is a well-reasoned, correctness-critical refactor. It fixes two latent bugs (a deadlock and a transactional ordering hazard) while simplifying the transaction model from a fragile refcount to a strict mutex-based serialization. The tests are comprehensive and the comments accurately describe the invariants. Core Design AnalysisSerialization mechanism ( The key insight: acquiring the mem write lock before enqueuing Deadlock fix in The removal of Eviction guard ( Correct. Issues Found1. Fragile infallibility assumption in // Enqueue the commit before releasing the mem write lock...
// This is safe because `MemDbTxMut::commit` is an infallible no-op that only drops the
// `parking_lot` write guard; it performs no I/O. If that ever becomes fallible, do not
// enqueue `CommitTxn` before the mem commit can fail.
self.tx.send(DBMessage::CommitTxn).map_err(|_| eyre::eyre!("DB thread gone, FATAL!"))?;
self.mem_db.commit()The comment documents this correctly, but there is no compiler-level enforcement. If 2. let mem_db = self.mem_db.write_txn()?;
self.tx.send(DBMessage::StartTxn).map_err(|_| eyre::eyre!("DB thread gone, FATAL!"))?;If 3. second_rx.recv_timeout(std::time::Duration::from_millis(500)),
Err(std::sync::mpsc::RecvTimeoutError::Timeout)500ms is a negative assertion (proving a thread is blocked). This is reasonable on typical hardware but could be flaky on memory-constrained CI runners where thread scheduling can be very slow. Consider using a condvar or a smaller sentinel to confirm the second thread has started trying to acquire the lock before the timeout check, making the test less dependent on scheduling latency. This is a low-risk concern given the current 500ms margin. 4. fn remove_txn(&self, txn: &mut <DB as Database>::TXMut<'_>) -> eyre::Result<()> {
txn.remove::<T>(&self.key)
}Without the old Minor / Nits
TestsThe four new regression tests cover exactly the right scenarios:
SummaryThe serialization correctness, the deadlock fix in |
Summary
LayeredDatabasewrite transactions through themem_dbwrite lock instead of refcounting overlapping transactions in the background writer.StartTxnwhile a txn is already open andCommitTxnwith no open txn now poison the writer instead of leaving ambiguous transaction state.Closes # — no linked issue.
Surface areas touched
eth_*,rayls_*, faucet)rayls-contracts/)etc/, scripts, Docker, compose).github/workflows/,Makefile)doc/, in-crate READMEs, root docs)Breaking / compatibility
None.
Test plan
cargo fmt --checkcargo clippy -p rayls-infrastructure-storage --features redb --all-targetscargo test -p rayls-infrastructure-storage --features redbcargo check --workspace --all-targetsdropped_write_txn_poisons_the_next_write_txnwrite_txn_serializes_writers_until_committxn_remove_survives_sync_persist_before_committxn_remove_and_insert_apply_in_channel_order