Drop matrix-sdk-sled
parent
7cd8898f5f
commit
3e2bc3a514
|
@ -538,7 +538,6 @@ dependencies = [
|
|||
"matrix-sdk",
|
||||
"matrix-sdk-base",
|
||||
"matrix-sdk-crypto",
|
||||
"matrix-sdk-sled",
|
||||
"matrix-sdk-sqlite",
|
||||
"matrix-sdk-test",
|
||||
"pprof",
|
||||
|
@ -1167,7 +1166,7 @@ dependencies = [
|
|||
"hashbrown 0.12.3",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core 0.9.7",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1634,16 +1633,6 @@ version = "2.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0845fa252299212f0389d64ba26f34fa32cfe41588355f21ed507c59a0f64541"
|
||||
|
||||
[[package]]
|
||||
name = "fs2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs_extra"
|
||||
version = "1.3.0"
|
||||
|
@ -1770,15 +1759,6 @@ dependencies = [
|
|||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fxhash"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
|
@ -2778,7 +2758,6 @@ dependencies = [
|
|||
"http",
|
||||
"matrix-sdk-common",
|
||||
"matrix-sdk-crypto",
|
||||
"matrix-sdk-sled",
|
||||
"matrix-sdk-sqlite",
|
||||
"napi",
|
||||
"napi-build",
|
||||
|
@ -2882,31 +2861,6 @@ dependencies = [
|
|||
"vodozemac",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-sdk-sled"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"async-trait",
|
||||
"fs_extra",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"glob",
|
||||
"matrix-sdk-base",
|
||||
"matrix-sdk-crypto",
|
||||
"matrix-sdk-store-encryption",
|
||||
"matrix-sdk-test",
|
||||
"once_cell",
|
||||
"ruma",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sled",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matrix-sdk-sqlite"
|
||||
version = "0.1.0"
|
||||
|
@ -3513,17 +3467,6 @@ version = "2.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core 0.8.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
|
@ -3531,21 +3474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"instant",
|
||||
"libc",
|
||||
"redox_syscall 0.2.16",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3835,7 +3764,7 @@ dependencies = [
|
|||
"log",
|
||||
"nix",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"smallvec",
|
||||
"symbolic-demangle",
|
||||
"tempfile",
|
||||
|
@ -4791,22 +4720,6 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sled"
|
||||
version = "0.34.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f96b4737c2ce5987354855aed3797279def4ebf734436c6aa4552cf8e169935"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"fs2",
|
||||
"fxhash",
|
||||
"libc",
|
||||
"log",
|
||||
"parking_lot 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sliding-sync-integration-test"
|
||||
version = "0.1.0"
|
||||
|
@ -4879,7 +4792,7 @@ checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b"
|
|||
dependencies = [
|
||||
"new_debug_unreachable",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.1",
|
||||
"parking_lot",
|
||||
"phf_shared 0.10.0",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
|
|
|
@ -12,7 +12,6 @@ criterion = { version = "0.4.0", features = ["async", "async_tokio", "html_repor
|
|||
matrix-sdk-base = { path = "../crates/matrix-sdk-base" }
|
||||
matrix-sdk-crypto = { path = "../crates/matrix-sdk-crypto", version = "0.6.0"}
|
||||
matrix-sdk-sqlite = { path = "../crates/matrix-sdk-sqlite", version = "0.1.0", default-features = false, features = ["crypto-store"] }
|
||||
matrix-sdk-sled = { path = "../crates/matrix-sdk-sled", version = "0.2.0", features = ["crypto-store"] }
|
||||
matrix-sdk-test = { path = "../testing/matrix-sdk-test", version = "0.6.0"}
|
||||
matrix-sdk = { path = "../crates/matrix-sdk" }
|
||||
ruma = { workspace = true }
|
||||
|
|
|
@ -2,7 +2,6 @@ use std::{ops::Deref, sync::Arc};
|
|||
|
||||
use criterion::*;
|
||||
use matrix_sdk_crypto::{EncryptionSettings, OlmMachine};
|
||||
use matrix_sdk_sled::SledCryptoStore;
|
||||
use matrix_sdk_sqlite::SqliteCryptoStore;
|
||||
use matrix_sdk_test::response_from_file;
|
||||
use ruma::{
|
||||
|
@ -90,18 +89,6 @@ pub fn keys_query(c: &mut Criterion) {
|
|||
drop(machine);
|
||||
}
|
||||
|
||||
// Benchmark (deprecated) sled store.
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
|
||||
let machine =
|
||||
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
|
||||
|
||||
group.bench_with_input(BenchmarkId::new("sled store", &name), &response, |b, response| {
|
||||
b.to_async(&runtime)
|
||||
.iter(|| async { machine.mark_request_as_sent(&txn_id, response).await.unwrap() })
|
||||
});
|
||||
|
||||
group.finish()
|
||||
}
|
||||
|
||||
|
@ -164,28 +151,6 @@ pub fn keys_claiming(c: &mut Criterion) {
|
|||
)
|
||||
});
|
||||
|
||||
group.bench_with_input(BenchmarkId::new("sled store", &name), &response, |b, response| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store =
|
||||
Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
|
||||
|
||||
let machine = runtime
|
||||
.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store))
|
||||
.unwrap();
|
||||
runtime
|
||||
.block_on(machine.mark_request_as_sent(&txn_id, &keys_query_response))
|
||||
.unwrap();
|
||||
(machine, &runtime, &txn_id)
|
||||
},
|
||||
move |(machine, runtime, txn_id)| {
|
||||
runtime.block_on(machine.mark_request_as_sent(txn_id, response)).unwrap()
|
||||
},
|
||||
BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
|
||||
group.finish()
|
||||
}
|
||||
|
||||
|
@ -269,37 +234,6 @@ pub fn room_key_sharing(c: &mut Criterion) {
|
|||
drop(machine);
|
||||
}
|
||||
|
||||
// Benchmark (deprecated) sled store.
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
|
||||
|
||||
let machine =
|
||||
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
|
||||
runtime.block_on(machine.mark_request_as_sent(&txn_id, &keys_query_response)).unwrap();
|
||||
runtime.block_on(machine.mark_request_as_sent(&txn_id, &response)).unwrap();
|
||||
|
||||
group.bench_function(BenchmarkId::new("sled store", &name), |b| {
|
||||
b.to_async(&runtime).iter(|| async {
|
||||
let requests = machine
|
||||
.share_room_key(
|
||||
room_id,
|
||||
users.iter().map(Deref::deref),
|
||||
EncryptionSettings::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert!(!requests.is_empty());
|
||||
|
||||
for request in requests {
|
||||
machine.mark_request_as_sent(&request.txn_id, &to_device_response).await.unwrap();
|
||||
}
|
||||
|
||||
machine.invalidate_group_session(room_id).await.unwrap();
|
||||
})
|
||||
});
|
||||
|
||||
group.finish()
|
||||
}
|
||||
|
||||
|
@ -349,22 +283,6 @@ pub fn devices_missing_sessions_collecting(c: &mut Criterion) {
|
|||
drop(machine);
|
||||
}
|
||||
|
||||
// Benchmark (deprecated) sled store.
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
|
||||
|
||||
let machine =
|
||||
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
|
||||
|
||||
runtime.block_on(machine.mark_request_as_sent(&txn_id, &response)).unwrap();
|
||||
|
||||
group.bench_function(BenchmarkId::new("sled store", &name), |b| {
|
||||
b.to_async(&runtime).iter(|| async {
|
||||
machine.get_missing_sessions(users.iter().map(Deref::deref)).await.unwrap()
|
||||
})
|
||||
});
|
||||
|
||||
group.finish()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use criterion::*;
|
||||
use matrix_sdk::{config::StoreConfig, Client, RoomInfo, RoomState, Session, StateChanges};
|
||||
use matrix_sdk_base::{store::MemoryStore, StateStore as _};
|
||||
use matrix_sdk_sled::SledStateStore;
|
||||
use matrix_sdk_sqlite::SqliteStateStore;
|
||||
use ruma::{device_id, user_id, RoomId};
|
||||
use tokio::runtime::Builder;
|
||||
|
@ -74,36 +73,6 @@ pub fn restore_session(c: &mut Criterion) {
|
|||
for encryption_password in [None, Some("hunter2")] {
|
||||
let encrypted_suffix = if encryption_password.is_some() { "encrypted" } else { "clear" };
|
||||
|
||||
// Sled
|
||||
let sled_path = tempfile::tempdir().unwrap().path().to_path_buf();
|
||||
let mut sled_store_builder = SledStateStore::builder().path(sled_path);
|
||||
if let Some(password) = encryption_password {
|
||||
sled_store_builder = sled_store_builder.passphrase(password.to_owned());
|
||||
}
|
||||
let sled_store = sled_store_builder.build().expect("Can't create sled store");
|
||||
runtime
|
||||
.block_on(sled_store.save_changes(&changes))
|
||||
.expect("initial filling of sled failed");
|
||||
|
||||
group.bench_with_input(
|
||||
BenchmarkId::new(format!("sled store {encrypted_suffix}"), NAME),
|
||||
&sled_store,
|
||||
|b, store| {
|
||||
b.to_async(&runtime).iter(|| async {
|
||||
let client = Client::builder()
|
||||
.homeserver_url("https://matrix.example.com")
|
||||
.store_config(StoreConfig::new().state_store(store.clone()))
|
||||
.build()
|
||||
.await
|
||||
.expect("Can't build client");
|
||||
client
|
||||
.restore_session(session.clone())
|
||||
.await
|
||||
.expect("couldn't restore session");
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
// Sqlite
|
||||
let sqlite_dir = tempfile::tempdir().unwrap();
|
||||
let sqlite_store = runtime
|
||||
|
|
|
@ -142,8 +142,8 @@ impl OlmMachine {
|
|||
/// * `path` - The path where the state of the machine should be persisted.
|
||||
///
|
||||
/// * `passphrase` - The passphrase that should be used to encrypt the data
|
||||
/// at rest in the Sled store. **Warning**, if no passphrase is given, the
|
||||
/// store and all its data will remain unencrypted.
|
||||
/// at rest in the crypto store. **Warning**, if no passphrase is given,
|
||||
/// the store and all its data will remain unencrypted.
|
||||
#[uniffi::constructor]
|
||||
pub fn new(
|
||||
user_id: String,
|
||||
|
|
|
@ -25,7 +25,6 @@ tracing = ["dep:tracing-subscriber"]
|
|||
|
||||
[dependencies]
|
||||
matrix-sdk-common = { version = "0.6.0", path = "../../crates/matrix-sdk-common", features = ["js"] }
|
||||
matrix-sdk-sled = { version = "0.2.0", path = "../../crates/matrix-sdk-sled", default-features = false, features = ["crypto-store"] }
|
||||
matrix-sdk-sqlite = { version = "0.1.0", path = "../../crates/matrix-sdk-sqlite", features = ["crypto-store"] }
|
||||
ruma = { workspace = true, features = ["rand"] }
|
||||
napi = { version = "2.9.1", default-features = false, features = ["napi6", "tokio_rt"] }
|
||||
|
|
|
@ -59,11 +59,8 @@ impl Deref for OlmMachineInner {
|
|||
#[derive(Default)]
|
||||
#[napi]
|
||||
pub enum StoreType {
|
||||
/// Use `matrix-sdk-sled`.
|
||||
#[default]
|
||||
Sled,
|
||||
|
||||
/// Use `matrix-sdk-sqlite`.
|
||||
#[default]
|
||||
Sqlite,
|
||||
}
|
||||
|
||||
|
@ -123,21 +120,6 @@ impl OlmMachine {
|
|||
inner: OlmMachineInner::Opened(ManuallyDrop::new(match store_path {
|
||||
Some(store_path) => {
|
||||
let machine = match store_type.unwrap_or_default() {
|
||||
StoreType::Sled => {
|
||||
matrix_sdk_crypto::OlmMachine::with_store(
|
||||
user_id,
|
||||
device_id,
|
||||
matrix_sdk_sled::SledCryptoStore::open(
|
||||
store_path,
|
||||
store_passphrase.as_deref(),
|
||||
)
|
||||
.await
|
||||
.map(Arc::new)
|
||||
.map_err(into_err)?,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
StoreType::Sqlite => {
|
||||
matrix_sdk_crypto::OlmMachine::with_store(
|
||||
user_id,
|
||||
|
|
|
@ -25,8 +25,7 @@ const fs = require("fs/promises");
|
|||
|
||||
describe("StoreType", () => {
|
||||
test("has the correct variant values", () => {
|
||||
expect(StoreType.Sled).toStrictEqual(0);
|
||||
expect(StoreType.Sqlite).toStrictEqual(1);
|
||||
expect(StoreType.Sqlite).toStrictEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -53,7 +52,6 @@ describe(OlmMachine.name, () => {
|
|||
|
||||
describe("can be instantiated with a store", () => {
|
||||
for (const [store_type, store_name] of [
|
||||
[StoreType.Sled, "sled"],
|
||||
[StoreType.Sqlite, "sqlite"],
|
||||
[null, "default"],
|
||||
]) {
|
||||
|
|
|
@ -13,7 +13,6 @@ coverage:
|
|||
- "crates/matrix-sdk-common/"
|
||||
- "crates/matrix-sdk-crypto/"
|
||||
- "crates/matrix-sdk-qrcode/"
|
||||
- "crates/matrix-sdk-sled/"
|
||||
- "crates/matrix-sdk-sqlite/"
|
||||
- "crates/matrix-sdk-store-encryption/"
|
||||
# Coverage of wasm tests isn't supported at the moment,
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
[package]
|
||||
name = "matrix-sdk-sled"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
authors = ["Damir Jelić <poljar@termina.org.uk>"]
|
||||
repository = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||
description = "Sled Storage backend for matrix-sdk for native environments"
|
||||
license = "Apache-2.0"
|
||||
rust-version = { workspace = true }
|
||||
readme = "README.md"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
default = ["state-store"]
|
||||
|
||||
state-store = ["dep:matrix-sdk-base"]
|
||||
crypto-store = [
|
||||
"dep:matrix-sdk-base",
|
||||
"dep:matrix-sdk-crypto",
|
||||
"matrix-sdk-base?/e2e-encryption",
|
||||
]
|
||||
|
||||
docsrs = [
|
||||
"crypto-store",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
fs_extra = "1.2.0"
|
||||
futures-core = { workspace = true }
|
||||
futures-util = { workspace = true }
|
||||
matrix-sdk-base = { version = "0.6.0", path = "../matrix-sdk-base", optional = true }
|
||||
matrix-sdk-crypto = { version = "0.6.0", path = "../matrix-sdk-crypto", optional = true }
|
||||
matrix-sdk-store-encryption = { version = "0.2.0", path = "../matrix-sdk-store-encryption" }
|
||||
ruma = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
sled = "0.34.7"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["fs"] }
|
||||
tracing = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = { workspace = true }
|
||||
glob = "0.3.0"
|
||||
matrix-sdk-base = { path = "../matrix-sdk-base", features = ["testing"] }
|
||||
matrix-sdk-crypto = { path = "../matrix-sdk-crypto", features = ["testing"] }
|
||||
matrix-sdk-test = { path = "../../testing/matrix-sdk-test" }
|
||||
once_cell = { workspace = true }
|
||||
tempfile = "3.3.0"
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
|
|
@ -1,25 +0,0 @@
|
|||
# matrix-sdk-sled
|
||||
|
||||
This crate implements a storage backend using [sled][sled] for native and mobile environments using the matrix-sdk-base primitives. When using **matrix-sdk** this is included by default.
|
||||
|
||||
_Note_: the future of [sled][sled] is unclear. While it is currently the default for mobile and native environments for matrix-rust-sdk, [we are actively looking at replacing it with a different storage backend](https://github.com/matrix-org/matrix-rust-sdk/issues/294).
|
||||
|
||||
|
||||
## Crate Feature Flags
|
||||
|
||||
The following crate feature flags are available:
|
||||
|
||||
* `state-store`: (on by default) Enables the state store
|
||||
* `crypto-store`: Enables the store for end-to-end encrypted data.
|
||||
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
These crates are built with the Rust language version 2021 and require a minimum compiler version of `1.62`.
|
||||
|
||||
## License
|
||||
|
||||
[Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
|
||||
[sled]: https://sled.rs/
|
|
@ -1,17 +0,0 @@
|
|||
use std::{env, process};
|
||||
|
||||
fn main() {
|
||||
let target_arch = env::var_os("CARGO_CFG_TARGET_ARCH");
|
||||
if target_arch.map_or(false, |arch| arch == "wasm32") {
|
||||
let err = "this crate does not support the target arch 'wasm32'";
|
||||
eprintln!(
|
||||
"\n\
|
||||
┏━━━━━━━━{pad}━┓\n\
|
||||
┃ error: {err} ┃\n\
|
||||
┗━━━━━━━━{pad}━┛\n\
|
||||
",
|
||||
pad = "━".repeat(err.len()),
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,290 +0,0 @@
|
|||
use std::{borrow::Cow, ops::Deref};
|
||||
|
||||
use matrix_sdk_store_encryption::StoreCipher;
|
||||
use ruma::{
|
||||
events::{
|
||||
receipt::ReceiptType, secret::request::SecretName, GlobalAccountDataEventType,
|
||||
RoomAccountDataEventType, StateEventType,
|
||||
},
|
||||
DeviceId, EventId, MxcUri, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, TransactionId,
|
||||
UserId,
|
||||
};
|
||||
|
||||
/// Hold any data to be used as an encoding key
|
||||
/// without checking for the existence of `ENCODE_SEPARATOR` within
|
||||
pub struct EncodeUnchecked<'a>(&'a [u8]);
|
||||
|
||||
#[cfg(feature = "state-store")]
|
||||
impl<'a> EncodeUnchecked<'a> {
|
||||
/// Wrap any `[u8]`
|
||||
pub fn from(bytes: &'a [u8]) -> Self {
|
||||
EncodeUnchecked(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EncodeKey for EncodeUnchecked<'a> {
|
||||
fn encode_as_bytes(&self) -> Cow<'a, [u8]> {
|
||||
(self.0).into()
|
||||
}
|
||||
}
|
||||
|
||||
pub const ENCODE_SEPARATOR: u8 = 0xff;
|
||||
|
||||
pub trait EncodeKey {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
[self.encode_as_bytes().deref(), &[ENCODE_SEPARATOR]].concat()
|
||||
}
|
||||
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
|
||||
let key = store_cipher.hash_key(table_name, &self.encode_as_bytes());
|
||||
[key.as_slice(), &[ENCODE_SEPARATOR]].concat()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EncodeKey + ?Sized> EncodeKey for &T {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
T::encode_as_bytes(self)
|
||||
}
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
T::encode(self)
|
||||
}
|
||||
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
|
||||
T::encode_secure(self, table_name, store_cipher)
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for str {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for String {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.as_str().as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for DeviceId {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.as_str().as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for EventId {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.as_str().as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for OwnedEventId {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.as_str().as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for RoomId {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.as_str().as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for OwnedRoomId {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.as_str().as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for TransactionId {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.as_str().as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for MxcUri {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
let s: &str = self.as_ref();
|
||||
s.as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for SecretName {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
let s: &str = self.as_ref();
|
||||
s.as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for ReceiptType {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
let s: &str = self.as_ref();
|
||||
s.as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for RoomAccountDataEventType {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.to_string().as_bytes().to_vec().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for UserId {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.as_str().as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for OwnedUserId {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.as_str().as_bytes().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for StateEventType {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.to_string().as_bytes().to_vec().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodeKey for GlobalAccountDataEventType {
|
||||
fn encode_as_bytes(&self) -> Cow<'_, [u8]> {
|
||||
self.to_string().as_bytes().to_vec().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B> EncodeKey for (A, B)
|
||||
where
|
||||
A: EncodeKey,
|
||||
B: EncodeKey,
|
||||
{
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
[
|
||||
self.0.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
self.1.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
|
||||
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
|
||||
[
|
||||
store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, C> EncodeKey for (A, B, C)
|
||||
where
|
||||
A: EncodeKey,
|
||||
B: EncodeKey,
|
||||
C: EncodeKey,
|
||||
{
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
[
|
||||
self.0.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
self.1.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
self.2.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
|
||||
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
|
||||
[
|
||||
store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
store_cipher.hash_key(table_name, &self.2.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, C, D> EncodeKey for (A, B, C, D)
|
||||
where
|
||||
A: EncodeKey,
|
||||
B: EncodeKey,
|
||||
C: EncodeKey,
|
||||
D: EncodeKey,
|
||||
{
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
[
|
||||
self.0.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
self.1.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
self.2.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
self.3.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
|
||||
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
|
||||
[
|
||||
store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
store_cipher.hash_key(table_name, &self.2.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
store_cipher.hash_key(table_name, &self.3.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, B, C, D, E> EncodeKey for (A, B, C, D, E)
|
||||
where
|
||||
A: EncodeKey,
|
||||
B: EncodeKey,
|
||||
C: EncodeKey,
|
||||
D: EncodeKey,
|
||||
E: EncodeKey,
|
||||
{
|
||||
fn encode(&self) -> Vec<u8> {
|
||||
[
|
||||
self.0.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
self.1.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
self.2.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
self.3.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
self.4.encode_as_bytes().deref(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
|
||||
fn encode_secure(&self, table_name: &str, store_cipher: &StoreCipher) -> Vec<u8> {
|
||||
[
|
||||
store_cipher.hash_key(table_name, &self.0.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
store_cipher.hash_key(table_name, &self.1.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
store_cipher.hash_key(table_name, &self.2.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
store_cipher.hash_key(table_name, &self.3.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
store_cipher.hash_key(table_name, &self.4.encode_as_bytes()).as_slice(),
|
||||
&[ENCODE_SEPARATOR],
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
#[cfg(any(feature = "state-store", feature = "crypto-store"))]
|
||||
use matrix_sdk_base::store::StoreConfig;
|
||||
#[cfg(feature = "state-store")]
|
||||
use matrix_sdk_base::store::StoreError;
|
||||
#[cfg(feature = "crypto-store")]
|
||||
use matrix_sdk_crypto::store::CryptoStoreError;
|
||||
use sled::Error as SledError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[cfg(feature = "crypto-store")]
|
||||
mod crypto_store;
|
||||
mod encode_key;
|
||||
#[cfg(feature = "state-store")]
|
||||
mod state_store;
|
||||
|
||||
#[cfg(feature = "crypto-store")]
|
||||
pub use crypto_store::SledCryptoStore;
|
||||
#[cfg(feature = "state-store")]
|
||||
pub use state_store::{MigrationConflictStrategy, SledStateStore, SledStateStoreBuilder};
|
||||
|
||||
/// All the errors that can occur when opening a sled store.
|
||||
#[derive(Error, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum OpenStoreError {
|
||||
/// An error occurred with the state store implementation.
|
||||
#[cfg(feature = "state-store")]
|
||||
#[error(transparent)]
|
||||
State(#[from] StoreError),
|
||||
|
||||
/// An error occurred with the crypto store implementation.
|
||||
#[cfg(feature = "crypto-store")]
|
||||
#[error(transparent)]
|
||||
Crypto(#[from] CryptoStoreError),
|
||||
|
||||
/// An error occurred with sled.
|
||||
#[error(transparent)]
|
||||
Sled(#[from] SledError),
|
||||
}
|
||||
|
||||
/// Create a [`StoreConfig`] with an opened [`SledStateStore`] that uses the
|
||||
/// given path and passphrase.
|
||||
///
|
||||
/// If the `e2e-encryption` Cargo feature is enabled, a [`SledCryptoStore`] with
|
||||
/// the same parameters is also opened.
|
||||
///
|
||||
/// [`StoreConfig`]: #StoreConfig
|
||||
#[cfg(any(feature = "state-store", feature = "crypto-store"))]
|
||||
pub async fn make_store_config(
|
||||
path: impl AsRef<std::path::Path>,
|
||||
passphrase: Option<&str>,
|
||||
) -> Result<StoreConfig, OpenStoreError> {
|
||||
#[cfg(all(feature = "crypto-store", feature = "state-store"))]
|
||||
{
|
||||
let (state_store, crypto_store) = open_stores_with_path(path, passphrase).await?;
|
||||
Ok(StoreConfig::new().state_store(state_store).crypto_store(crypto_store))
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "crypto-store", not(feature = "state-store")))]
|
||||
{
|
||||
let crypto_store = SledCryptoStore::open(path, passphrase).await?;
|
||||
Ok(StoreConfig::new().crypto_store(crypto_store))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "crypto-store"))]
|
||||
{
|
||||
let mut store_builder = SledStateStore::builder().path(path.as_ref().to_path_buf());
|
||||
|
||||
if let Some(passphrase) = passphrase {
|
||||
store_builder = store_builder.passphrase(passphrase.to_owned());
|
||||
}
|
||||
let state_store = store_builder.build().map_err(StoreError::backend)?;
|
||||
|
||||
Ok(StoreConfig::new().state_store(state_store))
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a [`StateStore`] and a [`CryptoStore`] that use the same database and
|
||||
/// passphrase.
|
||||
#[cfg(all(feature = "state-store", feature = "crypto-store"))]
|
||||
async fn open_stores_with_path(
|
||||
path: impl AsRef<std::path::Path>,
|
||||
passphrase: Option<&str>,
|
||||
) -> Result<(SledStateStore, SledCryptoStore), OpenStoreError> {
|
||||
let mut store_builder = SledStateStore::builder().path(path.as_ref().to_path_buf());
|
||||
if let Some(passphrase) = passphrase {
|
||||
store_builder = store_builder.passphrase(passphrase.to_owned());
|
||||
}
|
||||
|
||||
let state_store = store_builder.build().map_err(StoreError::backend)?;
|
||||
let crypto_store = state_store.open_crypto_store().await?;
|
||||
Ok((state_store, crypto_store))
|
||||
}
|
|
@ -1,825 +0,0 @@
|
|||
// Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use matrix_sdk_base::{
|
||||
store::{Result as StoreResult, StoreError},
|
||||
RoomInfo, StateStoreDataKey,
|
||||
};
|
||||
use ruma::{
|
||||
events::{
|
||||
room::member::{StrippedRoomMemberEvent, SyncRoomMemberEvent},
|
||||
StateEventType,
|
||||
},
|
||||
serde::Raw,
|
||||
};
|
||||
use serde_json::value::{RawValue as RawJsonValue, Value as JsonValue};
|
||||
use sled::{transaction::TransactionError, Batch, Transactional, Tree};
|
||||
use tracing::debug;
|
||||
|
||||
use super::{keys, Result, RoomMember, SledStateStore, SledStoreError};
|
||||
use crate::encode_key::EncodeKey;
|
||||
|
||||
const DATABASE_VERSION: u8 = 7;
|
||||
|
||||
const VERSION_KEY: &str = "state-store-version";
|
||||
|
||||
/// Sometimes Migrations can't proceed without having to drop existing
|
||||
/// data. This allows you to configure, how these cases should be handled.
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub enum MigrationConflictStrategy {
|
||||
/// Just drop the data, we don't care that we have to sync again
|
||||
Drop,
|
||||
/// Raise a `SledStoreError::MigrationConflict` error with the path to the
|
||||
/// DB in question. The caller then has to take care about what they want
|
||||
/// to do and try again after.
|
||||
Raise,
|
||||
/// _Default_: The _entire_ database is backed up under
|
||||
/// `$path.$timestamp.backup` (this includes the crypto store if they
|
||||
/// are linked), before the state tables are dropped.
|
||||
BackupAndDrop,
|
||||
}
|
||||
|
||||
impl SledStateStore {
|
||||
pub(super) fn upgrade(&mut self) -> Result<()> {
|
||||
let old_version = self.db_version()?;
|
||||
|
||||
if old_version == 0 {
|
||||
// we are fresh, let's write the current version
|
||||
return self.set_db_version(DATABASE_VERSION);
|
||||
}
|
||||
if old_version == DATABASE_VERSION {
|
||||
// current, we don't have to do anything
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
debug!(old_version, new_version = DATABASE_VERSION, "Upgrading the Sled state store");
|
||||
|
||||
if old_version == 1 && self.store_cipher.is_some() {
|
||||
// we stored some fields un-encrypted. Drop them to force re-creation
|
||||
return Err(SledStoreError::MigrationConflict {
|
||||
path: self.path.take().expect("Path must exist for a migration to fail"),
|
||||
old_version: old_version.into(),
|
||||
new_version: DATABASE_VERSION.into(),
|
||||
});
|
||||
}
|
||||
|
||||
if old_version < 3 {
|
||||
self.migrate_to_v3()?;
|
||||
}
|
||||
|
||||
if old_version < 4 {
|
||||
self.migrate_to_v4()?;
|
||||
}
|
||||
|
||||
if old_version < 5 {
|
||||
self.migrate_to_v5()?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Version 6 was dropped and migration is similar to v7.
|
||||
|
||||
if old_version < 7 {
|
||||
self.migrate_to_v7()?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// FUTURE UPGRADE CODE GOES HERE
|
||||
|
||||
// can't upgrade from that version to the new one
|
||||
Err(SledStoreError::MigrationConflict {
|
||||
path: self.path.take().expect("Path must exist for a migration to fail"),
|
||||
old_version: old_version.into(),
|
||||
new_version: DATABASE_VERSION.into(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the version of the database.
|
||||
///
|
||||
/// Returns `0` for a new database.
|
||||
fn db_version(&self) -> Result<u8> {
|
||||
Ok(self
|
||||
.inner
|
||||
.get(VERSION_KEY)?
|
||||
.map(|v| {
|
||||
let (version_bytes, _) = v.split_at(std::mem::size_of::<u8>());
|
||||
u8::from_be_bytes(version_bytes.try_into().unwrap_or_default())
|
||||
})
|
||||
.unwrap_or_default())
|
||||
}
|
||||
|
||||
fn set_db_version(&self, version: u8) -> Result<()> {
|
||||
self.inner.insert(VERSION_KEY, version.to_be_bytes().as_ref())?;
|
||||
self.inner.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn drop_v1_tables(self) -> StoreResult<()> {
|
||||
for name in V1_DB_STORES {
|
||||
self.inner.drop_tree(name).map_err(StoreError::backend)?;
|
||||
}
|
||||
self.inner.remove(VERSION_KEY).map_err(StoreError::backend)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn v3_fix_tree(&self, tree: &Tree, batch: &mut Batch) -> Result<()> {
|
||||
fn maybe_fix_json(raw_json: &RawJsonValue) -> Result<Option<JsonValue>> {
|
||||
let json = raw_json.get();
|
||||
|
||||
if json.contains(r#""content":null"#) {
|
||||
let mut value: JsonValue = serde_json::from_str(json)?;
|
||||
if let Some(content) = value.get_mut("content") {
|
||||
if matches!(content, JsonValue::Null) {
|
||||
*content = JsonValue::Object(Default::default());
|
||||
return Ok(Some(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
for entry in tree.iter() {
|
||||
let (key, value) = entry?;
|
||||
let raw_json: Box<RawJsonValue> = self.deserialize_value(&value)?;
|
||||
|
||||
if let Some(fixed_json) = maybe_fix_json(&raw_json)? {
|
||||
batch.insert(key, self.serialize_value(&fixed_json)?);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn migrate_to_v3(&self) -> Result<()> {
|
||||
let mut room_info_batch = sled::Batch::default();
|
||||
self.v3_fix_tree(&self.room_info, &mut room_info_batch)?;
|
||||
|
||||
let mut room_state_batch = sled::Batch::default();
|
||||
self.v3_fix_tree(&self.room_state, &mut room_state_batch)?;
|
||||
|
||||
let ret: Result<(), TransactionError<SledStoreError>> = (&self.room_info, &self.room_state)
|
||||
.transaction(|(room_info, room_state)| {
|
||||
room_info.apply_batch(&room_info_batch)?;
|
||||
room_state.apply_batch(&room_state_batch)?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
ret?;
|
||||
|
||||
self.set_db_version(3u8)
|
||||
}
|
||||
|
||||
/// Replace the SYNC_TOKEN and SESSION trees by KV.
|
||||
fn migrate_to_v4(&self) -> Result<()> {
|
||||
{
|
||||
let session = &self.inner.open_tree(old_keys::SESSION)?;
|
||||
let mut batch = sled::Batch::default();
|
||||
|
||||
// Sync token
|
||||
let sync_token = session.get(StateStoreDataKey::SYNC_TOKEN.encode())?;
|
||||
if let Some(sync_token) = sync_token {
|
||||
batch.insert(StateStoreDataKey::SYNC_TOKEN.encode(), sync_token);
|
||||
}
|
||||
|
||||
// Filters
|
||||
let key = self.encode_key(keys::SESSION, StateStoreDataKey::FILTER);
|
||||
for res in session.scan_prefix(key) {
|
||||
let (key, value) = res?;
|
||||
batch.insert(key, value);
|
||||
}
|
||||
self.kv.apply_batch(batch)?;
|
||||
}
|
||||
|
||||
// This was unused so we can just drop it.
|
||||
self.inner.drop_tree(old_keys::SYNC_TOKEN)?;
|
||||
self.inner.drop_tree(old_keys::SESSION)?;
|
||||
|
||||
self.set_db_version(4)
|
||||
}
|
||||
|
||||
/// Move the member events with the other state events.
|
||||
fn migrate_to_v5(&self) -> Result<()> {
|
||||
{
|
||||
let members = &self.inner.open_tree(old_keys::MEMBER)?;
|
||||
let mut state_batch = sled::Batch::default();
|
||||
|
||||
for room_info in
|
||||
self.room_info.iter().map(|r| self.deserialize_value::<RoomInfo>(&r?.1))
|
||||
{
|
||||
let room_info = room_info?;
|
||||
let room_id = room_info.room_id();
|
||||
let prefix = self.encode_key(old_keys::MEMBER, room_id);
|
||||
|
||||
for entry in members.scan_prefix(prefix) {
|
||||
let (_, value) = entry?;
|
||||
let raw_member_event =
|
||||
self.deserialize_value::<Raw<SyncRoomMemberEvent>>(&value)?;
|
||||
let state_key =
|
||||
raw_member_event.get_field::<String>("state_key")?.unwrap_or_default();
|
||||
let key = self.encode_key(
|
||||
keys::ROOM_STATE,
|
||||
(room_id, StateEventType::RoomMember, state_key),
|
||||
);
|
||||
state_batch.insert(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
let stripped_members = &self.inner.open_tree(old_keys::STRIPPED_ROOM_MEMBER)?;
|
||||
let mut stripped_state_batch = sled::Batch::default();
|
||||
|
||||
for room_info in
|
||||
self.stripped_room_infos.iter().map(|r| self.deserialize_value::<RoomInfo>(&r?.1))
|
||||
{
|
||||
let room_info = room_info?;
|
||||
let room_id = room_info.room_id();
|
||||
let prefix = self.encode_key(old_keys::STRIPPED_ROOM_MEMBER, room_id);
|
||||
|
||||
for entry in stripped_members.scan_prefix(prefix) {
|
||||
let (_, value) = entry?;
|
||||
let raw_member_event =
|
||||
self.deserialize_value::<Raw<StrippedRoomMemberEvent>>(&value)?;
|
||||
let state_key =
|
||||
raw_member_event.get_field::<String>("state_key")?.unwrap_or_default();
|
||||
let key = self.encode_key(
|
||||
keys::STRIPPED_ROOM_STATE,
|
||||
(room_id, StateEventType::RoomMember, state_key),
|
||||
);
|
||||
stripped_state_batch.insert(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
let ret: Result<(), TransactionError<SledStoreError>> =
|
||||
(&self.room_state, &self.stripped_room_state).transaction(
|
||||
|(room_state, stripped_room_state)| {
|
||||
room_state.apply_batch(&state_batch)?;
|
||||
stripped_room_state.apply_batch(&stripped_state_batch)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
ret?;
|
||||
}
|
||||
|
||||
self.inner.drop_tree(old_keys::MEMBER)?;
|
||||
self.inner.drop_tree(old_keys::STRIPPED_ROOM_MEMBER)?;
|
||||
|
||||
self.set_db_version(5)
|
||||
}
|
||||
|
||||
/// Remove the old user IDs stores and populate the new ones.
|
||||
fn migrate_to_v7(&self) -> Result<()> {
|
||||
{
|
||||
// Reset v6 stores.
|
||||
self.user_ids.clear()?;
|
||||
self.stripped_user_ids.clear()?;
|
||||
|
||||
// We only have joined and invited user IDs in the old stores, so instead we
|
||||
// use the room member events to populate the new stores.
|
||||
let state = &self.inner.open_tree(keys::ROOM_STATE)?;
|
||||
let mut user_ids_batch = sled::Batch::default();
|
||||
|
||||
for room_info in
|
||||
self.room_info.iter().map(|r| self.deserialize_value::<RoomInfo>(&r?.1))
|
||||
{
|
||||
let room_info = room_info?;
|
||||
let room_id = room_info.room_id();
|
||||
let prefix =
|
||||
self.encode_key(keys::ROOM_STATE, (room_id, StateEventType::RoomMember));
|
||||
|
||||
for entry in state.scan_prefix(prefix) {
|
||||
let (_, value) = entry?;
|
||||
let member_event = self
|
||||
.deserialize_value::<Raw<SyncRoomMemberEvent>>(&value)?
|
||||
.deserialize()?;
|
||||
let key = self.encode_key(keys::USER_ID, (room_id, member_event.state_key()));
|
||||
let value = self.serialize_value(&RoomMember::from(&member_event))?;
|
||||
user_ids_batch.insert(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
let stripped_state = &self.inner.open_tree(keys::STRIPPED_ROOM_STATE)?;
|
||||
let mut stripped_user_ids_batch = sled::Batch::default();
|
||||
|
||||
for room_info in
|
||||
self.stripped_room_infos.iter().map(|r| self.deserialize_value::<RoomInfo>(&r?.1))
|
||||
{
|
||||
let room_info = room_info?;
|
||||
let room_id = room_info.room_id();
|
||||
let prefix = self
|
||||
.encode_key(keys::STRIPPED_ROOM_STATE, (room_id, StateEventType::RoomMember));
|
||||
|
||||
for entry in stripped_state.scan_prefix(prefix) {
|
||||
let (_, value) = entry?;
|
||||
let stripped_member_event = self
|
||||
.deserialize_value::<Raw<StrippedRoomMemberEvent>>(&value)?
|
||||
.deserialize()?;
|
||||
let key = self.encode_key(
|
||||
keys::STRIPPED_USER_ID,
|
||||
(room_id, &stripped_member_event.state_key),
|
||||
);
|
||||
let value = self.serialize_value(&RoomMember::from(&stripped_member_event))?;
|
||||
stripped_user_ids_batch.insert(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
let ret: Result<(), TransactionError<SledStoreError>> =
|
||||
(&self.user_ids, &self.stripped_user_ids).transaction(
|
||||
|(user_ids, stripped_user_ids)| {
|
||||
user_ids.apply_batch(&user_ids_batch)?;
|
||||
stripped_user_ids.apply_batch(&stripped_user_ids_batch)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
);
|
||||
ret?;
|
||||
}
|
||||
|
||||
self.inner.drop_tree(old_keys::JOINED_USER_ID)?;
|
||||
self.inner.drop_tree(old_keys::INVITED_USER_ID)?;
|
||||
self.inner.drop_tree(old_keys::STRIPPED_JOINED_USER_ID)?;
|
||||
self.inner.drop_tree(old_keys::STRIPPED_INVITED_USER_ID)?;
|
||||
|
||||
self.set_db_version(7)
|
||||
}
|
||||
}
|
||||
|
||||
mod old_keys {
|
||||
/// Old stores.
|
||||
pub const SYNC_TOKEN: &str = "sync_token";
|
||||
pub const SESSION: &str = "session";
|
||||
pub const MEMBER: &str = "member";
|
||||
pub const STRIPPED_ROOM_MEMBER: &str = "stripped-room-member";
|
||||
pub const INVITED_USER_ID: &str = "invited-user-id";
|
||||
pub const JOINED_USER_ID: &str = "joined-user-id";
|
||||
pub const STRIPPED_INVITED_USER_ID: &str = "stripped-invited-user-id";
|
||||
pub const STRIPPED_JOINED_USER_ID: &str = "stripped-joined-user-id";
|
||||
}
|
||||
|
||||
pub const V1_DB_STORES: &[&str] = &[
|
||||
keys::ACCOUNT_DATA,
|
||||
old_keys::SYNC_TOKEN,
|
||||
keys::DISPLAY_NAME,
|
||||
old_keys::INVITED_USER_ID,
|
||||
old_keys::JOINED_USER_ID,
|
||||
keys::MEDIA,
|
||||
old_keys::MEMBER,
|
||||
keys::PRESENCE,
|
||||
keys::PROFILE,
|
||||
keys::ROOM_ACCOUNT_DATA,
|
||||
keys::ROOM_EVENT_RECEIPT,
|
||||
keys::ROOM_INFO,
|
||||
keys::ROOM_STATE,
|
||||
keys::ROOM_USER_RECEIPT,
|
||||
keys::ROOM,
|
||||
old_keys::SESSION,
|
||||
old_keys::STRIPPED_INVITED_USER_ID,
|
||||
old_keys::STRIPPED_JOINED_USER_ID,
|
||||
keys::STRIPPED_ROOM_INFO,
|
||||
old_keys::STRIPPED_ROOM_MEMBER,
|
||||
keys::STRIPPED_ROOM_STATE,
|
||||
keys::CUSTOM,
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use assert_matches::assert_matches;
|
||||
use matrix_sdk_base::{
|
||||
deserialized_responses::RawMemberEvent, RoomInfo, RoomMemberships, RoomState,
|
||||
StateStoreDataKey,
|
||||
};
|
||||
use matrix_sdk_test::{async_test, test_json};
|
||||
use ruma::{
|
||||
events::{
|
||||
room::{
|
||||
member::{StrippedRoomMemberEvent, SyncRoomMemberEvent},
|
||||
topic::RoomTopicEventContent,
|
||||
},
|
||||
AnySyncStateEvent, StateEventType,
|
||||
},
|
||||
room_id,
|
||||
serde::Raw,
|
||||
user_id,
|
||||
};
|
||||
use serde_json::json;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use super::{old_keys, MigrationConflictStrategy};
|
||||