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}