connpool/core/
connection.rs1use std::fmt;
5use std::sync::atomic::{AtomicU64, Ordering};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum CloseReason {
11 Expired,
13 MaxUses,
15 Idle,
17 Unhealthy,
19 Error,
21 ResetFailed,
23 Discarded,
25 PoolClosed,
27}
28
29impl CloseReason {
30 pub fn as_str(&self) -> &'static str {
32 match self {
33 CloseReason::Expired => "expired",
34 CloseReason::MaxUses => "max_uses",
35 CloseReason::Idle => "idle",
36 CloseReason::Unhealthy => "unhealthy",
37 CloseReason::Error => "error",
38 CloseReason::ResetFailed => "reset_failed",
39 CloseReason::Discarded => "discarded",
40 CloseReason::PoolClosed => "pool_closed",
41 }
42 }
43}
44
45impl fmt::Display for CloseReason {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 f.write_str(self.as_str())
48 }
49}
50
51static CONN_IDS: AtomicU64 = AtomicU64::new(0);
52
53#[derive(Debug, Clone)]
56pub struct PooledConnection<T> {
57 pub(crate) raw: T,
58 pub(crate) created_at: f64,
59 pub(crate) expires_at: Option<f64>,
60 pub(crate) last_returned_at: Option<f64>,
61 pub(crate) checked_out_at: Option<f64>,
62 pub(crate) use_count: u64,
63 pub(crate) leak_reported: bool,
64 pub(crate) id: u64,
65}
66
67impl<T> PooledConnection<T> {
68 pub(crate) fn new(raw: T, created_at: f64, expires_at: Option<f64>) -> Self {
70 Self {
71 raw,
72 created_at,
73 expires_at,
74 last_returned_at: Some(created_at),
75 checked_out_at: None,
76 use_count: 0,
77 leak_reported: false,
78 id: CONN_IDS.fetch_add(1, Ordering::Relaxed) + 1,
79 }
80 }
81
82 pub fn raw(&self) -> &T {
84 &self.raw
85 }
86
87 pub fn id(&self) -> u64 {
89 self.id
90 }
91
92 pub fn use_count(&self) -> u64 {
94 self.use_count
95 }
96
97 pub fn created_at(&self) -> f64 {
99 self.created_at
100 }
101
102 pub fn age(&self, now: f64) -> f64 {
104 now - self.created_at
105 }
106
107 pub fn idle_time(&self, now: f64) -> f64 {
109 now - self.last_returned_at.unwrap_or(self.created_at)
110 }
111
112 pub fn hold_time(&self, now: f64) -> f64 {
114 match self.checked_out_at {
115 Some(t) => now - t,
116 None => 0.0,
117 }
118 }
119
120 pub(crate) fn mark_checked_out(&mut self, now: f64) {
121 self.checked_out_at = Some(now);
122 self.use_count += 1;
123 self.leak_reported = false;
124 }
125
126 pub(crate) fn mark_returned(&mut self, now: f64) {
127 self.checked_out_at = None;
128 self.last_returned_at = Some(now);
129 }
130}
131
132impl<T> fmt::Display for PooledConnection<T> {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 write!(f, "<PooledConnection #{} uses={}>", self.id, self.use_count)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn close_reason_wire_values() {
144 assert_eq!(CloseReason::Expired.as_str(), "expired");
145 assert_eq!(CloseReason::MaxUses.as_str(), "max_uses");
146 assert_eq!(CloseReason::Idle.as_str(), "idle");
147 assert_eq!(CloseReason::Unhealthy.as_str(), "unhealthy");
148 assert_eq!(CloseReason::Error.as_str(), "error");
149 assert_eq!(CloseReason::ResetFailed.as_str(), "reset_failed");
150 assert_eq!(CloseReason::Discarded.as_str(), "discarded");
151 assert_eq!(CloseReason::PoolClosed.as_str(), "pool_closed");
152 assert_eq!(CloseReason::Idle.to_string(), "idle");
153 assert_eq!(format!("{:?}", CloseReason::Idle), "Idle");
154 }
155
156 #[test]
157 fn pooled_connection_lifecycle() {
158 let mut c = PooledConnection::new("conn", 100.0, Some(150.0));
159 assert_eq!(*c.raw(), "conn");
160 assert!(c.id() >= 1);
161 assert_eq!(c.use_count(), 0);
162 assert_eq!(c.created_at(), 100.0);
163 assert_eq!(c.age(110.0), 10.0);
164 assert_eq!(c.idle_time(105.0), 5.0); assert_eq!(c.hold_time(105.0), 0.0); c.mark_checked_out(120.0);
168 assert_eq!(c.use_count(), 1);
169 assert_eq!(c.hold_time(130.0), 10.0);
170
171 c.mark_returned(140.0);
172 assert_eq!(c.hold_time(150.0), 0.0);
173 assert_eq!(c.idle_time(145.0), 5.0);
174
175 assert!(c.to_string().starts_with("<PooledConnection #"));
176 assert!(format!("{c:?}").contains("PooledConnection"));
177 }
178
179 #[test]
180 fn ids_are_distinct_and_monotonic() {
181 let a = PooledConnection::new((), 0.0, None);
182 let b = PooledConnection::new((), 0.0, None);
183 assert!(b.id() > a.id());
184 let mut c = PooledConnection::new((), 10.0, None);
186 c.last_returned_at = None;
187 assert_eq!(c.idle_time(25.0), 15.0);
188 }
189}