Skip to content

Commit 8a151d3

Browse files
authored
Enable configuring retryable status codes (#3243)
1 parent c2d5787 commit 8a151d3

File tree

9 files changed

+190
-27
lines changed

9 files changed

+190
-27
lines changed

sdk/core/azure_core/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- Removed `Poller::wait()` function. Call `await` on a `Poller` to wait for it to complete and, upon success, return the final model.
2222
- Removed `xml::read_xml_str()`.
2323
- Renamed `BearerTokenCredentialPolicy` to `BearerTokenAuthorizationPolicy`.
24+
- Renamed `RetryPolicy::get_retry_headers()` to `RetryPolicy::retry_headers()`
2425
- Renamed `xml::read_xml()` to `xml::from_xml()` congruent with `json::from_json()`.
2526

2627
### Bugs Fixed

sdk/core/azure_core/src/http/pipeline.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ impl Pipeline {
174174
retry_headers: RetryHeaders {
175175
retry_headers: vec![X_MS_RETRY_AFTER_MS, RETRY_AFTER_MS, RETRY_AFTER],
176176
},
177+
..PipelineOptions::default()
177178
});
178179

179180
Self(http::Pipeline::new(

sdk/core/typespec_client_core/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Features Added
66

7+
- Added `PipelineOptions::retry_status_codes` for configuring which status codes should trigger a retry.
78
- Added `UrlExt::append_path()`.
89

910
### Breaking Changes

sdk/core/typespec_client_core/src/http/options/mod.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub use transport::*;
1212
use crate::http::{
1313
headers::RETRY_AFTER,
1414
policies::{Policy, RetryHeaders},
15-
Context,
15+
Context, StatusCode,
1616
};
1717
use std::borrow::Cow;
1818
use std::fmt::Debug;
@@ -64,7 +64,15 @@ pub struct ClientMethodOptions<'a> {
6464
pub struct PipelineOptions {
6565
/// The set of headers which should be considered when
6666
/// determining the interval to wait for retry attempts.
67+
/// This field doesn't apply to custom retry policies.
6768
pub retry_headers: RetryHeaders,
69+
70+
/// The status codes that should trigger retries. This
71+
/// field doesn't apply to custom retry policies.
72+
///
73+
/// When empty, the default retry status codes are used as
74+
/// described by [`crate::http::policies::RetryPolicy::retry_status_codes`].
75+
pub retry_status_codes: Vec<StatusCode>,
6876
}
6977

7078
impl Default for PipelineOptions {
@@ -73,6 +81,7 @@ impl Default for PipelineOptions {
7381
retry_headers: RetryHeaders {
7482
retry_headers: vec![RETRY_AFTER],
7583
},
84+
retry_status_codes: Vec::new(),
7685
}
7786
}
7887
}

sdk/core/typespec_client_core/src/http/options/retry.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
// Licensed under the MIT License.
33

44
use crate::{
5-
http::policies::{
6-
ExponentialRetryPolicy, FixedRetryPolicy, NoRetryPolicy, Policy, RetryHeaders, RetryPolicy,
5+
http::{
6+
policies::{
7+
ExponentialRetryPolicy, FixedRetryPolicy, NoRetryPolicy, Policy, RetryHeaders,
8+
RetryPolicy,
9+
},
10+
StatusCode,
711
},
812
time::Duration,
913
};
@@ -87,20 +91,26 @@ impl RetryOptions {
8791
}
8892
}
8993

90-
pub(crate) fn to_policy(&self, retry_headers: RetryHeaders) -> Arc<dyn Policy> {
94+
pub(crate) fn to_policy(
95+
&self,
96+
retry_headers: RetryHeaders,
97+
retry_status_codes: &[StatusCode],
98+
) -> Arc<dyn Policy> {
9199
match &self.mode {
92100
RetryMode::Exponential(options) => Arc::new(ExponentialRetryPolicy::new(
93101
options.initial_delay,
94102
options.max_retries,
95103
options.max_total_elapsed,
96104
options.max_delay,
97105
retry_headers,
106+
retry_status_codes.to_vec(),
98107
)),
99108
RetryMode::Fixed(options) => Arc::new(FixedRetryPolicy::new(
100109
options.delay,
101110
options.max_retries,
102111
options.max_total_elapsed,
103112
retry_headers,
113+
retry_status_codes.to_vec(),
104114
)),
105115
RetryMode::Custom(c) => c.clone(),
106116
RetryMode::None => Arc::new(NoRetryPolicy::new(retry_headers)),

sdk/core/typespec_client_core/src/http/pipeline.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ impl Pipeline {
7373

7474
let pipeline_options = pipeline_options.unwrap_or_default();
7575

76-
let retry_policy = options.retry.to_policy(pipeline_options.retry_headers);
76+
let retry_policy = options.retry.to_policy(
77+
pipeline_options.retry_headers.clone(),
78+
&pipeline_options.retry_status_codes,
79+
);
7780
pipeline.push(retry_policy);
7881

7982
pipeline.extend_from_slice(&per_try_policies);
@@ -134,10 +137,8 @@ mod tests {
134137
use crate::{
135138
error::{Error, ErrorKind},
136139
http::{
137-
headers::{Headers, RETRY_AFTER},
138-
policies::{PolicyResult, RetryHeaders},
139-
BufResponse, FixedRetryOptions, JsonFormat, Method, Response, RetryOptions, StatusCode,
140-
Transport,
140+
headers::Headers, policies::PolicyResult, BufResponse, FixedRetryOptions, JsonFormat,
141+
Method, Response, RetryOptions, StatusCode, Transport,
141142
},
142143
stream::BytesStream,
143144
Bytes,
@@ -180,12 +181,7 @@ mod tests {
180181
transport: Some(Transport::with_policy(Arc::new(Responder {}))),
181182
..Default::default()
182183
};
183-
let pipeline_options = PipelineOptions {
184-
retry_headers: RetryHeaders {
185-
retry_headers: vec![RETRY_AFTER],
186-
},
187-
};
188-
let pipeline = Pipeline::new(options, Vec::new(), Vec::new(), Some(pipeline_options));
184+
let pipeline = Pipeline::new(options, Vec::new(), Vec::new(), None);
189185
let mut request = Request::new("http://localhost".parse().unwrap(), Method::Get);
190186
let raw_response = pipeline
191187
.send(&Context::default(), &mut request, None)

sdk/core/typespec_client_core/src/http/policies/retry/exponential.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
// Licensed under the MIT License.
33

44
use super::RetryPolicy;
5-
use crate::{http::policies::RetryHeaders, time::Duration};
5+
use crate::{
6+
http::{policies::RetryHeaders, StatusCode},
7+
time::Duration,
8+
};
69

710
/// Retry policy with exponential back-off.
811
///
@@ -18,6 +21,7 @@ pub(crate) struct ExponentialRetryPolicy {
1821
max_elapsed: Duration,
1922
max_delay: Duration,
2023
retry_headers: RetryHeaders,
24+
retry_status_codes: Vec<StatusCode>,
2125
}
2226

2327
impl ExponentialRetryPolicy {
@@ -27,13 +31,15 @@ impl ExponentialRetryPolicy {
2731
max_elapsed: Duration,
2832
max_delay: Duration,
2933
retry_headers: RetryHeaders,
34+
retry_status_codes: Vec<StatusCode>,
3035
) -> Self {
3136
Self {
3237
initial_delay: initial_delay.max(Duration::milliseconds(1)),
3338
max_retries,
3439
max_elapsed,
3540
max_delay: max_delay.max(Duration::seconds(1)),
3641
retry_headers,
42+
retry_status_codes,
3743
}
3844
}
3945
}
@@ -43,10 +49,14 @@ impl RetryPolicy for ExponentialRetryPolicy {
4349
retry_count >= self.max_retries || time_since_start >= self.max_elapsed
4450
}
4551

46-
fn get_retry_headers(&self) -> Option<&RetryHeaders> {
52+
fn retry_headers(&self) -> Option<&RetryHeaders> {
4753
Some(&self.retry_headers)
4854
}
4955

56+
fn retry_status_codes(&self) -> &[StatusCode] {
57+
&self.retry_status_codes
58+
}
59+
5060
fn sleep_duration(&self, retry_count: u32) -> Duration {
5161
let sleep_ms = self.initial_delay.whole_milliseconds() as u64 * 2u64.pow(retry_count)
5262
+ u64::from(rand::random::<u8>());
@@ -83,6 +93,7 @@ mod tests {
8393
RETRY_AFTER,
8494
],
8595
},
96+
vec![],
8697
);
8798

8899
let mut elapsed_time = Duration::seconds(0);

sdk/core/typespec_client_core/src/http/policies/retry/fixed.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
use crate::{http::policies::RetryHeaders, time::Duration};
4+
use crate::{
5+
http::{policies::RetryHeaders, StatusCode},
6+
time::Duration,
7+
};
58

69
/// Retry policy with a fixed back-off.
710
///
@@ -15,6 +18,7 @@ pub(crate) struct FixedRetryPolicy {
1518
max_retries: u32,
1619
max_elapsed: Duration,
1720
retry_headers: RetryHeaders,
21+
retry_status_codes: Vec<StatusCode>,
1822
}
1923

2024
impl FixedRetryPolicy {
@@ -23,12 +27,14 @@ impl FixedRetryPolicy {
2327
max_retries: u32,
2428
max_elapsed: Duration,
2529
retry_headers: RetryHeaders,
30+
retry_status_codes: Vec<StatusCode>,
2631
) -> Self {
2732
Self {
2833
delay: delay.max(Duration::milliseconds(10)),
2934
max_retries,
3035
max_elapsed,
3136
retry_headers,
37+
retry_status_codes,
3238
}
3339
}
3440
}
@@ -38,10 +44,14 @@ impl super::RetryPolicy for FixedRetryPolicy {
3844
retry_count >= self.max_retries || time_since_start >= self.max_elapsed
3945
}
4046

41-
fn get_retry_headers(&self) -> Option<&RetryHeaders> {
47+
fn retry_headers(&self) -> Option<&RetryHeaders> {
4248
Some(&self.retry_headers)
4349
}
4450

51+
fn retry_status_codes(&self) -> &[StatusCode] {
52+
&self.retry_status_codes
53+
}
54+
4555
fn sleep_duration(&self, _retry_count: u32) -> Duration {
4656
let sleep_ms = self.delay.whole_milliseconds() as u64 + u64::from(rand::random::<u8>());
4757
Duration::milliseconds(sleep_ms as i64)

0 commit comments

Comments
 (0)