ccxt_core/ws_client/
config.rs1use rand::Rng;
4use std::time::Duration;
5
6#[derive(Debug, Clone)]
8pub struct BackoffConfig {
9 pub base_delay: Duration,
11 pub max_delay: Duration,
13 pub jitter_factor: f64,
15 pub multiplier: f64,
17}
18
19impl Default for BackoffConfig {
20 fn default() -> Self {
21 Self {
22 base_delay: Duration::from_secs(1),
23 max_delay: Duration::from_secs(60),
24 jitter_factor: 0.25,
25 multiplier: 2.0,
26 }
27 }
28}
29
30#[derive(Debug, Clone)]
32pub struct BackoffStrategy {
33 config: BackoffConfig,
34}
35
36impl BackoffStrategy {
37 pub fn new(config: BackoffConfig) -> Self {
39 Self { config }
40 }
41
42 pub fn with_defaults() -> Self {
44 Self::new(BackoffConfig::default())
45 }
46
47 pub fn config(&self) -> &BackoffConfig {
49 &self.config
50 }
51
52 #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
54 pub fn calculate_delay(&self, attempt: u32) -> Duration {
55 let base_ms = self.config.base_delay.as_millis() as f64;
56 let multiplier = self.config.multiplier;
57 let max_ms = self.config.max_delay.as_millis() as f64;
58
59 let exponential_delay_ms = base_ms * multiplier.powi(attempt as i32);
60 let capped_delay_ms = exponential_delay_ms.min(max_ms);
61
62 let jitter_ms = if self.config.jitter_factor > 0.0 {
63 let jitter_range = capped_delay_ms * self.config.jitter_factor;
64 rand::rng().random::<f64>() * jitter_range
65 } else {
66 0.0
67 };
68
69 Duration::from_millis((capped_delay_ms + jitter_ms) as u64)
70 }
71
72 #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
74 pub fn calculate_delay_without_jitter(&self, attempt: u32) -> Duration {
75 let base_ms = self.config.base_delay.as_millis() as f64;
76 let multiplier = self.config.multiplier;
77 let max_ms = self.config.max_delay.as_millis() as f64;
78
79 let exponential_delay_ms = base_ms * multiplier.powi(attempt as i32);
80 let capped_delay_ms = exponential_delay_ms.min(max_ms);
81
82 Duration::from_millis(capped_delay_ms as u64)
83 }
84}
85
86pub const DEFAULT_MAX_SUBSCRIPTIONS: usize = 100;
88
89pub const DEFAULT_SHUTDOWN_TIMEOUT: u64 = 5000;
91
92pub const DEFAULT_MESSAGE_CHANNEL_CAPACITY: usize = 1000;
98
99pub const DEFAULT_WRITE_CHANNEL_CAPACITY: usize = 100;
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
108pub enum BackpressureStrategy {
109 #[default]
112 DropOldest,
113 DropNewest,
116 Block,
119}
120
121#[derive(Debug, Clone)]
123pub struct WsConfig {
124 pub url: String,
126 pub connect_timeout: u64,
128 pub ping_interval: u64,
130 pub reconnect_interval: u64,
132 pub max_reconnect_attempts: u32,
134 pub auto_reconnect: bool,
136 pub enable_compression: bool,
138 pub pong_timeout: u64,
140 pub backoff_config: BackoffConfig,
142 pub max_subscriptions: usize,
144 pub shutdown_timeout: u64,
146 pub message_channel_capacity: usize,
151 pub write_channel_capacity: usize,
155 pub backpressure_strategy: BackpressureStrategy,
159 pub max_message_size: Option<usize>,
163 pub max_frame_size: Option<usize>,
167}
168
169impl Default for WsConfig {
170 fn default() -> Self {
171 Self {
172 url: String::new(),
173 connect_timeout: 10000,
174 ping_interval: 30000,
175 reconnect_interval: 5000,
176 max_reconnect_attempts: 5,
177 auto_reconnect: true,
178 enable_compression: false,
179 pong_timeout: 30000, backoff_config: BackoffConfig::default(),
181 max_subscriptions: DEFAULT_MAX_SUBSCRIPTIONS,
182 shutdown_timeout: DEFAULT_SHUTDOWN_TIMEOUT,
183 message_channel_capacity: DEFAULT_MESSAGE_CHANNEL_CAPACITY,
184 write_channel_capacity: DEFAULT_WRITE_CHANNEL_CAPACITY,
185 backpressure_strategy: BackpressureStrategy::default(),
186 max_message_size: Some(128 * 1024 * 1024),
187 max_frame_size: Some(32 * 1024 * 1024),
188 }
189 }
190}