diff --git a/crates/matrix-sdk-base/src/store/mod.rs b/crates/matrix-sdk-base/src/store/mod.rs index f54919362..5a03c1fa0 100644 --- a/crates/matrix-sdk-base/src/store/mod.rs +++ b/crates/matrix-sdk-base/src/store/mod.rs @@ -94,6 +94,13 @@ pub enum StoreError { /// The store failed to encode or decode some data. #[error("Error encoding or decoding data from the store: {0}")] Codec(String), + + /// The database format has changed in a backwards incompatible way. + #[error( + "The database format changed in an incompatible way, current \ + version: {0}, latest version: {1}" + )] + UnsupportedDatabaseVersion(usize, usize), /// Redacting an event in the store has failed. /// /// This should never happen. diff --git a/crates/matrix-sdk-sled/src/state_store.rs b/crates/matrix-sdk-sled/src/state_store.rs index 71cbedb4b..5d1bfa3cd 100644 --- a/crates/matrix-sdk-sled/src/state_store.rs +++ b/crates/matrix-sdk-sled/src/state_store.rs @@ -111,6 +111,9 @@ impl Into for SledStoreError { } } } +const DATABASE_VERSION: u8 = 1; + +const VERSION_KEY: &str = "state-store-version"; const ACCOUNT_DATA: &str = "account-data"; const CUSTOM: &str = "custom"; @@ -216,7 +219,7 @@ impl SledStore { let room_timeline_metadata = db.open_tree(TIMELINE_METADATA)?; let room_event_id_to_position = db.open_tree(ROOM_EVENT_ID_POSITION)?; - Ok(Self { + let database = Self { path, inner: db, store_cipher, @@ -243,7 +246,10 @@ impl SledStore { room_timeline, room_timeline_metadata, room_event_id_to_position, - }) + }; + + database.upgrade()?; + Ok(database) } pub fn open() -> StoreResult { @@ -298,6 +304,41 @@ impl SledStore { SledStore::open_helper(db, Some(path), None) } + fn upgrade(&self) -> StoreResult<()> { + let db_version = + self.inner.get(VERSION_KEY).map_err(|e| StoreError::Backend(anyhow!(e)))?.map(|v| { + let (version_bytes, _) = v.split_at(std::mem::size_of::()); + u8::from_be_bytes(version_bytes.try_into().unwrap_or_default()) + }); + + let old_version = match db_version { + None => { + // we are fresh, let's write the current version + self.inner + .insert(VERSION_KEY, DATABASE_VERSION.to_be_bytes().as_ref()) + .map_err(|e| StoreError::Backend(anyhow!(e)))?; + self.inner.flush().map_err(|e| StoreError::Backend(anyhow!(e)))?; + return Ok(()); + } + Some(version) if version == DATABASE_VERSION => { + // current, we don't have to do anything + return Ok(()); + } + Some(version) => version, + }; + + tracing::debug!( + old_version, + new_version = DATABASE_VERSION, + "Upgrading the Sled state store" + ); + + // FUTURE UPGRADE CODE GOES HERE + + // can't upgrade from that version to the new one + Err(StoreError::UnsupportedDatabaseVersion(old_version.into(), DATABASE_VERSION.into())) + } + /// Open a `CryptoStore` that uses the same database as this store. /// /// The given passphrase will be used to encrypt private data.