Skip to content

Commit fef7890

Browse files
authored
Merge pull request #43 from ryanfowler/fix-pool-journal-mode-race
Fix DatabaseBusy error on pool creation with journal_mode
2 parents 10123e5 + 9017185 commit fef7890

File tree

2 files changed

+75
-17
lines changed

2 files changed

+75
-17
lines changed

src/pool.rs

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,38 @@ impl PoolBuilder {
104104
/// ```
105105
pub async fn open(self) -> Result<Pool, Error> {
106106
let num_conns = self.get_num_conns();
107-
let opens = (0..num_conns).map(|_| {
107+
108+
// Open the first connection with full config (including journal_mode).
109+
// This must complete before opening remaining connections to avoid
110+
// concurrent PRAGMA writes on a new database file.
111+
let first = ClientBuilder {
112+
path: self.path.clone(),
113+
flags: self.flags,
114+
journal_mode: self.journal_mode,
115+
vfs: self.vfs.clone(),
116+
}
117+
.open()
118+
.await?;
119+
120+
// Open remaining connections without journal_mode since it's a
121+
// database-level setting already applied by the first connection.
122+
let opens = (1..num_conns).map(|_| {
108123
ClientBuilder {
109124
path: self.path.clone(),
110125
flags: self.flags,
111-
journal_mode: self.journal_mode,
126+
journal_mode: None,
112127
vfs: self.vfs.clone(),
113128
}
114129
.open()
115130
});
116-
let clients = join_all(opens)
117-
.await
118-
.into_iter()
119-
.collect::<Result<Vec<Client>, Error>>()?;
131+
let mut clients = vec![first];
132+
clients.extend(
133+
join_all(opens)
134+
.await
135+
.into_iter()
136+
.collect::<Result<Vec<Client>, Error>>()?,
137+
);
138+
120139
Ok(Pool {
121140
state: Arc::new(State {
122141
clients,
@@ -139,17 +158,33 @@ impl PoolBuilder {
139158
/// ```
140159
pub fn open_blocking(self) -> Result<Pool, Error> {
141160
let num_conns = self.get_num_conns();
142-
let clients = (0..num_conns)
143-
.map(|_| {
144-
ClientBuilder {
145-
path: self.path.clone(),
146-
flags: self.flags,
147-
journal_mode: self.journal_mode,
148-
vfs: self.vfs.clone(),
149-
}
150-
.open_blocking()
151-
})
152-
.collect::<Result<Vec<Client>, Error>>()?;
161+
162+
// Open the first connection with full config (including journal_mode).
163+
let first = ClientBuilder {
164+
path: self.path.clone(),
165+
flags: self.flags,
166+
journal_mode: self.journal_mode,
167+
vfs: self.vfs.clone(),
168+
}
169+
.open_blocking()?;
170+
171+
// Open remaining connections without journal_mode since it's a
172+
// database-level setting already applied by the first connection.
173+
let mut clients = vec![first];
174+
clients.extend(
175+
(1..num_conns)
176+
.map(|_| {
177+
ClientBuilder {
178+
path: self.path.clone(),
179+
flags: self.flags,
180+
journal_mode: None,
181+
vfs: self.vfs.clone(),
182+
}
183+
.open_blocking()
184+
})
185+
.collect::<Result<Vec<Client>, Error>>()?,
186+
);
187+
153188
Ok(Pool {
154189
state: Arc::new(State {
155190
clients,

tests/tests.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ macro_rules! async_test {
8383
async_test!(test_journal_mode);
8484
async_test!(test_concurrency);
8585
async_test!(test_pool);
86+
async_test!(test_pool_journal_mode);
8687
async_test!(test_pool_conn_for_each);
8788
async_test!(test_pool_close_concurrent);
8889
async_test!(test_pool_num_conns_zero_clamps);
@@ -170,6 +171,28 @@ async fn test_pool() {
170171
.expect("collecting query results");
171172
}
172173

174+
async fn test_pool_journal_mode() {
175+
let tmp_dir = tempfile::tempdir().unwrap();
176+
let pool = PoolBuilder::new()
177+
.journal_mode(JournalMode::Wal)
178+
.path(tmp_dir.path().join("sqlite.db"))
179+
.num_conns(4)
180+
.open()
181+
.await
182+
.expect("pool unable to be opened");
183+
184+
// Verify all connections see WAL journal mode.
185+
let results = pool
186+
.conn_for_each(|conn| conn.query_row("PRAGMA journal_mode", (), |row| row.get(0)))
187+
.await;
188+
for result in results {
189+
let mode: String = result.unwrap();
190+
assert_eq!(mode, "wal");
191+
}
192+
193+
pool.close().await.expect("closing pool");
194+
}
195+
173196
async fn test_pool_conn_for_each() {
174197
// make dummy db
175198
let tmp_dir = tempfile::tempdir().unwrap();

0 commit comments

Comments
 (0)