Skip to content

Commit

Permalink
Merge pull request #3 from robsdedude/feature/max-connection-lifetime
Browse files Browse the repository at this point in the history
Add max_connection_lifetime config option
  • Loading branch information
robsdedude authored Dec 28, 2023
2 parents 6458c4e + efc7fd0 commit 65b3b20
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ A bump in MSRV is considered a minor breaking change.
* [x] `connection_timeout`
* [x] `connection_acquisition_timeout`
* [x] `resolver`
* [ ] `max_connection_lifetime`
* [x] `max_connection_lifetime`
* [x] routing and direct connections
* [ ] `keep_alive` (not supported by `std` https://github.com/rust-lang/rust/issues/69774)
* [x] `TLS`
Expand Down
1 change: 1 addition & 0 deletions neo4j/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ impl Driver {
tls_config: connection_config.tls_config.map(Arc::new),
user_agent: config.user_agent,
auth: config.auth,
max_connection_lifetime: config.max_connection_lifetime,
max_connection_pool_size: config.max_connection_pool_size,
connection_timeout: config.connection_timeout,
connection_acquisition_timeout: config.connection_acquisition_timeout,
Expand Down
35 changes: 34 additions & 1 deletion neo4j/src/driver/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ const DEFAULT_USER_AGENT: &str = env!("NEO4J_DEFAULT_USER_AGENT");
pub(crate) const DEFAULT_FETCH_SIZE: i64 = 1000;
pub(crate) const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_secs(30);
pub(crate) const DEFAULT_CONNECTION_ACQUISITION_TIMEOUT: Duration = Duration::from_secs(60);
pub(crate) const DEFAULT_MAX_CONNECTION_LIFETIME: Duration = Duration::from_secs(3600);

/// Configure how the driver should behave.
#[derive(Debug)]
pub struct DriverConfig {
pub(crate) user_agent: String,
pub(crate) auth: AuthConfig,
// max_connection_lifetime
pub(crate) max_connection_lifetime: Option<Duration>,
pub(crate) idle_time_before_connection_test: Option<Duration>,
pub(crate) max_connection_pool_size: usize,
pub(crate) fetch_size: i64,
Expand Down Expand Up @@ -133,6 +134,7 @@ impl Default for DriverConfig {
Self {
user_agent: String::from(DEFAULT_USER_AGENT),
auth: AuthConfig::Static(Default::default()),
max_connection_lifetime: Some(DEFAULT_MAX_CONNECTION_LIFETIME),
idle_time_before_connection_test: None,
max_connection_pool_size: 100,
fetch_size: DEFAULT_FETCH_SIZE,
Expand Down Expand Up @@ -216,6 +218,37 @@ impl DriverConfig {
self
}

/// Limit the maximum lifetime of any connection.
///
/// When a connection is attempted to be picked up from the connection pool, it will be closed
/// if it has been created longer than this duration ago.
#[inline]
pub fn with_max_connection_lifetime(mut self, max_connection_lifetime: Duration) -> Self {
self.max_connection_lifetime = Some(max_connection_lifetime);
self
}

/// Disable closing connections based on their age.
///
/// See also [`DriverConfig::with_max_connection_lifetime()`].
#[inline]
pub fn without_max_connection_lifetime(mut self) -> Self {
self.max_connection_lifetime = None;
self
}

/// Use the default maximum connection lifetime.
///
/// Currently, this is `3600` seconds.
/// This is an implementation detail and may change in the future.
///
/// See also [`DriverConfig::with_max_connection_lifetime()`].
#[inline]
pub fn with_default_max_connection_lifetime(mut self) -> Self {
self.max_connection_lifetime = Some(DEFAULT_MAX_CONNECTION_LIFETIME);
self
}

/// Configure connections that have been idle for longer than this duration whenever they are
/// pulled from the connection pool to be tested before being used.
///
Expand Down
12 changes: 11 additions & 1 deletion neo4j/src/driver/io/bolt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,9 @@ impl<RW: Read + Write> Bolt<RW> {
pub(crate) fn needs_reset(&self) -> bool {
self.data.needs_reset()
}
pub(crate) fn is_older_than(&self, duration: Duration) -> bool {
self.data.is_older_than(duration)
}
pub(crate) fn is_idle_for(&self, timeout: Duration) -> bool {
self.data.is_idle_for(timeout)
}
Expand Down Expand Up @@ -493,6 +496,7 @@ pub(crate) struct BoltData<RW: Read + Write> {
auth: Option<Arc<AuthToken>>,
session_auth: bool,
auth_reset: AuthResetHandle,
created_at: Instant,
idle_since: Instant,
}

Expand All @@ -505,6 +509,7 @@ impl<RW: Read + Write> BoltData<RW> {
address: Arc<Address>,
) -> Self {
let address_str = address.to_string();
let now = Instant::now();
Self {
message_buff: VecDeque::with_capacity(2048),
responses: VecDeque::with_capacity(10),
Expand All @@ -522,7 +527,8 @@ impl<RW: Read + Write> BoltData<RW> {
auth: None,
session_auth: false,
auth_reset: Default::default(),
idle_since: Instant::now(),
created_at: now,
idle_since: now,
}
}

Expand Down Expand Up @@ -698,6 +704,10 @@ impl<RW: Read + Write> BoltData<RW> {
.unwrap_or(true)
}

fn is_older_than(&self, duration: Duration) -> bool {
self.created_at.elapsed() >= duration
}

fn is_idle_for(&self, timeout: Duration) -> bool {
self.idle_since.elapsed() >= timeout
}
Expand Down
1 change: 1 addition & 0 deletions neo4j/src/driver/io/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ pub(crate) struct PoolConfig {
pub(crate) tls_config: Option<Arc<ClientConfig>>,
pub(crate) user_agent: String,
pub(crate) auth: AuthConfig,
pub(crate) max_connection_lifetime: Option<Duration>,
pub(crate) max_connection_pool_size: usize,
pub(crate) connection_timeout: Option<Duration>,
pub(crate) connection_acquisition_timeout: Option<Duration>,
Expand Down
7 changes: 7 additions & 0 deletions neo4j/src/driver/io/pool/single_pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,13 @@ impl UnpreparedSinglePooledBolt {
}
Some(mut connection) => {
// room for health check etc. (return None on failed health check)
if let Some(max_lifetime) = self.pool.config.max_connection_lifetime {
if connection.is_idle_for(max_lifetime) {
connection.debug_log(|| String::from("connection reached max lifetime"));
connection.close();
return Ok(None);
}
}
match idle_time_before_connection_test {
None => {}
Some(timeout) => {
Expand Down

0 comments on commit 65b3b20

Please sign in to comment.