Skip to content

Commit 5d7c83a

Browse files
authored
feat(seatbelt): moved Attempt type to root (#309)
This type is useful even outside of middleware context and having this type re-exported from multiple locations caused problems.
1 parent efe4ae7 commit 5d7c83a

16 files changed

Lines changed: 64 additions & 47 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ layered = { path = "crates/layered", default-features = false, version = "0.3.0"
3636
ohno = { path = "crates/ohno", default-features = false, version = "0.3.1" }
3737
ohno_macros = { path = "crates/ohno_macros", default-features = false, version = "0.3.0" }
3838
recoverable = { path = "crates/recoverable", default-features = false, version = "0.1.1" }
39-
seatbelt = { path = "crates/seatbelt", default-features = false, version = "0.4.0" }
39+
seatbelt = { path = "crates/seatbelt", default-features = false, version = "0.4.1" }
4040
testing_aids = { path = "crates/testing_aids", default-features = false }
4141
thread_aware = { path = "crates/thread_aware", default-features = false, version = "0.6.2" }
4242
thread_aware_macros = { path = "crates/thread_aware_macros", default-features = false, version = "0.6.1" }

crates/seatbelt/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [0.4.1] - 2026-03-10
4+
5+
- ✨ Features
6+
7+
- expose `seatbelt::Attempt` and obsolete `seatbelt::retry::Attempt` and `seatbelt::hedging::Attempt`
8+
39
## [0.4.0] - 2026-03-06
410

511
- ⚠️ Breaking

crates/seatbelt/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[package]
55
name = "seatbelt"
66
description = "Resilience and recovery mechanisms for fallible operations."
7-
version = "0.4.0"
7+
version = "0.4.1"
88
readme = "README.md"
99
keywords = ["oxidizer", "resilience", "layered", "recovery", "retry"]
1010
categories = ["data-structures"]

crates/seatbelt/README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,12 @@ This crate provides several optional features that can be enabled in your `Cargo
182182
This crate was developed as part of <a href="../..">The Oxidizer Project</a>. Browse this crate's <a href="https://github.com/microsoft/oxidizer/tree/main/crates/seatbelt">source code</a>.
183183
</sub>
184184

185-
[__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG4STOoP2_1kjG_YFfNmBbFbQG7vz1CXQ1d10GxetSJWWkufEYWSFgmdsYXllcmVkZTAuMy4wgmtyZWNvdmVyYWJsZWUwLjEuMYJoc2VhdGJlbHRlMC40LjCCZHRpY2tlMC4yLjGCbXRvd2VyX3NlcnZpY2VlMC4zLjM
185+
[__cargo_doc2readme_dependencies_info]: ggGkYW0CYXSEGy4k8ldDFPOhG2VNeXtD5nnKG6EPY6OfW5wBG8g18NOFNdxpYXKEG4STOoP2_1kjG_YFfNmBbFbQG7vz1CXQ1d10GxetSJWWkufEYWSFgmdsYXllcmVkZTAuMy4wgmtyZWNvdmVyYWJsZWUwLjEuMYJoc2VhdGJlbHRlMC40LjGCZHRpY2tlMC4yLjGCbXRvd2VyX3NlcnZpY2VlMC4zLjM
186186
[__link0]: https://crates.io/crates/layered/0.3.0
187187
[__link1]: https://docs.rs/layered/0.3.0/layered/?search=Stack
188-
[__link10]: https://docs.rs/seatbelt/0.4.0/seatbelt/hedging/index.html
189-
[__link11]: https://docs.rs/seatbelt/0.4.0/seatbelt/breaker/index.html
190-
[__link12]: https://docs.rs/seatbelt/0.4.0/seatbelt/fallback/index.html
188+
[__link10]: https://docs.rs/seatbelt/0.4.1/seatbelt/hedging/index.html
189+
[__link11]: https://docs.rs/seatbelt/0.4.1/seatbelt/breaker/index.html
190+
[__link12]: https://docs.rs/seatbelt/0.4.1/seatbelt/fallback/index.html
191191
[__link13]: https://github.com/microsoft/oxidizer/blob/main/crates/seatbelt/examples/timeout.rs
192192
[__link14]: https://github.com/microsoft/oxidizer/blob/main/crates/seatbelt/examples/timeout_advanced.rs
193193
[__link15]: https://github.com/microsoft/oxidizer/blob/main/crates/seatbelt/examples/retry.rs
@@ -201,16 +201,16 @@ This crate was developed as part of <a href="../..">The Oxidizer Project</a>. Br
201201
[__link22]: https://github.com/microsoft/oxidizer/blob/main/crates/seatbelt/examples/tower.rs
202202
[__link23]: https://github.com/microsoft/oxidizer/blob/main/crates/seatbelt/examples/config.rs
203203
[__link24]: https://github.com/microsoft/oxidizer/blob/main/crates/seatbelt/examples/config.json
204-
[__link25]: https://docs.rs/seatbelt/0.4.0/seatbelt/timeout/index.html
205-
[__link26]: https://docs.rs/seatbelt/0.4.0/seatbelt/retry/index.html
206-
[__link27]: https://docs.rs/seatbelt/0.4.0/seatbelt/hedging/index.html
207-
[__link28]: https://docs.rs/seatbelt/0.4.0/seatbelt/breaker/index.html
208-
[__link29]: https://docs.rs/seatbelt/0.4.0/seatbelt/fallback/index.html
204+
[__link25]: https://docs.rs/seatbelt/0.4.1/seatbelt/timeout/index.html
205+
[__link26]: https://docs.rs/seatbelt/0.4.1/seatbelt/retry/index.html
206+
[__link27]: https://docs.rs/seatbelt/0.4.1/seatbelt/hedging/index.html
207+
[__link28]: https://docs.rs/seatbelt/0.4.1/seatbelt/breaker/index.html
208+
[__link29]: https://docs.rs/seatbelt/0.4.1/seatbelt/fallback/index.html
209209
[__link3]: https://crates.io/crates/tick/0.2.1
210210
[__link30]: https://docs.rs/tower_service/0.3.3/tower_service/?search=Service
211-
[__link4]: https://docs.rs/seatbelt/0.4.0/seatbelt/?search=ResilienceContext
212-
[__link5]: https://docs.rs/seatbelt/0.4.0/seatbelt/?search=ResilienceContext
211+
[__link4]: https://docs.rs/seatbelt/0.4.1/seatbelt/?search=ResilienceContext
212+
[__link5]: https://docs.rs/seatbelt/0.4.1/seatbelt/?search=ResilienceContext
213213
[__link6]: https://docs.rs/recoverable/0.1.1/recoverable/?search=RecoveryInfo
214214
[__link7]: https://docs.rs/recoverable/0.1.1/recoverable/?search=Recovery
215-
[__link8]: https://docs.rs/seatbelt/0.4.0/seatbelt/timeout/index.html
216-
[__link9]: https://docs.rs/seatbelt/0.4.0/seatbelt/retry/index.html
215+
[__link8]: https://docs.rs/seatbelt/0.4.1/seatbelt/timeout/index.html
216+
[__link9]: https://docs.rs/seatbelt/0.4.1/seatbelt/retry/index.html

crates/seatbelt/examples/retry_advanced.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use layered::{Execute, Service, Stack};
1414
use ohno::AppError;
1515
use opentelemetry_sdk::metrics::SdkMeterProvider;
1616
use opentelemetry_stdout::MetricExporter;
17-
use seatbelt::retry::{Attempt, Retry};
17+
use seatbelt::Attempt;
18+
use seatbelt::retry::Retry;
1819
use seatbelt::{RecoveryInfo, ResilienceContext};
1920
use tick::Clock;
2021
use tracing_subscriber::layer::SubscriberExt;

crates/seatbelt/src/attempt.rs

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,57 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
#![allow(dead_code, reason = "need to work across many combinations of features, let's just allow it")]
5+
46
use std::fmt::Display;
57

68
/// Attribute key for the attempt index.
7-
#[cfg(any(feature = "metrics", test))]
89
pub(crate) const ATTEMPT_INDEX: &str = "resilience.attempt.index";
910

1011
/// Attribute key for whether this is the last attempt.
11-
#[cfg(any(feature = "metrics", test))]
1212
pub(crate) const ATTEMPT_IS_LAST: &str = "resilience.attempt.is_last";
1313

1414
/// Attribute key for the recovery kind that triggered the attempt.
15-
#[cfg(any(feature = "metrics", test))]
1615
pub(crate) const ATTEMPT_RECOVERY_KIND: &str = "resilience.attempt.recovery.kind";
1716

18-
/// Represents a single attempt in a resilience operation.
17+
/// Tracks the current attempt within a resilience operation.
18+
///
19+
/// Resilience middleware creates an `Attempt` for each execution of the inner service and
20+
/// passes it to user-provided callbacks. You can use the attempt information to vary behavior
21+
/// per attempt - for example, routing to a different endpoint or injecting the attempt into
22+
/// request extensions for downstream observability.
23+
///
24+
/// # Default
1925
///
20-
/// This struct tracks the current attempt index, and it provides methods to check if this is the
21-
/// first or last attempt.
26+
/// The [`Default`] value represents a single-shot operation with no retries:
2227
///
23-
/// The default attempt has:
28+
/// - [`index()`](Self::index): `0` (first attempt, zero-based)
29+
/// - [`is_first()`](Self::is_first): `true`
30+
/// - [`is_last()`](Self::is_last): `true`
2431
///
25-
/// - `attempt`: 0 (first attempt, 0-based indexing)
26-
/// - `is_first`: true
27-
/// - `is_last`: true
32+
/// # Display
2833
///
29-
/// This represents a single-shot operation with no retries, where the first
30-
/// attempt is also the final attempt.
34+
/// The [`Display`] implementation writes the attempt [`index()`](Self::index) as a decimal
35+
/// number, which is useful for logging and diagnostics.
3136
///
3237
/// # Examples
3338
///
3439
/// ```
35-
/// use seatbelt::retry::Attempt;
40+
/// use seatbelt::Attempt;
3641
///
37-
/// // Create the first attempt (attempt 0)
42+
/// // First attempt of several (more attempts may follow)
3843
/// let attempt = Attempt::new(0, false);
3944
/// assert!(attempt.is_first());
4045
/// assert!(!attempt.is_last());
4146
/// assert_eq!(attempt.index(), 0);
4247
///
43-
/// // Create the last attempt (attempt 2)
48+
/// // Final attempt (no further retries will be made)
4449
/// let last_attempt = Attempt::new(2, true);
4550
/// assert!(!last_attempt.is_first());
4651
/// assert!(last_attempt.is_last());
4752
/// assert_eq!(last_attempt.index(), 2);
4853
///
49-
/// // Use the default attempt (single-shot operation)
54+
/// // Default: single-shot, no retries
5055
/// let default_attempt = Attempt::default();
5156
/// assert_eq!(default_attempt.index(), 0);
5257
/// assert!(default_attempt.is_first());
@@ -65,27 +70,29 @@ impl Default for Attempt {
6570
}
6671

6772
impl Attempt {
68-
/// Creates a new attempt with the given attempt index and maximum attempts.
73+
/// Creates a new attempt with the given zero-based `index` and a flag indicating whether
74+
/// this is the last attempt the middleware will make.
6975
///
7076
/// # Examples
7177
///
7278
/// ```
73-
/// use seatbelt::retry::Attempt;
79+
/// use seatbelt::Attempt;
7480
///
7581
/// let attempt = Attempt::new(0, false);
7682
/// assert_eq!(attempt.index(), 0);
83+
/// assert!(!attempt.is_last());
7784
/// ```
7885
#[must_use]
7986
pub fn new(index: u32, is_last: bool) -> Self {
8087
Self { index, is_last }
8188
}
8289

83-
/// Returns true if this is the first attempt (attempt 0).
90+
/// Returns `true` if this is the first attempt (`index == 0`).
8491
///
8592
/// # Examples
8693
///
8794
/// ```
88-
/// use seatbelt::retry::Attempt;
95+
/// use seatbelt::Attempt;
8996
///
9097
/// let first_attempt = Attempt::new(0, false);
9198
/// assert!(first_attempt.is_first());
@@ -98,12 +105,12 @@ impl Attempt {
98105
self.index == 0
99106
}
100107

101-
/// Returns true if this is the last allowed attempt.
108+
/// Returns `true` if no further attempts will be made after this one.
102109
///
103110
/// # Examples
104111
///
105112
/// ```
106-
/// use seatbelt::retry::Attempt;
113+
/// use seatbelt::Attempt;
107114
///
108115
/// let not_last = Attempt::new(1, false);
109116
/// assert!(!not_last.is_last());
@@ -121,7 +128,7 @@ impl Attempt {
121128
/// # Examples
122129
///
123130
/// ```
124-
/// use seatbelt::retry::Attempt;
131+
/// use seatbelt::Attempt;
125132
///
126133
/// let attempt = Attempt::new(3, false);
127134
/// assert_eq!(attempt.index(), 3);

crates/seatbelt/src/hedging/args.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use std::time::Duration;
55

6-
use super::Attempt;
6+
use crate::Attempt;
77
use tick::Clock;
88

99
/// Arguments for the [`clone_input_with`][super::HedgingLayer::clone_input_with] callback function.

crates/seatbelt/src/hedging/layer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ mod tests {
316316
use tick::Clock;
317317

318318
use super::*;
319-
use crate::hedging::Attempt;
319+
use crate::Attempt;
320320
use crate::testing::RecoverableType;
321321

322322
#[test]

crates/seatbelt/src/hedging/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ mod layer;
212212
mod service;
213213
mod telemetry;
214214

215+
#[deprecated(note = "import from `seatbelt::Attempt` instead")]
215216
pub use crate::attempt::Attempt;
216217
pub use args::{CloneArgs, HedgingDelayArgs, OnExecuteArgs, RecoveryArgs};
217218
pub use config::HedgingConfig;

0 commit comments

Comments
 (0)