1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//! Timeout resilience middleware for services, applications, and libraries.
//!
//! This module provides timeout functionality to cancel long-running operations and prevent
//! services from hanging indefinitely when processing requests. The primary types are
//! [`Timeout`] and [`TimeoutLayer`]:
//!
//! - [`Timeout`] is the middleware that wraps an inner service and enforces timeout behavior
//! - [`TimeoutLayer`] is used to configure and construct the timeout middleware
//!
//! # Quick Start
//!
//! ```rust
//! # use std::time::Duration;
//! # use std::io;
//! # use tick::Clock;
//! # use layered::{Execute, Service, Stack};
//! # use seatbelt::timeout::Timeout;
//! # use seatbelt::ResilienceContext;
//! # async fn example(clock: Clock) -> Result<(), io::Error> {
//! let context = ResilienceContext::new(&clock).name("my_service");
//!
//! let stack = (
//! Timeout::layer("timeout", &context)
//! .timeout_error(|_| io::Error::new(io::ErrorKind::TimedOut, "operation timed out"))
//! .timeout(Duration::from_secs(30)),
//! Execute::new(my_operation),
//! );
//!
//! let service = stack.into_service();
//! let result = service.execute("input".to_string()).await;
//! # Ok(())
//! # }
//! # async fn my_operation(input: String) -> Result<String, io::Error> { Ok(input) }
//! ```
//!
//! # Configuration
//!
//! The [`TimeoutLayer`] uses a type state pattern to enforce that all required properties are
//! configured before the layer can be built. This compile-time safety ensures that you cannot
//! accidentally create a timeout layer without properly specifying the timeout duration
//! and the timeout output generator:
//!
//! - [`timeout_output`][TimeoutLayer::timeout_output] or [`timeout_error`][TimeoutLayer::timeout_error]: Required function to generate output when timeout occurs
//! - [`timeout`][TimeoutLayer::timeout]: Required timeout duration for operations
//!
//! Each timeout layer requires an identifier for telemetry purposes. This identifier should use
//! `snake_case` naming convention to maintain consistency across the codebase.
//!
//! The default timeout is configured via [`TimeoutLayer::timeout`]. You can override that
//! per request with [`TimeoutLayer::timeout_override`].
//!
//! # Defaults
//!
//! The timeout middleware uses the following default values when optional configuration is not provided:
//!
//! | Parameter | Default Value | Description | Configured By |
//! |-----------|---------------|-------------|---------------|
//! | Timeout duration | `None` (required) | Maximum duration to wait for operation completion | [`timeout`][TimeoutLayer::timeout] |
//! | Timeout output | `None` (required) | Output value to return when timeout occurs | [`timeout_output`][TimeoutLayer::timeout_output], [`timeout_error`][TimeoutLayer::timeout_error] |
//! | Timeout override | `None` | Uses default timeout for all requests | [`timeout_override`][TimeoutLayer::timeout_override] |
//! | On timeout callback | `None` | No observability by default | [`on_timeout`][TimeoutLayer::on_timeout] |
//! | Enable condition | Always enabled | Timeout protection is applied to all requests | [`enable_if`][TimeoutLayer::enable_if], [`enable_always`][TimeoutLayer::enable_always], [`disable`][TimeoutLayer::disable] |
//!
//! Unlike other middleware, timeout requires explicit configuration of both the timeout duration
//! and the output generator function, as there are no reasonable universal defaults for these values.
//!
//! # Thread Safety
//!
//! The [`Timeout`] type is thread-safe and implements both `Send` and `Sync` as enforced by
//! the `Service` trait it implements. This allows timeout middleware to be safely shared
//! across multiple threads and used in concurrent environments.
//!
//! # Telemetry
//!
//! ## Metrics
//!
//! - **Metric**: `resilience.event` (counter)
//! - **When**: Emitted when the inner service does not complete within the configured timeout duration
//! - **Attributes**:
//! - `resilience.pipeline.name`: Pipeline identifier from [`ResilienceContext::name`][crate::ResilienceContext::name]
//! - `resilience.strategy.name`: Timeout identifier from [`Timeout::layer`]
//! - `resilience.event.name`: Always `timeout`
//!
//! # Examples
//!
//! ## Basic Usage
//!
//! This example demonstrates the basic usage of configuring and using timeout middleware.
//!
//! ```rust
//! # use std::time::Duration;
//! # use tick::Clock;
//! # use layered::{Execute, Service, Stack};
//! # use seatbelt::ResilienceContext;
//! # use seatbelt::timeout::Timeout;
//! # async fn example(clock: Clock) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
//! // Define common options for resilience middleware. The clock is runtime-specific and
//! // must be provided. See its documentation for details.
//! let context = ResilienceContext::new(&clock);
//!
//! let stack = (
//! Timeout::layer("my_timeout", &context)
//! // Required: timeout middleware needs to know what output to return when timeout occurs
//! .timeout_output(|args| {
//! format!("timeout error, duration: {}ms", args.timeout().as_millis())
//! })
//! // Required: timeout duration must be set
//! .timeout(Duration::from_secs(30)),
//! Execute::new(execute_unreliable_operation),
//! );
//!
//! // Build the service
//! let service = stack.into_service();
//!
//! // Execute the service
//! let result = service.execute("quick".to_string()).await;
//! # let _result = result;
//! # Ok(())
//! # }
//!
//! # async fn execute_unreliable_operation(input: String) -> String { input }
//! ```
//!
//! ## Advanced Usage
//!
//! This example demonstrates advanced usage of the timeout middleware, including working with
//! Result-based outputs, custom configurations, and timeout overrides.
//!
//! ```rust
//! # use std::time::Duration;
//! # use std::io;
//! # use tick::Clock;
//! # use layered::{Execute, Service, Stack};
//! # use seatbelt::ResilienceContext;
//! # use seatbelt::timeout::Timeout;
//! # async fn example(clock: Clock) -> Result<(), io::Error> {
//! // Define common options for resilience middleware.
//! let context = ResilienceContext::new(&clock);
//!
//! let stack = (
//! Timeout::layer("my_timeout", &context)
//! // Return an error for Result outputs on timeout
//! .timeout_error(|args| io::Error::new(io::ErrorKind::TimedOut, "request timed out"))
//! // Default timeout
//! .timeout(Duration::from_secs(30))
//! // Callback for when a timeout occurs
//! .on_timeout(|_output: &Result<String, io::Error>, args| {
//! println!("timeout occurred after {}ms", args.timeout().as_millis());
//! })
//! // Provide per-input timeout overrides (fallback to default on None)
//! .timeout_override(|input: &String, args| {
//! match input.as_str() {
//! "quick" => Some(Duration::from_secs(5)), // override
//! "slow" => Some(Duration::from_secs(60)),
//! _ => None, // use default (args.default_timeout())!
//! }
//! })
//! // Optionally disable timeouts for some inputs
//! .enable_if(|input: &String| !input.starts_with("bypass_")),
//! Execute::new(execute_unreliable_operation),
//! );
//!
//! // Build and execute the service
//! let service = stack.into_service();
//! let result = service.execute("quick".to_string()).await?;
//! # let _result = result;
//! # Ok(())
//! # }
//! # async fn execute_unreliable_operation(input: String) -> Result<String, io::Error> { Ok(input) }
//! ```
pub use ;
pub use ;
pub use TimeoutConfig;
pub use TimeoutLayer;
pub use Timeout;
pub use TimeoutFuture;
pub use TimeoutShared;