From 2a968661e71d2783c5cea7418b2c7404f39c150e Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Mon, 26 Jan 2026 17:08:49 +0100 Subject: [PATCH] feat(sqlite): allow storing the same events in multiple linked chunks --- .../014_event_chunks_event_id_index.sql | 34 +++++++++++++++++++ .../src/event_cache_store.rs | 12 ++++++- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 crates/matrix-sdk-sqlite/migrations/event_cache_store/014_event_chunks_event_id_index.sql diff --git a/crates/matrix-sdk-sqlite/migrations/event_cache_store/014_event_chunks_event_id_index.sql b/crates/matrix-sdk-sqlite/migrations/event_cache_store/014_event_chunks_event_id_index.sql new file mode 100644 index 000000000..2924b3057 --- /dev/null +++ b/crates/matrix-sdk-sqlite/migrations/event_cache_store/014_event_chunks_event_id_index.sql @@ -0,0 +1,34 @@ +-- Remove uniqueness constraint on event_id, replace with regular index. +-- Empties event cache as data migration is not needed. + +DELETE FROM linked_chunks; +DELETE FROM event_chunks; -- should be done by cascading +DELETE FROM gap_chunks; -- should be done by cascading + +DROP TABLE event_chunks; + +-- Recreate event_chunks with new PRIMARY KEY +CREATE TABLE "event_chunks" ( + -- Which linked chunk does this event belong to? (hashed key shared with linked_chunks) + "linked_chunk_id" BLOB NOT NULL, + -- Which chunk does this event refer to? Corresponds to a `ChunkIdentifier`. + "chunk_id" INTEGER NOT NULL, + + -- `OwnedEventId` for events. + "event_id" BLOB NOT NULL, + -- Position (index) in the chunk. + "position" INTEGER NOT NULL, + + -- We need a uniqueness constraint over the `linked_chunk_id`, `chunk_id` and + -- `position` tuple because (i) they must be unique, (ii) it dramatically + -- improves the performance. Also, we don't have a ROWID, so we must use a PRIMARY KEY, hence + -- we use this composite key as the primary key. + PRIMARY KEY (linked_chunk_id, chunk_id, position), + + -- If the owning chunk gets deleted, delete the entry too. + FOREIGN KEY (linked_chunk_id, chunk_id) REFERENCES linked_chunks(linked_chunk_id, id) ON DELETE CASCADE +) +WITHOUT ROWID; + +-- Create a non-unique index on `event_id` for query performance. +CREATE INDEX "event_chunks_event_id_idx" ON "event_chunks" ("event_id"); diff --git a/crates/matrix-sdk-sqlite/src/event_cache_store.rs b/crates/matrix-sdk-sqlite/src/event_cache_store.rs index 73eee0a9e..2f8aee43e 100644 --- a/crates/matrix-sdk-sqlite/src/event_cache_store.rs +++ b/crates/matrix-sdk-sqlite/src/event_cache_store.rs @@ -67,7 +67,7 @@ const DATABASE_NAME: &str = "matrix-sdk-event-cache.sqlite3"; /// This is used to figure whether the SQLite database requires a migration. /// Every new SQL migration should imply a bump of this number, and changes in /// the [`run_migrations`] function. -const DATABASE_VERSION: u8 = 13; +const DATABASE_VERSION: u8 = 14; /// The string used to identify a chunk of type events, in the `type` field in /// the database. @@ -496,6 +496,16 @@ async fn run_migrations(conn: &SqliteAsyncConn, version: u8) -> Result<()> { .await?; } + if version < 14 { + conn.with_transaction(|txn| { + txn.execute_batch(include_str!( + "../migrations/event_cache_store/014_event_chunks_event_id_index.sql" + ))?; + txn.set_db_version(14) + }) + .await?; + } + Ok(()) }