foyer_storage/io/device/
throttle.rs1use std::{fmt::Display, num::NonZeroUsize, str::FromStr};
16
17#[derive(Debug, Clone, PartialEq, Eq, Default)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub enum IopsCounter {
21 #[default]
23 PerIo,
24 PerIoSize(NonZeroUsize),
26}
27
28impl IopsCounter {
29 pub fn per_io() -> Self {
31 Self::PerIo
32 }
33
34 pub fn per_io_size(io_size: usize) -> Self {
38 Self::PerIoSize(NonZeroUsize::new(io_size).expect("io size must be non-zero"))
39 }
40
41 pub fn count(&self, bytes: usize) -> usize {
43 match self {
44 IopsCounter::PerIo => 1,
45 IopsCounter::PerIoSize(size) => bytes / *size + if bytes % *size != 0 { 1 } else { 0 },
46 }
47 }
48}
49
50impl Display for IopsCounter {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 match self {
53 IopsCounter::PerIo => write!(f, "PerIo"),
54 IopsCounter::PerIoSize(size) => write!(f, "PerIoSize({size})"),
55 }
56 }
57}
58
59impl FromStr for IopsCounter {
60 type Err = anyhow::Error;
61
62 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
63 let s = s.trim();
64 match s {
65 "PerIo" => Ok(IopsCounter::PerIo),
66 _ if s.starts_with("PerIoSize(") && s.ends_with(')') => {
67 let num = &s[10..s.len() - 1];
68 let v = num.parse::<NonZeroUsize>()?;
69 Ok(IopsCounter::PerIoSize(v))
70 }
71 _ => Err(anyhow::anyhow!("Invalid IopsCounter format: {}", s)),
72 }
73 }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Default)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
79#[cfg_attr(feature = "clap", derive(clap::Args))]
80pub struct Throttle {
81 #[cfg_attr(feature = "clap", clap(long))]
83 pub write_iops: Option<NonZeroUsize>,
84 #[cfg_attr(feature = "clap", clap(long))]
86 pub read_iops: Option<NonZeroUsize>,
87 #[cfg_attr(feature = "clap", clap(long))]
89 pub write_throughput: Option<NonZeroUsize>,
90 #[cfg_attr(feature = "clap", clap(long))]
92 pub read_throughput: Option<NonZeroUsize>,
93 #[cfg_attr(feature = "clap", clap(long, default_value = "PerIo"))]
95 pub iops_counter: IopsCounter,
96}
97
98impl Throttle {
99 pub fn new() -> Self {
101 Self::default()
102 }
103
104 pub fn with_write_iops(mut self, iops: usize) -> Self {
106 self.write_iops = NonZeroUsize::new(iops);
107 self
108 }
109
110 pub fn with_read_iops(mut self, iops: usize) -> Self {
112 self.read_iops = NonZeroUsize::new(iops);
113 self
114 }
115
116 pub fn with_write_throughput(mut self, throughput: usize) -> Self {
118 self.write_throughput = NonZeroUsize::new(throughput);
119 self
120 }
121
122 pub fn with_read_throughput(mut self, throughput: usize) -> Self {
124 self.read_throughput = NonZeroUsize::new(throughput);
125 self
126 }
127
128 pub fn with_iops_counter(mut self, counter: IopsCounter) -> Self {
130 self.iops_counter = counter;
131 self
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_throttle_default() {
141 assert!(matches!(
142 Throttle::new(),
143 Throttle {
144 write_iops: None,
145 read_iops: None,
146 write_throughput: None,
147 read_throughput: None,
148 iops_counter: IopsCounter::PerIo,
149 }
150 ));
151 }
152
153 #[test]
154 fn test_iops_counter_from_str() {
155 assert!(matches!(IopsCounter::from_str("PerIo"), Ok(IopsCounter::PerIo)));
156 assert!(matches!(IopsCounter::from_str(" PerIo "), Ok(IopsCounter::PerIo)));
157 assert!(matches!(IopsCounter::from_str("PerIo "), Ok(IopsCounter::PerIo)));
158 assert!(matches!(IopsCounter::from_str(" PerIo"), Ok(IopsCounter::PerIo)));
159
160 let _num = NonZeroUsize::new(1024).unwrap();
161
162 assert!(matches!(
163 IopsCounter::from_str("PerIoSize(1024)"),
164 Ok(IopsCounter::PerIoSize(_num))
165 ));
166 assert!(matches!(
167 IopsCounter::from_str(" PerIoSize(1024) "),
168 Ok(IopsCounter::PerIoSize(_num))
169 ));
170 assert!(matches!(
171 IopsCounter::from_str("PerIoSize(1024) "),
172 Ok(IopsCounter::PerIoSize(_num))
173 ));
174 assert!(matches!(
175 IopsCounter::from_str(" PerIoSize(1024)"),
176 Ok(IopsCounter::PerIoSize(_num))
177 ));
178
179 assert!(IopsCounter::from_str("PerIoSize(0)").is_err());
180 assert!(IopsCounter::from_str("PerIoSize(1024a)").is_err());
181
182 assert!(IopsCounter::from_str("invalid_string").is_err());
183 }
184}