diff --git a/crates/imap/src/op/status.rs b/crates/imap/src/op/status.rs
index 84e3fbe2..822e8e96 100644
--- a/crates/imap/src/op/status.rs
+++ b/crates/imap/src/op/status.rs
@@ -33,8 +33,10 @@ use jmap_proto::{
object::Object,
types::{collection::Collection, id::Id, keyword::Keyword, property::Property, value::Value},
};
-use store::roaring::RoaringBitmap;
-use store::Deserialize;
+use store::{
+ roaring::RoaringBitmap, write::key::DeserializeBigEndian, IndexKeyPrefix, IterateParams,
+};
+use store::{Deserialize, U32_LEN};
use tokio::io::AsyncRead;
use crate::core::{Mailbox, Session, SessionData};
@@ -379,16 +381,34 @@ impl SessionData {
let mut total_size = 0u32;
self.jmap
.store
- .sort_index(
- account_id,
- Collection::Email,
- Property::Size,
- true,
- |bytes, document_id| {
+ .iterate(
+ IterateParams::new(
+ IndexKeyPrefix {
+ account_id,
+ collection: Collection::Email.into(),
+ field: Property::Size.into(),
+ },
+ IndexKeyPrefix {
+ account_id,
+ collection: Collection::Email.into(),
+ field: u8::from(Property::Size) + 1,
+ },
+ )
+ .ascending()
+ .no_values(),
+ |key, _| {
+ let id_pos = key.len() - U32_LEN;
+ let document_id = key.deserialize_be_u32(id_pos)?;
+
if message_ids.contains(document_id) {
- u32::deserialize(bytes).map(|size| {
- total_size += size;
- })?;
+ key.get(IndexKeyPrefix::len()..id_pos)
+ .ok_or_else(|| {
+ store::Error::InternalError("Invalid key length".to_string())
+ })
+ .and_then(u32::deserialize)
+ .map(|size| {
+ total_size += size;
+ })?;
}
Ok(true)
},
diff --git a/crates/jmap/src/lib.rs b/crates/jmap/src/lib.rs
index ea2eaa44..89684284 100644
--- a/crates/jmap/src/lib.rs
+++ b/crates/jmap/src/lib.rs
@@ -48,7 +48,7 @@ use services::{
};
use smtp::core::SMTP;
use store::{
- backend::rocksdb::RocksDbStore,
+ backend::{rocksdb::RocksDbStore, sqlite::SqliteStore},
fts::FtsFilter,
parking_lot::Mutex,
query::{sort::Pagination, Comparator, Filter, ResultSet, SortedResultSet},
@@ -201,11 +201,11 @@ impl JMAP {
.await
.failed("Unable to open database"),
));*/
- /*let store = Store::SQLite(Arc::new(
+ let store = Store::SQLite(Arc::new(
SqliteStore::open(config)
.await
.failed("Unable to open database"),
- ));*/
+ ));
/*let store = Store::FoundationDb(Arc::new(
FdbStore::open(config)
.await
@@ -216,11 +216,11 @@ impl JMAP {
.await
.failed("Unable to open database"),
));*/
- let store = Store::RocksDb(Arc::new(
+ /*let store = Store::RocksDb(Arc::new(
RocksDbStore::open(config)
.await
.failed("Unable to open database"),
- ));
+ ));*/
let blob_store = store.clone().into();
/*let blob_store = BlobStore::Fs(Arc::new(
FsStore::open(config)
diff --git a/crates/main/Cargo.toml b/crates/main/Cargo.toml
index 562192c3..4aaba837 100644
--- a/crates/main/Cargo.toml
+++ b/crates/main/Cargo.toml
@@ -31,8 +31,8 @@ tracing = "0.1"
jemallocator = "0.5.0"
[features]
-#default = ["sqlite", "foundationdb", "postgres", "mysql"]
-default = ["rocks"]
+default = ["sqlite", "foundationdb", "postgres", "mysql", "rocks"]
+#default = []
sqlite = ["store/sqlite"]
foundationdb = ["store/foundation"]
postgres = ["store/postgres"]
diff --git a/crates/store/src/backend/foundationdb/mod.rs b/crates/store/src/backend/foundationdb/mod.rs
index 82a11b4e..8b4338a4 100644
--- a/crates/store/src/backend/foundationdb/mod.rs
+++ b/crates/store/src/backend/foundationdb/mod.rs
@@ -27,7 +27,6 @@ use crate::Error;
pub mod blob;
pub mod main;
-pub mod purge;
pub mod read;
pub mod write;
diff --git a/crates/store/src/backend/foundationdb/purge.rs b/crates/store/src/backend/foundationdb/purge.rs
deleted file mode 100644
index 4a5590ca..00000000
--- a/crates/store/src/backend/foundationdb/purge.rs
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (c) 2023 Stalwart Labs Ltd.
- *
- * This file is part of the Stalwart Mail Server.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- * in the LICENSE file at the top-level directory of this distribution.
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- *
- * You can be released from the requirements of the AGPLv3 license by
- * purchasing a commercial license. Please contact licensing@stalw.art
- * for more details.
-*/
-
-use foundationdb::{
- options::{self, MutationType},
- FdbError, KeySelector, RangeOption,
-};
-use futures::StreamExt;
-
-use crate::{
- write::{bitmap::DenseBitmap, key::KeySerializer},
- SUBSPACE_BITMAPS, SUBSPACE_INDEXES, SUBSPACE_INDEX_VALUES, SUBSPACE_LOGS, SUBSPACE_VALUES,
- U32_LEN,
-};
-
-use super::FdbStore;
-
-const MAX_COMMIT_ATTEMPTS: u8 = 25;
-
-impl FdbStore {
- pub(crate) async fn purge_bitmaps(&self) -> crate::Result<()> {
- // Obtain all empty bitmaps
- let trx = self.db.create_trx()?;
- let mut iter = trx.get_ranges(
- RangeOption {
- begin: KeySelector::first_greater_or_equal(&[SUBSPACE_BITMAPS, 0u8][..]),
- end: KeySelector::first_greater_or_equal(&[SUBSPACE_BITMAPS, u8::MAX][..]),
- mode: options::StreamingMode::WantAll,
- reverse: false,
- ..Default::default()
- },
- true,
- );
- let mut delete_keys = Vec::new();
-
- while let Some(values) = iter.next().await {
- for value in values? {
- if value.value().iter().all(|byte| *byte == 0) {
- delete_keys.push(value.key().to_vec());
- }
- }
- }
- if delete_keys.is_empty() {
- return Ok(());
- }
-
- // Delete keys
- let bitmap = DenseBitmap::empty();
- for chunk in delete_keys.chunks(1024) {
- let mut retry_count = 0;
- loop {
- let trx = self.db.create_trx()?;
- for key in chunk {
- trx.atomic_op(key, &bitmap.bitmap, MutationType::CompareAndClear);
- }
- match trx.commit().await {
- Ok(_) => {
- break;
- }
- Err(err) => {
- if retry_count < MAX_COMMIT_ATTEMPTS {
- err.on_error().await?;
- retry_count += 1;
- } else {
- return Err(FdbError::from(err).into());
- }
- }
- }
- }
- }
-
- Ok(())
- }
-
- pub(crate) async fn purge_account(&self, account_id: u32) -> crate::Result<()> {
- for subspace in [
- SUBSPACE_BITMAPS,
- SUBSPACE_VALUES,
- SUBSPACE_LOGS,
- SUBSPACE_INDEXES,
- SUBSPACE_INDEX_VALUES,
- ] {
- let from_key = KeySerializer::new(U32_LEN + 2)
- .write(subspace)
- .write(account_id)
- .write(0u8)
- .finalize();
- let to_key = KeySerializer::new(U32_LEN + 2)
- .write(subspace)
- .write(account_id)
- .write(u8::MAX)
- .finalize();
-
- let trx = self.db.create_trx()?;
- trx.clear_range(&from_key, &to_key);
- if let Err(err) = trx.commit().await {
- return Err(FdbError::from(err).into());
- }
- }
-
- Ok(())
- }
-}
diff --git a/crates/store/src/backend/foundationdb/read.rs b/crates/store/src/backend/foundationdb/read.rs
index 8d037c79..5b6ce565 100644
--- a/crates/store/src/backend/foundationdb/read.rs
+++ b/crates/store/src/backend/foundationdb/read.rs
@@ -29,14 +29,9 @@ use futures::StreamExt;
use roaring::RoaringBitmap;
use crate::{
- query::{self, Operator},
- write::{
- bitmap::DeserializeBlock,
- key::{DeserializeBigEndian, KeySerializer},
- BitmapClass, ValueClass,
- },
- BitmapKey, Deserialize, IndexKey, IndexKeyPrefix, IterateParams, Key, ValueKey, SUBSPACE_BLOBS,
- SUBSPACE_INDEXES, U32_LEN,
+ write::{bitmap::DeserializeBlock, key::DeserializeBigEndian, BitmapClass, ValueClass},
+ BitmapKey, Deserialize, IterateParams, Key, ValueKey, SUBSPACE_BLOBS, SUBSPACE_INDEXES,
+ U32_LEN,
};
use super::FdbStore;
@@ -91,141 +86,6 @@ impl FdbStore {
Ok(if !bm.is_empty() { Some(bm) } else { None })
}
- pub(crate) async fn range_to_bitmap(
- &self,
- account_id: u32,
- collection: u8,
- field: u8,
- value: &[u8],
- op: query::Operator,
- ) -> crate::Result