feat(sqlite): Run VACUUM operation after removing a room
A room can be associated to a lot of data, depending on the number of members in the room. So freeing space on the filesystem should be worth it in some cases. An (extreme) example: I have a test account that is in ~60 rooms, a few of those big public rooms, including Matrix HQ. The size of the matrix-sdk-state.sqlite3 file is 542 MB. Using this PR and leaving, then forgetting Matrix HQ brings the DB down to 255 MB.
This commit is contained in:
@@ -10,6 +10,8 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
- Implement the new method of `EventCacheStoreMedia` for `SqliteEventCacheStore`.
|
||||
([#4603](https://github.com/matrix-org/matrix-rust-sdk/pull/4603))
|
||||
- Defragment an sqlite state store after removing a room.
|
||||
([#4651](https://github.com/matrix-org/matrix-rust-sdk/pull/4651))
|
||||
|
||||
## [0.10.0] - 2025-02-04
|
||||
|
||||
|
||||
@@ -36,8 +36,6 @@ use matrix_sdk_store_encryption::StoreCipher;
|
||||
use ruma::{time::SystemTime, MilliSecondsSinceUnixEpoch, MxcUri, RoomId};
|
||||
use rusqlite::{params_from_iter, OptionalExtension, Transaction, TransactionBehavior};
|
||||
use tokio::fs;
|
||||
#[cfg(not(test))]
|
||||
use tracing::warn;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use crate::{
|
||||
@@ -959,19 +957,10 @@ impl EventCacheStoreMedia for SqliteEventCacheStore {
|
||||
})
|
||||
.await?;
|
||||
|
||||
// If we removed media, use the VACUUM command to defragment the
|
||||
// database and free space on the filesystem.
|
||||
// If we removed media, defragment the database and free space on the
|
||||
// filesystem.
|
||||
if removed {
|
||||
if let Err(error) = conn.execute("VACUUM", ()).await {
|
||||
// Since this is an optimisation step, do not propagate the error
|
||||
// but log it.
|
||||
#[cfg(not(test))]
|
||||
warn!("Failed to vacuum database: {error}");
|
||||
|
||||
// We want to know if there is an error with this step during tests.
|
||||
#[cfg(test)]
|
||||
return Err(error.into());
|
||||
}
|
||||
conn.vacuum().await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -336,7 +336,7 @@ impl SqliteStateStore {
|
||||
// Defragment the DB and optimize its size on the filesystem.
|
||||
// This should have been run in the migration for version 7, to reduce the size
|
||||
// of the DB as we removed the media cache.
|
||||
conn.execute_batch("VACUUM").await?;
|
||||
conn.vacuum().await?;
|
||||
conn.set_kv("version", vec![12]).await?;
|
||||
}
|
||||
|
||||
@@ -1739,40 +1739,42 @@ impl StateStore for SqliteStateStore {
|
||||
let this = self.clone();
|
||||
let room_id = room_id.to_owned();
|
||||
|
||||
self.acquire()
|
||||
.await?
|
||||
.with_transaction(move |txn| {
|
||||
let room_info_room_id = this.encode_key(keys::ROOM_INFO, &room_id);
|
||||
txn.remove_room_info(&room_info_room_id)?;
|
||||
let conn = self.acquire().await?;
|
||||
|
||||
let state_event_room_id = this.encode_key(keys::STATE_EVENT, &room_id);
|
||||
txn.remove_room_state_events(&state_event_room_id, None)?;
|
||||
conn.with_transaction(move |txn| -> Result<()> {
|
||||
let room_info_room_id = this.encode_key(keys::ROOM_INFO, &room_id);
|
||||
txn.remove_room_info(&room_info_room_id)?;
|
||||
|
||||
let member_room_id = this.encode_key(keys::MEMBER, &room_id);
|
||||
txn.remove_room_members(&member_room_id, None)?;
|
||||
let state_event_room_id = this.encode_key(keys::STATE_EVENT, &room_id);
|
||||
txn.remove_room_state_events(&state_event_room_id, None)?;
|
||||
|
||||
let profile_room_id = this.encode_key(keys::PROFILE, &room_id);
|
||||
txn.remove_room_profiles(&profile_room_id)?;
|
||||
let member_room_id = this.encode_key(keys::MEMBER, &room_id);
|
||||
txn.remove_room_members(&member_room_id, None)?;
|
||||
|
||||
let room_account_data_room_id = this.encode_key(keys::ROOM_ACCOUNT_DATA, &room_id);
|
||||
txn.remove_room_account_data(&room_account_data_room_id)?;
|
||||
let profile_room_id = this.encode_key(keys::PROFILE, &room_id);
|
||||
txn.remove_room_profiles(&profile_room_id)?;
|
||||
|
||||
let receipt_room_id = this.encode_key(keys::RECEIPT, &room_id);
|
||||
txn.remove_room_receipts(&receipt_room_id)?;
|
||||
let room_account_data_room_id = this.encode_key(keys::ROOM_ACCOUNT_DATA, &room_id);
|
||||
txn.remove_room_account_data(&room_account_data_room_id)?;
|
||||
|
||||
let display_name_room_id = this.encode_key(keys::DISPLAY_NAME, &room_id);
|
||||
txn.remove_room_display_names(&display_name_room_id)?;
|
||||
let receipt_room_id = this.encode_key(keys::RECEIPT, &room_id);
|
||||
txn.remove_room_receipts(&receipt_room_id)?;
|
||||
|
||||
let send_queue_room_id = this.encode_key(keys::SEND_QUEUE, &room_id);
|
||||
txn.remove_room_send_queue(&send_queue_room_id)?;
|
||||
let display_name_room_id = this.encode_key(keys::DISPLAY_NAME, &room_id);
|
||||
txn.remove_room_display_names(&display_name_room_id)?;
|
||||
|
||||
let dependent_send_queue_room_id =
|
||||
this.encode_key(keys::DEPENDENTS_SEND_QUEUE, &room_id);
|
||||
txn.remove_room_dependent_send_queue(&dependent_send_queue_room_id)?;
|
||||
let send_queue_room_id = this.encode_key(keys::SEND_QUEUE, &room_id);
|
||||
txn.remove_room_send_queue(&send_queue_room_id)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
let dependent_send_queue_room_id =
|
||||
this.encode_key(keys::DEPENDENTS_SEND_QUEUE, &room_id);
|
||||
txn.remove_room_dependent_send_queue(&dependent_send_queue_room_id)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
conn.vacuum().await
|
||||
}
|
||||
|
||||
async fn save_send_queue_request(
|
||||
|
||||
@@ -136,6 +136,24 @@ pub(crate) trait SqliteAsyncConnExt {
|
||||
self.execute_batch("PRAGMA journal_size_limit = 10000000;").await.map_err(Error::from)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Defragment the database and free space on the filesystem.
|
||||
///
|
||||
/// Only returns an error in tests, otherwise the error is only logged.
|
||||
async fn vacuum(&self) -> Result<()> {
|
||||
if let Err(error) = self.execute_batch("VACUUM").await {
|
||||
// Since this is an optimisation step, do not propagate the error
|
||||
// but log it.
|
||||
#[cfg(not(any(test, debug_assertions)))]
|
||||
tracing::warn!("Failed to vacuum database: {error}");
|
||||
|
||||
// We want to know if there is an error with this step during tests.
|
||||
#[cfg(any(test, debug_assertions))]
|
||||
return Err(error.into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
||||
Reference in New Issue
Block a user