Skip to main content

snix_store/
utils.rs

1use std::{collections::HashMap, sync::Arc};
2
3use snix_castore::blob_engine::{self, BlobEngine};
4use snix_castore::blobstore::BlobStore;
5use snix_castore::chunkstore::ChunkStore;
6use snix_castore::directoryservice::DirectoryService;
7use snix_castore::utils as castore_utils;
8use url::Url;
9
10use crate::composition::REG;
11use crate::nar::NarCalculationService;
12use crate::pathinfoservice::PathInfoService;
13use snix_castore::composition::{
14    Composition, DeserializeWithRegistry, ServiceBuilder, with_registry,
15};
16
17#[derive(serde::Deserialize, Default)]
18pub struct CompositionConfigs {
19    pub blob_stores:
20        HashMap<String, DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn BlobStore>>>>,
21    pub chunk_stores:
22        HashMap<String, DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn ChunkStore>>>>,
23    pub directoryservices: HashMap<
24        String,
25        DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn DirectoryService>>>,
26    >,
27    pub pathinfoservices: HashMap<
28        String,
29        DeserializeWithRegistry<Box<dyn ServiceBuilder<Output = dyn PathInfoService>>>,
30    >,
31}
32
33/// Provides a set of clap arguments to configure snix-\[ca\]store services.
34///
35/// This particular variant has defaults tailored for usecases accessing data
36/// directly locally, like the `snix store daemon` command.
37#[derive(clap::Parser, Clone)]
38#[group(id = "StoreServiceUrls")]
39pub struct ServiceUrls {
40    #[clap(flatten)]
41    pub castore_service_addrs: castore_utils::ServiceUrls,
42
43    #[arg(long, env, default_value = "redb:/var/lib/snix-store/pathinfo.redb")]
44    pub path_info_service_addr: Url,
45}
46
47/// Provides a set of clap arguments to configure snix-\[ca\]store services.
48///
49/// This particular variant has defaults tailored for usecases accessing data
50/// from another running snix daemon, via gRPC.
51#[derive(clap::Parser, Clone)]
52#[group(id = "StoreServiceUrlsGrpc")]
53pub struct ServiceUrlsGrpc {
54    #[clap(flatten)]
55    castore_service_addrs: castore_utils::ServiceUrlsGrpc,
56
57    #[arg(long, env, default_value = "grpc+http://[::1]:8000")]
58    path_info_service_addr: Url,
59}
60
61/// Provides a set of clap arguments to configure snix-\[ca\]store services.
62///
63/// This particular variant has defaults tailored for usecases keeping all data
64/// in memory.
65/// It's currently used in snix-cli-eval, as we don't really care about persistency
66/// there yet, and using something else here might make some perf output harder
67/// to interpret.
68#[derive(clap::Parser, Clone)]
69#[group(id = "StoreServiceUrlsMemory")]
70pub struct ServiceUrlsMemory {
71    #[clap(flatten)]
72    castore_service_addrs: castore_utils::ServiceUrlsMemory,
73
74    #[arg(long, env, default_value = "redb+memory:")]
75    path_info_service_addr: Url,
76}
77
78impl From<ServiceUrlsGrpc> for ServiceUrls {
79    fn from(urls: ServiceUrlsGrpc) -> ServiceUrls {
80        ServiceUrls {
81            castore_service_addrs: urls.castore_service_addrs.into(),
82            path_info_service_addr: urls.path_info_service_addr,
83        }
84    }
85}
86
87impl From<ServiceUrlsMemory> for ServiceUrls {
88    fn from(urls: ServiceUrlsMemory) -> ServiceUrls {
89        ServiceUrls {
90            castore_service_addrs: urls.castore_service_addrs.into(),
91            path_info_service_addr: urls.path_info_service_addr,
92        }
93    }
94}
95
96/// Deserializes service addresses into composition config, configuring each
97/// service as the single "root".
98/// If the `xp-composition-cli` feature is enabled, and a file specified in the
99/// `--experimental-store-composition` parameter, this is used instead.
100pub async fn addrs_to_configs(
101    urls: impl Into<ServiceUrls>,
102) -> Result<CompositionConfigs, Box<dyn std::error::Error + Send + Sync>> {
103    let ServiceUrls {
104        castore_service_addrs,
105        path_info_service_addr,
106    } = urls.into();
107    let castore_utils::ServiceUrls {
108        blob_store_addr,
109        chunk_store_addr,
110        directory_service_addr,
111        #[cfg(feature = "xp-composition-cli")]
112        experimental_store_composition,
113    } = castore_service_addrs;
114
115    #[cfg(feature = "xp-composition-cli")]
116    if let Some(conf_path) = experimental_store_composition {
117        let conf_text = tokio::fs::read_to_string(conf_path).await?;
118        return Ok(with_registry(&REG, || toml::from_str(&conf_text))?);
119    }
120
121    let mut configs: CompositionConfigs = Default::default();
122
123    configs.blob_stores.insert(
124        "root".into(),
125        with_registry(&REG, || blob_store_addr.try_into())?,
126    );
127    configs.chunk_stores.insert(
128        "root".into(),
129        with_registry(&REG, || chunk_store_addr.try_into())?,
130    );
131    configs.directoryservices.insert(
132        "root".into(),
133        with_registry(&REG, || directory_service_addr.try_into())?,
134    );
135    configs.pathinfoservices.insert(
136        "root".into(),
137        with_registry(&REG, || path_info_service_addr.try_into())?,
138    );
139
140    Ok(configs)
141}
142
143/// Construct the store handles from their addrs.
144pub async fn construct_services(
145    urls: impl Into<ServiceUrls>,
146) -> Result<
147    (
148        Arc<dyn BlobStore>,
149        Arc<dyn ChunkStore>,
150        Arc<dyn BlobEngine>,
151        Arc<dyn DirectoryService>,
152        Arc<dyn PathInfoService>,
153        Arc<dyn NarCalculationService>,
154    ),
155    Box<dyn std::error::Error + Send + Sync>,
156> {
157    let configs = addrs_to_configs(urls).await?;
158    construct_services_from_configs(configs).await
159}
160
161/// Construct the store handles from their addrs.
162pub async fn construct_services_from_configs(
163    configs: CompositionConfigs,
164) -> Result<
165    (
166        Arc<dyn BlobStore>,
167        Arc<dyn ChunkStore>,
168        Arc<dyn BlobEngine>,
169        Arc<dyn DirectoryService>,
170        Arc<dyn PathInfoService>,
171        Arc<dyn NarCalculationService>,
172    ),
173    Box<dyn std::error::Error + Send + Sync>,
174> {
175    let mut comp = Composition::new(&REG);
176
177    comp.extend(configs.blob_stores);
178    comp.extend(configs.chunk_stores);
179    comp.extend(configs.directoryservices);
180    comp.extend(configs.pathinfoservices);
181
182    let blob_store: Arc<dyn BlobStore> = comp.build("root").await?;
183    let chunk_store: Arc<dyn ChunkStore> = comp.build("root").await?;
184    let blob_engine: Arc<dyn BlobEngine> = Arc::new(
185        blob_engine::ChunkingBlobEngine::builder()
186            .blob_store(blob_store.clone())
187            .chunk_store(chunk_store.clone())
188            .build(),
189    );
190
191    let directory_service: Arc<dyn DirectoryService> = comp.build("root").await?;
192    let path_info_service: Arc<dyn PathInfoService> = comp.build("root").await?;
193
194    // HACK: The grpc client also implements NarCalculationService, and we
195    // really want to use it (otherwise we'd need to fetch everything again for hashing).
196    // Until we revamped store composition and config, detect this special case here.
197    let nar_calculation_service: Arc<dyn NarCalculationService> = path_info_service
198        .nar_calculation_service()
199        .unwrap_or_else(|| {
200            Arc::new(crate::nar::Renderer::new(
201                blob_engine.clone(),
202                directory_service.clone(),
203            ))
204        });
205
206    Ok((
207        blob_store,
208        chunk_store,
209        blob_engine,
210        directory_service,
211        path_info_service,
212        nar_calculation_service,
213    ))
214}
215
216/// Returns a new [PathInfoService]. Should only be used for tests.
217pub fn gen_test_pathinfo_service() -> impl PathInfoService + Clone {
218    crate::pathinfoservice::RedbPathInfoService::new_temporary(
219        "test".to_string(),
220        crate::pathinfoservice::RedbPathInfoServiceConfig::default(),
221    )
222    .expect("creating pathinfoservice to succeed")
223}