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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Copyright 2019 Joyent, Inc.

//! A multi-node service connection pool
//!
//! Cueball is a library for "playing pool" -- managing a pool of connections to
//! a multi-node service. This implementation of cueball is inspired by the
//! original Node.js implementation of
//! [cueball](https://joyent.github.io/node-cueball/) that is used by many of
//! Joyent's services and software components. The rust implementation relies on
//! two primary traits in order to manage a set of connections across a set
//! nodes providing a service. These are the
//! [`Resolver`](resolver/trait.Resolver.html) trait and the
//! [`Connection`](connection/trait.Connection.html) trait.
//!
//! ## Resolvers
//!
//! A *resolver* is responsible for locating all of the nodes or backends
//! available within a logical service, obtaining their IP address and port
//! information (or whatever is required to connect to them) and tracking
//! them. This is normally a service discovery client of some form. An example
//! of this would be a DNS-based Resolver implementation that uses DNS SRV
//! records as a form of service discovery mechanism to find backends.
//!
//! ## Connections
//!
//! In cueball, a *connection* is not necessarily just a TCP socket. It can be
//! anything that provides some kind of logical connection to a service, as long
//! as it obeys a similar interface to a socket.
//!
//! This is intended to allow users of the API to represent a "connection" as an
//! application or session layer concept. For example, it could be useful to
//! construct a pool of connections to an LDAP server that perform a bind
//! operation (authenticate) before they are considered *connected*.
//!
//! In addition to a [`Resolver`](resolver/trait.Resolver.html) and
//! [`Connection`](connection/trait.Connection.html) implementation cueball
//! users also provide the cueball connection pool with a function to establish
//! a *connection* to the desired service. The trait bounds established by the
//! cueball connection pool for this function are as follows:
//! ```rust.ignore
//! FnMut(&Backend) -> C + Send + 'static
//! where C: Connection
//! ```
//! The requirement is a function that takes a reference to a
//! [`Backend`](backend/struct.Backend.html) from a resolver and returns some
//! instance of a [`Connection`](connection/trait.Connection.html).
//!
//! The purpose of this function is to provide a way to capture application
//! level configuration information required to establish a *connection* to a
//! service. *e.g.* A database connection might require application-specific
//! configuration such as a database name or user name in order to establish a
//! connection.
//!
//! ## Rebalancing
//!
//! As [`Backend`](backend/struct.Backend.html)s for a service come and go the
//! connection pool rebalances the configured number of connections
//! (`max_connections`) across the available set of
//! [`Backend`](backend/struct.Backend.html)s. Rebalancing occurs when a
//! [`Resolver`](resolver/trait.Resolver.html) notifies the connection pool that
//! a new backend has been added or that an existing backend has been
//! removed. The connection pool rebalances the connections in response to one
//! of these events in order to maintain an even distribution of the connections
//! among the available backends.
//!
//! The connection pool uses a configurable delay when a message is received
//! from the [`Resolver`](resolver/trait.Resolver.html) prior to performing the
//! actual rebalancing. This delay is to account for situations where multiple
//! messages might be sent by the [`Resolver`](resolver/trait.Resolver.html) in
//! a very short span of time and allows the connection pool to be more
//! efficient in rebalancing the connections. The default rebalancing delay time
//! is 100 milliseconds.
//!
//! Rebalancing can cause the connection pool to temporarily exceed the maximum
//! number of connections configured for the pool. If the
//! [`Resolver`](resolver/trait.Resolver.html) notifies the connection pool that
//! a backend is removed, but connections for that backend are still in use the
//! connection count may exceed the maximum until those connections are returned
//! to the connection pool and discarded.
//!
//! ## Decoherence
//!
//! Decoherence in cueball is used to mean a periodic random shuffling of the order
//! of connections in the connection pool. The goal of decoherence is to avoid
//! undesirable patterns that could emerge in the lifetime of the connection
//! pool. For example suppose that a service has three backends, `A`, `B`, and
//! `C` and the connection pool has a maximum connection count of nine. The
//! initial connection distribution might look as follows:
//!
//! | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
//! |---|---|---|---|---|---|---|---|---|
//! | A | B | C | A | B | C | A | B | C |
//!
//! The cueball connection pool uses a queue internally to store the
//! connections. Given that connections from the pool may be claimed for
//! nonuniform periods of time it is possible that the queue could arrive at the
//! following state from its initial state:
//!
//! | 1 | 4 | 7 | 2 | 5 | 8 | 3 | 6 | 9 |
//! |---|---|---|---|---|---|---|---|---|
//! | A | A | A | B | B | B | C | C | C |
//!
//! This situation is not ideal because the same backend must handle multiple
//! consecutive requests while the other backends are idle. The ideal for
//! cueball is to have an even distribution of work among the backends not just
//! with respect to connection count, but also with respect to to request
//! distribution over time. Now admittedly the above example is an extreme
//! case and the pattern could quickly resolve itself based on the workload, but
//! there is no guarantee that would be the case. The goal of the periodic
//! decoherence shuffle in cueball is to disrupt these sorts of patterns that
//! might arise and persist for an extended period.
//!
//! There is one configuration options related to decoherence:
//! `decoherence_interval`. The `decoherence_interval` represents the length of
//! the period of the decoherence shuffle in seconds. If no value is specified
//! for this in the
//! [`ConnectionPoolOptions`](connection_pool/types/struct.ConnectionPoolOptions.html)
//! struct the default value is 300 seconds.
//!
//! ## Example
//!
//! Use of cueball for connection management requires both an implementation of
//! the [`Resolver`](resolver/trait.Resolver.html) trait and an implementation
//! of the [`Connection`](connection/trait.Connection.html) trait. Implementers
//! of the [`Resolver`](resolver/trait.Resolver.html) trait provide information
//! to the connection pool about the nodes availble to provide a given
//! service. The [`Connection`](connection/trait.Connection.html) trait defines
//! a behavior for establishing and closing a *connection* to a particular
//! service.
//!
//! Here is an example that uses a hypothetical
//! [`Resolver`](resolver/trait.Resolver.html) and
//! [`Connection`](connection/trait.Connection.html) implementation to create a
//! cueball connection pool.
//!
//! ```rust,ignore
//! use std::thread;
//! use std::net::{IpAddr, Ipv4Addr, SocketAddr};
//! use std::sync::{Arc, Barrier, Mutex};
//! use std::sync::mpsc::Sender;
//! use std::{thread, time};
//!
//! use slog::{Drain, Logger, info, o};
//!
//! use cueball::backend;
//! use cueball::backend::{Backend, BackendAddress, BackendPort};
//! use cueball::connection::Connection;
//! use cueball::connection_pool::ConnectionPool;
//! use cueball::connection_pool::types::ConnectionPoolOptions;
//! use cueball::error::Error;
//! use cueball::resolver::{BackendAddedMsg, BackendMsg, Resolver};
//!
//! fn main() {
//!     let plain = slog_term::PlainSyncDecorator::new(std::io::stdout());
//!     let log = Logger::root(
//!         Mutex::new(
//!             slog_term::FullFormat::new(plain).build()
//!         ).fuse(),
//!         o!("build-id" => "0.1.0")
//!     );
//!
//!     let be1 = (IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 55555);
//!     let be2 = (IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 55556);
//!     let be3 = (IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 55557);
//!
//!     let resolver = FakeResolver::new(vec![be1, be2, be3]);
//!
//!     let pool_opts = ConnectionPoolOptions::<FakeResolver> {
//!         max_connections: 15,
//!         claim_timeout: Some(1000)
//!         resolver: resolver,
//!         log: log.clone(),
//!         decoherence_interval: None,
//!     };
//!
//!     let pool = ConnectionPool::<DummyConnection, FakeResolver>::new(pool_opts);
//!
//!     for _ in 0..10 {
//!         let pool = pool.clone();
//!         thread::spawn(move || {
//!             let conn = pool.claim()?;
//!             // Do stuff here
//!             // The connection is returned to the pool when it falls out of scope.
//!         })
//!     }
//! }
//! ```
//!
//! There are several implementations of the
//! [`Resolver`](resolver/trait.Resolver.html) and
//! [`Connection`](connection/trait.Connection.html) traits that may be useful
//! to anyone looking to get started with `cueball`:
//!
//! ## [`Resolver`](resolver/trait.Resolver.html) trait implementer
//!
//! * [`cueball-static-resolver`](https://github.com/joyent/rust-cueball-static-resolver)
//! * [`cueball-dns-resolver`](https://github.com/joyent/rust-cueball-dns-resolver)
//!
//! ## [`Connection`](connection/trait.Connection.html) trait implementer
//!
//! * [`cueball-tcp-stream-connection`](https://github.com/joyent/rust-cueball-tcp-stream-connection)
//! * [`cueball-postgres-connection`](https://github.com/joyent/rust-cueball-postgres-connection)

#![allow(missing_docs)]

pub mod backend;
pub mod connection;
pub mod connection_pool;
pub mod error;
pub mod resolver;