How to delete sqlite file after closing sqlx database pool
I was writing some units tests for my database code that uses sqlx. Long story short, after creating the sqlite database and running some queries, I wasn't able to reliably delete the database files. I kept getting this OS error...
Os {
code: 32,
kind: Uncategorized,
message: "The process cannot access the file
because it is being used by another
process."
}
From what I can tell, there isn't a clean way to avoid this. See the comments below:
https://github.com/launchbadge/sqlx/issues/2294#issuecomment-1386335858 https://github.com/launchbadge/sqlx/issues/2375#issuecomment-1451132623
I believe this is also what the "Note: Drop Behavior" section of the sqlx documention is getting at.
https://docs.rs/sqlx/latest/sqlx/struct.Pool.html#note-drop-behavior
At the end of the day, the only solution I found was to call thread::sleep
after closing the pool, e.g.
pool.close().await;
std::thread::sleep(std::time::Duration::from_millis(2000));
// Do cleanup
See below for all the details.
Before discovering the github comments, I spend some time learning more about the tokio runtime and async rust, thinking I was missing something important (and maybe I still am).
My database tests look generally like this...
#[sqlx::test]
async fn test_insert() {
// The db is created, seeded, and has a
// database pool created.
let db = custom_database(
"sqlite://test.db"
).await;
// query the database
// assert some thing about the results
// ...
}
... followed by some code to delete the database file. I ended up with two variations of the cleanup code. Originally, I had this...
db.close_pool().await;
let file = std::path::Path::new("test.db");
let cleanup = std::fs::remove_file(file);
assert!(cleanup.is_ok(), "db cleanup failed");
And it was failing intermittently. So then I learned more about Tokio and tried this...
let pool = db.pool().clone();
let cleanup_success = tokio::spawn(async move {
// Wait on the close event
pool.close_event().await;
info!(name = "pool closing. Running cleanup");
let file = std::path::Path::new("test.db");
let cleanup = std::fs::remove_file(file);
cleanup.is_ok()
});
db.close_pool().await;
assert!(cleanup_success.await.unwrap(), "db cleanup failed");
Which isn't wrong, but it has the same problem as above. I tried asking Google Gemini...
When can I safely delete the sqlite database file after closing the sqlx database pool?
I'm getting an error indicating that the file is still in use.
The response wasn't very helpful. That's when I looked at the git repo and found the tickets linked above and determined that
thread::sleep
might actually be the only solution.
In the end, I returned to my first implementation with the added sleep call and all is well!
db.close_pool().await;
std::thread::sleep(std::time::Duration::from_millis(2000));
let file = std::path::Path::new("test.db");
let cleanup = std::fs::remove_file(file);
assert!(cleanup.is_ok(), "db cleanup failed");
If there's a more reliable option let me know in the comments!