mirror of
https://github.com/telemt/telemt.git
synced 2026-11-02 13:52:03 +03:00
Dead-Code Cleaning
This commit is contained in:
@@ -7,6 +7,7 @@ use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rand::RngExt;
|
||||
#[cfg(test)]
|
||||
use tokio::sync::Semaphore;
|
||||
use tokio::task::JoinSet;
|
||||
use tracing::{debug, info, warn};
|
||||
@@ -65,27 +66,17 @@ struct FamilyFloorPlan {
|
||||
warm_cap_effective_total: usize,
|
||||
active_writers_current: usize,
|
||||
warm_writers_current: usize,
|
||||
target_writers_total: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct FamilyReconnectOutcome {
|
||||
key: (i32, IpFamily),
|
||||
dc: i32,
|
||||
family: IpFamily,
|
||||
required: usize,
|
||||
endpoint_count: usize,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
struct ScheduledReconnects<'a> {
|
||||
inflight: &'a mut HashMap<(i32, IpFamily), usize>,
|
||||
keys: Vec<(i32, IpFamily)>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl ScheduledReconnects<'_> {
|
||||
fn current(&self, key: &(i32, IpFamily)) -> usize {
|
||||
self.inflight.get(key).copied().unwrap_or(0)
|
||||
}
|
||||
|
||||
fn reserve(&mut self, key: (i32, IpFamily)) {
|
||||
self.keys.push(key);
|
||||
@@ -93,6 +84,7 @@ impl ScheduledReconnects<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Drop for ScheduledReconnects<'_> {
|
||||
fn drop(&mut self) {
|
||||
for key in self.keys.drain(..) {
|
||||
@@ -118,9 +110,9 @@ mod scheduler;
|
||||
mod drain;
|
||||
// Per-family reconnect scheduling and health state.
|
||||
mod family;
|
||||
// Adaptive-floor planning and warning rate limits.
|
||||
// Adaptive-floor planning.
|
||||
mod floor_plan;
|
||||
// Idle-writer refresh and cap-aware swaps.
|
||||
// Idle-writer refresh under the current capacity policy.
|
||||
mod idle_refresh;
|
||||
// Single-endpoint recovery and shadow rotation.
|
||||
mod recovery;
|
||||
@@ -128,18 +120,15 @@ mod recovery;
|
||||
mod zombie_watchdog;
|
||||
|
||||
pub use drain::me_drain_timeout_enforcer;
|
||||
pub(in crate::transport::middle_proxy) use drain::{
|
||||
health_drain_close_budget, reap_draining_writers,
|
||||
};
|
||||
#[cfg(test)]
|
||||
pub(in crate::transport::middle_proxy) use drain::health_drain_close_budget;
|
||||
pub(in crate::transport::middle_proxy) use drain::reap_draining_writers;
|
||||
use family::{check_family, update_family_runtime_state};
|
||||
use floor_plan::{
|
||||
build_family_floor_plan, live_active_writers_for_dc_family, should_emit_rate_limited_warn,
|
||||
};
|
||||
use idle_refresh::{maybe_refresh_idle_writer_for_dc, maybe_swap_idle_writer_for_cap};
|
||||
use floor_plan::build_family_floor_plan;
|
||||
use idle_refresh::maybe_refresh_idle_writer_for_dc;
|
||||
pub use monitor::me_health_monitor;
|
||||
use recovery::{
|
||||
has_bound_clients_on_endpoint, maybe_rotate_single_endpoint_shadow,
|
||||
recover_single_endpoint_outage,
|
||||
};
|
||||
pub use zombie_watchdog::me_zombie_writer_watchdog;
|
||||
|
||||
|
||||
@@ -1,56 +1,6 @@
|
||||
use super::*;
|
||||
|
||||
pub(super) fn should_emit_rate_limited_warn(
|
||||
next_allowed: &mut HashMap<(i32, IpFamily), Instant>,
|
||||
key: (i32, IpFamily),
|
||||
now: Instant,
|
||||
cooldown: Duration,
|
||||
) -> bool {
|
||||
let Some(ready_at) = next_allowed.get(&key).copied() else {
|
||||
next_allowed.insert(key, now + cooldown);
|
||||
return true;
|
||||
};
|
||||
if now >= ready_at {
|
||||
next_allowed.insert(key, now + cooldown);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub(super) async fn live_active_writers_for_dc_family(
|
||||
pool: &Arc<MePool>,
|
||||
dc: i32,
|
||||
family: IpFamily,
|
||||
) -> usize {
|
||||
let active_generation = pool.current_generation();
|
||||
let writers = pool.writers.read().await;
|
||||
writers
|
||||
.iter()
|
||||
.filter(|writer| {
|
||||
if writer.draining.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
return false;
|
||||
}
|
||||
if writer.writer_dc != dc {
|
||||
return false;
|
||||
}
|
||||
if writer.generation != active_generation {
|
||||
return false;
|
||||
}
|
||||
if !matches!(
|
||||
crate::transport::middle_proxy::pool::WriterContour::from_u8(
|
||||
writer.contour.load(std::sync::atomic::Ordering::Relaxed),
|
||||
),
|
||||
crate::transport::middle_proxy::pool::WriterContour::Active
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
match family {
|
||||
IpFamily::V4 => writer.addr.is_ipv4(),
|
||||
IpFamily::V6 => writer.addr.is_ipv6(),
|
||||
}
|
||||
})
|
||||
.count()
|
||||
}
|
||||
|
||||
pub(super) fn adaptive_floor_class_min(
|
||||
pool: &Arc<MePool>,
|
||||
@@ -177,7 +127,6 @@ pub(super) async fn build_family_floor_plan(
|
||||
warm_cap_effective_total: warm_cap_configured_total,
|
||||
active_writers_current,
|
||||
warm_writers_current,
|
||||
target_writers_total: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -199,7 +148,6 @@ pub(super) async fn build_family_floor_plan(
|
||||
warm_cap_effective_total: warm_cap_configured_total,
|
||||
active_writers_current,
|
||||
warm_writers_current,
|
||||
target_writers_total: target_total,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -251,7 +199,6 @@ pub(super) async fn build_family_floor_plan(
|
||||
}
|
||||
let active_cap_effective_total =
|
||||
active_cap_configured_total.max(other_active.saturating_add(min_sum));
|
||||
let target_writers_total = other_active.saturating_add(target_sum);
|
||||
FamilyFloorPlan {
|
||||
by_dc,
|
||||
active_cap_configured_total,
|
||||
@@ -260,6 +207,5 @@ pub(super) async fn build_family_floor_plan(
|
||||
warm_cap_effective_total: warm_cap_configured_total,
|
||||
active_writers_current,
|
||||
warm_writers_current,
|
||||
target_writers_total,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,146 +1,5 @@
|
||||
use super::*;
|
||||
|
||||
pub(super) async fn maybe_swap_idle_writer_for_cap(
|
||||
pool: &Arc<MePool>,
|
||||
rng: &Arc<SecureRandom>,
|
||||
dc: i32,
|
||||
family: IpFamily,
|
||||
endpoints: &[SocketAddr],
|
||||
required: usize,
|
||||
floor_targets_by_dc: &HashMap<i32, usize>,
|
||||
live_writer_ids_by_addr: &HashMap<(i32, SocketAddr), Vec<u64>>,
|
||||
writer_idle_since: &HashMap<u64, u64>,
|
||||
bound_clients_by_writer: &HashMap<u64, usize>,
|
||||
) -> bool {
|
||||
let Some(replacement_endpoint) = endpoints
|
||||
.iter()
|
||||
.min_by_key(|endpoint| {
|
||||
live_writer_ids_by_addr
|
||||
.get(&(dc, **endpoint))
|
||||
.map_or(0, Vec::len)
|
||||
})
|
||||
.copied()
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
let mut alive_by_dc = HashMap::<i32, usize>::new();
|
||||
for ((writer_dc, endpoint), writer_ids) in live_writer_ids_by_addr {
|
||||
if endpoint.is_ipv4() == matches!(family, IpFamily::V4) {
|
||||
*alive_by_dc.entry(*writer_dc).or_insert(0) += writer_ids.len();
|
||||
}
|
||||
}
|
||||
|
||||
let now_epoch_secs = MePool::now_epoch_secs();
|
||||
let mut candidates = Vec::<(u64, SocketAddr, i32, u64, usize)>::new();
|
||||
for ((writer_dc, endpoint), writer_ids) in live_writer_ids_by_addr {
|
||||
if endpoint.is_ipv4() != matches!(family, IpFamily::V4) {
|
||||
continue;
|
||||
}
|
||||
let Some(donor_floor) = floor_targets_by_dc.get(writer_dc).copied() else {
|
||||
continue;
|
||||
};
|
||||
if alive_by_dc.get(writer_dc).copied().unwrap_or(0) <= donor_floor {
|
||||
continue;
|
||||
}
|
||||
for writer_id in writer_ids {
|
||||
if bound_clients_by_writer.get(writer_id).copied().unwrap_or(0) > 0 {
|
||||
continue;
|
||||
}
|
||||
let Some(idle_since_epoch_secs) = writer_idle_since.get(writer_id).copied() else {
|
||||
continue;
|
||||
};
|
||||
candidates.push((
|
||||
*writer_id,
|
||||
*endpoint,
|
||||
*writer_dc,
|
||||
now_epoch_secs.saturating_sub(idle_since_epoch_secs),
|
||||
donor_floor,
|
||||
));
|
||||
}
|
||||
}
|
||||
candidates.sort_unstable_by(|left, right| right.3.cmp(&left.3));
|
||||
|
||||
for (old_writer_id, donor_endpoint, donor_dc, idle_age_secs, donor_floor) in candidates {
|
||||
let expected_role = {
|
||||
let writers = pool.writers.read().await;
|
||||
writers
|
||||
.iter()
|
||||
.find(|writer| writer.id == old_writer_id)
|
||||
.map(WriterRole::from_writer)
|
||||
};
|
||||
let Some(expected_role) = expected_role else {
|
||||
continue;
|
||||
};
|
||||
if expected_role.dc != donor_dc
|
||||
|| expected_role.family != family
|
||||
|| expected_role.contour != WriterContour::Active
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let Some(mut reservation) = pool
|
||||
.registry
|
||||
.try_reserve_writer_replacement(old_writer_id)
|
||||
.await
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let replace = pool.replace_writer_with_generation_contour_for_dc(
|
||||
replacement_endpoint,
|
||||
rng.as_ref(),
|
||||
pool.current_generation(),
|
||||
WriterContour::Active,
|
||||
dc,
|
||||
expected_role,
|
||||
WriterReplacementPurpose::FloorRebalance {
|
||||
donor_floor,
|
||||
receiver_floor: required,
|
||||
},
|
||||
&mut reservation,
|
||||
);
|
||||
match tokio::time::timeout(pool.reconnect_runtime.me_one_timeout, replace).await {
|
||||
Ok(Ok(())) => {
|
||||
info!(
|
||||
dc = %dc,
|
||||
?family,
|
||||
%replacement_endpoint,
|
||||
donor_dc = %donor_dc,
|
||||
%donor_endpoint,
|
||||
old_writer_id,
|
||||
idle_age_secs,
|
||||
"Adaptive floor cap rebalance committed"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
Ok(Err(error)) => {
|
||||
debug!(
|
||||
dc = %dc,
|
||||
?family,
|
||||
%replacement_endpoint,
|
||||
donor_dc = %donor_dc,
|
||||
old_writer_id,
|
||||
idle_age_secs,
|
||||
%error,
|
||||
"Adaptive floor cap rebalance failed"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
Err(_) => {
|
||||
debug!(
|
||||
dc = %dc,
|
||||
?family,
|
||||
%replacement_endpoint,
|
||||
donor_dc = %donor_dc,
|
||||
old_writer_id,
|
||||
idle_age_secs,
|
||||
"Adaptive floor cap rebalance timed out"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub(super) async fn maybe_refresh_idle_writer_for_dc(
|
||||
pool: &Arc<MePool>,
|
||||
|
||||
@@ -15,6 +15,7 @@ pub(super) fn has_bound_clients_on_endpoint(
|
||||
}
|
||||
|
||||
/// Restores missing single-endpoint coverage under the bounded outage retry policy.
|
||||
#[cfg(test)]
|
||||
pub(super) async fn recover_single_endpoint_outage(
|
||||
pool: &Arc<MePool>,
|
||||
rng: &Arc<SecureRandom>,
|
||||
|
||||
@@ -169,6 +169,7 @@ impl MePool {
|
||||
}
|
||||
|
||||
/// Applies the configured or HTTP-detected NAT IP to non-public addresses.
|
||||
#[cfg(test)]
|
||||
pub(super) fn translate_ip_for_nat(&self, ip: IpAddr) -> IpAddr {
|
||||
Self::translate_ip_with_nat(
|
||||
ip,
|
||||
|
||||
@@ -193,22 +193,6 @@ impl MePool {
|
||||
self.refill_states.lock().contains_key(&key)
|
||||
}
|
||||
|
||||
pub(super) async fn connect_endpoints_round_robin(
|
||||
self: &Arc<Self>,
|
||||
dc: i32,
|
||||
endpoints: &[SocketAddr],
|
||||
rng: &SecureRandom,
|
||||
) -> bool {
|
||||
self.connect_endpoints_round_robin_with_generation_contour(
|
||||
dc,
|
||||
endpoints,
|
||||
rng,
|
||||
self.current_generation(),
|
||||
WriterContour::Active,
|
||||
WriterOpenIntent::Normal,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub(super) async fn connect_endpoints_round_robin_with_generation_contour(
|
||||
self: &Arc<Self>,
|
||||
|
||||
@@ -378,6 +378,7 @@ impl MePool {
|
||||
}
|
||||
|
||||
/// Returns the currently authoritative endpoint set for drain and coverage decisions.
|
||||
#[cfg(test)]
|
||||
pub(in crate::transport::middle_proxy) async fn desired_dc_endpoints(
|
||||
&self,
|
||||
) -> HashMap<i32, HashSet<SocketAddr>> {
|
||||
|
||||
@@ -169,14 +169,6 @@ impl MePool {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reconciles one immutable endpoint snapshot without draining uncovered groups.
|
||||
pub async fn zero_downtime_reinit_after_map_change(
|
||||
self: &Arc<Self>,
|
||||
rng: &SecureRandom,
|
||||
) -> bool {
|
||||
let endpoint_snapshot = self.endpoint_snapshot.load_full();
|
||||
self.reinit_endpoint_snapshot(rng, endpoint_snapshot).await
|
||||
}
|
||||
|
||||
/// Reconciles exactly the revision owned by the scheduler's retry objective.
|
||||
pub(in crate::transport::middle_proxy) async fn reinit_endpoint_snapshot(
|
||||
@@ -417,8 +409,4 @@ impl MePool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Applies periodic rotation using the same coverage and generation fences as map changes.
|
||||
pub async fn zero_downtime_reinit_periodic(self: &Arc<Self>, rng: &SecureRandom) -> bool {
|
||||
self.zero_downtime_reinit_after_map_change(rng).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,10 @@ pub(super) enum WriterReplacementPurpose {
|
||||
/// Replaces an idle writer before the upstream idle lifetime expires.
|
||||
IdleRefresh,
|
||||
/// Moves one idle slot from a donor above floor to a receiver below floor.
|
||||
#[cfg_attr(
|
||||
not(test),
|
||||
expect(dead_code, reason = "Legacy purpose is exercised by commit-fence regression tests")
|
||||
)]
|
||||
FloorRebalance {
|
||||
/// Minimum writer count that must remain in the donor group.
|
||||
donor_floor: usize,
|
||||
|
||||
@@ -152,6 +152,7 @@ impl MePool {
|
||||
|
||||
/// Commits writer visibility and lifecycle ownership after all cancellation points.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[cfg(test)]
|
||||
pub(in crate::transport::middle_proxy) async fn publish_prepared_writer<F>(
|
||||
self: &Arc<Self>,
|
||||
writer: MeWriter,
|
||||
|
||||
@@ -407,37 +407,6 @@ impl MePool {
|
||||
removed
|
||||
}
|
||||
|
||||
pub(crate) async fn mark_writer_draining_with_timeout(
|
||||
self: &Arc<Self>,
|
||||
writer_id: u64,
|
||||
timeout: Option<Duration>,
|
||||
allow_drain_fallback: bool,
|
||||
) {
|
||||
let timeout = timeout.filter(|d| !d.is_zero());
|
||||
let found = {
|
||||
let ws = self.writers.write().await;
|
||||
let mut registry_registration = self.registry.prepare_writer_registration().await;
|
||||
if let Some(w) = ws.iter().find(|w| w.id == writer_id) {
|
||||
if !allow_drain_fallback {
|
||||
registry_registration.retire(writer_id);
|
||||
}
|
||||
self.apply_writer_draining_state(w, timeout, allow_drain_fallback);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if !found {
|
||||
return;
|
||||
}
|
||||
|
||||
let timeout_secs = timeout.map(|d| d.as_secs()).unwrap_or(0);
|
||||
debug!(
|
||||
writer_id,
|
||||
timeout_secs, allow_drain_fallback, "ME writer marked draining"
|
||||
);
|
||||
}
|
||||
|
||||
/// Publishes drain metadata before the release-store that makes draining observable.
|
||||
pub(in crate::transport::middle_proxy) fn apply_writer_draining_state(
|
||||
@@ -472,10 +441,6 @@ impl MePool {
|
||||
!already_draining
|
||||
}
|
||||
|
||||
pub(crate) async fn mark_writer_draining(self: &Arc<Self>, writer_id: u64) {
|
||||
self.mark_writer_draining_with_timeout(writer_id, Some(Duration::from_secs(300)), false)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(in crate::transport::middle_proxy) fn writer_accepts_new_binding(
|
||||
&self,
|
||||
|
||||
@@ -180,6 +180,8 @@ impl ConnRegistry {
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
/// Creates a test registry with the requested route channel capacity.
|
||||
#[cfg(test)]
|
||||
pub fn with_route_channel_capacity(route_channel_capacity: usize) -> Self {
|
||||
let route_channel_capacity = route_channel_capacity.max(1);
|
||||
Self::with_route_limits(
|
||||
@@ -288,6 +290,8 @@ impl ConnRegistry {
|
||||
.store(watermark, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Registers a test route without a lifetime cleanup lease.
|
||||
#[cfg(test)]
|
||||
pub async fn register(&self) -> (u64, mpsc::Receiver<MeResponse>) {
|
||||
self.register_route()
|
||||
}
|
||||
|
||||
@@ -348,6 +348,8 @@ impl ConnRegistry {
|
||||
WriterBindOutcome::Bound
|
||||
}
|
||||
|
||||
/// Binds a test route and reports whether publication succeeded.
|
||||
#[cfg(test)]
|
||||
pub async fn bind_writer(&self, conn_id: u64, writer_id: u64, meta: ConnMeta) -> bool {
|
||||
matches!(
|
||||
self.bind_writer_with_outcome(conn_id, writer_id, meta)
|
||||
|
||||
Reference in New Issue
Block a user