Skip to main content

CommonLibrary/Transport/
TransportConfig.rs

1//! # TransportConfig
2//!
3//! Configuration structures for transport implementations.
4
5use std::{collections::HashMap, time::Duration};
6
7use serde::{Deserialize, Serialize};
8
9use super::Common::TransportType;
10
11/// Global transport configuration.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct TransportConfig {
14	/// Default timeout for requests that don't specify one.
15	pub DefaultTimeout:Duration,
16
17	/// Maximum number of retry attempts for retryable errors.
18	pub MaximumRetries:u32,
19
20	/// Base retry delay for exponential backoff.
21	pub RetryBaseDelay:Duration,
22
23	/// Maximum retry delay cap (prevents extremely long backoffs).
24	pub RetryMaximumDelay:Duration,
25
26	/// Whether retry with jitter is enabled (recommended for distributed
27	/// systems).
28	pub RetryJitterEnabled:bool,
29
30	/// Circuit breaker failure threshold (number of consecutive failures before
31	/// opening).
32	pub CircuitBreakerFailureThreshold:u32,
33
34	/// Circuit breaker reset timeout (how long to wait before half-open).
35	pub CircuitBreakerResetTimeout:Duration,
36
37	/// Whether health checks are enabled.
38	pub HealthChecksEnabled:bool,
39
40	/// Health check interval (how often to perform health checks).
41	pub HealthCheckInterval:Duration,
42
43	/// Metrics collection enabled flag.
44	pub MetricsEnabled:bool,
45
46	/// Transport-specific configuration overrides.
47	#[serde(skip_serializing_if = "HashMap::is_empty")]
48	pub TransportConfigurations:HashMap<TransportType, serde_json::Value>,
49
50	/// Allowed transport types for auto-selection.
51	#[serde(skip_serializing_if = "Vec::is_empty")]
52	pub AllowedTransports:Vec<TransportType>,
53
54	/// Forbidden transport types (never used even if available).
55	#[serde(skip_serializing_if = "Vec::is_empty")]
56	pub ForbiddenTransports:Vec<TransportType>,
57}
58
59impl Default for TransportConfig {
60	fn default() -> Self {
61		Self {
62			DefaultTimeout:Duration::from_secs(30),
63
64			MaximumRetries:3,
65
66			RetryBaseDelay:Duration::from_millis(100),
67
68			RetryMaximumDelay:Duration::from_secs(10),
69
70			RetryJitterEnabled:true,
71
72			CircuitBreakerFailureThreshold:5,
73
74			CircuitBreakerResetTimeout:Duration::from_secs(60),
75
76			HealthChecksEnabled:true,
77
78			HealthCheckInterval:Duration::from_secs(30),
79
80			MetricsEnabled:true,
81
82			TransportConfigurations:HashMap::new(),
83
84			AllowedTransports:Vec::new(),
85
86			ForbiddenTransports:Vec::new(),
87		}
88	}
89}
90
91impl TransportConfig {
92	/// Creates a new `TransportConfig` with default values.
93	pub fn New() -> Self { Self::default() }
94
95	/// Sets the default request timeout.
96	pub fn WithDefaultTimeout(mut self, Timeout:Duration) -> Self {
97		self.DefaultTimeout = Timeout;
98
99		self
100	}
101
102	/// Sets the maximum number of retry attempts.
103	pub fn WithMaximumRetries(mut self, MaximumRetries:u32) -> Self {
104		self.MaximumRetries = MaximumRetries;
105
106		self
107	}
108
109	/// Sets the base retry delay for exponential backoff.
110	pub fn WithRetryBaseDelay(mut self, Delay:Duration) -> Self {
111		self.RetryBaseDelay = Delay;
112
113		self
114	}
115
116	/// Sets the maximum retry delay cap.
117	pub fn WithRetryMaximumDelay(mut self, Delay:Duration) -> Self {
118		self.RetryMaximumDelay = Delay;
119
120		self
121	}
122
123	/// Enables or disables retry jitter.
124	pub fn WithRetryJitter(mut self, Enabled:bool) -> Self {
125		self.RetryJitterEnabled = Enabled;
126
127		self
128	}
129
130	/// Sets the circuit breaker failure threshold.
131	pub fn WithCircuitBreakerThreshold(mut self, Threshold:u32) -> Self {
132		self.CircuitBreakerFailureThreshold = Threshold;
133
134		self
135	}
136
137	/// Sets the circuit breaker reset timeout.
138	pub fn WithCircuitBreakerResetTimeout(mut self, Timeout:Duration) -> Self {
139		self.CircuitBreakerResetTimeout = Timeout;
140
141		self
142	}
143
144	/// Enables or disables health checks.
145	pub fn WithHealthChecksEnabled(mut self, Enabled:bool) -> Self {
146		self.HealthChecksEnabled = Enabled;
147
148		self
149	}
150
151	/// Sets the health check interval.
152	pub fn WithHealthCheckInterval(mut self, Interval:Duration) -> Self {
153		self.HealthCheckInterval = Interval;
154
155		self
156	}
157
158	/// Enables or disables metrics collection.
159	pub fn WithMetricsEnabled(mut self, Enabled:bool) -> Self {
160		self.MetricsEnabled = Enabled;
161
162		self
163	}
164
165	/// Adds a transport-specific configuration override.
166	pub fn WithTransportConfiguration(mut self, TransportKind:TransportType, Configuration:serde_json::Value) -> Self {
167		self.TransportConfigurations.insert(TransportKind, Configuration);
168
169		self
170	}
171
172	/// Gets the transport-specific configuration for the given type, if any.
173	pub fn GetTransportConfiguration(&self, TransportKind:TransportType) -> Option<&serde_json::Value> {
174		self.TransportConfigurations.get(&TransportKind)
175	}
176
177	/// Sets the allowed transport types for auto-selection.
178	pub fn WithAllowedTransports(mut self, Transports:Vec<TransportType>) -> Self {
179		self.AllowedTransports = Transports;
180
181		self
182	}
183
184	/// Adds a forbidden transport type.
185	pub fn AddForbiddenTransport(mut self, TransportKind:TransportType) -> Self {
186		self.ForbiddenTransports.push(TransportKind);
187
188		self
189	}
190
191	/// Alias for `AddForbiddenTransport`.
192	pub fn WithForbiddenTransport(self, TransportKind:TransportType) -> Self {
193		self.AddForbiddenTransport(TransportKind)
194	}
195
196	/// Checks if a transport type is allowed by this configuration.
197	pub fn IsAllowed(&self, TransportKind:TransportType) -> bool {
198		if self.ForbiddenTransports.contains(&TransportKind) {
199			return false;
200		}
201
202		if self.AllowedTransports.is_empty() {
203			true
204		} else {
205			self.AllowedTransports.contains(&TransportKind)
206		}
207	}
208
209	/// Gets the effective timeout for a request, considering request-specific
210	/// overrides.
211	pub fn EffectiveTimeout(&self, RequestTimeoutMilliseconds:Option<u64>) -> Duration {
212		RequestTimeoutMilliseconds
213			.map(Duration::from_millis)
214			.unwrap_or(self.DefaultTimeout)
215	}
216
217	/// Gets the effective retry delay for a given attempt number, considering
218	/// jitter.
219	pub fn EffectiveRetryDelay(&self, Attempt:u32) -> Duration {
220		let Multiplier = 1u32.checked_shl(Attempt.min(30)).unwrap_or(u32::MAX);
221
222		let mut Delay = self.RetryBaseDelay.checked_mul(Multiplier).unwrap_or(self.RetryMaximumDelay);
223
224		if Delay > self.RetryMaximumDelay {
225			Delay = self.RetryMaximumDelay;
226		}
227
228		if self.RetryJitterEnabled {
229			let Nanoseconds = std::time::SystemTime::now()
230				.duration_since(std::time::UNIX_EPOCH)
231				.map(|Duration| Duration.subsec_nanos())
232				.unwrap_or(0);
233
234			let JitterFraction = (Nanoseconds % 1000) as f64 / 500.0 - 1.0;
235
236			let JitterAmount = Delay.as_millis() as f64 * 0.25;
237
238			let AdjustedMilliseconds = (Delay.as_millis() as f64 + JitterFraction * JitterAmount).max(1.0) as u64;
239
240			Delay = Duration::from_millis(AdjustedMilliseconds);
241		}
242
243		Delay
244	}
245}
246
247#[cfg(test)]
248mod tests {
249
250	use super::*;
251
252	#[test]
253	fn TestTransportConfigDefaults() {
254		let Configuration = TransportConfig::default();
255
256		assert_eq!(Configuration.DefaultTimeout, Duration::from_secs(30));
257
258		assert_eq!(Configuration.MaximumRetries, 3);
259
260		assert!(Configuration.HealthChecksEnabled);
261
262		assert!(Configuration.MetricsEnabled);
263	}
264
265	#[test]
266	fn TestTransportConfigBuilder() {
267		let Configuration = TransportConfig::default()
268			.WithDefaultTimeout(Duration::from_secs(60))
269			.WithMaximumRetries(5)
270			.WithRetryJitter(false);
271
272		assert_eq!(Configuration.DefaultTimeout, Duration::from_secs(60));
273
274		assert_eq!(Configuration.MaximumRetries, 5);
275
276		assert!(!Configuration.RetryJitterEnabled);
277	}
278
279	#[test]
280	fn TestIsAllowed() {
281		let Configuration = TransportConfig::default();
282
283		assert!(Configuration.IsAllowed(TransportType::Grpc));
284
285		let Configuration = Configuration.WithForbiddenTransport(TransportType::Grpc);
286
287		assert!(!Configuration.IsAllowed(TransportType::Grpc));
288
289		assert!(Configuration.IsAllowed(TransportType::Ipc));
290
291		let Configuration = Configuration.WithAllowedTransports(vec![TransportType::Ipc]);
292
293		assert!(!Configuration.IsAllowed(TransportType::Grpc));
294
295		assert!(Configuration.IsAllowed(TransportType::Ipc));
296	}
297
298	#[test]
299	fn TestEffectiveTimeout() {
300		let Configuration = TransportConfig::default().WithDefaultTimeout(Duration::from_secs(30));
301
302		assert_eq!(Configuration.EffectiveTimeout(None), Duration::from_secs(30));
303
304		assert_eq!(Configuration.EffectiveTimeout(Some(5000)), Duration::from_millis(5000));
305	}
306
307	#[test]
308	fn TestEffectiveRetryDelay() {
309		let Configuration = TransportConfig::default()
310			.WithRetryBaseDelay(Duration::from_millis(100))
311			.WithRetryMaximumDelay(Duration::from_secs(10))
312			.WithRetryJitter(false);
313
314		assert_eq!(Configuration.EffectiveRetryDelay(0), Duration::from_millis(100));
315
316		assert_eq!(Configuration.EffectiveRetryDelay(1), Duration::from_millis(200));
317
318		assert_eq!(Configuration.EffectiveRetryDelay(2), Duration::from_millis(400));
319
320		assert_eq!(Configuration.EffectiveRetryDelay(10), Duration::from_secs(10));
321	}
322}