Skip to content

Commit

Permalink
sql: enable auto_vacuum on all connections
Browse files Browse the repository at this point in the history
In execute_migration transaction first update the version, and only
then execute the rest of the migration. This ensures that transaction
is immediately recognized as write transaction and there is no need
to promote it from read transaction to write transaction later, e.g.
in the case of "DROP TABLE IF EXISTS" that is a read only operation if
the table does not exist. Promoting a read transaction to write
transaction may result in an error if database is locked.

When removing an account, try 5 times with 1 second sleep in between
in case removal of database files fails. This happens on Windows
platform sometimes due to a known bug in r2d2 [1].

[1] sfackler/r2d2#99
  • Loading branch information
link2xt committed Apr 17, 2022
1 parent 14ab3c8 commit aba99b7
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- Further improve finding the correct server after logging in #3208
- `get_connectivity_html()` returns HTML as non-scalable #3213
- add update-serial to `DC_EVENT_WEBXDC_STATUS_UPDATE` #3215
- enable `auto_vacuum` on all SQL connections #2955


## 1.77.0
Expand Down
25 changes: 22 additions & 3 deletions src/accounts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,27 @@ impl Accounts {
drop(ctx);

if let Some(cfg) = self.config.get_account(id).await {
fs::remove_dir_all(async_std::path::PathBuf::from(&cfg.dir))
.await
.context("failed to remove account data")?;
let mut counter = 0;
loop {
counter += 1;

if let Err(err) = fs::remove_dir_all(async_std::path::PathBuf::from(&cfg.dir))
.await
.context("failed to remove account data")
{
// Spend up to 1 minute trying to remove the files.
// Files may remain locked up to 30 seconds due to r2d2 bug:
// https://github.com/sfackler/r2d2/issues/99
if counter > 60 {
return Err(err);
}

// Wait 1 second and try again.
async_std::task::sleep(std::time::Duration::from_millis(1000)).await;
} else {
break;
}
}
}
self.config.remove_account(id).await?;

Expand Down Expand Up @@ -186,6 +204,7 @@ impl Accounts {
let new_blobdir = Context::derive_blobdir(&new_dbfile);
let new_walfile = Context::derive_walfile(&new_dbfile);

async_std::task::sleep(std::time::Duration::from_millis(5000)).await;
let res = {
fs::create_dir_all(&account_config.dir)
.await
Expand Down
28 changes: 10 additions & 18 deletions src/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,16 @@ impl Sql {
Duration::from_secs(10).as_millis()
))?;
c.pragma_update(None, "key", passphrase.clone())?;
c.pragma_update(None, "auto_vacuum", &"INCREMENTAL".to_string())?;
// Try to enable auto_vacuum. This will only be
// applied if the database is new or after successful
// VACUUM, which usually happens before backup export.
// When auto_vacuum is INCREMENTAL, it is possible to
// use PRAGMA incremental_vacuum to return unused
// database pages to the filesystem.
c.pragma_update(None, "journal_mode", &"WAL".to_string())?;
// Default synchronous=FULL is much slower. NORMAL is sufficient for WAL mode.
c.pragma_update(None, "synchronous", &"NORMAL".to_string())?;
Ok(())
});

Expand All @@ -200,24 +210,6 @@ impl Sql {
async fn try_open(&self, context: &Context, dbfile: &Path, passphrase: String) -> Result<()> {
*self.pool.write().await = Some(Self::new_pool(dbfile, passphrase.to_string())?);

{
let conn = self.get_conn().await?;

// Try to enable auto_vacuum. This will only be
// applied if the database is new or after successful
// VACUUM, which usually happens before backup export.
// When auto_vacuum is INCREMENTAL, it is possible to
// use PRAGMA incremental_vacuum to return unused
// database pages to the filesystem.
conn.pragma_update(None, "auto_vacuum", &"INCREMENTAL".to_string())?;

// journal_mode is persisted, it is sufficient to change it only for one handle.
conn.pragma_update(None, "journal_mode", &"WAL".to_string())?;

// Default synchronous=FULL is much slower. NORMAL is sufficient for WAL mode.
conn.pragma_update(None, "synchronous", &"NORMAL".to_string())?;
}

self.run_migrations(context).await?;

Ok(())
Expand Down
4 changes: 2 additions & 2 deletions src/sql/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,14 +630,14 @@ impl Sql {

async fn execute_migration(&self, query: &'static str, version: i32) -> Result<()> {
self.transaction(move |transaction| {
transaction.execute_batch(query)?;

// set raw config inside the transaction
transaction.execute(
"UPDATE config SET value=? WHERE keyname=?;",
paramsv![format!("{}", version), VERSION_CFG],
)?;

transaction.execute_batch(query)?;

Ok(())
})
.await
Expand Down

0 comments on commit aba99b7

Please sign in to comment.