Skip to main content

relay_knowledge/adapters/
storage.rs

1//! SQLite storage construction behind the application factory contract.
2
3use std::{path::PathBuf, sync::Arc};
4
5use crate::{
6    paths::RuntimePaths,
7    storage::{
8        KnowledgeStore, KnowledgeStoreFactory, KnowledgeStoreFactoryFuture,
9        PartitionedSqliteKnowledgeStore, SqliteGraphStore, StorageError, StorageTopology,
10        StorageTopologySnapshot,
11    },
12};
13
14/// Configured SQLite factory assembled by the outer bootstrap layer.
15#[derive(Debug, Clone)]
16pub struct SqliteKnowledgeStoreFactory {
17    database_path: PathBuf,
18    paths: RuntimePaths,
19    topology: StorageTopology,
20}
21
22impl SqliteKnowledgeStoreFactory {
23    /// Captures validated paths and topology without opening storage eagerly.
24    pub fn new(paths: RuntimePaths, topology: StorageTopology) -> Self {
25        Self {
26            database_path: paths.database_file(),
27            paths,
28            topology,
29        }
30    }
31}
32
33impl KnowledgeStoreFactory for SqliteKnowledgeStoreFactory {
34    fn open(&self) -> KnowledgeStoreFactoryFuture<'_, Arc<dyn KnowledgeStore>> {
35        let config = self.clone();
36        Box::pin(async move {
37            tokio::task::spawn_blocking(move || open_store(config))
38                .await
39                .map_err(StorageError::from)?
40        })
41    }
42
43    fn topology_snapshot(&self) -> KnowledgeStoreFactoryFuture<'_, StorageTopologySnapshot> {
44        let config = self.clone();
45        Box::pin(async move {
46            tokio::task::spawn_blocking(move || {
47                PartitionedSqliteKnowledgeStore::topology_snapshot_from_catalog(
48                    config.database_path,
49                    &config.paths,
50                )
51            })
52            .await
53            .map_err(StorageError::from)?
54        })
55    }
56}
57
58fn open_store(
59    config: SqliteKnowledgeStoreFactory,
60) -> Result<Arc<dyn KnowledgeStore>, StorageError> {
61    match config.topology {
62        StorageTopology::SingleSqlite => {
63            if PartitionedSqliteKnowledgeStore::has_active_catalog(&config.database_path)? {
64                return Err(StorageError::InvalidInput(
65                    "single_sqlite cannot open a database with active partitioned_sqlite shards; set RELAY_KNOWLEDGE_STORAGE_TOPOLOGY=partitioned_sqlite or migrate the shard catalog before rollback".to_owned(),
66                ));
67            }
68            Ok(Arc::new(SqliteGraphStore::open(config.database_path)?) as Arc<dyn KnowledgeStore>)
69        }
70        StorageTopology::PartitionedSqlite => Ok(Arc::new(PartitionedSqliteKnowledgeStore::open(
71            config.database_path,
72            config.paths,
73        )?) as Arc<dyn KnowledgeStore>),
74    }
75}
76
77#[cfg(test)]
78#[path = "storage_tests.rs"]
79mod tests;