use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use crate::{atomic_load, atomic_store, config::RequesterPolicy};
use super::{limit_heap::LimitHeap, PolicyTrigger};
#[derive(Default, Debug)]
pub struct PolicyData {
pub(super) policy: RequesterPolicy,
pub(super) cooling_down: AtomicBool,
pub(super) wait_time: u64,
limit: AtomicUsize,
pub(super) heap_initialized: AtomicBool,
pub(super) errors: [AtomicUsize; 3],
pub(super) remove_limit: AtomicBool,
pub(super) heap: std::sync::RwLock<LimitHeap>,
pub(super) rate_limit: Option<usize>,
}
impl PolicyData {
pub fn new(policy: RequesterPolicy, timeout: u64) -> Self {
const MAX_WAIT_TIME_MS: u64 = 30_000;
let wait_time = ((timeout as f64 / 2.0) * 1000.0) as u64;
let wait_time = wait_time.min(MAX_WAIT_TIME_MS);
Self {
policy,
wait_time,
..Default::default()
}
}
pub fn with_rate_limit(mut self, rate_limit: usize) -> Self {
self.rate_limit = Some(rate_limit);
self
}
pub(super) fn set_reqs_sec(&self, reqs_sec: usize) {
if let Ok(mut guard) = self.heap.write() {
guard.original = reqs_sec as i32;
guard.build();
if let Some(cap) = self.rate_limit {
guard.clamp_to_max(cap as i32);
}
self.set_limit(guard.inner[0] as usize); self.heap_initialized.store(true, Ordering::Release);
} else {
log::warn!("Could not acquire heap write lock in set_reqs_sec; heap not initialized");
}
}
pub(super) fn set_errors(&self, trigger: PolicyTrigger, errors: usize) {
if trigger == PolicyTrigger::TryAdjustUp {
return;
}
atomic_store!(self.errors[trigger.as_index()], errors);
}
pub(super) fn get_errors(&self, trigger: PolicyTrigger) -> usize {
if trigger == PolicyTrigger::TryAdjustUp {
return 0;
}
atomic_load!(self.errors[trigger.as_index()])
}
pub(super) fn heap_initialized(&self) -> bool {
atomic_load!(self.heap_initialized, Ordering::Acquire)
}
pub(super) fn reset_heap(&self) {
if let Ok(mut guard) = self.heap.write() {
*guard = LimitHeap::default();
self.heap_initialized.store(false, Ordering::Release);
} else {
log::warn!("Could not acquire heap write lock in reset_heap");
}
}
fn set_limit(&self, limit: usize) {
atomic_store!(self.limit, limit);
}
pub(super) fn get_limit(&self) -> usize {
atomic_load!(self.limit)
}
pub(super) fn adjust_up(&self, streak_counter: &usize) {
if let Ok(mut heap) = self.heap.try_write() {
if *streak_counter > 2 {
let current = heap.value();
heap.move_up();
heap.move_up();
if current > heap.value() {
if heap.has_parent() && heap.parent_value() > current {
heap.move_up();
}
}
} else if heap.has_children() {
heap.move_left();
} else {
let current = heap.value();
heap.move_up();
heap.move_up();
if current > heap.value() {
heap.move_up();
}
}
if !heap.has_parent() {
atomic_store!(self.remove_limit, true);
}
self.set_limit(heap.value() as usize);
} else {
log::debug!("Could not acquire heap write lock in adjust_up; rate limit unchanged");
}
}
pub(super) fn adjust_down(&self) {
if let Ok(mut heap) = self.heap.try_write() {
if heap.has_children() {
heap.move_right();
self.set_limit(heap.value() as usize);
}
} else {
log::debug!("Could not acquire heap write lock in adjust_down; rate limit unchanged");
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn set_reqs_sec_builds_heap_and_sets_initial_value() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
assert_eq!(pd.wait_time, 3500);
pd.set_reqs_sec(400);
assert_eq!(pd.get_limit(), 200);
assert_eq!(pd.heap.read().unwrap().original, 400);
assert_eq!(pd.heap.read().unwrap().current, 0);
assert_eq!(pd.heap.read().unwrap().inner[0], 200);
assert_eq!(pd.heap.read().unwrap().inner[1], 300);
assert_eq!(pd.heap.read().unwrap().inner[2], 100);
}
#[test]
fn policy_data_getters_and_setters() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_errors(PolicyTrigger::Errors, 20);
assert_eq!(pd.get_errors(PolicyTrigger::Errors), 20);
pd.set_errors(PolicyTrigger::Status403, 15);
assert_eq!(pd.get_errors(PolicyTrigger::Status403), 15);
pd.set_errors(PolicyTrigger::Status429, 10);
assert_eq!(pd.get_errors(PolicyTrigger::Status429), 10);
pd.set_limit(200);
assert_eq!(pd.get_limit(), 200);
}
#[test]
fn policy_data_adjust_down_simple() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_reqs_sec(400);
assert_eq!(pd.get_limit(), 200);
pd.adjust_down();
assert_eq!(pd.get_limit(), 100);
}
#[test]
fn policy_data_adjust_down_no_children() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_reqs_sec(400);
assert_eq!(pd.get_limit(), 200);
let mut guard = pd.heap.write().unwrap();
guard.move_to(250);
guard.set_value(27);
pd.set_limit(guard.value() as usize);
drop(guard);
pd.adjust_down();
assert_eq!(pd.get_limit(), 27);
}
#[test]
fn policy_data_adjust_up_simple() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_reqs_sec(400);
assert_eq!(pd.get_limit(), 200);
pd.adjust_up(&0);
assert_eq!(pd.get_limit(), 300);
}
#[test]
fn policy_data_adjust_up_with_streak_and_2_moves() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_reqs_sec(400);
assert_eq!(pd.get_limit(), 200);
pd.heap.write().unwrap().move_to(9);
assert_eq!(pd.heap.read().unwrap().value(), 275);
pd.adjust_up(&3);
assert_eq!(pd.heap.read().unwrap().value(), 300);
assert_eq!(pd.limit.load(Ordering::Relaxed), 300);
assert!(!pd.remove_limit.load(Ordering::Relaxed));
}
#[test]
fn policy_data_adjust_up_with_streak_and_2_moves_to_arrive_at_root() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_reqs_sec(400);
assert_eq!(pd.get_limit(), 200);
pd.heap.write().unwrap().move_to(4);
assert_eq!(pd.heap.read().unwrap().value(), 250);
pd.adjust_up(&3);
assert_eq!(pd.heap.read().unwrap().value(), 200);
assert_eq!(pd.limit.load(Ordering::Relaxed), 200);
assert!(pd.remove_limit.load(Ordering::Relaxed));
}
#[test]
fn policy_data_adjust_up_with_streak_and_2_moves_to_find_less_than_current() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_reqs_sec(400);
assert_eq!(pd.get_limit(), 200);
pd.heap.write().unwrap().move_to(15);
assert_eq!(pd.heap.read().unwrap().value(), 387);
pd.adjust_up(&3);
assert_eq!(pd.heap.read().unwrap().value(), 350);
assert_eq!(pd.limit.load(Ordering::Relaxed), 350);
assert!(!pd.remove_limit.load(Ordering::Relaxed));
}
#[test]
fn policy_data_adjust_up_with_streak_and_3_moves() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_reqs_sec(400);
assert_eq!(pd.get_limit(), 200);
pd.heap.write().unwrap().move_to(19);
assert_eq!(pd.heap.read().unwrap().value(), 287);
pd.adjust_up(&3);
assert_eq!(pd.heap.read().unwrap().value(), 300);
assert_eq!(pd.limit.load(Ordering::Relaxed), 300);
assert!(!pd.remove_limit.load(Ordering::Relaxed));
}
#[test]
fn policy_data_adjust_up_with_no_children_2_moves() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_reqs_sec(400);
assert_eq!(pd.get_limit(), 200);
pd.heap.write().unwrap().move_to(241);
assert_eq!(pd.heap.read().unwrap().value(), 41);
pd.adjust_up(&0);
assert_eq!(pd.heap.read().unwrap().value(), 43);
assert_eq!(pd.limit.load(Ordering::Relaxed), 43);
assert!(!pd.remove_limit.load(Ordering::Relaxed));
}
#[test]
fn policy_data_adjust_up_with_no_children_3_moves() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_reqs_sec(400);
assert_eq!(pd.get_limit(), 200);
pd.heap.write().unwrap().move_to(240);
assert_eq!(pd.heap.read().unwrap().value(), 45);
pd.adjust_up(&0);
assert_eq!(pd.heap.read().unwrap().value(), 37);
assert_eq!(pd.limit.load(Ordering::Relaxed), 37);
assert!(!pd.remove_limit.load(Ordering::Relaxed));
}
#[test]
fn increase_limit_heap_coverage_by_hitting_edge_cases() {
let pd = PolicyData::new(RequesterPolicy::AutoBail, 7);
pd.set_reqs_sec(400);
println!("{:?}", pd.heap.read().unwrap());
pd.heap.write().unwrap().move_to(240);
assert_eq!(pd.heap.write().unwrap().move_right(), 240);
assert_eq!(pd.heap.write().unwrap().move_left(), 240);
pd.heap.write().unwrap().move_to(0);
assert_eq!(pd.heap.write().unwrap().move_up(), 0);
assert_eq!(pd.heap.write().unwrap().parent_value(), 400);
}
}