objectstore_service/backend/
mod.rs

1//! Storage backend implementations.
2//!
3//! This module contains the [`Backend`](common::Backend) trait and its
4//! implementations. Each backend adapts a specific storage system (BigTable,
5//! GCS, local filesystem, S3-compatible) to a uniform interface that
6//! [`StorageService`](crate::StorageService) consumes.
7//!
8//! Two-tier routing is encapsulated in [`TieredStorage`](tiered::TieredStorage)
9//! and can be configured via [`StorageConfig::Tiered`].
10
11use anyhow::Result;
12use serde::{Deserialize, Serialize};
13
14pub mod bigtable;
15pub mod changelog;
16pub mod common;
17pub mod gcs;
18pub mod in_memory;
19pub mod local_fs;
20pub mod s3_compatible;
21pub mod tiered;
22
23#[cfg(test)]
24pub(crate) mod testing;
25
26/// Storage backend configuration.
27///
28/// The `type` field in YAML or `__TYPE` in environment variables determines which variant is used.
29///
30/// Used to configure storage backends via [`from_config`].
31#[derive(Debug, Clone, Deserialize, Serialize)]
32#[serde(tag = "type", rename_all = "lowercase")]
33pub enum StorageConfig {
34    /// Local filesystem storage backend (type `"filesystem"`).
35    FileSystem(local_fs::FileSystemConfig),
36
37    /// S3-compatible storage backend (type `"s3compatible"`).
38    S3Compatible(s3_compatible::S3CompatibleConfig),
39
40    /// [Google Cloud Storage] backend (type `"gcs"`).
41    ///
42    /// [Google Cloud Storage]: https://cloud.google.com/storage
43    Gcs(gcs::GcsConfig),
44
45    /// [Google Bigtable] backend (type `"bigtable"`).
46    ///
47    /// [Google Bigtable]: https://cloud.google.com/bigtable
48    BigTable(bigtable::BigTableConfig),
49
50    /// Tiered storage backend (type `"tiered"`).
51    ///
52    /// Routes objects across two backends based on size: small objects go to
53    /// `high_volume`, large objects go to `long_term`. Nesting `Tiered` inside
54    /// another `Tiered` is not supported and will return an error at startup.
55    Tiered(tiered::TieredStorageConfig),
56}
57
58/// Constructs a type-erased [`Backend`](common::Backend) from the given [`StorageConfig`].
59pub async fn from_config(config: StorageConfig) -> Result<Box<dyn common::Backend>> {
60    Ok(match config {
61        StorageConfig::Tiered(c) => {
62            let hv = hv_from_config(c.high_volume).await?;
63            let lt = from_leaf_config(*c.long_term).await?;
64            let log = Box::new(changelog::NoopChangeLog);
65            Box::new(tiered::TieredStorage::new(hv, lt, log))
66        }
67        // All non-Tiered variants are handled by from_leaf_config. A wildcard
68        // is intentional here: any new leaf variant should fall through to
69        // from_leaf_config, which will handle it or produce a compile error.
70        _ => from_leaf_config(config).await?,
71    })
72}
73
74async fn from_leaf_config(config: StorageConfig) -> Result<Box<dyn common::Backend>> {
75    Ok(match config {
76        StorageConfig::FileSystem(c) => Box::new(local_fs::LocalFsBackend::new(c)),
77        StorageConfig::S3Compatible(c) => {
78            Box::new(s3_compatible::S3CompatibleBackend::without_token(c))
79        }
80        StorageConfig::Gcs(c) => Box::new(gcs::GcsBackend::new(c).await?),
81        StorageConfig::BigTable(c) => Box::new(bigtable::BigTableBackend::new(c).await?),
82        StorageConfig::Tiered(_) => anyhow::bail!("nested tiered storage is not supported"),
83    })
84}
85
86/// Configuration for the high-volume backend in a [`tiered::TieredStorageConfig`].
87///
88/// Only backends that implement [`common::HighVolumeBackend`] are valid here.
89/// Currently this is limited to BigTable.
90#[derive(Debug, Clone, Deserialize, Serialize)]
91#[serde(tag = "type", rename_all = "lowercase")]
92pub enum HighVolumeStorageConfig {
93    /// [Google Bigtable] backend.
94    ///
95    /// [Google Bigtable]: https://cloud.google.com/bigtable
96    BigTable(bigtable::BigTableConfig),
97}
98
99/// Constructs a type-erased [`common::HighVolumeBackend`] from the given config.
100async fn hv_from_config(
101    config: HighVolumeStorageConfig,
102) -> anyhow::Result<Box<dyn common::HighVolumeBackend>> {
103    Ok(match config {
104        HighVolumeStorageConfig::BigTable(c) => Box::new(bigtable::BigTableBackend::new(c).await?),
105    })
106}