Files
Stalwart/crates/store/src/backend/composite/distributed_blob.rs

138 lines
5.5 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
*
* SPDX-License-Identifier: LicenseRef-SEL
*
* This file is subject to the Stalwart Enterprise License Agreement (SEL) and
* is NOT open source software.
*
*/
use std::ops::Range;
use utils::config::{utils::AsKey, Config};
use crate::{BlobBackend, Store, Stores};
pub struct CompositeBlob {
pub stores: Vec<BlobBackend>,
}
impl CompositeBlob {
pub fn open(config: &mut Config, prefix: impl AsKey, stores: &Stores) -> Option<Self> {
let prefix = prefix.as_key();
let store_ids = config
.values((&prefix, "stores"))
.map(|(_, v)| v.to_string())
.collect::<Vec<_>>();
let mut blob_stores = Vec::with_capacity(store_ids.len());
for store_id in store_ids {
if let Some(store) = stores.blob_stores.get(&store_id) {
blob_stores.push(store.backend.clone());
} else {
config.new_build_error(
(&prefix, "stores"),
format!("Blob store {store_id} not found"),
);
return None;
}
}
if !blob_stores.is_empty() {
Some(Self {
stores: blob_stores,
})
} else {
config.new_build_error((&prefix, "stores"), "No blob stores specified");
None
}
}
#[inline(always)]
fn get_store(&self, key: &[u8]) -> &BlobBackend {
&self.stores[key.first().copied().unwrap_or_default() as usize % self.stores.len()]
}
pub async fn get_blob(
&self,
key: &[u8],
read_range: Range<usize>,
) -> trc::Result<Option<Vec<u8>>> {
Box::pin(async move {
match self.get_store(key) {
BlobBackend::Store(store) => match store {
#[cfg(feature = "sqlite")]
Store::SQLite(store) => store.get_blob(key, read_range).await,
#[cfg(feature = "foundation")]
Store::FoundationDb(store) => store.get_blob(key, read_range).await,
#[cfg(feature = "postgres")]
Store::PostgreSQL(store) => store.get_blob(key, read_range).await,
#[cfg(feature = "mysql")]
Store::MySQL(store) => store.get_blob(key, read_range).await,
#[cfg(feature = "rocks")]
Store::RocksDb(store) => store.get_blob(key, read_range).await,
Store::SQLReadReplica(store) => store.get_blob(key, read_range).await,
Store::None => Err(trc::StoreEvent::NotConfigured.into()),
},
BlobBackend::Fs(store) => store.get_blob(key, read_range).await,
#[cfg(feature = "s3")]
BlobBackend::S3(store) => store.get_blob(key, read_range).await,
BlobBackend::Composite(_) => unimplemented!(),
}
})
.await
}
pub async fn put_blob(&self, key: &[u8], data: &[u8]) -> trc::Result<()> {
Box::pin(async move {
match self.get_store(key) {
BlobBackend::Store(store) => match store {
#[cfg(feature = "sqlite")]
Store::SQLite(store) => store.put_blob(key, data).await,
#[cfg(feature = "foundation")]
Store::FoundationDb(store) => store.put_blob(key, data).await,
#[cfg(feature = "postgres")]
Store::PostgreSQL(store) => store.put_blob(key, data).await,
#[cfg(feature = "mysql")]
Store::MySQL(store) => store.put_blob(key, data).await,
#[cfg(feature = "rocks")]
Store::RocksDb(store) => store.put_blob(key, data).await,
Store::SQLReadReplica(store) => store.put_blob(key, data).await,
Store::None => Err(trc::StoreEvent::NotConfigured.into()),
},
BlobBackend::Fs(store) => store.put_blob(key, data).await,
#[cfg(feature = "s3")]
BlobBackend::S3(store) => store.put_blob(key, data).await,
BlobBackend::Composite(_) => unimplemented!(),
}
})
.await
}
pub async fn delete_blob(&self, key: &[u8]) -> trc::Result<bool> {
Box::pin(async move {
match self.get_store(key) {
BlobBackend::Store(store) => match store {
#[cfg(feature = "sqlite")]
Store::SQLite(store) => store.delete_blob(key).await,
#[cfg(feature = "foundation")]
Store::FoundationDb(store) => store.delete_blob(key).await,
#[cfg(feature = "postgres")]
Store::PostgreSQL(store) => store.delete_blob(key).await,
#[cfg(feature = "mysql")]
Store::MySQL(store) => store.delete_blob(key).await,
#[cfg(feature = "rocks")]
Store::RocksDb(store) => store.delete_blob(key).await,
Store::SQLReadReplica(store) => store.delete_blob(key).await,
Store::None => Err(trc::StoreEvent::NotConfigured.into()),
},
BlobBackend::Fs(store) => store.delete_blob(key).await,
#[cfg(feature = "s3")]
BlobBackend::S3(store) => store.delete_blob(key).await,
BlobBackend::Composite(_) => unimplemented!(),
}
})
.await
}
}