dynomite/net/mod.rs
1//! Connection state machines and transport listeners.
2//!
3//! This module wires together the I/O substrate from
4//! [`crate::io`], the message model from [`crate::msg`], and the
5//! protocol codecs in [`crate::proto`] into a runtime that:
6//!
7//! * accepts client connections through a [`proxy::Proxy`] listener,
8//! * accepts inbound peer connections through a
9//! [`dnode_proxy::DnodeProxy`] listener,
10//! * fans out to backend datastores through a per-pool
11//! [`pool::ConnPool`] of outbound [`server::ServerConn`]s,
12//! * fans out to peer nodes through a
13//! [`dnode_server::DnodeServerConn`] outbound connection,
14//! * tracks consecutive-failure auto-eject decisions via
15//! [`auto_eject::AutoEject`].
16//!
17//! Every connection holds a boxed
18//! [`Transport`](crate::io::reactor::Transport) so the same FSM
19//! drives TCP today and QUIC behind the `quic` cargo feature.
20//!
21//! Cluster routing decisions (request forwarding across racks /
22//! DCs, peer selection, gossip) live in Stage 10's `cluster::*`
23//! module and are dispatched into via the [`Dispatcher`] trait
24//! below.
25//!
26//! # Examples
27//!
28//! ```no_run
29//! use dynomite::net::{ConnPool, ConnPoolConfig};
30//! # use dynomite::io::reactor::ConnRole;
31//! # use dynomite::io::reactor::TcpTransport;
32//! # use std::time::Duration;
33//! # async fn demo() -> std::io::Result<()> {
34//! let cfg = ConnPoolConfig {
35//! max_connections: 4,
36//! server_failure_limit: 3,
37//! server_retry_timeout_ms: 1_000,
38//! auto_eject: true,
39//! };
40//! let pool: ConnPool<TcpTransport> = ConnPool::new(cfg);
41//! assert_eq!(pool.config().max_connections, 4);
42//! # Ok(())
43//! # }
44//! ```
45
46pub mod auto_eject;
47pub mod client;
48pub mod conn;
49pub mod dispatcher;
50pub mod dnode_client;
51pub mod dnode_proxy;
52pub mod dnode_server;
53pub mod listener;
54pub mod pool;
55pub mod proxy;
56pub mod server;
57pub mod tls;
58
59#[cfg(feature = "quic")]
60pub mod quic;
61
62#[cfg(feature = "quic")]
63pub use self::quic::{connect as quic_connect, QuicConfig, QuicListener, QuicProxy, QuicTransport};
64
65use std::io;
66
67use thiserror::Error;
68
69pub use self::auto_eject::{AutoEject, AutoEjectState};
70pub use self::client::{ClientHandler, ClientLoopOutcome};
71pub use self::conn::{Conn, ConnHandle, ConnStats};
72pub use self::dispatcher::{
73 DispatchOutcome, Dispatcher, NoopDispatcher, OutboundEnvelope, ServerSink,
74};
75pub use self::dnode_client::DnodeClientHandler;
76pub use self::dnode_proxy::DnodeProxy;
77pub use self::dnode_server::DnodeServerConn;
78pub use self::listener::{bind_dual_stack, BindOptions};
79pub use self::pool::{ConnFactory, ConnHandle as PoolHandle, ConnPool, ConnPoolConfig};
80pub use self::proxy::Proxy;
81pub use self::server::ServerConn;
82pub use self::tls::{
83 acceptor_from, connector_from, load_client_config, load_server_config, server_name_owned,
84 SharedTlsProfiles, TlsClientTransport, TlsError, TlsServerTransport,
85};
86
87/// Top-level error type returned by net::* operations.
88#[derive(Debug, Error)]
89pub enum NetError {
90 /// I/O error from the transport layer.
91 #[error("io error: {0}")]
92 Io(#[from] io::Error),
93 /// Protocol parsing error reported by a datastore codec.
94 #[error("protocol parse error: {0}")]
95 Parse(String),
96 /// DNODE codec error.
97 #[error("dnode error: {0}")]
98 Dnode(String),
99 /// The pool exhausted its retry budget for a server.
100 #[error("server has been auto-ejected")]
101 Ejected,
102 /// The pool reached its maximum connection count and there is
103 /// no idle handle to return.
104 #[error("connection pool exhausted")]
105 PoolExhausted,
106 /// The pool was shut down while a caller was waiting for a
107 /// handle.
108 #[error("connection pool shut down")]
109 PoolShutdown,
110 /// Connection closed while operation was in flight.
111 #[error("connection closed")]
112 Closed,
113 /// TLS-handshake or load-time error.
114 #[error("tls error: {0}")]
115 Tls(String),
116 /// Authentication handshake (e.g. Redis `AUTH`) rejected by
117 /// the peer. Carries the trimmed reply text so operators can
118 /// distinguish `-NOAUTH`, `-ERR invalid password`, and
119 /// similar variants without parsing the formatted message.
120 #[error("authentication rejected: {0}")]
121 Auth(String),
122}
123
124impl From<crate::net::tls::TlsError> for NetError {
125 fn from(value: crate::net::tls::TlsError) -> Self {
126 NetError::Tls(value.to_string())
127 }
128}
129
130impl From<crate::proto::dnode::DnodeError> for NetError {
131 fn from(value: crate::proto::dnode::DnodeError) -> Self {
132 NetError::Dnode(format!("{value:?}"))
133 }
134}