sui_castore/storage/
mod.rs1pub mod index;
20pub mod local;
21pub mod pg;
22pub mod redis;
23pub mod s3;
24pub mod tiered;
25
26use std::sync::Arc;
27
28pub use index::StorageIndex;
29pub use local::LocalStorage;
30pub use pg::{PgCacheConn, PgStorageBackend, PgTable};
31pub use redis::{RedisBackend, RedisConn};
32pub use s3::S3Storage;
33pub use tiered::{TieredBackend, TieredTier, WritePolicy, TIERED_BACKEND_TIER};
34
35#[cfg(feature = "redis-client")]
36pub use redis::RedisConnectionManager;
37
38#[cfg(feature = "postgres")]
39pub use pg::SqlxPgCacheConn;
40
41use async_trait::async_trait;
42use futures::future::BoxFuture;
43
44use crate::config::BackendConfig;
45use crate::StoreError;
46
47#[async_trait]
52pub trait StorageBackend: Send + Sync {
53 async fn get_narinfo(&self, hash: &str) -> Result<Option<String>, StoreError>;
55
56 async fn put_narinfo(&self, hash: &str, content: &str) -> Result<(), StoreError>;
58
59 async fn get_nar(&self, path: &str) -> Result<Option<Vec<u8>>, StoreError>;
61
62 async fn put_nar(&self, path: &str, data: &[u8]) -> Result<(), StoreError>;
64
65 async fn delete(&self, hash: &str) -> Result<(), StoreError>;
67
68 async fn list_narinfos(&self) -> Result<Vec<String>, StoreError>;
70
71 async fn wipe_all(&self) -> Result<usize, StoreError> {
78 let hashes = self.list_narinfos().await?;
79 let n = hashes.len();
80 for hash in hashes {
81 self.delete(&hash).await?;
82 }
83 Ok(n)
84 }
85}
86
87pub fn build_backend(
106 config: &BackendConfig,
107) -> BoxFuture<'_, Result<Arc<dyn StorageBackend>, StoreError>> {
108 Box::pin(async move {
109 match config {
110 BackendConfig::Local { path } => {
111 Ok(Arc::new(LocalStorage::new(path.clone())) as Arc<dyn StorageBackend>)
112 }
113 BackendConfig::S3 { bucket, region, endpoint } => {
114 let s3 = S3Storage::new(bucket.clone(), region.clone(), endpoint.clone())?;
115 Ok(Arc::new(s3) as Arc<dyn StorageBackend>)
116 }
117 BackendConfig::Redis { url, ttl_secs } => build_redis(url, *ttl_secs).await,
118 BackendConfig::Pg { url, max_conns } => build_pg(url, *max_conns).await,
119 BackendConfig::Tiered { l1, l2, l3, write_policy } => {
120 let l1 = build_backend(l1).await?;
121 let l2 = build_backend(l2).await?;
122 let l3 = build_backend(l3).await?;
123 Ok(Arc::new(TieredBackend::with_write_policy(l1, l2, l3, *write_policy))
124 as Arc<dyn StorageBackend>)
125 }
126 }
127 })
128}
129
130#[cfg(feature = "redis-client")]
131async fn build_redis(
132 url: &str,
133 ttl_secs: Option<u64>,
134) -> Result<Arc<dyn StorageBackend>, StoreError> {
135 let backend = match ttl_secs {
136 Some(t) => RedisBackend::connect_with_ttl(url, t).await?,
137 None => RedisBackend::connect(url).await?,
138 };
139 Ok(Arc::new(backend) as Arc<dyn StorageBackend>)
140}
141
142#[cfg(not(feature = "redis-client"))]
143async fn build_redis(
144 _url: &str,
145 _ttl_secs: Option<u64>,
146) -> Result<Arc<dyn StorageBackend>, StoreError> {
147 Err(StoreError::NotImplemented(
148 "redis L1 backend requires building sui-castore with --features redis-client",
149 ))
150}
151
152#[cfg(feature = "postgres")]
153async fn build_pg(url: &str, max_conns: u32) -> Result<Arc<dyn StorageBackend>, StoreError> {
154 let backend = PgStorageBackend::connect(url, max_conns).await?;
155 Ok(Arc::new(backend) as Arc<dyn StorageBackend>)
156}
157
158#[cfg(not(feature = "postgres"))]
159async fn build_pg(_url: &str, _max_conns: u32) -> Result<Arc<dyn StorageBackend>, StoreError> {
160 Err(StoreError::NotImplemented(
161 "postgres L2 backend requires building sui-castore with --features postgres",
162 ))
163}