google_cloud_gax/retry_policy/
too_many_requests.rs1use super::{Error, RetryPolicy, RetryResult, RetryState, ThrottleResult};
16use crate::error::rpc::Code;
17use std::time::Duration;
18
19#[derive(Debug)]
29pub struct TooManyRequests<P>
30where
31 P: RetryPolicy,
32{
33 inner: P,
34}
35
36impl<P> TooManyRequests<P>
37where
38 P: RetryPolicy,
39{
40 pub fn new(inner: P) -> Self {
56 Self { inner }
57 }
58
59 fn is_resource_exhausted(e: &Error) -> bool {
60 e.status()
61 .is_some_and(|s| s.code == Code::ResourceExhausted)
62 }
63
64 fn is_too_many_requests(e: &Error) -> bool {
65 e.http_status_code()
66 .is_some_and(|code| code == http::StatusCode::TOO_MANY_REQUESTS.as_u16())
67 }
68}
69
70impl<P> RetryPolicy for TooManyRequests<P>
71where
72 P: RetryPolicy,
73{
74 fn on_error(&self, state: &RetryState, error: Error) -> RetryResult {
75 if Self::is_resource_exhausted(&error) || Self::is_too_many_requests(&error) {
76 return RetryResult::Continue(error);
77 }
78 self.inner.on_error(state, error)
79 }
80
81 fn on_throttle(&self, state: &RetryState, error: Error) -> ThrottleResult {
82 self.inner.on_throttle(state, error)
83 }
84
85 fn remaining_time(&self, state: &RetryState) -> Option<Duration> {
86 self.inner.remaining_time(state)
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::super::tests::{MockPolicy, idempotent_state};
93 use super::*;
94 use crate::error::rpc::Status;
95 use crate::retry_policy::NeverRetry;
96 use std::time::Instant;
97
98 fn too_many_requests() -> Error {
99 Error::service(Status::default().set_code(Code::ResourceExhausted))
100 }
101
102 fn too_many_requests_http() -> Error {
103 Error::http(
104 http::StatusCode::TOO_MANY_REQUESTS.as_u16(),
105 http::HeaderMap::new(),
106 bytes::Bytes::new(),
107 )
108 }
109
110 fn permanent() -> Error {
111 Error::service(Status::default().set_code(Code::PermissionDenied))
112 }
113
114 fn transient() -> Error {
115 Error::service(Status::default().set_code(Code::Unavailable))
116 }
117
118 #[test]
119 fn on_error() {
120 let policy = TooManyRequests::new(NeverRetry);
121 let result = policy.on_error(&idempotent_state(Instant::now()), too_many_requests());
122 assert!(matches!(result, RetryResult::Continue(_)), "{result:?}");
123 let result = policy.on_error(&idempotent_state(Instant::now()), too_many_requests_http());
124 assert!(matches!(result, RetryResult::Continue(_)), "{result:?}");
125 let result = policy.on_error(&idempotent_state(Instant::now()), permanent());
126 assert!(matches!(result, RetryResult::Exhausted(_)), "{result:?}");
127 }
128
129 #[test]
130 fn ext() {
131 use super::super::RetryPolicyExt;
132 let policy = NeverRetry.continue_on_too_many_requests();
133 let result = policy.on_error(&idempotent_state(Instant::now()), too_many_requests());
134 assert!(matches!(result, RetryResult::Continue(_)), "{result:?}");
135 let result = policy.on_error(&idempotent_state(Instant::now()), too_many_requests_http());
136 assert!(matches!(result, RetryResult::Continue(_)), "{result:?}");
137 let result = policy.on_error(&idempotent_state(Instant::now()), permanent());
138 assert!(matches!(result, RetryResult::Exhausted(_)), "{result:?}");
139 }
140
141 #[test]
142 fn forwards() {
143 let mut mock = MockPolicy::new();
144 mock.expect_on_error()
145 .times(1..)
146 .returning(|_, e| RetryResult::Permanent(e));
147 mock.expect_on_throttle()
148 .times(1..)
149 .returning(|_, e| ThrottleResult::Exhausted(e));
150 mock.expect_remaining_time().times(1).returning(|_| None);
151
152 let policy = TooManyRequests::new(mock);
153 let result = policy.on_error(&idempotent_state(Instant::now()), transient());
154 assert!(matches!(result, RetryResult::Permanent(_)), "{result:?}");
155 let result = policy.on_error(&idempotent_state(Instant::now()), too_many_requests());
156 assert!(matches!(result, RetryResult::Continue(_)), "{result:?}");
157
158 let result = policy.on_throttle(&idempotent_state(Instant::now()), transient());
159 assert!(matches!(result, ThrottleResult::Exhausted(_)));
160 let result = policy.on_throttle(&idempotent_state(Instant::now()), too_many_requests());
161 assert!(matches!(result, ThrottleResult::Exhausted(_)));
162
163 let result = policy.remaining_time(&idempotent_state(Instant::now()));
164 assert!(result.is_none(), "{result:?}");
165 }
166}