use rand::seq::SliceRandom;
use rand::Rng;
use crate::app::CalculatedLimits;
use crate::resource_manager::ResourceType;
pub(crate) const MIN_STEP_RATE: f64 = 0.01;
pub(crate) const MAX_STEP_RATE: f64 = 0.10;
pub(crate) const BASELINE_ALPHA: f64 = 0.1;
pub(crate) const REALITY_CHECK_FACTOR: f64 = 2.0;
#[cfg(test)]
pub(crate) const DEFAULT_TUNING_CADENCE_SECS: u64 = 90;
#[cfg(test)]
pub(crate) const DEFAULT_TUNING_LOOKBACK_SECS: usize = 60;
pub(crate) const MIN_TUNING_CADENCE_SECS: u64 = 15;
pub(crate) const MAX_TUNING_CADENCE_SECS: u64 = 180;
pub(crate) const FAST_START_CADENCE_SECS: u64 = 20;
pub(crate) const FAST_START_CYCLES: u8 = 3;
pub(crate) const MIN_TUNING_LOOKBACK_SECS: usize = 15;
pub(crate) const MAX_TUNING_LOOKBACK_SECS: usize = 60;
const LOOKBACK_RATIO: f64 = 0.7;
const IMPROVEMENT_SPEEDUP_FACTOR: f64 = 0.85;
const STAGNATION_BACKOFF_FACTOR: f64 = 1.6;
const REGRESSION_SPEEDUP_FACTOR: f64 = 0.5;
const STAGNATION_BACKOFF_START_CYCLES: u32 = 2;
const RAPID_REGRESSION_RATIO: f64 = 0.90;
const SEVERE_REGRESSION_RATIO: f64 = 0.75;
const PENALTY_SPIKE_DELTA: f64 = 0.25;
const CADENCE_CHANGE_PRESSURE_TRIGGER: u8 = 4;
const CADENCE_CHANGE_PRESSURE_DECAY: u8 = 1;
const HIGH_NOISE_REL_STDDEV: f64 = 0.25;
const REGRESSION_SPEEDUP_BUDGET_CYCLES: u8 = 3;
const MIN_CADENCE_NO_IMPROVEMENT_BACKOFF_CYCLES: u32 = 3;
const STALE_BEST_DECAY_START_CYCLES: u32 = 6;
const STALE_BEST_DECAY_FACTOR: f64 = 0.97;
pub(crate) const MIN_PEERS: usize = 20;
pub(crate) const MIN_DISK: usize = 2;
pub(crate) const MIN_RESERVE: usize = 0;
pub(crate) const MAX_TRADE_ATTEMPTS: usize = 5;
#[derive(Debug, Clone)]
pub(crate) struct TuningState {
pub(crate) last_tuning_score: u64,
pub(crate) current_tuning_score: u64,
pub(crate) last_tuning_limits: CalculatedLimits,
pub(crate) baseline_speed_ema: f64,
}
impl TuningState {
pub(crate) fn new(initial_limits: CalculatedLimits) -> Self {
Self {
last_tuning_score: 0,
current_tuning_score: 0,
last_tuning_limits: initial_limits,
baseline_speed_ema: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct TuningController {
cadence_secs: u64,
lookback_secs: usize,
countdown_secs: u64,
adaptive_enabled: bool,
stagnation_cycles: u32,
no_improvement_cycles: u32,
fast_start_cycles_remaining: u8,
regression_speedup_budget_remaining: u8,
last_penalty_factor: Option<f64>,
cadence_change_pressure: u8,
state: TuningState,
}
impl TuningController {
#[cfg(test)]
pub(crate) fn new_fixed(initial_limits: CalculatedLimits) -> Self {
Self {
cadence_secs: DEFAULT_TUNING_CADENCE_SECS,
lookback_secs: DEFAULT_TUNING_LOOKBACK_SECS,
countdown_secs: DEFAULT_TUNING_CADENCE_SECS,
adaptive_enabled: false,
stagnation_cycles: 0,
no_improvement_cycles: 0,
fast_start_cycles_remaining: 0,
regression_speedup_budget_remaining: 0,
last_penalty_factor: None,
cadence_change_pressure: 0,
state: TuningState::new(initial_limits),
}
}
pub(crate) fn new_adaptive(initial_limits: CalculatedLimits) -> Self {
let cadence_secs = FAST_START_CADENCE_SECS;
Self {
cadence_secs,
lookback_secs: derive_lookback_secs(cadence_secs),
countdown_secs: cadence_secs,
adaptive_enabled: true,
stagnation_cycles: 0,
no_improvement_cycles: 0,
fast_start_cycles_remaining: FAST_START_CYCLES,
regression_speedup_budget_remaining: REGRESSION_SPEEDUP_BUDGET_CYCLES,
last_penalty_factor: None,
cadence_change_pressure: 0,
state: TuningState::new(initial_limits),
}
}
pub(crate) fn cadence_secs(&self) -> u64 {
self.cadence_secs
}
pub(crate) fn lookback_secs(&self) -> usize {
self.lookback_secs
}
pub(crate) fn countdown_secs(&self) -> u64 {
self.countdown_secs
}
pub(crate) fn state(&self) -> &TuningState {
&self.state
}
pub(crate) fn on_second_tick(&mut self) {
self.countdown_secs = self.countdown_secs.saturating_sub(1);
}
pub(crate) fn reset_for_objective_change(&mut self, current_limits: &CalculatedLimits) {
self.state.last_tuning_score = 0;
self.state.current_tuning_score = 0;
self.state.baseline_speed_ema = 0.0;
self.state.last_tuning_limits = current_limits.clone();
self.stagnation_cycles = 0;
self.no_improvement_cycles = 0;
self.last_penalty_factor = None;
self.cadence_change_pressure = 0;
if self.adaptive_enabled {
self.cadence_secs = FAST_START_CADENCE_SECS;
self.lookback_secs = derive_lookback_secs(self.cadence_secs);
self.fast_start_cycles_remaining = FAST_START_CYCLES;
self.regression_speedup_budget_remaining = REGRESSION_SPEEDUP_BUDGET_CYCLES;
}
self.countdown_secs = self.cadence_secs;
}
pub(crate) fn update_live_score(
&mut self,
relevant_history: &[u64],
current_scpb: f64,
scpb_max: f64,
) -> TuningScore {
let live_score = compute_tuning_score(relevant_history, current_scpb, scpb_max);
self.state.current_tuning_score = live_score.new_score;
live_score
}
pub(crate) fn evaluate_cycle(
&mut self,
current_limits: &CalculatedLimits,
relevant_history: &[u64],
current_scpb: f64,
scpb_max: f64,
) -> TuningEvaluation {
let score = self.update_live_score(relevant_history, current_scpb, scpb_max);
let evaluation = evaluate_tuning_cycle_from_score(
current_limits,
&self.state.last_tuning_limits,
self.state.last_tuning_score,
self.state.baseline_speed_ema,
score,
);
self.state.baseline_speed_ema = evaluation.updated_baseline_speed_ema;
self.state.last_tuning_score = evaluation.updated_last_tuning_score;
self.state.last_tuning_limits = evaluation.updated_last_tuning_limits.clone();
self.apply_cadence_policy(&evaluation, score.penalty_factor, relevant_history);
self.countdown_secs = self.cadence_secs;
evaluation
}
fn apply_cadence_policy(
&mut self,
evaluation: &TuningEvaluation,
penalty_factor: f64,
relevant_history: &[u64],
) {
if !self.adaptive_enabled {
return;
}
let previous_penalty = self.last_penalty_factor.unwrap_or(penalty_factor);
let previous_cadence = self.cadence_secs;
let rel_stddev = relative_stddev(relevant_history);
let high_noise = rel_stddev >= HIGH_NOISE_REL_STDDEV;
let severe_regression = evaluation.best_score_before > 0
&& (evaluation.new_score as f64)
< ((evaluation.best_score_before as f64) * SEVERE_REGRESSION_RATIO);
let rapid_regression = evaluation.best_score_before > 0
&& (evaluation.new_score as f64)
< ((evaluation.best_score_before as f64) * RAPID_REGRESSION_RATIO);
let regression_signal = severe_regression || (!high_noise && rapid_regression);
let penalty_spike = penalty_factor > (previous_penalty + PENALTY_SPIKE_DELTA);
if evaluation.accepted_improvement {
self.stagnation_cycles = 0;
self.no_improvement_cycles = 0;
self.regression_speedup_budget_remaining = REGRESSION_SPEEDUP_BUDGET_CYCLES;
self.cadence_secs = scaled_cadence(
self.cadence_secs,
IMPROVEMENT_SPEEDUP_FACTOR,
ScaleDirection::Down,
);
} else {
self.no_improvement_cycles = self.no_improvement_cycles.saturating_add(1);
let can_speedup_regression = self.regression_speedup_budget_remaining > 0;
if (regression_signal || penalty_spike) && can_speedup_regression {
self.stagnation_cycles = 0;
self.regression_speedup_budget_remaining =
self.regression_speedup_budget_remaining.saturating_sub(1);
self.cadence_secs = scaled_cadence(
self.cadence_secs,
REGRESSION_SPEEDUP_FACTOR,
ScaleDirection::Down,
);
} else {
self.stagnation_cycles = self.stagnation_cycles.saturating_add(1);
if self.cadence_secs == MIN_TUNING_CADENCE_SECS
&& self.no_improvement_cycles >= MIN_CADENCE_NO_IMPROVEMENT_BACKOFF_CYCLES
{
self.cadence_secs = scaled_cadence(
self.cadence_secs,
STAGNATION_BACKOFF_FACTOR,
ScaleDirection::Up,
);
self.stagnation_cycles = 0;
} else if self.stagnation_cycles >= STAGNATION_BACKOFF_START_CYCLES {
self.cadence_secs = scaled_cadence(
self.cadence_secs,
STAGNATION_BACKOFF_FACTOR,
ScaleDirection::Up,
);
}
}
}
if self.fast_start_cycles_remaining > 0 {
self.cadence_secs = self.cadence_secs.min(FAST_START_CADENCE_SECS);
self.fast_start_cycles_remaining = self.fast_start_cycles_remaining.saturating_sub(1);
}
if self.cadence_secs != previous_cadence {
self.cadence_change_pressure = self.cadence_change_pressure.saturating_add(1);
} else {
self.cadence_change_pressure = self
.cadence_change_pressure
.saturating_sub(CADENCE_CHANGE_PRESSURE_DECAY);
}
if self.cadence_change_pressure >= CADENCE_CHANGE_PRESSURE_TRIGGER {
self.cadence_secs = scaled_cadence(
self.cadence_secs,
STAGNATION_BACKOFF_FACTOR,
ScaleDirection::Up,
);
self.cadence_change_pressure /= 2;
}
if self.no_improvement_cycles >= STALE_BEST_DECAY_START_CYCLES {
let baseline_floor = self.state.baseline_speed_ema as u64;
let decayed = (self.state.last_tuning_score as f64 * STALE_BEST_DECAY_FACTOR) as u64;
self.state.last_tuning_score = decayed.max(baseline_floor);
}
self.lookback_secs = derive_lookback_secs(self.cadence_secs);
self.last_penalty_factor = Some(penalty_factor);
}
}
#[derive(Debug, Clone, Copy)]
enum ScaleDirection {
Up,
Down,
}
fn scaled_cadence(cadence_secs: u64, factor: f64, direction: ScaleDirection) -> u64 {
let scaled = match direction {
ScaleDirection::Up => (cadence_secs as f64 * factor).ceil() as u64,
ScaleDirection::Down => (cadence_secs as f64 * factor).floor() as u64,
};
scaled.clamp(MIN_TUNING_CADENCE_SECS, MAX_TUNING_CADENCE_SECS)
}
fn derive_lookback_secs(cadence_secs: u64) -> usize {
let derived = ((cadence_secs as f64) * LOOKBACK_RATIO).round() as usize;
let clamped = derived.clamp(MIN_TUNING_LOOKBACK_SECS, MAX_TUNING_LOOKBACK_SECS);
clamped.min(cadence_secs as usize)
}
fn relative_stddev(values: &[u64]) -> f64 {
if values.len() < 2 {
return 0.0;
}
let mean = values.iter().copied().map(|v| v as f64).sum::<f64>() / values.len() as f64;
if mean <= 0.0 {
return 0.0;
}
let var = values
.iter()
.map(|v| {
let dv = *v as f64 - mean;
dv * dv
})
.sum::<f64>()
/ values.len() as f64;
var.sqrt() / mean
}
pub(crate) fn normalize_limits_for_mode(
limits: &CalculatedLimits,
is_seeding: bool,
) -> CalculatedLimits {
if is_seeding {
let total_budget = limits
.reserve_permits
.saturating_add(limits.max_connected_peers)
.saturating_add(limits.disk_read_permits)
.saturating_add(limits.disk_write_permits);
let peer_slots = total_budget.saturating_mul(70) / 100;
let read_slots = total_budget.saturating_sub(peer_slots);
return CalculatedLimits {
reserve_permits: 0,
max_connected_peers: peer_slots,
disk_read_permits: read_slots,
disk_write_permits: 0,
};
}
let disk_budget = limits
.disk_read_permits
.saturating_add(limits.disk_write_permits);
let read_slots = disk_budget.saturating_mul(30) / 100;
let write_slots = disk_budget.saturating_sub(read_slots);
CalculatedLimits {
reserve_permits: limits.reserve_permits,
max_connected_peers: limits.max_connected_peers,
disk_read_permits: read_slots,
disk_write_permits: write_slots,
}
}
#[derive(Debug, Clone)]
pub(crate) struct TuningEvaluation {
pub(crate) new_raw_score: u64,
pub(crate) penalty_factor: f64,
pub(crate) new_score: u64,
pub(crate) updated_baseline_speed_ema: f64,
pub(crate) best_score_before: u64,
pub(crate) baseline_u64: u64,
pub(crate) updated_last_tuning_score: u64,
pub(crate) updated_last_tuning_limits: CalculatedLimits,
pub(crate) effective_limits: CalculatedLimits,
pub(crate) accepted_improvement: bool,
pub(crate) reality_check_applied: bool,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct TuningScore {
pub(crate) new_raw_score: u64,
pub(crate) penalty_factor: f64,
pub(crate) new_score: u64,
}
pub(crate) fn compute_tuning_score(
relevant_history: &[u64],
current_scpb: f64,
scpb_max: f64,
) -> TuningScore {
let new_raw_score = if relevant_history.is_empty() {
0
} else {
relevant_history.iter().sum::<u64>() / relevant_history.len() as u64
};
let penalty_factor = (current_scpb / scpb_max - 1.0).max(0.0);
let new_score = (new_raw_score as f64 / (1.0 + penalty_factor)) as u64;
TuningScore {
new_raw_score,
penalty_factor,
new_score,
}
}
#[cfg(test)]
pub(crate) fn evaluate_tuning_cycle(
current_limits: &CalculatedLimits,
last_tuning_limits: &CalculatedLimits,
last_tuning_score: u64,
baseline_speed_ema: f64,
relevant_history: &[u64],
current_scpb: f64,
scpb_max: f64,
) -> TuningEvaluation {
let score = compute_tuning_score(relevant_history, current_scpb, scpb_max);
evaluate_tuning_cycle_from_score(
current_limits,
last_tuning_limits,
last_tuning_score,
baseline_speed_ema,
score,
)
}
pub(crate) fn evaluate_tuning_cycle_from_score(
current_limits: &CalculatedLimits,
last_tuning_limits: &CalculatedLimits,
last_tuning_score: u64,
baseline_speed_ema: f64,
score: TuningScore,
) -> TuningEvaluation {
let new_score_f64 = score.new_score as f64;
let updated_baseline_speed_ema = if baseline_speed_ema == 0.0 {
new_score_f64
} else {
(new_score_f64 * BASELINE_ALPHA) + (baseline_speed_ema * (1.0 - BASELINE_ALPHA))
};
let best_score_before = last_tuning_score;
let baseline_u64 = updated_baseline_speed_ema as u64;
let mut updated_last_tuning_score = last_tuning_score;
let mut updated_last_tuning_limits = last_tuning_limits.clone();
let mut effective_limits = current_limits.clone();
let mut accepted_improvement = false;
let mut reality_check_applied = false;
if score.new_score > best_score_before {
updated_last_tuning_score = score.new_score;
updated_last_tuning_limits = current_limits.clone();
accepted_improvement = true;
} else {
effective_limits = last_tuning_limits.clone();
if best_score_before > 10_000
&& best_score_before > (updated_baseline_speed_ema * REALITY_CHECK_FACTOR) as u64
{
updated_last_tuning_score = baseline_u64;
reality_check_applied = true;
}
}
TuningEvaluation {
new_raw_score: score.new_raw_score,
penalty_factor: score.penalty_factor,
new_score: score.new_score,
updated_baseline_speed_ema,
best_score_before,
baseline_u64,
updated_last_tuning_score,
updated_last_tuning_limits,
effective_limits,
accepted_improvement,
reality_check_applied,
}
}
fn get_limit(limits: &CalculatedLimits, resource: ResourceType) -> usize {
match resource {
ResourceType::PeerConnection => limits.max_connected_peers,
ResourceType::DiskRead => limits.disk_read_permits,
ResourceType::DiskWrite => limits.disk_write_permits,
ResourceType::Reserve => limits.reserve_permits,
}
}
fn set_limit(limits: &mut CalculatedLimits, resource: ResourceType, value: usize) {
match resource {
ResourceType::PeerConnection => limits.max_connected_peers = value,
ResourceType::DiskRead => limits.disk_read_permits = value,
ResourceType::DiskWrite => limits.disk_write_permits = value,
ResourceType::Reserve => limits.reserve_permits = value,
}
}
pub(crate) fn make_random_adjustment(
limits: CalculatedLimits,
is_seeding: bool,
) -> (CalculatedLimits, String) {
let mut rng = rand::rng();
make_random_adjustment_with_rng(limits, is_seeding, &mut rng)
}
pub(crate) fn make_random_adjustment_with_rng<R: Rng + ?Sized>(
limits: CalculatedLimits,
is_seeding: bool,
rng: &mut R,
) -> (CalculatedLimits, String) {
let mut limits = if is_seeding {
normalize_limits_for_mode(&limits, true)
} else {
limits
};
let mut parameters = vec![
ResourceType::PeerConnection,
ResourceType::DiskRead,
ResourceType::Reserve,
];
if !is_seeding {
parameters.push(ResourceType::DiskWrite);
}
if parameters.len() < 2 {
return (
limits,
"Skipped all trade attempts (0): insufficient adjustable resources".to_string(),
);
}
for attempt in 0..MAX_TRADE_ATTEMPTS {
parameters.shuffle(rng);
let source_param = parameters[0];
let dest_param = parameters[1];
let source_val = get_limit(&limits, source_param);
let dest_val = get_limit(&limits, dest_param);
let source_min = match source_param {
ResourceType::PeerConnection => MIN_PEERS,
ResourceType::DiskRead => MIN_DISK,
ResourceType::DiskWrite => MIN_DISK,
ResourceType::Reserve => MIN_RESERVE,
};
let step_rate = rng.random_range(MIN_STEP_RATE..=MAX_STEP_RATE);
let amount_to_trade = ((source_val as f64 * step_rate).ceil() as usize).max(1);
let can_give = source_val >= source_min.saturating_add(amount_to_trade);
if can_give {
set_limit(
&mut limits,
source_param,
source_val.saturating_sub(amount_to_trade),
);
set_limit(
&mut limits,
dest_param,
dest_val.saturating_add(amount_to_trade),
);
let description = format!(
"Traded {} from {:?} to {:?} (Attempt {})",
amount_to_trade,
source_param,
dest_param,
attempt + 1
);
return (limits, description);
}
}
let description = format!(
"Skipped all trade attempts ({}) this cycle: blocked by bounds",
MAX_TRADE_ATTEMPTS
);
(limits, description)
}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::StdRng;
use rand::SeedableRng;
#[derive(Clone, Debug)]
struct SyntheticWorkload {
optimum: CalculatedLimits,
peak_score: u64,
reserve_penalty: u64,
peer_penalty: u64,
read_penalty: u64,
write_penalty: u64,
base_scpb: f64,
scpb_slope: f64,
}
impl SyntheticWorkload {
fn sample(&self, limits: &CalculatedLimits) -> (u64, f64) {
let reserve_delta = limits
.reserve_permits
.abs_diff(self.optimum.reserve_permits);
let peer_delta = limits
.max_connected_peers
.abs_diff(self.optimum.max_connected_peers);
let read_delta = limits
.disk_read_permits
.abs_diff(self.optimum.disk_read_permits);
let write_delta = limits
.disk_write_permits
.abs_diff(self.optimum.disk_write_permits);
let raw_penalty = (peer_delta as u64)
.saturating_mul(peer_delta as u64)
.saturating_mul(self.peer_penalty)
.saturating_add(
(reserve_delta as u64)
.saturating_mul(reserve_delta as u64)
.saturating_mul(self.reserve_penalty),
)
.saturating_add(
(read_delta as u64)
.saturating_mul(read_delta as u64)
.saturating_mul(self.read_penalty),
)
.saturating_add(
(write_delta as u64)
.saturating_mul(write_delta as u64)
.saturating_mul(self.write_penalty),
);
let raw_score = self.peak_score.saturating_sub(raw_penalty);
let disk_delta = limits
.disk_read_permits
.saturating_add(limits.disk_write_permits)
.abs_diff(self.optimum.disk_read_permits + self.optimum.disk_write_permits);
let scpb = self.base_scpb + (disk_delta as f64 * self.scpb_slope);
(raw_score, scpb)
}
}
#[derive(Debug)]
struct SimulationResult {
best_limits: CalculatedLimits,
best_score: u64,
accepted_count: usize,
reverted_count: usize,
score_trace: Vec<u64>,
}
struct SimulationState {
limits: CalculatedLimits,
last_tuning_limits: CalculatedLimits,
last_tuning_score: u64,
baseline_speed_ema: f64,
rng: StdRng,
}
impl SimulationState {
fn new(initial_limits: CalculatedLimits, seed: u64) -> Self {
Self {
limits: initial_limits.clone(),
last_tuning_limits: initial_limits,
last_tuning_score: 0,
baseline_speed_ema: 0.0,
rng: StdRng::seed_from_u64(seed),
}
}
fn run_phase(&mut self, cycles: usize, workload: &SyntheticWorkload) -> SimulationResult {
let mut accepted_count = 0usize;
let mut reverted_count = 0usize;
let mut score_trace = Vec::with_capacity(cycles);
let adaptive_max_scpb = 10.0;
for _ in 0..cycles {
let (raw_score, scpb) = workload.sample(&self.limits);
let history = [raw_score; 60];
let evaluation = evaluate_tuning_cycle(
&self.limits,
&self.last_tuning_limits,
self.last_tuning_score,
self.baseline_speed_ema,
&history,
scpb,
adaptive_max_scpb,
);
if evaluation.accepted_improvement {
accepted_count = accepted_count.saturating_add(1);
} else {
reverted_count = reverted_count.saturating_add(1);
}
score_trace.push(evaluation.new_score);
self.baseline_speed_ema = evaluation.updated_baseline_speed_ema;
self.last_tuning_score = evaluation.updated_last_tuning_score;
self.last_tuning_limits = evaluation.updated_last_tuning_limits;
self.limits = evaluation.effective_limits;
let (next_limits, _desc) =
make_random_adjustment_with_rng(self.limits.clone(), false, &mut self.rng);
self.limits = next_limits;
}
SimulationResult {
best_limits: self.last_tuning_limits.clone(),
best_score: self.last_tuning_score,
accepted_count,
reverted_count,
score_trace,
}
}
}
fn simulate_tuning_cycles(
initial_limits: CalculatedLimits,
cycles: usize,
seed: u64,
workload: &SyntheticWorkload,
) -> SimulationResult {
let mut state = SimulationState::new(initial_limits, seed);
state.run_phase(cycles, workload)
}
#[test]
fn tuner_simulation_converges_toward_known_optimum_no_noise() {
let initial_limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 110,
disk_read_permits: 30,
disk_write_permits: 20,
};
let workload = SyntheticWorkload {
optimum: CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 72,
disk_read_permits: 14,
disk_write_permits: 10,
},
peak_score: 120_000,
reserve_penalty: 0,
peer_penalty: 4,
read_penalty: 70,
write_penalty: 80,
base_scpb: 4.0,
scpb_slope: 0.15,
};
let result = simulate_tuning_cycles(initial_limits, 500, 7, &workload);
assert!(
result.best_score > 100_000,
"Expected strong improvement in best score"
);
assert!(
result
.best_limits
.max_connected_peers
.abs_diff(workload.optimum.max_connected_peers)
<= 12
);
assert!(
result
.best_limits
.disk_read_permits
.abs_diff(workload.optimum.disk_read_permits)
<= 4
);
assert!(
result
.best_limits
.disk_write_permits
.abs_diff(workload.optimum.disk_write_permits)
<= 4
);
}
#[test]
fn tuner_simulation_under_disk_pressure_converges_to_lower_disk_limits() {
let initial_limits = CalculatedLimits {
reserve_permits: 18,
max_connected_peers: 72,
disk_read_permits: 34,
disk_write_permits: 26,
};
let workload = SyntheticWorkload {
optimum: CalculatedLimits {
reserve_permits: 18,
max_connected_peers: 72,
disk_read_permits: 10,
disk_write_permits: 8,
},
peak_score: 100_000,
reserve_penalty: 0,
peer_penalty: 0,
read_penalty: 120,
write_penalty: 140,
base_scpb: 7.5,
scpb_slope: 0.18,
};
let initial_disk_total =
initial_limits.disk_read_permits + initial_limits.disk_write_permits;
let result = simulate_tuning_cycles(initial_limits, 600, 41, &workload);
let best_disk_total =
result.best_limits.disk_read_permits + result.best_limits.disk_write_permits;
let optimum_disk_total =
workload.optimum.disk_read_permits + workload.optimum.disk_write_permits;
assert!(
result.best_score > 80_000,
"Expected disk-throttled workload to recover a strong score"
);
assert!(
best_disk_total + 20 <= initial_disk_total,
"Expected disk budget to throttle down materially under pressure"
);
assert!(
best_disk_total.abs_diff(optimum_disk_total) <= 4,
"Expected best disk budget to converge near the lower-pressure optimum"
);
assert!(
result
.best_limits
.disk_read_permits
.abs_diff(workload.optimum.disk_read_permits)
<= 3
);
assert!(
result
.best_limits
.disk_write_permits
.abs_diff(workload.optimum.disk_write_permits)
<= 3
);
}
#[test]
fn tuner_simulation_converges_toward_nonzero_reserve_optimum() {
let initial_limits = CalculatedLimits {
reserve_permits: 4,
max_connected_peers: 92,
disk_read_permits: 20,
disk_write_permits: 14,
};
let workload = SyntheticWorkload {
optimum: CalculatedLimits {
reserve_permits: 34,
max_connected_peers: 72,
disk_read_permits: 14,
disk_write_permits: 10,
},
peak_score: 110_000,
reserve_penalty: 45,
peer_penalty: 10,
read_penalty: 55,
write_penalty: 65,
base_scpb: 5.0,
scpb_slope: 0.10,
};
let result = simulate_tuning_cycles(initial_limits, 600, 73, &workload);
assert!(
result.best_score > 95_000,
"Expected strong recovery after moving excess budget into reserve"
);
assert!(
result.best_limits.reserve_permits > 20,
"Expected tuner to increase reserve materially from the initial allocation"
);
assert!(
result
.best_limits
.reserve_permits
.abs_diff(workload.optimum.reserve_permits)
<= 5,
"Expected reserve allocation to converge near the optimum"
);
assert!(
result
.best_limits
.max_connected_peers
.abs_diff(workload.optimum.max_connected_peers)
<= 8
);
assert!(
result
.best_limits
.disk_read_permits
.abs_diff(workload.optimum.disk_read_permits)
<= 4
);
assert!(
result
.best_limits
.disk_write_permits
.abs_diff(workload.optimum.disk_write_permits)
<= 4
);
}
#[test]
fn tuner_simulation_recovers_from_high_reserve_phase_when_demand_returns() {
let initial_limits = CalculatedLimits {
reserve_permits: 4,
max_connected_peers: 92,
disk_read_permits: 20,
disk_write_permits: 14,
};
let reserve_heavy_workload = SyntheticWorkload {
optimum: CalculatedLimits {
reserve_permits: 34,
max_connected_peers: 72,
disk_read_permits: 14,
disk_write_permits: 10,
},
peak_score: 110_000,
reserve_penalty: 45,
peer_penalty: 10,
read_penalty: 55,
write_penalty: 65,
base_scpb: 5.0,
scpb_slope: 0.10,
};
let active_workload = SyntheticWorkload {
optimum: CalculatedLimits {
reserve_permits: 6,
max_connected_peers: 90,
disk_read_permits: 20,
disk_write_permits: 14,
},
peak_score: 140_000,
reserve_penalty: 90,
peer_penalty: 16,
read_penalty: 55,
write_penalty: 60,
base_scpb: 5.0,
scpb_slope: 0.08,
};
let mut state = SimulationState::new(initial_limits, 101);
let reserve_phase = state.run_phase(500, &reserve_heavy_workload);
let recovery_phase = state.run_phase(700, &active_workload);
assert!(
reserve_phase.best_limits.reserve_permits >= 28,
"Expected first phase to converge into a high-reserve steady state, got {}",
reserve_phase.best_limits.reserve_permits
);
assert!(
recovery_phase.best_score > 95_000,
"Expected strong recovery after shifting budget back out of reserve, got {}",
recovery_phase.best_score
);
assert!(
recovery_phase.best_limits.reserve_permits + 14
<= reserve_phase.best_limits.reserve_permits,
"Expected reserve allocation to fall materially once demand returns, reserve phase={}, recovery phase={}",
reserve_phase.best_limits.reserve_permits,
recovery_phase.best_limits.reserve_permits
);
assert!(
recovery_phase
.best_limits
.reserve_permits
.abs_diff(active_workload.optimum.reserve_permits)
<= 5,
"Expected reserve allocation to reconverge near the lower-demand optimum, got {}",
recovery_phase.best_limits.reserve_permits
);
assert!(
recovery_phase
.best_limits
.max_connected_peers
.abs_diff(active_workload.optimum.max_connected_peers)
<= 8
);
assert!(
recovery_phase
.best_limits
.disk_read_permits
.abs_diff(active_workload.optimum.disk_read_permits)
<= 4
);
assert!(
recovery_phase
.best_limits
.disk_write_permits
.abs_diff(active_workload.optimum.disk_write_permits)
<= 4
);
}
#[test]
fn tuner_evaluation_reverts_when_candidate_is_worse() {
let current_limits = CalculatedLimits {
reserve_permits: 30,
max_connected_peers: 140,
disk_read_permits: 40,
disk_write_permits: 32,
};
let good_limits = CalculatedLimits {
reserve_permits: 30,
max_connected_peers: 80,
disk_read_permits: 14,
disk_write_permits: 10,
};
let history = [10_000u64; 60];
let eval = evaluate_tuning_cycle(
¤t_limits,
&good_limits,
40_000,
15_000.0,
&history,
12.0,
10.0,
);
assert!(!eval.accepted_improvement);
assert_eq!(
eval.effective_limits.max_connected_peers,
good_limits.max_connected_peers
);
assert_eq!(
eval.effective_limits.disk_read_permits,
good_limits.disk_read_permits
);
assert_eq!(
eval.effective_limits.disk_write_permits,
good_limits.disk_write_permits
);
}
#[test]
fn tuner_simulation_plateau_stays_stable_without_runaway() {
let initial_limits = CalculatedLimits {
reserve_permits: 25,
max_connected_peers: 80,
disk_read_permits: 12,
disk_write_permits: 10,
};
let workload = SyntheticWorkload {
optimum: CalculatedLimits {
reserve_permits: 25,
max_connected_peers: 80,
disk_read_permits: 12,
disk_write_permits: 10,
},
peak_score: 50_000,
reserve_penalty: 0,
peer_penalty: 0,
read_penalty: 0,
write_penalty: 0,
base_scpb: 3.0,
scpb_slope: 0.0,
};
let result = simulate_tuning_cycles(initial_limits, 120, 13, &workload);
assert!(result.reverted_count > result.accepted_count);
assert!(result.score_trace.windows(2).all(|w| w[0] == w[1]));
}
#[test]
fn random_adjustment_respects_min_bounds_over_many_steps() {
let mut limits = CalculatedLimits {
reserve_permits: 40,
max_connected_peers: MIN_PEERS + 10,
disk_read_permits: MIN_DISK + 5,
disk_write_permits: MIN_DISK + 5,
};
let mut rng = StdRng::seed_from_u64(99);
for _ in 0..2_000 {
let (next, _desc) = make_random_adjustment_with_rng(limits, false, &mut rng);
limits = next;
assert!(limits.max_connected_peers >= MIN_PEERS);
assert!(limits.disk_read_permits >= MIN_DISK);
assert!(limits.disk_write_permits >= MIN_DISK);
}
}
#[test]
fn tuner_evaluation_reality_check_resets_stale_best_score() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 90,
disk_read_permits: 16,
disk_write_permits: 12,
};
let history = [800u64; 60];
let eval = evaluate_tuning_cycle(&limits, &limits, 60_000, 1_000.0, &history, 10.0, 10.0);
assert!(eval.reality_check_applied);
assert_eq!(eval.updated_last_tuning_score, eval.baseline_u64);
assert!(!eval.accepted_improvement);
}
#[test]
fn seeding_adjustment_disables_disk_write_trades_and_sets_zero_write_slots() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let mut rng = StdRng::seed_from_u64(123);
for _ in 0..200 {
let (next, _desc) = make_random_adjustment_with_rng(limits.clone(), true, &mut rng);
assert_eq!(next.disk_write_permits, 0);
}
}
#[test]
fn seeding_adjustment_preserves_total_disk_slots_by_moving_write_to_read() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let expected_total = limits
.reserve_permits
.saturating_add(limits.max_connected_peers)
.saturating_add(limits.disk_read_permits)
.saturating_add(limits.disk_write_permits);
let mut rng = StdRng::seed_from_u64(321);
for _ in 0..200 {
let (next, _desc) = make_random_adjustment_with_rng(limits.clone(), true, &mut rng);
assert_eq!(next.disk_write_permits, 0);
let next_total = next
.reserve_permits
.saturating_add(next.max_connected_peers)
.saturating_add(next.disk_read_permits)
.saturating_add(next.disk_write_permits);
assert_eq!(next_total, expected_total);
}
}
#[test]
fn normalize_limits_for_mode_seeding_zeros_write_and_preserves_total() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let normalized = normalize_limits_for_mode(&limits, true);
let before_total = limits
.reserve_permits
.saturating_add(limits.max_connected_peers)
.saturating_add(limits.disk_read_permits)
.saturating_add(limits.disk_write_permits);
let after_total = normalized
.reserve_permits
.saturating_add(normalized.max_connected_peers)
.saturating_add(normalized.disk_read_permits)
.saturating_add(normalized.disk_write_permits);
assert_eq!(normalized.disk_write_permits, 0);
assert_eq!(before_total, after_total);
}
#[test]
fn normalize_limits_for_mode_seeding_targets_70_30_peer_read_and_zero_reserve_write() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let normalized = normalize_limits_for_mode(&limits, true);
assert_eq!(normalized.max_connected_peers, 74);
assert_eq!(normalized.disk_read_permits, 32);
assert_eq!(normalized.reserve_permits, 0);
assert_eq!(normalized.disk_write_permits, 0);
}
#[test]
fn normalize_limits_for_mode_downloading_targets_30_70_read_write_split() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let normalized = normalize_limits_for_mode(&limits, false);
let before_disk_total = limits.disk_read_permits + limits.disk_write_permits;
let after_disk_total = normalized.disk_read_permits + normalized.disk_write_permits;
assert_eq!(before_disk_total, after_disk_total);
assert_eq!(normalized.disk_read_permits, 6);
assert_eq!(normalized.disk_write_permits, 16);
}
#[test]
fn tuning_controller_fixed_policy_uses_default_lookback_and_countdown() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let mut controller = TuningController::new_fixed(limits);
assert_eq!(controller.cadence_secs(), DEFAULT_TUNING_CADENCE_SECS);
assert_eq!(controller.lookback_secs(), DEFAULT_TUNING_LOOKBACK_SECS);
assert_eq!(controller.countdown_secs(), DEFAULT_TUNING_CADENCE_SECS);
controller.on_second_tick();
assert_eq!(
controller.countdown_secs(),
DEFAULT_TUNING_CADENCE_SECS.saturating_sub(1)
);
}
#[test]
fn tuning_controller_objective_reset_clears_scores_and_ema() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let mut controller = TuningController::new_fixed(limits.clone());
let history = [30_000u64; 60];
let _ = controller.evaluate_cycle(&limits, &history, 12.0, 10.0);
assert!(controller.state().current_tuning_score > 0);
controller.reset_for_objective_change(&limits);
assert_eq!(controller.state().last_tuning_score, 0);
assert_eq!(controller.state().current_tuning_score, 0);
assert_eq!(controller.state().baseline_speed_ema, 0.0);
}
#[test]
fn tuning_controller_evaluate_cycle_resets_countdown_and_tracks_best() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let mut controller = TuningController::new_fixed(limits.clone());
controller.on_second_tick();
controller.on_second_tick();
assert_eq!(controller.countdown_secs(), DEFAULT_TUNING_CADENCE_SECS - 2);
let strong_history = [40_000u64; 60];
let eval = controller.evaluate_cycle(&limits, &strong_history, 8.0, 10.0);
assert_eq!(controller.countdown_secs(), DEFAULT_TUNING_CADENCE_SECS);
assert!(eval.accepted_improvement);
assert_eq!(controller.state().last_tuning_score, eval.new_score);
assert_eq!(
controller.state().last_tuning_limits.max_connected_peers,
limits.max_connected_peers
);
}
#[test]
fn adaptive_controller_starts_fast_with_linked_lookback() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let controller = TuningController::new_adaptive(limits);
assert_eq!(controller.cadence_secs(), FAST_START_CADENCE_SECS);
assert!(controller.lookback_secs() <= controller.cadence_secs() as usize);
assert_eq!(controller.countdown_secs(), FAST_START_CADENCE_SECS);
}
#[test]
fn adaptive_controller_backs_off_after_stagnation() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let mut controller = TuningController::new_adaptive(limits.clone());
let history_good = [40_000u64; 60];
let _ = controller.evaluate_cycle(&limits, &history_good, 8.0, 10.0);
let cadence_after_accept = controller.cadence_secs();
let history_same = [40_000u64; 60];
let _ = controller.evaluate_cycle(&limits, &history_same, 8.0, 10.0);
let cadence_after_first_stall = controller.cadence_secs();
let _ = controller.evaluate_cycle(&limits, &history_same, 8.0, 10.0);
let cadence_after_second_stall = controller.cadence_secs();
assert!(cadence_after_accept <= FAST_START_CADENCE_SECS);
assert!(cadence_after_accept >= MIN_TUNING_CADENCE_SECS);
assert_eq!(cadence_after_first_stall, cadence_after_accept);
assert!(cadence_after_second_stall > cadence_after_first_stall);
}
#[test]
fn adaptive_controller_speeds_up_on_rapid_regression() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let mut controller = TuningController::new_adaptive(limits.clone());
let history_good = [50_000u64; 60];
let _ = controller.evaluate_cycle(&limits, &history_good, 8.0, 10.0);
let history_same = [50_000u64; 60];
let _ = controller.evaluate_cycle(&limits, &history_same, 8.0, 10.0);
let _ = controller.evaluate_cycle(&limits, &history_same, 8.0, 10.0);
let cadence_before_drop = controller.cadence_secs();
let history_drop = [10_000u64; 60];
let _ = controller.evaluate_cycle(&limits, &history_drop, 8.0, 10.0);
let cadence_after_drop = controller.cadence_secs();
assert!(cadence_before_drop > MIN_TUNING_CADENCE_SECS);
assert!(cadence_after_drop < cadence_before_drop);
}
#[test]
fn adaptive_controller_forces_backoff_when_change_pressure_is_high() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let mut controller = TuningController::new_adaptive(limits.clone());
controller.cadence_change_pressure = CADENCE_CHANGE_PRESSURE_TRIGGER - 1;
let history_good = [40_000u64; 60];
let _ = controller.evaluate_cycle(&limits, &history_good, 8.0, 10.0);
assert!(controller.cadence_secs() > FAST_START_CADENCE_SECS);
assert!(controller.cadence_change_pressure < CADENCE_CHANGE_PRESSURE_TRIGGER);
}
#[test]
fn adaptive_controller_limits_regression_speedups_then_backs_off() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let mut controller = TuningController::new_adaptive(limits.clone());
let history_good = [50_000u64; 60];
let _ = controller.evaluate_cycle(&limits, &history_good, 8.0, 10.0);
let baseline_cadence = controller.cadence_secs();
let history_drop = [10_000u64; 60];
let mut saw_backoff = false;
let mut previous = baseline_cadence;
for _ in 0..10 {
let _ = controller.evaluate_cycle(&limits, &history_drop, 8.0, 10.0);
let current = controller.cadence_secs();
if current > previous {
saw_backoff = true;
break;
}
previous = current;
}
assert!(saw_backoff);
}
#[test]
fn adaptive_controller_decays_stale_best_after_repeated_no_improvement() {
let limits = CalculatedLimits {
reserve_permits: 20,
max_connected_peers: 64,
disk_read_permits: 12,
disk_write_permits: 10,
};
let mut controller = TuningController::new_adaptive(limits.clone());
let history_good = [60_000u64; 60];
let _ = controller.evaluate_cycle(&limits, &history_good, 8.0, 10.0);
let best_before = controller.state().last_tuning_score;
let history_worse = [35_000u64; 60];
for _ in 0..(STALE_BEST_DECAY_START_CYCLES + 1) {
let _ = controller.evaluate_cycle(&limits, &history_worse, 8.0, 10.0);
}
assert!(controller.state().last_tuning_score < best_before);
}
}