From 316295bb774d03d21caa6cdb916ed5f8c7cd0beb Mon Sep 17 00:00:00 2001 From: Devin R Date: Thu, 23 Apr 2020 21:57:54 -0400 Subject: [PATCH] state_store: add initial_use to let client know its safe to load state --- src/async_client.rs | 4 +++- src/base_client.rs | 5 +++++ src/state/mod.rs | 4 ++++ src/state/state_store.rs | 12 ++++++++---- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/async_client.rs b/src/async_client.rs index 66251e17..280768f6 100644 --- a/src/async_client.rs +++ b/src/async_client.rs @@ -610,7 +610,9 @@ impl AsyncClient { #[instrument] pub async fn sync(&self, mut sync_settings: SyncSettings) -> Result { { - if self.base_client.read().await.is_state_store_synced() { + // if the client hasn't been synced from the state store don't sync again + if !self.base_client.read().await.is_state_store_synced() { + // this will bail out returning false if the store has not been set up if let Ok(synced) = self.sync_with_state_store().await { if synced { // once synced, update the sync token to the last known state from `StateStore`. diff --git a/src/base_client.rs b/src/base_client.rs index 3f150cec..0b9d5f88 100644 --- a/src/base_client.rs +++ b/src/base_client.rs @@ -148,6 +148,11 @@ impl Client { /// Returns `true` when a sync has successfully completed. pub(crate) async fn sync_with_state_store(&mut self) -> Result { if let Some(store) = self.state_store.as_ref() { + // return false and continues with a sync request then saves the state and creates + // and populates the files during the sync + if !store.initial_use().await? { + return Ok(false); + } let ClientState { session, sync_token, diff --git a/src/state/mod.rs b/src/state/mod.rs index f643570e..19b1460b 100644 --- a/src/state/mod.rs +++ b/src/state/mod.rs @@ -60,6 +60,10 @@ impl ClientState { /// Abstraction around the data store to avoid unnecessary request on client initialization. #[async_trait::async_trait] pub trait StateStore: Send + Sync { + /// Signals to the `AsyncClient` if this is the first time a StateStore` has been used. + /// + /// Returns true if `StateStore` has been set up and ready to be loaded from. + async fn initial_use(&self) -> Result; /// Loads the state of `BaseClient` through `StateStore::Store` type. async fn load_client_state(&self) -> Result; /// Load the state of a single `Room` by `RoomId`. diff --git a/src/state/state_store.rs b/src/state/state_store.rs index 964c4e84..99f84797 100644 --- a/src/state/state_store.rs +++ b/src/state/state_store.rs @@ -29,6 +29,12 @@ impl JsonStore { #[async_trait::async_trait] impl StateStore for JsonStore { + async fn initial_use(&self) -> Result { + let mut path = self.path.clone(); + path.push("client.json"); + Ok(fs::read_to_string(path).map_or(false, |s| !s.is_empty())) + } + async fn load_client_state(&self) -> Result { let mut path = self.path.clone(); path.push("client.json"); @@ -242,10 +248,8 @@ mod test { AsyncClient::new_with_config(homeserver.clone(), Some(session.clone()), config) .unwrap(); let sync_settings = SyncSettings::new().timeout(std::time::Duration::from_millis(3000)); - // fake a sync to skip the load with the state store, this will fail as the files won't exist - // but the `AsyncClient::sync` will skip `StateStore::load_*` - assert!(client.sync_with_state_store().await.is_err()); - // gather state to save to the db + + // gather state to save to the db, the first time through loading will be skipped let _ = client.sync(sync_settings.clone()).await.unwrap(); // now syncing the client will update from the state store