From f0193cc8ac75333fd33da02ba5156031e7ed1d16 Mon Sep 17 00:00:00 2001 From: Daniel Salinas Date: Fri, 27 Mar 2026 13:18:46 -0400 Subject: [PATCH] Fix indexedb migration to clean up gaps + chunks properly --- .../src/event_cache_store/migrations.rs | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/crates/matrix-sdk-indexeddb/src/event_cache_store/migrations.rs b/crates/matrix-sdk-indexeddb/src/event_cache_store/migrations.rs index a0f75d139..2488c7ea9 100644 --- a/crates/matrix-sdk-indexeddb/src/event_cache_store/migrations.rs +++ b/crates/matrix-sdk-indexeddb/src/event_cache_store/migrations.rs @@ -21,10 +21,10 @@ use thiserror::Error; /// The current version and keys used in the database. pub mod current { - use super::{Version, v3}; + use super::{Version, v4}; - pub const VERSION: Version = Version::V3; - pub use v3::keys; + pub const VERSION: Version = Version::V4; + pub use v4::keys; } /// Opens a connection to the IndexedDB database and takes care of upgrading it @@ -58,6 +58,8 @@ pub enum Version { V2 = 2, /// Version 3 of the database, for details see [`v3`]. V3 = 3, + /// Version 4 of the database, for details see [`v4`]. + V4 = 4, } impl Version { @@ -67,7 +69,8 @@ impl Version { Self::V0 => v0::upgrade(transaction).map(Some), Self::V1 => v1::upgrade(transaction).map(Some), Self::V2 => v2::upgrade(transaction).map(Some), - Self::V3 => Ok(None), + Self::V3 => v3::upgrade(transaction).map(Some), + Self::V4 => Ok(None), } } } @@ -85,6 +88,7 @@ impl TryFrom for Version { 1 => Ok(Version::V1), 2 => Ok(Version::V2), 3 => Ok(Version::V3), + 4 => Ok(Version::V4), v => Err(UnknownVersionError(v)), } } @@ -250,6 +254,10 @@ mod v3 { /// /// Note that this operation removes the existing events object store and /// all of its contents. + /// + /// **Bug**: This migration failed to also clear the `linked_chunks` and + /// `gaps` stores, leaving orphaned chunk structures that reference events + /// which no longer exist. This is corrected by the V4 migration. pub fn update_events_object_store(transaction: &Transaction<'_>) -> Result<(), Error> { remove_events_object_store(transaction)?; create_events_object_store(transaction.db())?; @@ -292,4 +300,36 @@ mod v3 { .build()?; Ok(()) } + + /// Upgrade database from `v3` to `v4` + pub fn upgrade(transaction: &Transaction<'_>) -> Result { + v4::empty_event_cache(transaction)?; + Ok(Version::V4) + } +} + +mod v4 { + // Re-use all the same keys from `v3`. + pub use super::v3::keys; + use super::*; + + /// The V3 migration only cleared the events object store but left + /// linked_chunks and gaps intact. This created an inconsistent state where + /// chunk structures reference events that no longer exist, and no gap + /// chunks exist to trigger backfill from the server. + /// + /// This migration clears linked_chunks and gaps for users who were already + /// migrated to V3, so rooms start fresh and properly re-sync. + pub fn empty_event_cache(transaction: &Transaction<'_>) -> Result<(), Error> { + let linked_chunks = transaction.object_store(keys::LINKED_CHUNKS)?; + linked_chunks.clear()?; + + let gaps = transaction.object_store(keys::GAPS)?; + gaps.clear()?; + + let events = transaction.object_store(keys::EVENTS)?; + events.clear()?; + + Ok(()) + } }