config_cache fixes (#3145)

* add simple backup export/import test

this test fails on current master
until the context is recrated.

* avoid config_cache races

adds needed SQL-statements to config_cache locking.

otherwise, another thread may alter the database
eg. between SELECT and the config_cache update -
resulting in the wrong value being written to config_cache.

* also update config_cache on initializing tables

VERSION_CFG is also set later, however,
not doing it here will result in bugs when we change DBVERSION at some point.

as this alters only VERSION_CFG and that is executed sequentially anyway,
race conditions between SQL and config_cache
seems not to be an issue in this case.

* clear config_cache after backup import

import replaces the whole database,
so config_cache needs to be invalidated as well.

we do that before import,
so in case a backup is imported only partly,
the cache does not add additional problems.

* update CHANGELOG
This commit is contained in:
bjoern
2022-03-22 22:46:29 +01:00
committed by GitHub
parent 86da1aa429
commit 8487eefe46
4 changed files with 61 additions and 6 deletions

View File

@@ -19,7 +19,7 @@
- place common headers like `From:` before the large `Autocrypt:` header #3079
- keep track of securejoin joiner status in database to survive restarts #2920
- remove never used `SentboxMove` option #3111
- improve speed by caching config values
- improve speed by caching config values #3131 #3145
- optimize `markseen_msgs` #3141
- automatically accept chats with outgoing messages #3143

View File

@@ -460,6 +460,8 @@ async fn import_backup(
context.get_dbfile().display()
);
context.sql.config_cache.write().await.clear();
let archive = Archive::new(backup_file);
let mut entries = archive.entries()?;
@@ -901,6 +903,54 @@ mod tests {
}
}
#[async_std::test]
async fn test_export_and_import_backup() -> Result<()> {
let backup_dir = tempfile::tempdir().unwrap();
let context1 = TestContext::new_alice().await;
assert!(context1.is_configured().await?);
let context2 = TestContext::new().await;
assert!(!context2.is_configured().await?);
assert!(has_backup(&context2, backup_dir.path().as_ref())
.await
.is_err());
// export from context1
assert!(imex(
&context1,
ImexMode::ExportBackup,
backup_dir.path().as_ref(),
None,
)
.await
.is_ok());
let _event = context1
.evtracker
.get_matching(|evt| matches!(evt, EventType::ImexProgress(1000)))
.await;
// import to context2
let backup = has_backup(&context2, backup_dir.path().as_ref()).await?;
assert!(
imex(&context2, ImexMode::ImportBackup, backup.as_ref(), None)
.await
.is_ok()
);
let _event = context2
.evtracker
.get_matching(|evt| matches!(evt, EventType::ImexProgress(1000)))
.await;
assert!(context2.is_configured().await?);
assert_eq!(
context2.get_config(Config::Addr).await?,
Some("alice@example.org".to_string())
);
Ok(())
}
#[test]
fn test_normalize_setup_code() {
let norm = normalize_setup_code("123422343234423452346234723482349234");

View File

@@ -48,7 +48,7 @@ pub struct Sql {
/// open without a passphrase.
is_encrypted: RwLock<Option<bool>>,
config_cache: RwLock<HashMap<String, Option<String>>>,
pub(crate) config_cache: RwLock<HashMap<String, Option<String>>>,
}
impl Sql {
@@ -501,6 +501,7 @@ impl Sql {
pub async fn set_raw_config(&self, key: impl AsRef<str>, value: Option<&str>) -> Result<()> {
let key = key.as_ref();
let mut lock = self.config_cache.write().await;
if let Some(value) = value {
let exists = self
.exists(
@@ -526,8 +527,6 @@ impl Sql {
self.execute("DELETE FROM config WHERE keyname=?;", paramsv![key])
.await?;
}
let mut lock = self.config_cache.write().await;
lock.insert(key.to_string(), value.map(|s| s.to_string()));
drop(lock);
@@ -544,6 +543,7 @@ impl Sql {
return Ok(c);
}
let mut lock = self.config_cache.write().await;
let value = self
.query_get_value(
"SELECT value FROM config WHERE keyname=?;",
@@ -551,8 +551,6 @@ impl Sql {
)
.await
.context(format!("failed to fetch raw config: {}", key.as_ref()))?;
let mut lock = self.config_cache.write().await;
lock.insert(key.as_ref().to_string(), value.clone());
drop(lock);

View File

@@ -36,6 +36,13 @@ pub async fn run(context: &Context, sql: &Sql) -> Result<(bool, bool, bool, bool
Ok(())
})
.await?;
let mut lock = context.sql.config_cache.write().await;
lock.insert(
VERSION_CFG.to_string(),
Some(format!("{}", dbversion_before_update)),
);
drop(lock);
} else {
exists_before_update = true;
dbversion_before_update = sql