From cf02e694f20a1ab9db08dd199c3faab54c1c8d49 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 9 Dec 2024 14:54:13 +0100 Subject: [PATCH] feat(event cache store): add a method to clear all rooms' linked chunks --- .../event_cache/store/integration_tests.rs | 69 +++++++++++++++++++ .../src/event_cache/store/memory_store.rs | 5 ++ .../src/event_cache/store/traits.rs | 10 +++ .../src/linked_chunk/relational.rs | 6 ++ .../src/event_cache_store.rs | 11 +++ 5 files changed, 101 insertions(+) diff --git a/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs b/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs index ad484c569..0c4f36bbb 100644 --- a/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs +++ b/crates/matrix-sdk-base/src/event_cache/store/integration_tests.rs @@ -114,6 +114,9 @@ pub trait EventCacheStoreIntegrationTests { /// Test that rebuilding a linked chunk from an empty store doesn't return /// anything. async fn test_rebuild_empty_linked_chunk(&self); + + /// Test that clear all the rooms' linked chunks works. + async fn test_clear_all_rooms_chunks(&self); } #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] @@ -372,6 +375,65 @@ impl EventCacheStoreIntegrationTests for DynEventCacheStore { // When I rebuild a linked chunk from an empty store, it's empty. assert!(self.reload_linked_chunk(&DEFAULT_TEST_ROOM_ID).await.unwrap().is_none()); } + + async fn test_clear_all_rooms_chunks(&self) { + use matrix_sdk_common::linked_chunk::ChunkIdentifier as CId; + + let r0 = room_id!("!r0:matrix.org"); + let r1 = room_id!("!r1:matrix.org"); + + // Add updates for the first room. + self.handle_linked_chunk_updates( + r0, + vec![ + // new chunk + Update::NewItemsChunk { previous: None, new: CId::new(0), next: None }, + // new items on 0 + Update::PushItems { + at: Position::new(CId::new(0), 0), + items: vec![make_test_event(r0, "hello"), make_test_event(r0, "world")], + }, + ], + ) + .await + .unwrap(); + + // Add updates for the second room. + self.handle_linked_chunk_updates( + r1, + vec![ + // Empty items chunk. + Update::NewItemsChunk { previous: None, new: CId::new(0), next: None }, + // a gap chunk + Update::NewGapChunk { + previous: Some(CId::new(0)), + new: CId::new(1), + next: None, + gap: Gap { prev_token: "bleu d'auvergne".to_owned() }, + }, + // another items chunk + Update::NewItemsChunk { previous: Some(CId::new(1)), new: CId::new(2), next: None }, + // new items on 0 + Update::PushItems { + at: Position::new(CId::new(2), 0), + items: vec![make_test_event(r0, "yummy")], + }, + ], + ) + .await + .unwrap(); + + // Sanity check: both linked chunks can be reloaded. + assert!(self.reload_linked_chunk(r0).await.unwrap().is_some()); + assert!(self.reload_linked_chunk(r1).await.unwrap().is_some()); + + // Clear the chunks. + self.clear_all_rooms_chunks().await.unwrap(); + + // Both rooms now have no linked chunk. + assert!(self.reload_linked_chunk(r0).await.unwrap().is_none()); + assert!(self.reload_linked_chunk(r1).await.unwrap().is_none()); + } } /// Macro building to allow your `EventCacheStore` implementation to run the @@ -440,6 +502,13 @@ macro_rules! event_cache_store_integration_tests { get_event_cache_store().await.unwrap().into_event_cache_store(); event_cache_store.test_rebuild_empty_linked_chunk().await; } + + #[async_test] + async fn test_clear_all_rooms_chunks() { + let event_cache_store = + get_event_cache_store().await.unwrap().into_event_cache_store(); + event_cache_store.test_clear_all_rooms_chunks().await; + } } }; } diff --git a/crates/matrix-sdk-base/src/event_cache/store/memory_store.rs b/crates/matrix-sdk-base/src/event_cache/store/memory_store.rs index 78fc8cfa5..8f921b580 100644 --- a/crates/matrix-sdk-base/src/event_cache/store/memory_store.rs +++ b/crates/matrix-sdk-base/src/event_cache/store/memory_store.rs @@ -115,6 +115,11 @@ impl EventCacheStore for MemoryStore { Ok(result) } + async fn clear_all_rooms_chunks(&self) -> Result<(), Self::Error> { + self.inner.write().unwrap().events.clear(); + Ok(()) + } + async fn add_media_content( &self, request: &MediaRequestParameters, diff --git a/crates/matrix-sdk-base/src/event_cache/store/traits.rs b/crates/matrix-sdk-base/src/event_cache/store/traits.rs index 94bc4a94f..50707a6dd 100644 --- a/crates/matrix-sdk-base/src/event_cache/store/traits.rs +++ b/crates/matrix-sdk-base/src/event_cache/store/traits.rs @@ -62,6 +62,12 @@ pub trait EventCacheStore: AsyncTraitDeps { room_id: &RoomId, ) -> Result>, Self::Error>; + /// Clear persisted events for all the rooms. + /// + /// This will empty and remove all the linked chunks stored previously, + /// using the above [`Self::handle_linked_chunk_updates`] methods. + async fn clear_all_rooms_chunks(&self) -> Result<(), Self::Error>; + /// Add a media file's content in the media store. /// /// # Arguments @@ -188,6 +194,10 @@ impl EventCacheStore for EraseEventCacheStoreError { self.0.reload_linked_chunk(room_id).await.map_err(Into::into) } + async fn clear_all_rooms_chunks(&self) -> Result<(), Self::Error> { + self.0.clear_all_rooms_chunks().await.map_err(Into::into) + } + async fn add_media_content( &self, request: &MediaRequestParameters, diff --git a/crates/matrix-sdk-common/src/linked_chunk/relational.rs b/crates/matrix-sdk-common/src/linked_chunk/relational.rs index 972e3cd8e..27f137406 100644 --- a/crates/matrix-sdk-common/src/linked_chunk/relational.rs +++ b/crates/matrix-sdk-common/src/linked_chunk/relational.rs @@ -80,6 +80,12 @@ impl RelationalLinkedChunk { Self { chunks: Vec::new(), items: Vec::new() } } + /// Removes all the chunks and items from this relational linked chunk. + pub fn clear(&mut self) { + self.chunks.clear(); + self.items.clear(); + } + /// Apply [`Update`]s. That's the only way to write data inside this /// relational linked chunk. pub fn apply_updates(&mut self, room_id: &RoomId, updates: Vec>) { diff --git a/crates/matrix-sdk-sqlite/src/event_cache_store.rs b/crates/matrix-sdk-sqlite/src/event_cache_store.rs index 2e9514123..08c8eb584 100644 --- a/crates/matrix-sdk-sqlite/src/event_cache_store.rs +++ b/crates/matrix-sdk-sqlite/src/event_cache_store.rs @@ -609,6 +609,17 @@ impl EventCacheStore for SqliteEventCacheStore { }) } + async fn clear_all_rooms_chunks(&self) -> Result<(), Self::Error> { + self.acquire() + .await? + .with_transaction(move |txn| { + // Remove all the chunks, and let cascading do its job. + txn.execute("DELETE FROM linked_chunks", ()) + }) + .await?; + Ok(()) + } + async fn add_media_content( &self, request: &MediaRequestParameters,