Skip to content

feat: refactor database locking model#321

Merged
frisitano merged 8 commits intomainfrom
feat/database-refactor
Sep 17, 2025
Merged

feat: refactor database locking model#321
frisitano merged 8 commits intomainfrom
feat/database-refactor

Conversation

@frisitano
Copy link
Collaborator

@frisitano frisitano commented Sep 16, 2025

Overview

This PR refactors the Database implementation to account for the fundamental properties of sqlite. Specifically, sqlite supports a single writer (across the database) and multiple concurrent writers (in WAL mode).

Implementation

We introduce two transaction types, TX and TXMut. Both the TX and TXMut types are created via the Database instance. To constrain the system to only allowing a single TXMut across all threads, we use an Arc<Mutex<()>> , a lock must be acquired on this type before a TXMut can be instantiated. The TX type can only perform read operations and the TXMut type can perform both read and write operations.

Fix

There is an edge case in the prepare_on_startup database operation in which we would use the next L1 block number + 1 of the previous batch to start from. This was problematic as we can have multiple batches submitted in the same L1 block, leading to skipped batches on startup. We have rectified this by starting from the L1 block number of the previous batch (removing the + 1).

greged93
greged93 previously approved these changes Sep 17, 2025
Copy link
Contributor

@greged93 greged93 left a comment

Choose a reason for hiding this comment

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

lgtm!

@@ -90,14 +128,21 @@ impl DatabaseConnectionProvider for Database {

Copy link
Contributor

Choose a reason for hiding this comment

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

impl DatabaseConnectionProvider for Database can we remove this? I think it's only used in test, feel free to keep it if it makes things easier.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'll put this behind a feature flag #[cfg(feature = "test-utils")] but leave it implemented here in this scope so we can access &self.connection from the implementation without making it public.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It looks like it's also used in args.rs, lets leave it as is for now.

@frisitano frisitano merged commit b5b8941 into main Sep 17, 2025
12 of 13 checks passed
@frisitano frisitano deleted the feat/database-refactor branch September 17, 2025 13:31

// derive the attributes and attach the corresponding batch info.
let attrs = derive(batch, provider, database, l1_v2_message_queue_start_index)
let attrs = derive(batch, provider, tx, l1_v2_message_queue_start_index)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering whether it is necessary to keep the read tx open for the entire duration? Do we need to make sure that the data we read here is "atomic"?

let tx = database.tx().await?;
let (latest_safe_block, _) =
database.get_latest_safe_l2_info().await?.expect("safe block must exist");
tx.get_latest_safe_l2_info().await?.expect("safe block must exist");
Copy link
Contributor

Choose a reason for hiding this comment

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

How long exactly is the read lock retained here?

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.

3 participants