Module tvix_castore::composition
source · Expand description
The composition module allows composing different kinds of services based on a set of service configurations at runtime.
Store configs are deserialized with serde. The registry provides a stateful mapping from the
type
tag of an internally tagged enum on the serde side to a Config struct which is
deserialized and then returned as a Box<dyn ServiceBuilder<Output = dyn BlobService>>
(the same for DirectoryService instead of BlobService etc).
§Example 1.: Implementing a new BlobService
You need a Config struct which implements DeserializeOwned
and
ServiceBuilder<Output = dyn BlobService>
.
Provide the user with a function to call with
their registry. You register your new type as:
use std::sync::Arc;
use tvix_castore::composition::*;
use tvix_castore::blobservice::BlobService;
#[derive(serde::Deserialize)]
struct MyBlobServiceConfig {
}
#[tonic::async_trait]
impl ServiceBuilder for MyBlobServiceConfig {
type Output = dyn BlobService;
async fn build(&self, _: &str, _: &CompositionContext) -> Result<Arc<Self::Output>, Box<dyn std::error::Error + Send + Sync + 'static>> {
todo!()
}
}
impl TryFrom<url::Url> for MyBlobServiceConfig {
type Error = Box<dyn std::error::Error + Send + Sync>;
fn try_from(url: url::Url) -> Result<Self, Self::Error> {
todo!()
}
}
pub fn add_my_service(reg: &mut Registry) {
reg.register::<Box<dyn ServiceBuilder<Output = dyn BlobService>>, MyBlobServiceConfig>("myblobservicetype");
}
Now, when a user deserializes a store config with the type tag “myblobservicetype” into a
Box<dyn ServiceBuilder<Output = Arc<dyn BlobService>>>
, it will be done via MyBlobServiceConfig
.
§Example 2.: Composing stores to get one store
use std::sync::Arc;
use tvix_castore::composition::*;
use tvix_castore::blobservice::BlobService;
let blob_services_configs_json = serde_json::json!({
"blobstore1": {
"type": "memory"
},
"blobstore2": {
"type": "memory"
},
"default": {
"type": "combined",
"local": "blobstore1",
"remote": "blobstore2"
}
});
let blob_services_configs = with_registry(®, || serde_json::from_value(blob_services_configs_json))?;
let mut blob_service_composition = Composition::new(®);
blob_service_composition.extend_with_configs::<dyn BlobService>(blob_services_configs);
let blob_service: Arc<dyn BlobService> = blob_service_composition.build("default").await?;
§Example 3.: Creating another registry extending the default registry with third-party types
let mut my_registry = tvix_castore::composition::Registry::default();
tvix_castore::composition::add_default_services(&mut my_registry);
add_my_service(&mut my_registry);
Continue with Example 2, with my_registry instead of REG
EXPERIMENTAL: If the xp-store-composition feature is enabled,
entrypoints can also be URL strings, which are created as
anonymous stores. Instantiations of the same URL will
result in a new, distinct anonymous store each time, so creating
two memory://
stores with this method will not share the same view.
This behavior might change in the future.
Structs§
- Wrapper type which implements Deserialize using the registry
- Resolves tag names to the corresponding Config type.
Enums§
Constants§
- The active Registry is global state, because there is no convenient and universal way to pass state into the functions usually used for deserialization, e.g.
serde_json::from_str
,toml::from_str
,serde_qs::from_str
.
Statics§
- The provided registry of tvix_castore, with all builtin BlobStore/DirectoryStore implementations
Traits§
- This is the trait usually implemented on a per-store-type Config struct and used to instantiate it.
Functions§
- Register the builtin services of tvix_castore with the given registry. This is useful for creating your own registry with the builtin types and extra third party types.
- Run the provided closure with a registry context. Any serde deserialize calls within the closure will use the registry to resolve tag names to the corresponding Config type.