use std::time::{Duration, Instant};
use futures::future::join_all;
use nodedb_physical::physical_plan::MetaOp;
use crate::bridge::envelope::{PhysicalPlan, Priority, Request, Status};
use crate::control::state::SharedState;
use crate::types::{DatabaseId, ReadConsistency, TenantId, TraceId, VShardId};
pub async fn dispatch_unregister_collection(
state: &SharedState,
database_id: u64,
tenant_id: u64,
name: &str,
purge_lsn: u64,
) -> crate::Result<()> {
let tenant = TenantId::new(tenant_id);
let database = DatabaseId::new(database_id);
let timeout = Duration::from_secs(state.tuning.network.default_deadline_secs);
let num_cores = state
.dispatcher
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
.num_cores();
let mut receivers = Vec::with_capacity(num_cores);
{
let mut dispatcher = state
.dispatcher
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let homing_core = dispatcher
.router()
.resolve(VShardId::from_collection_in_database(database, name))
.unwrap_or(0);
for core_id in 0..num_cores {
let request_id = state.next_request_id();
let request = Request {
request_id,
tenant_id: tenant,
database_id: database,
vshard_id: VShardId::new(core_id as u32),
plan: PhysicalPlan::Meta(MetaOp::UnregisterCollection {
tenant_id,
name: name.to_string(),
purge_lsn,
reclaim_l1_files: core_id == homing_core,
}),
deadline: Instant::now() + timeout,
priority: Priority::Background,
trace_id: TraceId::ZERO,
consistency: ReadConsistency::Strong,
idempotency_key: None,
event_source: crate::event::EventSource::User,
user_roles: Vec::new(),
user_id: None,
statement_digest: None,
txn_id: None,
wal_lsn: None,
resolved_now_ms: None,
admission: crate::bridge::envelope::Admission::Exempt(
crate::bridge::envelope::ExemptReason::AlreadyOrdered,
),
};
let receiver = state.tracker.register(request_id);
if let Err(error) = dispatcher.dispatch_to_core(core_id, request) {
state.tracker.cancel(&request_id);
for (registered_id, _, _) in &receivers {
state.tracker.cancel(registered_id);
}
return Err(error);
}
receivers.push((request_id, core_id, receiver));
}
}
let responses = join_all(receivers.into_iter().map(
|(_request_id, core_id, mut receiver)| async move {
let response = tokio::time::timeout(timeout, receiver.recv())
.await
.map_err(|_| crate::Error::Dispatch {
detail: format!("collection reclaim timed out on core {core_id}"),
})?
.ok_or_else(|| crate::Error::Dispatch {
detail: format!("collection reclaim channel closed on core {core_id}"),
})?;
if response.status != Status::Ok {
return Err(crate::Error::Storage {
engine: "collection-purge".into(),
detail: format!(
"UnregisterCollection for tenant {tenant_id} collection '{name}' \
failed on core {core_id}: {:?}",
response.error_code
),
});
}
Ok(())
},
))
.await;
for response in responses {
response?;
}
Ok(())
}