Skip to main content

ConnectionCounters

Struct ConnectionCounters 

Source
pub struct ConnectionCounters {
    pub create_calls: usize,
    pub open_calls: usize,
    pub close_calls: usize,
}
Expand description

Statistics tracking connection lifecycle operations per connection plugin name.

ConnectionCounters provides a simple counter-based mechanism for monitoring connection operations in the ConnectionManager. Each connection plugin name (e.g., “ssh”, “netconf”, “http”) has its own set of counters that track how many times connections of that type have been created, opened, and closed.

These counters are useful for:

  • Performance Monitoring: Identify connection pool efficiency and reuse patterns
  • Debugging: Detect connection leaks, excessive creation, or improper cleanup
  • Testing: Verify connection lifecycle behavior in unit and integration tests
  • Metrics: Export connection statistics for observability systems

§Counter Semantics

  • create_calls - Incremented when a new connection instance is created by the factory. This happens on the first call to get_or_create() for a unique ConnectionKey. Multiple calls with the same key do not increment this counter.

  • open_calls - Incremented when open() is called on a connection. This happens when open_connection() is called and the connection’s is_alive() returns false. Calling open_connection() on an already-alive connection does not increment this counter.

  • close_calls - Incremented when a connection is closed via close_connection() or close_all_connections(). Each connection is counted only once when it’s removed from the pool.

§Thread Safety

The counters are stored in a DashMap<String, ConnectionCounters> in the ConnectionManager, providing thread-safe concurrent access. Multiple threads can increment counters for different connection plugin names simultaneously without blocking each other.

§Usage Patterns

§Ideal Pattern (Efficient Connection Reuse)

create_calls: 1
open_calls:   1
close_calls:  1

This indicates a connection was created once, opened once, and properly cleaned up. Multiple operations reused the same connection without reopening it.

§Connection Leak Pattern

create_calls: 5
open_calls:   5
close_calls:  0

This indicates connections are being created but never closed, suggesting a resource leak.

§Excessive Recreation Pattern

create_calls: 100
open_calls:   100
close_calls:  100

This indicates connections are being created and destroyed repeatedly instead of being reused, suggesting inefficient connection pooling.

§Examples

§Monitoring Connection Usage

let manager = ConnectionManager::with_connection_factory(factory);
let key = ConnectionKey::new("router1", "ssh");
let params = ResolvedConnectionParams {
    hostname: "10.0.0.1".to_string(),
    port: Some(22),
    username: Some("admin".to_string()),
    password: None,
    platform: None,
    extras: None,
};

// Perform operations
let runtime = Builder::new_current_thread().enable_all().build().unwrap();
runtime.block_on(async {
    manager.open_connection(&key, &params).await?;
    manager.open_connection(&key, &params).await?; // Reuses existing connection
    Ok::<(), String>(())
})?;
manager.close_connection(&key);

// Check counters
let counters = manager.connection_counters_for("ssh").unwrap();
assert_eq!(counters.create_calls, 1); // Created once
assert_eq!(counters.open_calls, 1);   // Opened once (second call reused)
assert_eq!(counters.close_calls, 1);  // Closed once

§Detecting Connection Leaks in Tests

let manager = ConnectionManager::with_connection_factory(factory);
let params = ResolvedConnectionParams {
    hostname: "10.0.0.1".to_string(),
    port: Some(22),
    username: Some("admin".to_string()),
    password: None,
    platform: None,
    extras: None,
};

// Open multiple connections
let runtime = Builder::new_current_thread().enable_all().build().unwrap();
for i in 1..=5 {
    let key = ConnectionKey::new(format!("router{}", i), "ssh");
    runtime.block_on(async { manager.open_connection(&key, &params).await })?;
}

// Verify all connections were created
let counters = manager.connection_counters_for("ssh").unwrap();
assert_eq!(counters.create_calls, 5);
assert_eq!(counters.open_calls, 5);

// Clean up and verify no leaks
manager.close_all_connections();
let counters = manager.connection_counters_for("ssh").unwrap();
assert_eq!(counters.close_calls, 5); // All connections closed

§Comparing Multiple Connection Types

let manager = ConnectionManager::with_connection_factory(factory);
let params = ResolvedConnectionParams {
    hostname: "10.0.0.1".to_string(),
    port: Some(22),
    username: Some("admin".to_string()),
    password: None,
    platform: None,
    extras: None,
};

// Open different connection plugin names
let runtime = Builder::new_current_thread().enable_all().build().unwrap();
runtime.block_on(async {
    manager.open_connection(&ConnectionKey::new("router1", "ssh"), &params).await?;
    manager.open_connection(&ConnectionKey::new("router1", "netconf"), &params).await?;
    Ok::<(), String>(())
})?;

// Get snapshot of all counters
let snapshot = manager.connection_counters_snapshot();
let ssh_counters = snapshot.get("ssh").unwrap();
let netconf_counters = snapshot.get("netconf").unwrap();

assert_eq!(ssh_counters.create_calls, 1);
assert_eq!(netconf_counters.create_calls, 1);

Fields§

§create_calls: usize§open_calls: usize§close_calls: usize

Trait Implementations§

Source§

impl Clone for ConnectionCounters

Source§

fn clone(&self) -> ConnectionCounters

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Copy for ConnectionCounters

Source§

impl Debug for ConnectionCounters

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ConnectionCounters

Source§

fn default() -> ConnectionCounters

Returns the “default value” for a type. Read more
Source§

impl Eq for ConnectionCounters

Source§

impl PartialEq for ConnectionCounters

Source§

fn eq(&self, other: &ConnectionCounters) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl StructuralPartialEq for ConnectionCounters

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.