pub mod index;
pub mod local;
pub mod pg;
pub mod redis;
pub mod s3;
pub mod tiered;
use std::sync::Arc;
pub use index::StorageIndex;
pub use local::LocalStorage;
pub use pg::{PgCacheConn, PgStorageBackend, PgTable};
pub use redis::{RedisBackend, RedisConn};
pub use s3::S3Storage;
pub use tiered::{TieredBackend, TieredTier, WritePolicy, TIERED_BACKEND_TIER};
#[cfg(feature = "redis-client")]
pub use redis::RedisConnectionManager;
#[cfg(feature = "postgres")]
pub use pg::SqlxPgCacheConn;
use async_trait::async_trait;
use futures::future::BoxFuture;
use crate::config::BackendConfig;
use crate::CacheError;
#[async_trait]
pub trait StorageBackend: Send + Sync {
async fn get_narinfo(&self, hash: &str) -> Result<Option<String>, CacheError>;
async fn put_narinfo(&self, hash: &str, content: &str) -> Result<(), CacheError>;
async fn get_nar(&self, path: &str) -> Result<Option<Vec<u8>>, CacheError>;
async fn put_nar(&self, path: &str, data: &[u8]) -> Result<(), CacheError>;
async fn delete(&self, hash: &str) -> Result<(), CacheError>;
async fn list_narinfos(&self) -> Result<Vec<String>, CacheError>;
async fn wipe_all(&self) -> Result<usize, CacheError> {
let hashes = self.list_narinfos().await?;
let n = hashes.len();
for hash in hashes {
self.delete(&hash).await?;
}
Ok(n)
}
}
pub fn build_backend(config: &BackendConfig) -> BoxFuture<'_, Result<Arc<dyn StorageBackend>, CacheError>> {
Box::pin(async move {
match config {
BackendConfig::Local { path } => {
Ok(Arc::new(LocalStorage::new(path.clone())) as Arc<dyn StorageBackend>)
}
BackendConfig::S3 { bucket, region, endpoint } => {
let s3 = S3Storage::new(bucket.clone(), region.clone(), endpoint.clone())?;
Ok(Arc::new(s3) as Arc<dyn StorageBackend>)
}
BackendConfig::Redis { url, ttl_secs } => build_redis(url, *ttl_secs).await,
BackendConfig::Pg { url, max_conns } => build_pg(url, *max_conns).await,
BackendConfig::Tiered { l1, l2, l3, write_policy } => {
let l1 = build_backend(l1).await?;
let l2 = build_backend(l2).await?;
let l3 = build_backend(l3).await?;
Ok(Arc::new(TieredBackend::with_write_policy(l1, l2, l3, *write_policy))
as Arc<dyn StorageBackend>)
}
}
})
}
#[cfg(feature = "redis-client")]
async fn build_redis(url: &str, ttl_secs: Option<u64>) -> Result<Arc<dyn StorageBackend>, CacheError> {
let backend = match ttl_secs {
Some(t) => RedisBackend::connect_with_ttl(url, t).await?,
None => RedisBackend::connect(url).await?,
};
Ok(Arc::new(backend) as Arc<dyn StorageBackend>)
}
#[cfg(not(feature = "redis-client"))]
async fn build_redis(_url: &str, _ttl_secs: Option<u64>) -> Result<Arc<dyn StorageBackend>, CacheError> {
Err(CacheError::NotImplemented(
"redis L1 backend requires building sui-cache with --features redis-client",
))
}
#[cfg(feature = "postgres")]
async fn build_pg(url: &str, max_conns: u32) -> Result<Arc<dyn StorageBackend>, CacheError> {
let backend = PgStorageBackend::connect(url, max_conns).await?;
Ok(Arc::new(backend) as Arc<dyn StorageBackend>)
}
#[cfg(not(feature = "postgres"))]
async fn build_pg(_url: &str, _max_conns: u32) -> Result<Arc<dyn StorageBackend>, CacheError> {
Err(CacheError::NotImplemented(
"postgres L2 backend requires building sui-cache with --features postgres",
))
}