aws_smithy_runtime/client/
retries.rs1pub mod classifiers;
8
9pub mod strategy;
11
12mod client_rate_limiter;
13pub(crate) mod token_bucket;
14
15use aws_smithy_types::config_bag::{Storable, StoreReplace};
16use std::fmt;
17use std::sync::{Arc, Mutex};
18use std::time::Duration;
19
20#[derive(Clone, Debug, Default)]
32pub(crate) struct LongPollingBackoff(Arc<Mutex<Option<Duration>>>);
33
34impl LongPollingBackoff {
35 pub(crate) fn set(&self, delay: Duration) {
36 *self.0.lock().expect("lock is acquired") = Some(delay);
37 }
38 pub(crate) fn take(&self) -> Option<Duration> {
39 self.0.lock().expect("lock is acquired").take()
40 }
41}
42
43impl Storable for LongPollingBackoff {
44 type Storer = StoreReplace<Self>;
45}
46
47pub use client_rate_limiter::{
48 ClientRateLimiter, ClientRateLimiterBuilder, ClientRateLimiterPartition,
49};
50pub use token_bucket::{TokenBucket, TokenBucketBuilder, MAXIMUM_CAPACITY};
51
52use std::borrow::Cow;
53
54#[non_exhaustive]
70#[derive(Clone, Debug)]
71pub struct RetryPartition {
72 pub(crate) inner: RetryPartitionInner,
73}
74
75#[derive(Clone, Debug)]
76pub(crate) enum RetryPartitionInner {
77 Default(Cow<'static, str>),
78 Custom {
79 name: Cow<'static, str>,
80 token_bucket: TokenBucket,
81 client_rate_limiter: ClientRateLimiter,
82 },
83}
84
85impl RetryPartition {
86 pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
88 Self {
89 inner: RetryPartitionInner::Default(name.into()),
90 }
91 }
92
93 pub fn custom(name: impl Into<Cow<'static, str>>) -> RetryPartitionBuilder {
95 RetryPartitionBuilder {
96 name: name.into(),
97 token_bucket: None,
98 client_rate_limiter: None,
99 }
100 }
101
102 fn name(&self) -> &str {
103 match &self.inner {
104 RetryPartitionInner::Default(name) => name,
105 RetryPartitionInner::Custom { name, .. } => name,
106 }
107 }
108}
109
110impl PartialEq for RetryPartition {
111 fn eq(&self, other: &Self) -> bool {
112 match (&self.inner, &other.inner) {
113 (RetryPartitionInner::Default(name1), RetryPartitionInner::Default(name2)) => {
114 name1 == name2
115 }
116 (
117 RetryPartitionInner::Custom { name: name1, .. },
118 RetryPartitionInner::Custom { name: name2, .. },
119 ) => name1 == name2,
120 _ => false,
122 }
123 }
124}
125
126impl Eq for RetryPartition {}
127
128impl std::hash::Hash for RetryPartition {
129 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
130 match &self.inner {
131 RetryPartitionInner::Default(name) => {
132 0u8.hash(state);
134 name.hash(state);
135 }
136 RetryPartitionInner::Custom { name, .. } => {
137 1u8.hash(state);
139 name.hash(state);
140 }
141 }
142 }
143}
144
145impl fmt::Display for RetryPartition {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.write_str(self.name())
148 }
149}
150
151impl Storable for RetryPartition {
152 type Storer = StoreReplace<RetryPartition>;
153}
154
155pub struct RetryPartitionBuilder {
157 name: Cow<'static, str>,
158 token_bucket: Option<TokenBucket>,
159 client_rate_limiter: Option<ClientRateLimiter>,
160}
161
162impl RetryPartitionBuilder {
163 pub fn token_bucket(mut self, token_bucket: TokenBucket) -> Self {
165 self.token_bucket = Some(token_bucket);
166 self
167 }
168
169 pub fn client_rate_limiter(mut self, client_rate_limiter: ClientRateLimiter) -> Self {
171 self.client_rate_limiter = Some(client_rate_limiter);
172 self
173 }
174
175 pub fn build(self) -> RetryPartition {
177 RetryPartition {
178 inner: RetryPartitionInner::Custom {
179 name: self.name,
180 token_bucket: self.token_bucket.unwrap_or_default(),
181 client_rate_limiter: self.client_rate_limiter.unwrap_or_default(),
182 },
183 }
184 }
185}
186#[cfg(test)]
187mod tests {
188 use super::*;
189 use std::collections::hash_map::DefaultHasher;
190 use std::hash::{Hash, Hasher};
191
192 fn hash_value<T: Hash>(t: &T) -> u64 {
193 let mut hasher = DefaultHasher::new();
194 t.hash(&mut hasher);
195 hasher.finish()
196 }
197
198 #[test]
199 fn test_retry_partition_equality() {
200 let default1 = RetryPartition::new("test");
201 let default2 = RetryPartition::new("test");
202 let default3 = RetryPartition::new("other");
203
204 let configured1 = RetryPartition::custom("test").build();
205 let configured2 = RetryPartition::custom("test").build();
206 let configured3 = RetryPartition::custom("other").build();
207
208 assert_eq!(default1, default2);
210 assert_eq!(configured1, configured2);
211
212 assert_ne!(default1, default3);
214 assert_ne!(configured1, configured3);
215
216 assert_ne!(default1, configured1);
218 }
219
220 #[test]
221 fn test_retry_partition_hash() {
222 let default = RetryPartition::new("test");
223 let configured = RetryPartition::custom("test").build();
224
225 assert_ne!(hash_value(&default), hash_value(&configured));
227
228 let default2 = RetryPartition::new("test");
230 assert_eq!(hash_value(&default), hash_value(&default2));
231 }
232}