use std::{collections::HashMap, sync::Arc};
use tokio::sync::{broadcast::Sender, Mutex, RwLock};
use zenoh::{internal::bail, session::Session, Result as ZResult};
use zenoh_backend_traits::{config::StorageConfig, History, VolumeInstance};
use crate::replication::{Action, Event, LogLatest, LogLatestKey, ReplicationService};
pub(crate) mod service;
pub(crate) use service::StorageService;
#[derive(Clone)]
pub enum StorageMessage {
Stop,
GetStatus(tokio::sync::mpsc::Sender<serde_json::Value>),
}
pub(crate) type LatestUpdates = HashMap<LogLatestKey, Event>;
#[derive(Clone)]
pub(crate) struct CacheLatest {
pub(crate) latest_updates: Arc<RwLock<LatestUpdates>>,
pub(crate) replication_log: Option<Arc<RwLock<LogLatest>>>,
}
impl CacheLatest {
pub fn new(
latest_updates: Arc<RwLock<LatestUpdates>>,
replication_log: Option<Arc<RwLock<LogLatest>>>,
) -> Self {
Self {
latest_updates,
replication_log,
}
}
}
pub(crate) async fn create_and_start_storage(
admin_key: String,
config: StorageConfig,
backend: &VolumeInstance,
zenoh_session: Arc<Session>,
) -> ZResult<Sender<StorageMessage>> {
tracing::trace!("Create storage '{}'", &admin_key);
let capability = backend.get_capability();
let storage = backend.create_storage(config.clone()).await?;
let parts: Vec<&str> = admin_key.split('/').collect();
let uuid = parts[2];
let storage_name = parts[7];
let name = format!("{uuid}/{storage_name}");
let (tx, rx_storage) = tokio::sync::broadcast::channel(1);
let rx_replication = tx.subscribe();
let mut entries = match storage.get_all_entries().await {
Ok(entries) => entries
.into_iter()
.map(|(stripped_key, ts)| {
let event = Event::new(stripped_key, ts, &Action::Put);
(event.log_key(), event)
})
.collect::<HashMap<_, _>>(),
Err(e) => bail!("`get_all_entries` failed with: {e:?}"),
};
let mut replication_log = None;
let mut latest_updates = HashMap::default();
if let Some(replica_config) = &config.replication {
if capability.history != History::Latest {
bail!(
"Replication was enabled for storage {name} but its history capability is not \
supported: found < {:?} >, expected < {:?} >",
capability.history,
History::Latest
);
}
let mut log_latest = LogLatest::new(
config.key_expr.clone(),
config.strip_prefix.clone(),
replica_config.clone(),
);
log_latest.update(entries.drain().map(|(_, event)| event));
replication_log = Some(Arc::new(RwLock::new(log_latest)));
} else {
latest_updates = entries;
}
let latest_updates = Arc::new(RwLock::new(latest_updates));
let storage = Arc::new(Mutex::new(storage));
tokio::task::spawn(async move {
let storage_service = Arc::new(
StorageService::new(
zenoh_session.clone(),
config.clone(),
&name,
storage,
capability,
CacheLatest::new(latest_updates.clone(), replication_log.clone()),
)
.await,
);
if let Some(replication_log) = replication_log {
tracing::debug!(
"Starting replication of storage '{}' on keyexpr '{}'",
name,
config.key_expr,
);
ReplicationService::spawn_start(
zenoh_session,
storage_service.clone(),
config.key_expr,
replication_log,
latest_updates,
rx_replication,
)
.await;
}
storage_service
.start_storage_queryable_subscriber(rx_storage)
.await;
});
Ok(tx)
}