#![allow(
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
reason = "M175: hill-climbing slot tuner — rates and slot counts bounded by realistic peer counts"
)]
#[allow(dead_code)] pub struct SlotTuner {
slots: usize,
min_slots: usize,
max_slots: usize,
prev_throughput: u64,
direction: i8, enabled: bool,
}
#[allow(dead_code)]
impl SlotTuner {
#[must_use]
pub fn new(initial: usize, min: usize, max: usize) -> Self {
Self {
slots: initial.clamp(min, max),
min_slots: min,
max_slots: max,
prev_throughput: 0,
direction: 1, enabled: true,
}
}
#[must_use]
pub fn disabled(slots: usize) -> Self {
Self {
slots,
min_slots: slots,
max_slots: slots,
prev_throughput: 0,
direction: 0,
enabled: false,
}
}
#[must_use]
pub fn current_slots(&self) -> usize {
self.slots
}
pub fn observe(&mut self, throughput: u64) {
if !self.enabled {
return;
}
if self.prev_throughput == 0 && throughput > 0 {
self.prev_throughput = throughput;
return;
}
if throughput > self.prev_throughput {
self.apply_direction();
} else if throughput < self.prev_throughput {
self.direction = -self.direction;
self.apply_direction();
}
self.prev_throughput = throughput;
}
fn apply_direction(&mut self) {
let new_slots = self.slots as i64 + i64::from(self.direction);
self.slots = (new_slots as usize).clamp(self.min_slots, self.max_slots);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_starts_at_initial_slots() {
let tuner = SlotTuner::new(4, 2, 20);
assert_eq!(tuner.current_slots(), 4);
}
#[test]
fn increases_slots_when_throughput_improves() {
let mut tuner = SlotTuner::new(4, 2, 20);
tuner.observe(100_000); tuner.observe(120_000); assert_eq!(tuner.current_slots(), 5);
}
#[test]
fn decreases_slots_when_throughput_drops() {
let mut tuner = SlotTuner::new(4, 2, 20);
tuner.observe(100_000); tuner.observe(120_000); assert_eq!(tuner.current_slots(), 5);
tuner.observe(90_000); assert_eq!(tuner.current_slots(), 4);
}
#[test]
fn respects_min_max_bounds() {
let mut tuner = SlotTuner::new(2, 2, 3);
tuner.observe(100_000);
tuner.observe(50_000); assert!(tuner.current_slots() >= 2);
let mut tuner = SlotTuner::new(3, 2, 3);
tuner.observe(100_000);
tuner.observe(200_000); assert!(tuner.current_slots() <= 3);
}
#[test]
fn disabled_returns_fixed_slots() {
let mut tuner = SlotTuner::disabled(4);
assert_eq!(tuner.current_slots(), 4);
tuner.observe(100_000);
tuner.observe(200_000);
assert_eq!(tuner.current_slots(), 4); }
#[test]
fn stagnant_throughput_holds_steady() {
let mut tuner = SlotTuner::new(4, 2, 20);
tuner.observe(100_000);
tuner.observe(100_000); assert_eq!(tuner.current_slots(), 4);
}
}