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
//! This crate provides an automatically managed connection pool for the
//! [tonic]'s [`Channel`](tonic::transport::Channel)s. It provides simpler to
//! use interface by automatically updating the pool with new channels based on
//! DNS resolution, detecting failed connections, and exposing methods to report
//! broken endpoints.
//!
//! For more detailed explanation of the internal workings of the pool, please
//! see the [implementation details](#implementation-details).
//!
//! # Usage
//!
//! Although this crate is designed to be used by gRPC clients generated by
//! [`soda-pool-build`] crate, it can also be used directly.
//!
//! ```no_run
//! # use std::error::Error;
//! use soda_pool::{ChannelPool, EndpointTemplate, ManagedChannelPoolBuilder};
//! use tonic::transport::Channel;
//! use url::Url;
//!
//! #[tokio::main]
//! async fn main() -> Result<(), Box<dyn Error>> {
//! let url = Url::parse("http://grpc.example.com:50051")?;
//! let endpoint_template = EndpointTemplate::new(url)?;
//! let pool = ManagedChannelPoolBuilder::new(endpoint_template).build();
//!
//! // Get a channel from the pool
//! let Some((ip_address, channel)) = pool.get_channel().await else {
//! Err(NoChannelAppError)?
//! };
//!
//! // Use the channel to make a gRPC request
//! let result = make_request(channel).await;
//! match result {
//! Ok(_) => println!("Request succeeded"),
//! Err(ref e) => {
//! // Warning: Error can also be returned from the gRPC server.
//! // Examine the error and decide whether to report the endpoint as broken.
//! println!("Request failed: {:?}. Reporting as broken", e);
//! pool.report_broken(ip_address).await;
//! }
//! }
//! Ok(())
//! }
//!
//! # #[derive(Debug)]
//! // Placeholder for application-specific error handling.
//! struct NoChannelAppError;
//! # impl Error for NoChannelAppError {}
//! # impl std::fmt::Display for NoChannelAppError {
//! # fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
//! # write!(f, "No channel available")
//! # }
//! # }
//!
//! // Placeholder for the actual gRPC request.
//! async fn make_request(channel: Channel) -> Result<(), Box<dyn Error>> {
//! return Ok(())
//! }
//! ```
//!
//! # Feature flags
//!
//! - `tls`: Enables TLS options for the [`EndpointTemplate`].
//! - `mock`: Enables the mock implementation of the [`ChannelPool`] trait for
//! testing purposes.
//!
//! # Implementation details
//!
//! On creation of the pool, two background tasks are started:
//! 1. A task that periodically resolves the DNS name and updates the pool with
//! new channels.
//!
//! Frequency of this check can be configured using
//! [`ManagedChannelPoolBuilder::dns_interval`] method. The default is 5
//! seconds.
//!
//! 2. A task that periodically checks connections previously marked as broken
//! and tries to reconnect them.
//!
//! Frequency of this check is calculated on per IP basis following the
//! exponential backoff algorithm. The backoff time will range from 1 to
//! approximately 60 seconds.
//!
//! Those tasks are shared between all clones of the pool and continue to run
//! until the last copy is dropped.
pub use *;
pub use *;
pub use *;