soda-pool 0.0.4

Connection pool for tonic's gRPC channels
Documentation
#![warn(clippy::unwrap_used)]
#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]

//! 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.

mod pool;
pub use pool::*;

mod dns;

mod broken_endpoints;

mod endpoint_template;
pub use endpoint_template::*;

mod retry;
pub use retry::*;

#[doc(hidden)]
pub mod deps;

mod macros;
mod ready_channels;

#[cfg(feature = "mock")]
pub mod mock;