use super::*;
use crate::bucket::RapidPlacement;
use futures_util::{StreamExt, stream::FuturesUnordered};
type Opened = Vec<(Replica, Box<dyn LogWriter>)>;
pub(super) struct Prepared {
pub id: String,
pub prefix: String,
opened: AbortOnDropHandle<Result<Option<Opened>>>,
}
impl Prepared {
pub fn new(prefix: String, zones: Vec<Arc<dyn LogZone>>, placement: RapidPlacement) -> Self {
let id = uuid::Uuid::new_v4().to_string();
let object = format!("{prefix}{id}.segment");
let opened = AbortOnDropHandle::new(tokio::spawn(async move {
match placement {
RapidPlacement::All => open_all(&zones, &object).await,
RapidPlacement::Nearest => open_nearest(zones, object).await,
}
}));
Self { id, prefix, opened }
}
pub async fn take(self) -> Result<Option<Opened>> {
self.opened
.await
.context("Rapid connection preparation failed")?
}
}
async fn open_all(zones: &[Arc<dyn LogZone>], object: &str) -> Result<Option<Opened>> {
let results =
futures_util::future::join_all(zones.iter().map(|zone| bounded(zone.open(object)))).await;
let mut opened = Vec::new();
for result in results {
match result {
Ok(copy) => opened.push(copy),
Err(error) => {
tracing::warn!(%error, "Rapid log unavailable; activation will use Standard")
}
}
}
if opened.len() == zones.len() {
return Ok(Some(opened));
}
for (replica, writer) in opened {
drop(writer);
if let Some(zone) = zones.iter().find(|zone| zone.bucket() == replica.bucket) {
let _ = bounded(zone.delete(&replica)).await;
}
}
Ok(None)
}
async fn open_nearest(zones: Vec<Arc<dyn LogZone>>, object: String) -> Result<Option<Opened>> {
let mut opens: FuturesUnordered<_> = zones
.into_iter()
.map(|zone| {
let object = object.clone();
async move {
let result = bounded(zone.open(&object)).await;
(zone, result)
}
})
.collect();
while let Some((_, result)) = opens.next().await {
match result {
Ok(copy) => {
tokio::spawn(discard(opens));
return Ok(Some(vec![copy]));
}
Err(error) => tracing::warn!(%error, "Rapid zone unavailable during placement"),
}
}
tracing::warn!("Rapid log unavailable; activation will use Standard");
Ok(None)
}
async fn discard<F>(mut opens: FuturesUnordered<F>)
where
F: std::future::Future<Output = (Arc<dyn LogZone>, Result<(Replica, Box<dyn LogWriter>)>)>,
{
while let Some((zone, result)) = opens.next().await {
if let Ok((replica, writer)) = result {
drop(writer);
if let Err(error) = bounded(zone.delete(&replica)).await {
tracing::warn!(%error, "unused Rapid placement was not deleted");
}
}
}
}