From eeb14f6cbed0ba465ccce06632c422e4fe31533e Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 9 Dec 2024 16:40:08 +0100 Subject: [PATCH] refactor!(event cache store): have the event cache store return raw linked chunks, not the full linked chunk And let the caller rebuild the linked chunk. This is slightly nicer in that it allows us to display the raw representation of a reloaded linked chunk, before checking its internal state is consistent; this will allow for better debug of issues related to the linked chunk internal state. No functional changes. --- .../event_cache/store/integration_tests.rs | 26 ++-- .../src/event_cache/store/memory_store.rs | 21 +-- .../src/event_cache/store/traits.rs | 10 +- .../src/linked_chunk/builder.rs | 18 ++- .../matrix-sdk-common/src/linked_chunk/mod.rs | 16 +++ .../src/linked_chunk/relational.rs | 78 +++++------ .../src/event_cache_store.rs | 128 +++++++----------- crates/matrix-sdk/src/event_cache/room/mod.rs | 34 +++-- 8 files changed, 170 insertions(+), 161 deletions(-) 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 0c4f36bbb..10651d446 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 @@ -21,7 +21,9 @@ use matrix_sdk_common::{ AlgorithmInfo, DecryptedRoomEvent, EncryptionInfo, SyncTimelineEvent, TimelineEventKind, VerificationState, }, - linked_chunk::{ChunkContent, Position, Update}, + linked_chunk::{ + ChunkContent, LinkedChunk, LinkedChunkBuilder, Position, RawLinkedChunk, Update, + }, }; use matrix_sdk_test::{event_factory::EventFactory, ALICE, DEFAULT_TEST_ROOM_ID}; use ruma::{ @@ -31,7 +33,7 @@ use ruma::{ use super::DynEventCacheStore; use crate::{ - event_cache::Gap, + event_cache::{Event, Gap}, media::{MediaFormat, MediaRequestParameters, MediaThumbnailSettings}, }; @@ -119,6 +121,12 @@ pub trait EventCacheStoreIntegrationTests { async fn test_clear_all_rooms_chunks(&self); } +fn rebuild_linked_chunk( + raws: Vec>, +) -> Option> { + LinkedChunkBuilder::from_raw_parts(raws).build().unwrap() +} + #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] #[cfg_attr(not(target_arch = "wasm32"), async_trait)] impl EventCacheStoreIntegrationTests for DynEventCacheStore { @@ -332,7 +340,8 @@ impl EventCacheStoreIntegrationTests for DynEventCacheStore { .unwrap(); // The linked chunk is correctly reloaded. - let lc = self.reload_linked_chunk(room_id).await.unwrap().expect("linked chunk not empty"); + let raws = self.reload_linked_chunk(room_id).await.unwrap(); + let lc = rebuild_linked_chunk(raws).expect("linked chunk not empty"); let mut chunks = lc.chunks(); @@ -373,7 +382,8 @@ impl EventCacheStoreIntegrationTests for DynEventCacheStore { async fn test_rebuild_empty_linked_chunk(&self) { // 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()); + let raw_parts = self.reload_linked_chunk(&DEFAULT_TEST_ROOM_ID).await.unwrap(); + assert!(rebuild_linked_chunk(raw_parts).is_none()); } async fn test_clear_all_rooms_chunks(&self) { @@ -424,15 +434,15 @@ impl EventCacheStoreIntegrationTests for DynEventCacheStore { .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()); + assert!(rebuild_linked_chunk(self.reload_linked_chunk(r0).await.unwrap()).is_some()); + assert!(rebuild_linked_chunk(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()); + assert!(rebuild_linked_chunk(self.reload_linked_chunk(r0).await.unwrap()).is_none()); + assert!(rebuild_linked_chunk(self.reload_linked_chunk(r1).await.unwrap()).is_none()); } } 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 8f921b580..02aeba58f 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 @@ -16,13 +16,13 @@ use std::{collections::HashMap, num::NonZeroUsize, sync::RwLock as StdRwLock, ti use async_trait::async_trait; use matrix_sdk_common::{ - linked_chunk::{relational::RelationalLinkedChunk, LinkedChunk, LinkedChunkBuilder, Update}, + linked_chunk::{relational::RelationalLinkedChunk, RawLinkedChunk, Update}, ring_buffer::RingBuffer, store_locks::memory_store_helper::try_take_leased_lock, }; use ruma::{MxcUri, OwnedMxcUri, RoomId}; -use super::{EventCacheStore, EventCacheStoreError, Result, DEFAULT_CHUNK_CAPACITY}; +use super::{EventCacheStore, EventCacheStoreError, Result}; use crate::{ event_cache::{Event, Gap}, media::{MediaRequestParameters, UniqueKey as _}, @@ -96,23 +96,12 @@ impl EventCacheStore for MemoryStore { async fn reload_linked_chunk( &self, room_id: &RoomId, - ) -> Result>, Self::Error> { + ) -> Result>, Self::Error> { let inner = self.inner.read().unwrap(); - - let mut builder = LinkedChunkBuilder::new(); - inner .events - .reload_chunks(room_id, &mut builder) - .map_err(|err| EventCacheStoreError::InvalidData { details: err })?; - - builder.with_update_history(); - - let result = builder.build().map_err(|err| EventCacheStoreError::InvalidData { - details: format!("when rebuilding a linked chunk: {err}"), - })?; - - Ok(result) + .reload_chunks(room_id) + .map_err(|err| EventCacheStoreError::InvalidData { details: err }) } async fn clear_all_rooms_chunks(&self) -> Result<(), Self::Error> { 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 50707a6dd..4251eb898 100644 --- a/crates/matrix-sdk-base/src/event_cache/store/traits.rs +++ b/crates/matrix-sdk-base/src/event_cache/store/traits.rs @@ -16,7 +16,7 @@ use std::{fmt, sync::Arc}; use async_trait::async_trait; use matrix_sdk_common::{ - linked_chunk::{LinkedChunk, Update}, + linked_chunk::{RawLinkedChunk, Update}, AsyncTraitDeps, }; use ruma::{MxcUri, RoomId}; @@ -29,6 +29,7 @@ use crate::{ /// A default capacity for linked chunks, when manipulating in conjunction with /// an `EventCacheStore` implementation. +// TODO: move back? pub const DEFAULT_CHUNK_CAPACITY: usize = 128; /// An abstract trait that can be used to implement different store backends @@ -56,11 +57,12 @@ pub trait EventCacheStore: AsyncTraitDeps { updates: Vec>, ) -> Result<(), Self::Error>; - /// Reconstruct a full linked chunk by reloading it from storage. + /// Return all the raw components of a linked chunk, so the caller may + /// reconstruct the linked chunk later. async fn reload_linked_chunk( &self, room_id: &RoomId, - ) -> Result>, Self::Error>; + ) -> Result>, Self::Error>; /// Clear persisted events for all the rooms. /// @@ -190,7 +192,7 @@ impl EventCacheStore for EraseEventCacheStoreError { async fn reload_linked_chunk( &self, room_id: &RoomId, - ) -> Result>, Self::Error> { + ) -> Result>, Self::Error> { self.0.reload_linked_chunk(room_id).await.map_err(Into::into) } diff --git a/crates/matrix-sdk-common/src/linked_chunk/builder.rs b/crates/matrix-sdk-common/src/linked_chunk/builder.rs index 16550866c..0bcb3e20f 100644 --- a/crates/matrix-sdk-common/src/linked_chunk/builder.rs +++ b/crates/matrix-sdk-common/src/linked_chunk/builder.rs @@ -21,7 +21,7 @@ use tracing::error; use super::{ Chunk, ChunkContent, ChunkIdentifier, ChunkIdentifierGenerator, Ends, LinkedChunk, - ObservableUpdates, + ObservableUpdates, RawLinkedChunk, }; /// A temporary chunk representation in the [`LinkedChunkBuilder`]. @@ -260,6 +260,22 @@ impl LinkedChunkBuilder { Ok(Some(LinkedChunk { links, chunk_identifier_generator, updates, marker: PhantomData })) } + + /// Fills a linked chunk builder from all the given raw parts. + pub fn from_raw_parts(raws: Vec>) -> Self { + let mut this = Self::new(); + for raw in raws { + match raw.content { + ChunkContent::Gap(gap) => { + this.push_gap(raw.previous, raw.id, raw.next, gap); + } + ChunkContent::Items(vec) => { + this.push_items(raw.previous, raw.id, raw.next, vec); + } + } + } + this + } } #[derive(thiserror::Error, Debug)] diff --git a/crates/matrix-sdk-common/src/linked_chunk/mod.rs b/crates/matrix-sdk-common/src/linked_chunk/mod.rs index 40f3fbb01..a0cb1a820 100644 --- a/crates/matrix-sdk-common/src/linked_chunk/mod.rs +++ b/crates/matrix-sdk-common/src/linked_chunk/mod.rs @@ -1419,6 +1419,22 @@ impl EmptyChunk { } } +/// The raw representation of a linked chunk, as persisted in storage. +#[derive(Debug)] +pub struct RawLinkedChunk { + /// Content section of the linked chunk. + pub content: ChunkContent, + + /// Link to the previous chunk, via its identifier. + pub previous: Option, + + /// Current chunk's identifier. + pub id: ChunkIdentifier, + + /// Link to the next chunk, via its identifier. + pub next: Option, +} + #[cfg(test)] mod tests { use std::{ diff --git a/crates/matrix-sdk-common/src/linked_chunk/relational.rs b/crates/matrix-sdk-common/src/linked_chunk/relational.rs index 27f137406..e823e927c 100644 --- a/crates/matrix-sdk-common/src/linked_chunk/relational.rs +++ b/crates/matrix-sdk-common/src/linked_chunk/relational.rs @@ -17,7 +17,7 @@ use ruma::{OwnedRoomId, RoomId}; -use super::LinkedChunkBuilder; +use super::{ChunkContent, RawLinkedChunk}; use crate::linked_chunk::{ChunkIdentifier, Position, Update}; /// A row of the [`RelationalLinkedChunk::chunks`]. @@ -290,11 +290,12 @@ where /// /// Return an error result if the data was malformed in the struct, with a /// string message explaining details about the error. - pub fn reload_chunks( + pub fn reload_chunks( &self, room_id: &RoomId, - builder: &mut LinkedChunkBuilder, - ) -> Result<(), String> { + ) -> Result>, String> { + let mut result = Vec::new(); + for chunk_row in self.chunks.iter().filter(|chunk| chunk.room_id == room_id) { // Find all items that correspond to the chunk. let mut items = self @@ -309,12 +310,12 @@ where let Some(first) = items.peek() else { // The only possibility is that we created an empty items chunk; mark it as // such, and continue. - builder.push_items( - chunk_row.previous_chunk, - chunk_row.chunk, - chunk_row.next_chunk, - Vec::new(), - ); + result.push(RawLinkedChunk { + content: ChunkContent::Items(Vec::new()), + previous: chunk_row.previous_chunk, + id: chunk_row.chunk, + next: chunk_row.next_chunk, + }); continue; }; @@ -339,12 +340,14 @@ where // Sort them by their position. collected_items.sort_unstable_by_key(|(_item, index)| *index); - builder.push_items( - chunk_row.previous_chunk, - chunk_row.chunk, - chunk_row.next_chunk, - collected_items.into_iter().map(|(item, _index)| item), - ); + result.push(RawLinkedChunk { + content: ChunkContent::Items( + collected_items.into_iter().map(|(item, _index)| item).collect(), + ), + previous: chunk_row.previous_chunk, + id: chunk_row.chunk, + next: chunk_row.next_chunk, + }); } Either::Gap(gap) => { @@ -358,17 +361,17 @@ where )); } - builder.push_gap( - chunk_row.previous_chunk, - chunk_row.chunk, - chunk_row.next_chunk, - gap.clone(), - ); + result.push(RawLinkedChunk { + content: ChunkContent::Gap(gap.clone()), + previous: chunk_row.previous_chunk, + id: chunk_row.chunk, + next: chunk_row.next_chunk, + }); } } } - Ok(()) + Ok(result) } } @@ -383,6 +386,7 @@ mod tests { use ruma::room_id; use super::{ChunkIdentifier as CId, *}; + use crate::linked_chunk::LinkedChunkBuilder; #[test] fn test_new_items_chunk() { @@ -828,25 +832,17 @@ mod tests { } #[test] - fn test_rebuild_empty_linked_chunk() { - let mut builder = LinkedChunkBuilder::<3, _, _>::new(); - + fn test_reload_empty_linked_chunk() { let room_id = room_id!("!r0:matrix.org"); - // When I rebuild a linked chunk from an empty store, + // When I reload the linked chunk components from an empty store, let relational_linked_chunk = RelationalLinkedChunk::::new(); - relational_linked_chunk.reload_chunks(room_id, &mut builder).unwrap(); - - let lc = builder.build().expect("building succeeds"); - - // The builder won't return a linked chunk. - assert!(lc.is_none()); + let result = relational_linked_chunk.reload_chunks(room_id).unwrap(); + assert!(result.is_empty()); } #[test] fn test_reload_linked_chunk_with_empty_items() { - let mut builder = LinkedChunkBuilder::<3, _, _>::new(); - let room_id = room_id!("!r0:matrix.org"); let mut relational_linked_chunk = RelationalLinkedChunk::::new(); @@ -858,9 +854,8 @@ mod tests { ); // It correctly gets reloaded as such. - relational_linked_chunk.reload_chunks(room_id, &mut builder).unwrap(); - - let lc = builder + let raws = relational_linked_chunk.reload_chunks(room_id).unwrap(); + let lc = LinkedChunkBuilder::<3, _, _>::from_raw_parts(raws) .build() .expect("building succeeds") .expect("this leads to a non-empty linked chunk"); @@ -870,8 +865,6 @@ mod tests { #[test] fn test_rebuild_linked_chunk() { - let mut builder = LinkedChunkBuilder::<3, _, _>::new(); - let room_id = room_id!("!r0:matrix.org"); let mut relational_linked_chunk = RelationalLinkedChunk::::new(); @@ -896,9 +889,8 @@ mod tests { ], ); - relational_linked_chunk.reload_chunks(room_id, &mut builder).unwrap(); - - let lc = builder + let raws = relational_linked_chunk.reload_chunks(room_id).unwrap(); + let lc = LinkedChunkBuilder::<3, _, _>::from_raw_parts(raws) .build() .expect("building succeeds") .expect("this leads to a non-empty linked chunk"); diff --git a/crates/matrix-sdk-sqlite/src/event_cache_store.rs b/crates/matrix-sdk-sqlite/src/event_cache_store.rs index 08c8eb584..be255445d 100644 --- a/crates/matrix-sdk-sqlite/src/event_cache_store.rs +++ b/crates/matrix-sdk-sqlite/src/event_cache_store.rs @@ -21,11 +21,8 @@ use std::{borrow::Cow, fmt, path::Path, sync::Arc}; use async_trait::async_trait; use deadpool_sqlite::{Object as SqliteAsyncConn, Pool as SqlitePool, Runtime}; use matrix_sdk_base::{ - event_cache::{ - store::{EventCacheStore, DEFAULT_CHUNK_CAPACITY}, - Event, Gap, - }, - linked_chunk::{ChunkContent, ChunkIdentifier, LinkedChunk, LinkedChunkBuilder, Update}, + event_cache::{store::EventCacheStore, Event, Gap}, + linked_chunk::{ChunkContent, ChunkIdentifier, RawLinkedChunk, Update}, media::{MediaRequestParameters, UniqueKey}, }; use matrix_sdk_store_encryption::StoreCipher; @@ -147,49 +144,11 @@ impl SqliteEventCacheStore { )) } - async fn load_chunks(&self, room_id: &RoomId) -> Result> { - let room_id = room_id.to_owned(); - let hashed_room_id = self.encode_key(keys::LINKED_CHUNKS, &room_id); - - let this = self.clone(); - - let result = self - .acquire() - .await? - .with_transaction(move |txn| -> Result<_> { - let mut items = Vec::new(); - - // Use `ORDER BY id` to get a deterministic ordering for testing purposes. - for data in txn - .prepare( - "SELECT id, previous, next, type FROM linked_chunks WHERE room_id = ? ORDER BY id", - )? - .query_map((&hashed_room_id,), Self::map_row_to_chunk)? - { - let (id, previous, next, chunk_type) = data?; - let new = txn.rebuild_chunk( - &this, - &hashed_room_id, - previous, - id, - next, - chunk_type.as_str(), - )?; - items.push(new); - } - - Ok(items) - }) - .await?; - - Ok(result) - } - async fn load_chunk_with_id( &self, room_id: &RoomId, chunk_id: ChunkIdentifier, - ) -> Result { + ) -> Result> { let hashed_room_id = self.encode_key(keys::LINKED_CHUNKS, room_id); let this = self.clone(); @@ -217,7 +176,7 @@ trait TransactionExtForLinkedChunks { index: u64, next: Option, chunk_type: &str, - ) -> Result; + ) -> Result>; fn load_gap_content( &self, @@ -243,7 +202,7 @@ impl TransactionExtForLinkedChunks for Transaction<'_> { id: u64, next: Option, chunk_type: &str, - ) -> Result { + ) -> Result> { let previous = previous.map(ChunkIdentifier::new); let next = next.map(ChunkIdentifier::new); let id = ChunkIdentifier::new(id); @@ -317,14 +276,6 @@ impl TransactionExtForLinkedChunks for Transaction<'_> { } } -struct RawLinkedChunk { - content: ChunkContent, - - previous: Option, - id: ChunkIdentifier, - next: Option, -} - async fn create_pool(path: &Path) -> Result { fs::create_dir_all(path).await.map_err(OpenStoreError::CreateDir)?; let cfg = deadpool_sqlite::Config::new(path.join("matrix-sdk-event-cache.sqlite3")); @@ -586,27 +537,42 @@ impl EventCacheStore for SqliteEventCacheStore { async fn reload_linked_chunk( &self, room_id: &RoomId, - ) -> Result>, Self::Error> { - let chunks = self.load_chunks(room_id).await?; + ) -> Result>, Self::Error> { + let room_id = room_id.to_owned(); + let hashed_room_id = self.encode_key(keys::LINKED_CHUNKS, &room_id); - let mut builder = LinkedChunkBuilder::new(); + let this = self.clone(); - for c in chunks { - match c.content { - ChunkContent::Gap(gap) => { - builder.push_gap(c.previous, c.id, c.next, gap); + let result = self + .acquire() + .await? + .with_transaction(move |txn| -> Result<_> { + let mut items = Vec::new(); + + // Use `ORDER BY id` to get a deterministic ordering for testing purposes. + for data in txn + .prepare( + "SELECT id, previous, next, type FROM linked_chunks WHERE room_id = ? ORDER BY id", + )? + .query_map((&hashed_room_id,), Self::map_row_to_chunk)? + { + let (id, previous, next, chunk_type) = data?; + let new = txn.rebuild_chunk( + &this, + &hashed_room_id, + previous, + id, + next, + chunk_type.as_str(), + )?; + items.push(new); } - ChunkContent::Items(items) => { - builder.push_items(c.previous, c.id, c.next, items); - } - } - } - builder.with_update_history(); + Ok(items) + }) + .await?; - builder.build().map_err(|err| Error::InvalidData { - details: format!("when rebuilding a linked chunk: {err}"), - }) + Ok(result) } async fn clear_all_rooms_chunks(&self) -> Result<(), Self::Error> { @@ -931,7 +897,7 @@ mod tests { .await .unwrap(); - let mut chunks = store.load_chunks(room_id).await.unwrap(); + let mut chunks = store.reload_linked_chunk(room_id).await.unwrap(); assert_eq!(chunks.len(), 3); @@ -982,7 +948,7 @@ mod tests { .await .unwrap(); - let mut chunks = store.load_chunks(room_id).await.unwrap(); + let mut chunks = store.reload_linked_chunk(room_id).await.unwrap(); assert_eq!(chunks.len(), 1); @@ -1030,7 +996,7 @@ mod tests { .await .unwrap(); - let mut chunks = store.load_chunks(room_id).await.unwrap(); + let mut chunks = store.reload_linked_chunk(room_id).await.unwrap(); assert_eq!(chunks.len(), 2); @@ -1103,7 +1069,7 @@ mod tests { .await .unwrap(); - let mut chunks = store.load_chunks(room_id).await.unwrap(); + let mut chunks = store.reload_linked_chunk(room_id).await.unwrap(); assert_eq!(chunks.len(), 1); @@ -1148,7 +1114,7 @@ mod tests { .await .unwrap(); - let mut chunks = store.load_chunks(room_id).await.unwrap(); + let mut chunks = store.reload_linked_chunk(room_id).await.unwrap(); assert_eq!(chunks.len(), 1); @@ -1207,7 +1173,7 @@ mod tests { .await .unwrap(); - let mut chunks = store.load_chunks(room_id).await.unwrap(); + let mut chunks = store.reload_linked_chunk(room_id).await.unwrap(); assert_eq!(chunks.len(), 1); @@ -1254,7 +1220,7 @@ mod tests { .await .unwrap(); - let mut chunks = store.load_chunks(room_id).await.unwrap(); + let mut chunks = store.reload_linked_chunk(room_id).await.unwrap(); assert_eq!(chunks.len(), 1); @@ -1305,7 +1271,7 @@ mod tests { .await .unwrap(); - let chunks = store.load_chunks(room_id).await.unwrap(); + let chunks = store.reload_linked_chunk(room_id).await.unwrap(); assert!(chunks.is_empty()); } @@ -1359,7 +1325,7 @@ mod tests { .unwrap(); // Check chunks from room 1. - let mut chunks_room1 = store.load_chunks(room1).await.unwrap(); + let mut chunks_room1 = store.reload_linked_chunk(room1).await.unwrap(); assert_eq!(chunks_room1.len(), 1); let c = chunks_room1.remove(0); @@ -1370,7 +1336,7 @@ mod tests { }); // Check chunks from room 2. - let mut chunks_room2 = store.load_chunks(room2).await.unwrap(); + let mut chunks_room2 = store.reload_linked_chunk(room2).await.unwrap(); assert_eq!(chunks_room2.len(), 1); let c = chunks_room2.remove(0); @@ -1415,7 +1381,7 @@ mod tests { // If the updates have been handled transactionally, then no new chunks should // have been added; failure of the second update leads to the first one being // rolled back. - let chunks = store.load_chunks(room_id).await.unwrap(); + let chunks = store.reload_linked_chunk(room_id).await.unwrap(); assert!(chunks.is_empty()); } } diff --git a/crates/matrix-sdk/src/event_cache/room/mod.rs b/crates/matrix-sdk/src/event_cache/room/mod.rs index 57a504a98..ee9586bed 100644 --- a/crates/matrix-sdk/src/event_cache/room/mod.rs +++ b/crates/matrix-sdk/src/event_cache/room/mod.rs @@ -573,8 +573,8 @@ mod private { use matrix_sdk_base::{ deserialized_responses::{SyncTimelineEvent, TimelineEventKind}, - event_cache::store::EventCacheStoreLock, - linked_chunk::Update, + event_cache::store::{EventCacheStoreError, EventCacheStoreLock}, + linked_chunk::{LinkedChunkBuilder, Update}, }; use once_cell::sync::OnceCell; use ruma::{serde::Raw, OwnedRoomId}; @@ -614,8 +614,15 @@ mod private { ) -> Result { let events = if let Some(store) = store.get() { let locked = store.lock().await?; - let chunks = locked.reload_linked_chunk(&room).await?; - RoomEvents::with_initial_chunks(chunks) + let raw_chunks = locked.reload_linked_chunk(&room).await?; + + let mut builder = LinkedChunkBuilder::from_raw_parts(raw_chunks); + builder.with_update_history(); + let linked_chunk = builder.build().map_err(|err| { + EventCacheStoreError::InvalidData { details: err.to_string() } + })?; + + RoomEvents::with_initial_chunks(linked_chunk) } else { RoomEvents::default() }; @@ -998,6 +1005,8 @@ mod tests { #[cfg(not(target_arch = "wasm32"))] // This uses the cross-process lock, so needs time support. #[async_test] async fn test_write_to_storage() { + use matrix_sdk_base::linked_chunk::LinkedChunkBuilder; + let room_id = room_id!("!galette:saucisse.bzh"); let f = EventFactory::new().room(room_id).sender(user_id!("@ben:saucisse.bzh")); @@ -1034,7 +1043,9 @@ mod tests { .await .unwrap(); - let linked_chunk = event_cache_store.reload_linked_chunk(room_id).await.unwrap().unwrap(); + let raws = event_cache_store.reload_linked_chunk(room_id).await.unwrap(); + let linked_chunk = + LinkedChunkBuilder::<3, _, _>::from_raw_parts(raws).build().unwrap().unwrap(); assert_eq!(linked_chunk.chunks().count(), 3); @@ -1065,6 +1076,7 @@ mod tests { #[cfg(not(target_arch = "wasm32"))] // This uses the cross-process lock, so needs time support. #[async_test] async fn test_write_to_storage_strips_bundled_relations() { + use matrix_sdk_base::linked_chunk::LinkedChunkBuilder; use ruma::events::BundledMessageLikeRelations; let room_id = room_id!("!galette:saucisse.bzh"); @@ -1121,7 +1133,9 @@ mod tests { } // The one in storage does not. - let linked_chunk = event_cache_store.reload_linked_chunk(room_id).await.unwrap().unwrap(); + let raws = event_cache_store.reload_linked_chunk(room_id).await.unwrap(); + let linked_chunk = + LinkedChunkBuilder::<3, _, _>::from_raw_parts(raws).build().unwrap().unwrap(); assert_eq!(linked_chunk.chunks().count(), 1); @@ -1144,6 +1158,8 @@ mod tests { #[cfg(not(target_arch = "wasm32"))] // This uses the cross-process lock, so needs time support. #[async_test] async fn test_clear() { + use matrix_sdk_base::linked_chunk::LinkedChunkBuilder; + use crate::{assert_let_timeout, event_cache::RoomEventCacheUpdate}; let room_id = room_id!("!galette:saucisse.bzh"); @@ -1244,11 +1260,13 @@ mod tests { assert!(items.is_empty()); // The event cache store too. - let reloaded = event_cache_store.reload_linked_chunk(room_id).await.unwrap(); + let raws = event_cache_store.reload_linked_chunk(room_id).await.unwrap(); + let linked_chunk = LinkedChunkBuilder::<3, _, _>::from_raw_parts(raws).build().unwrap(); + // Note: while the event cache store could return `None` here, clearing it will // reset it to its initial form, maintaining the invariant that it // contains a single items chunk that's empty. - let linked_chunk = reloaded.unwrap(); + let linked_chunk = linked_chunk.unwrap(); assert_eq!(linked_chunk.num_items(), 0); }