Skip to main content

net/adapter/net/redex/
bandwidth.rs

1//! Per-stream bandwidth class for the v0.3 Phase D replication
2//! admission gate + send-queue priority + anti-starvation hatch.
3//!
4//! The class rides the [`SyncRequest`](super::replication::SyncRequest)
5//! wire frame so receivers can hint sender-side priority on a
6//! per-request basis. The [`ReplicationConfig`](super::replication_config::ReplicationConfig)
7//! carries a per-channel default that requests inherit when their
8//! wire-encoded class is absent (legacy peers) or when the caller
9//! didn't explicitly override.
10//!
11//! This is the canonical home of the type. The blob layer's
12//! `dataforts::blob::bandwidth` re-exports it alongside the
13//! `dataforts:blob-bandwidth-class-supported` capability tag +
14//! the [`BandwidthClassSupportProbe`](super::super::dataforts::blob::bandwidth::BandwidthClassSupportProbe)
15//! downgrade hook.
16
17/// Per-stream bandwidth class hint. Drives the v0.3 Phase D
18/// admission gate (`BandwidthBudget::try_consume_with_class`) +
19/// the anti-starvation hatch.
20///
21/// `Foreground` is the default — interactive workloads, normal
22/// RPC responses, anything a person is waiting on.
23///
24/// `Background` is admitted only when the bucket has at least
25/// `(1 - background_fraction) × capacity` available. The
26/// anti-starvation hatch one-shot-bypasses the gate when
27/// `Background` has been denied for > 60 s.
28///
29/// `Realtime` bypasses the rate-limit failure path entirely
30/// (still subject to disk-pressure circuit-breakers). Reserved
31/// for control-plane traffic and operator-triggered repair
32/// sweeps.
33#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
34pub enum BandwidthClass {
35    /// Default class — interactive / user-driven work.
36    ///
37    /// **WARNING: v0.2-compat default — silently slips past D-phase
38    /// rate bounds.** This is `Default` so existing v0.2 callers
39    /// migrating to v0.3 preserve their pre-D2 latency
40    /// characteristics: every replication request keeps acting like
41    /// Foreground unless the caller explicitly tags Background or
42    /// Realtime. The trade-off is that a Phase-D-aware peer that
43    /// FORGETS to override the default also gets full Foreground
44    /// rate, undermining the budget bound. Code that emits or
45    /// constructs `SyncRequest` / `RuntimeInputs` SHOULD pick a
46    /// class explicitly per workload, not rely on `Default`.
47    #[default]
48    Foreground,
49    /// TB-scale background work — backfills, migrations,
50    /// cold-blob warming. Bounded by `background_fraction`.
51    Background,
52    /// Operator-pinned. Bypasses per-class rate budget.
53    Realtime,
54}
55
56impl BandwidthClass {
57    /// Wire-encoded discriminant. Pinned for backward compat:
58    /// new variants must take new discriminant values; never
59    /// re-purpose an existing value. Wrapping a legacy peer's
60    /// missing-class trailing byte defaults to `Foreground` via
61    /// [`Self::from_wire_or_default`].
62    pub const FOREGROUND_WIRE: u8 = 0;
63    /// See [`Self::FOREGROUND_WIRE`].
64    pub const BACKGROUND_WIRE: u8 = 1;
65    /// See [`Self::FOREGROUND_WIRE`].
66    pub const REALTIME_WIRE: u8 = 2;
67
68    /// Encode to the 1-byte wire form. The replication layer
69    /// appends this to [`SyncRequest`](super::replication::SyncRequest)
70    /// frames as a trailing byte; legacy 55-byte frames omit it
71    /// entirely and are read back via [`Self::from_wire_or_default`].
72    pub fn as_u8(self) -> u8 {
73        match self {
74            Self::Foreground => Self::FOREGROUND_WIRE,
75            Self::Background => Self::BACKGROUND_WIRE,
76            Self::Realtime => Self::REALTIME_WIRE,
77        }
78    }
79
80    /// Decode from the 1-byte wire form. Unknown discriminants
81    /// (a forward-compat scenario where a future variant lands
82    /// before this reader knows about it) decode as `Foreground`
83    /// — conservative degrade that keeps unknown-class requests
84    /// admitted under the most permissive gate rather than
85    /// silently dropped.
86    pub fn from_wire_or_default(byte: u8) -> Self {
87        match byte {
88            Self::BACKGROUND_WIRE => Self::Background,
89            Self::REALTIME_WIRE => Self::Realtime,
90            // FOREGROUND_WIRE + any unknown future variant.
91            _ => Self::Foreground,
92        }
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn default_is_foreground() {
102        assert_eq!(BandwidthClass::default(), BandwidthClass::Foreground);
103    }
104
105    #[test]
106    fn wire_round_trip_for_every_variant() {
107        for c in [
108            BandwidthClass::Foreground,
109            BandwidthClass::Background,
110            BandwidthClass::Realtime,
111        ] {
112            assert_eq!(BandwidthClass::from_wire_or_default(c.as_u8()), c);
113        }
114    }
115
116    #[test]
117    fn unknown_wire_discriminant_decodes_as_foreground() {
118        // A future variant with discriminant 7 should decode as
119        // Foreground on this reader — conservative degrade.
120        assert_eq!(
121            BandwidthClass::from_wire_or_default(7),
122            BandwidthClass::Foreground
123        );
124        assert_eq!(
125            BandwidthClass::from_wire_or_default(255),
126            BandwidthClass::Foreground
127        );
128    }
129}