Skip to main content

rs_zero/rpc/
config.rs

1use std::{net::SocketAddr, time::Duration};
2
3/// RPC client configuration.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct RpcClientConfig {
6    /// Endpoint URI, for example `http://127.0.0.1:50051`.
7    pub endpoint: String,
8    /// Connection establishment timeout.
9    pub connect_timeout: Duration,
10    /// Per-request timeout.
11    pub request_timeout: Duration,
12    /// Optional resilience behavior used by generated clients or helpers.
13    pub resilience: RpcResilienceConfig,
14    /// Client retry behavior.
15    pub retry: RpcRetryConfig,
16    /// Deadline propagation behavior.
17    pub deadline: RpcDeadlineConfig,
18    /// Client load-balancing behavior.
19    pub load_balance: RpcLoadBalanceConfig,
20    /// Optional service discovery target.
21    pub discovery: RpcDiscoveryConfig,
22    /// Streaming RPC instrumentation behavior.
23    pub streaming: RpcStreamingConfig,
24}
25
26impl RpcClientConfig {
27    /// Creates a client config with practical defaults.
28    pub fn new(endpoint: impl Into<String>) -> Self {
29        Self {
30            endpoint: endpoint.into(),
31            connect_timeout: Duration::from_secs(3),
32            request_timeout: Duration::from_secs(5),
33            resilience: RpcResilienceConfig::default(),
34            retry: RpcRetryConfig::default(),
35            deadline: RpcDeadlineConfig::default(),
36            load_balance: RpcLoadBalanceConfig::default(),
37            discovery: RpcDiscoveryConfig::default(),
38            streaming: RpcStreamingConfig::default(),
39        }
40    }
41}
42
43impl RpcClientConfig {
44    /// Creates a client config with production-oriented resilience defaults.
45    pub fn production_defaults(endpoint: impl Into<String>) -> Self {
46        Self {
47            resilience: RpcResilienceConfig::production_defaults(),
48            retry: RpcRetryConfig::production_defaults(),
49            deadline: RpcDeadlineConfig::production_defaults(),
50            load_balance: RpcLoadBalanceConfig::production_defaults(),
51            streaming: RpcStreamingConfig::production_defaults(),
52            ..Self::new(endpoint)
53        }
54    }
55
56    /// Creates a client config with production-oriented resilience defaults.
57    #[allow(deprecated)]
58    #[deprecated(note = "use production_defaults instead")]
59    pub fn go_zero_defaults(endpoint: impl Into<String>) -> Self {
60        Self::production_defaults(endpoint)
61    }
62}
63
64/// RPC server configuration.
65#[derive(Debug, Clone, PartialEq, Eq)]
66pub struct RpcServerConfig {
67    /// Logical service name.
68    pub name: String,
69    /// Server listen address.
70    pub addr: SocketAddr,
71    /// Resilience adapter boundary for tonic services.
72    pub resilience: RpcResilienceConfig,
73    /// Streaming RPC instrumentation behavior.
74    pub streaming: RpcStreamingConfig,
75}
76
77impl RpcServerConfig {
78    /// Creates server config.
79    pub fn new(name: impl Into<String>, addr: SocketAddr) -> Self {
80        Self {
81            name: name.into(),
82            addr,
83            resilience: RpcResilienceConfig::default(),
84            streaming: RpcStreamingConfig::default(),
85        }
86    }
87}
88
89impl RpcServerConfig {
90    /// Creates a server config with production-oriented resilience defaults.
91    pub fn production_defaults(name: impl Into<String>, addr: SocketAddr) -> Self {
92        Self {
93            resilience: RpcResilienceConfig::production_defaults(),
94            streaming: RpcStreamingConfig::production_defaults(),
95            ..Self::new(name, addr)
96        }
97    }
98
99    /// Creates a server config with production-oriented resilience defaults.
100    #[allow(deprecated)]
101    #[deprecated(note = "use production_defaults instead")]
102    pub fn go_zero_defaults(name: impl Into<String>, addr: SocketAddr) -> Self {
103        Self::production_defaults(name, addr)
104    }
105}
106
107/// RPC retry configuration.
108#[derive(Debug, Clone, PartialEq, Eq)]
109pub struct RpcRetryConfig {
110    /// Whether client retries are enabled.
111    pub enabled: bool,
112    /// Maximum total attempts including the first call.
113    pub max_attempts: u32,
114    /// Initial retry backoff.
115    pub initial_backoff: Duration,
116    /// Maximum retry backoff.
117    pub max_backoff: Duration,
118    /// Retryable tonic codes.
119    pub retryable_codes: Vec<tonic::Code>,
120}
121
122impl Default for RpcRetryConfig {
123    fn default() -> Self {
124        Self {
125            enabled: false,
126            max_attempts: 1,
127            initial_backoff: Duration::from_millis(50),
128            max_backoff: Duration::from_secs(1),
129            retryable_codes: vec![
130                tonic::Code::Unavailable,
131                tonic::Code::DeadlineExceeded,
132                tonic::Code::ResourceExhausted,
133            ],
134        }
135    }
136}
137
138impl RpcRetryConfig {
139    /// Returns a production-oriented retry profile for client calls.
140    pub fn production_defaults() -> Self {
141        Self {
142            enabled: true,
143            max_attempts: 3,
144            initial_backoff: Duration::from_millis(50),
145            max_backoff: Duration::from_millis(500),
146            ..Self::default()
147        }
148    }
149
150    /// Returns a production-oriented retry profile for client calls.
151    #[allow(deprecated)]
152    #[deprecated(note = "use production_defaults instead")]
153    pub fn go_zero_defaults() -> Self {
154        Self::production_defaults()
155    }
156}
157
158/// RPC deadline propagation configuration.
159#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct RpcDeadlineConfig {
161    /// Whether outgoing requests should carry remaining timeout metadata.
162    pub propagate: bool,
163    /// Whether retries should be clipped by remaining request budget.
164    pub clip_retries_to_budget: bool,
165}
166
167impl Default for RpcDeadlineConfig {
168    fn default() -> Self {
169        Self {
170            propagate: false,
171            clip_retries_to_budget: true,
172        }
173    }
174}
175
176impl RpcDeadlineConfig {
177    /// Returns production-oriented deadline defaults.
178    pub fn production_defaults() -> Self {
179        Self {
180            propagate: true,
181            clip_retries_to_budget: true,
182        }
183    }
184
185    /// Returns production-oriented deadline defaults.
186    #[allow(deprecated)]
187    #[deprecated(note = "use production_defaults instead")]
188    pub fn go_zero_defaults() -> Self {
189        Self::production_defaults()
190    }
191}
192
193/// RPC load balance policy.
194#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
195pub enum LoadBalancePolicy {
196    /// Use the configured static endpoint directly.
197    #[default]
198    Static,
199    /// Select discovered instances by weight and round-robin cursor.
200    WeightedRoundRobin,
201}
202
203/// RPC load balance configuration.
204#[derive(Debug, Clone, PartialEq, Eq, Default)]
205pub struct RpcLoadBalanceConfig {
206    /// Selection policy.
207    pub policy: LoadBalancePolicy,
208}
209
210impl RpcLoadBalanceConfig {
211    /// Returns production-oriented load balance defaults.
212    pub fn production_defaults() -> Self {
213        Self {
214            policy: LoadBalancePolicy::WeightedRoundRobin,
215        }
216    }
217
218    /// Returns production-oriented load balance defaults.
219    #[allow(deprecated)]
220    #[deprecated(note = "use production_defaults instead")]
221    pub fn go_zero_defaults() -> Self {
222        Self::production_defaults()
223    }
224}
225
226/// RPC service discovery configuration.
227#[derive(Debug, Clone, PartialEq, Eq, Default)]
228pub struct RpcDiscoveryConfig {
229    /// Optional logical service name used by discovery clients.
230    pub service: Option<String>,
231}
232
233/// RPC streaming instrumentation configuration.
234#[derive(Debug, Clone, PartialEq, Eq, Default)]
235pub struct RpcStreamingConfig {
236    /// Whether stream send/receive events should be observed.
237    pub observe: bool,
238    /// Whether stream completion should be mapped to resilience outcomes.
239    pub resilience: bool,
240    /// Optional per-stream timeout.
241    pub timeout: Option<Duration>,
242}
243
244impl RpcStreamingConfig {
245    /// Returns production-oriented streaming defaults.
246    pub fn production_defaults() -> Self {
247        Self {
248            observe: true,
249            resilience: true,
250            timeout: Some(Duration::from_secs(30)),
251        }
252    }
253
254    /// Returns production-oriented streaming defaults.
255    #[allow(deprecated)]
256    #[deprecated(note = "use production_defaults instead")]
257    pub fn go_zero_defaults() -> Self {
258        Self::production_defaults()
259    }
260}
261
262/// RPC resilience configuration shared by future tonic layers.
263#[derive(Debug, Clone, PartialEq, Eq)]
264pub struct RpcResilienceConfig {
265    /// Whether RPC breaker integration should be enabled by adapters.
266    pub breaker_enabled: bool,
267    /// Consecutive failures that open an RPC breaker.
268    pub breaker_failure_threshold: u32,
269    /// Time before an open RPC breaker allows a half-open trial call.
270    pub breaker_reset_timeout: Duration,
271    /// Whether the RPC breaker uses Google SRE style adaptive rejection.
272    pub breaker_sre_enabled: bool,
273    /// SRE breaker multiplier in millis. `1500` means `k = 1.5`.
274    pub breaker_sre_k_millis: u32,
275    /// Minimum total samples before SRE breaker rejection can start.
276    pub breaker_sre_protection: u64,
277    /// Optional maximum number of in-flight RPC calls.
278    pub max_concurrency: Option<usize>,
279    /// Per-call timeout used by generated service adapters.
280    pub request_timeout: Duration,
281    /// Whether adaptive RPC shedding is enabled.
282    pub shedding_enabled: bool,
283    /// Maximum in-flight calls used by the adaptive shedder.
284    pub shedding_max_in_flight: Option<usize>,
285    /// Minimum samples before latency-based shedding can reject.
286    pub shedding_min_request_count: u64,
287    /// Average latency threshold used by adaptive shedding.
288    pub shedding_max_latency: Duration,
289    /// CPU usage threshold used by adaptive shedding, where `1000` means 100%.
290    pub shedding_cpu_threshold_millis: u32,
291    /// Cool-off duration after a recent adaptive shedder drop.
292    pub shedding_cool_off: Duration,
293    /// Number of buckets used by adaptive shedder rolling windows.
294    pub shedding_window_buckets: usize,
295    /// Duration represented by each adaptive shedder bucket.
296    pub shedding_window_bucket_duration: Duration,
297    /// Optional Redis-backed RPC limiter.
298    #[cfg(all(feature = "resil", feature = "cache-redis"))]
299    pub rate_limiter: RpcRateLimiterConfig,
300}
301
302impl Default for RpcResilienceConfig {
303    fn default() -> Self {
304        Self {
305            breaker_enabled: false,
306            breaker_failure_threshold: 5,
307            breaker_reset_timeout: Duration::from_secs(30),
308            breaker_sre_enabled: false,
309            breaker_sre_k_millis: 1500,
310            breaker_sre_protection: 5,
311            max_concurrency: None,
312            request_timeout: Duration::from_secs(5),
313            shedding_enabled: false,
314            shedding_max_in_flight: None,
315            shedding_min_request_count: 20,
316            shedding_max_latency: Duration::from_millis(250),
317            shedding_cpu_threshold_millis: 900,
318            shedding_cool_off: Duration::from_secs(1),
319            shedding_window_buckets: 50,
320            shedding_window_bucket_duration: Duration::from_millis(100),
321            #[cfg(all(feature = "resil", feature = "cache-redis"))]
322            rate_limiter: RpcRateLimiterConfig::default(),
323        }
324    }
325}
326
327impl RpcResilienceConfig {
328    /// Returns a production-oriented resilience profile for unary RPC calls.
329    pub fn production_defaults() -> Self {
330        Self {
331            breaker_enabled: true,
332            breaker_sre_enabled: true,
333            max_concurrency: Some(1024),
334            request_timeout: Duration::from_secs(5),
335            shedding_enabled: true,
336            shedding_max_in_flight: Some(1024),
337            shedding_min_request_count: 20,
338            shedding_max_latency: Duration::from_millis(250),
339            ..Self::default()
340        }
341    }
342
343    /// Returns a production-oriented resilience profile for unary RPC calls.
344    #[allow(deprecated)]
345    #[deprecated(note = "use production_defaults instead")]
346    pub fn go_zero_defaults() -> Self {
347        Self::production_defaults()
348    }
349}
350
351/// Redis-backed RPC limiter selection.
352#[cfg(all(feature = "resil", feature = "cache-redis"))]
353#[derive(Debug, Clone, PartialEq, Eq, Default)]
354pub enum RpcRateLimiterConfig {
355    /// No Redis limiter is applied.
356    #[default]
357    Disabled,
358    /// Token-bucket limiter backed by Redis.
359    RedisToken(crate::resil::RedisTokenLimiterConfig),
360    /// Fixed-window period limiter backed by Redis.
361    RedisPeriod(crate::resil::RedisPeriodLimiterConfig),
362}