CAS Contention in Traffic Bucket bounded

This commit is contained in:
Alexey
2026-09-25 06:32:01 +03:00
parent 08109d53e8
commit 26a574780e
9 changed files with 1308 additions and 159 deletions
+96 -2
View File
@@ -33,45 +33,91 @@ const REGISTRY_SHARDS: usize = 64;
const FAIR_EPOCH_MS: u64 = 20;
const MAX_BORROW_CHUNK_BYTES: u64 = 32 * 1024;
const CLEANUP_INTERVAL_SECS: u64 = 60;
const RESERVE_CAS_ATTEMPT_LIMIT: usize = 16;
const REFUND_CAS_ATTEMPT_LIMIT: usize = 8;
const PACKED_USAGE_BITS: u32 = 28;
const PACKED_USAGE_MASK: u64 = (1u64 << PACKED_USAGE_BITS) - 1;
const PACKED_EPOCH_MAX: u64 = (1u64 << (u64::BITS - PACKED_USAGE_BITS)) - 1;
/// Traffic direction used by rate-limit accounting.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RateDirection {
/// Traffic received from a client.
Up,
/// Traffic sent to a client.
Down,
}
/// Result of an immediate traffic-budget request.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TrafficConsumeResult {
/// Number of bytes granted by all applicable limits.
pub granted: u64,
/// Whether the per-user limit prevented a grant.
pub blocked_user: bool,
/// Whether the per-CIDR limit prevented a grant.
pub blocked_cidr: bool,
}
/// Process-wide traffic-limiter counters and gauges.
#[derive(Debug, Clone, Copy)]
pub struct TrafficLimiterMetricsSnapshot {
/// Per-user upload throttle events.
pub user_throttle_up_total: u64,
/// Per-user download throttle events.
pub user_throttle_down_total: u64,
/// Per-CIDR upload throttle events.
pub cidr_throttle_up_total: u64,
/// Per-CIDR download throttle events.
pub cidr_throttle_down_total: u64,
/// Per-user accumulated upload wait time in milliseconds.
pub user_wait_up_ms_total: u64,
/// Per-user accumulated download wait time in milliseconds.
pub user_wait_down_ms_total: u64,
/// Per-CIDR accumulated upload wait time in milliseconds.
pub cidr_wait_up_ms_total: u64,
/// Per-CIDR accumulated download wait time in milliseconds.
pub cidr_wait_down_ms_total: u64,
/// Per-user upload reservations that exhausted their CAS budget.
pub user_reserve_cas_retry_exhausted_up_total: u64,
/// Per-user download reservations that exhausted their CAS budget.
pub user_reserve_cas_retry_exhausted_down_total: u64,
/// Per-user upload refunds that exhausted their CAS budget.
pub user_refund_cas_retry_exhausted_up_total: u64,
/// Per-user download refunds that exhausted their CAS budget.
pub user_refund_cas_retry_exhausted_down_total: u64,
/// Per-CIDR upload reservations that exhausted their CAS budget.
pub cidr_reserve_cas_retry_exhausted_up_total: u64,
/// Per-CIDR download reservations that exhausted their CAS budget.
pub cidr_reserve_cas_retry_exhausted_down_total: u64,
/// Per-CIDR upload refunds that exhausted their CAS budget.
pub cidr_refund_cas_retry_exhausted_up_total: u64,
/// Per-CIDR download refunds that exhausted their CAS budget.
pub cidr_refund_cas_retry_exhausted_down_total: u64,
/// Active leases with a per-user rate limit.
pub user_active_leases: u64,
/// Active leases with a per-CIDR rate limit.
pub cidr_active_leases: u64,
/// Configured per-user rate-limit entries.
pub user_policy_entries: u64,
/// Configured per-CIDR rate-limit entries.
pub cidr_policy_entries: u64,
}
#[derive(Default)]
struct CasContentionMetrics {
reserve_exhausted_total: AtomicU64,
refund_exhausted_total: AtomicU64,
}
#[derive(Default)]
struct ScopeMetrics {
throttle_up_total: AtomicU64,
throttle_down_total: AtomicU64,
wait_up_ms_total: AtomicU64,
wait_down_ms_total: AtomicU64,
contention_up: Arc<CasContentionMetrics>,
contention_down: Arc<CasContentionMetrics>,
active_leases: AtomicU64,
policy_entries: AtomicU64,
}
@@ -86,6 +132,54 @@ struct AtomicRatePair {
#[derive(Default)]
struct DirectionBucket {
state: AtomicU64,
contention: Arc<CasContentionMetrics>,
#[cfg(test)]
forced_reserve_failures: AtomicU64,
#[cfg(test)]
forced_refund_failures: AtomicU64,
#[cfg(test)]
reserve_cas_attempts: AtomicU64,
#[cfg(test)]
refund_cas_attempts: AtomicU64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum BucketReserveError {
StaleEpoch,
Contended,
RefundContended,
ReserveAndRefundContended,
FairShareContended,
}
impl BucketReserveError {
fn exhausted_reserve_budget(self) -> bool {
matches!(
self,
Self::Contended | Self::ReserveAndRefundContended
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum BucketRefundOutcome {
Complete,
Contended,
}
struct ReserveCasBudget {
remaining: usize,
}
struct BucketReservation {
granted: u64,
debit: Option<DirectionDebit>,
}
struct CidrReservation {
granted: u64,
aggregate_debit: Option<DirectionDebit>,
user_debit: Option<DirectionDebit>,
}
struct UserBucket {
@@ -95,13 +189,11 @@ struct UserBucket {
active_leases: AtomicU64,
}
#[derive(Default)]
struct CidrDirectionBucket {
used: Arc<DirectionBucket>,
active_users: Arc<DirectionBucket>,
}
#[derive(Default)]
struct CidrUserDirectionState {
used: Arc<DirectionBucket>,
}
@@ -165,6 +257,7 @@ struct TrafficLeaseBinding {
cidr_user_share: Option<Arc<CidrUserShare>>,
}
/// A live traffic-limiter binding for one authenticated client session.
pub struct TrafficLease {
limiter: Arc<TrafficLimiter>,
user: String,
@@ -173,6 +266,7 @@ pub struct TrafficLease {
refresh: ParkingMutex<()>,
}
/// Owns rate-limit policy, shared buckets, and limiter telemetry.
pub struct TrafficLimiter {
policy: ArcSwap<PolicySnapshot>,
policy_update: ParkingMutex<()>,