use std::time::Duration;
use nodedb_cluster::{MetadataEntry, encode_entry};
use crate::control::state::SharedState;
use crate::error::Error;
pub mod drain;
pub mod drain_propose;
pub mod propose;
pub mod refcount;
pub mod release;
pub mod renewal;
pub mod shutdown_release;
mod wall_time;
pub(super) use wall_time::wall_now_ns;
pub use drain::{DescriptorDrainTracker, DrainEntry};
pub use drain_propose::{descriptor_id_and_prior_version, drain_for_ddl};
pub use propose::{DEFAULT_LEASE_DURATION, acquire_lease, compute_expires_at, force_refresh_lease};
pub use refcount::{LeaseRefCount, QueryLeaseScope};
pub use release::release_leases;
pub use renewal::{LeaseRenewalConfig, LeaseRenewalLoop};
pub(super) const PROPOSE_TIMEOUT: Duration = Duration::from_secs(5);
pub(super) fn propose_and_wait(
shared: &SharedState,
entry: &MetadataEntry,
operation: &'static str,
) -> Result<u64, Error> {
let Some(handle) = shared.metadata_raft.get() else {
return Err(Error::Config {
detail: format!("descriptor lease {operation}: no metadata raft handle"),
});
};
let raw = encode_entry(entry).map_err(|e| Error::Config {
detail: format!("descriptor lease {operation} encode: {e}"),
})?;
let log_index = handle.propose(raw)?;
let watcher = shared.applied_index_watcher(nodedb_cluster::METADATA_GROUP_ID);
let outcome = tokio::task::block_in_place(|| watcher.wait_for(log_index, PROPOSE_TIMEOUT));
if !outcome.is_reached() {
return Err(Error::Config {
detail: format!(
"descriptor lease {operation} did not apply within {PROPOSE_TIMEOUT:?} \
(log index {log_index}, current: {}, outcome: {outcome:?})",
watcher.current()
),
});
}
Ok(log_index)
}