Skip to main content

reddb_server/runtime/
health_connection.rs

1use super::*;
2
3impl HealthProvider for RedDBRuntime {
4    fn health(&self) -> HealthReport {
5        self.health_with_runtime_diagnostics(self.inner.db.health())
6    }
7}
8
9impl RedDBRuntime {
10    pub(crate) fn health_from_catalog(
11        &self,
12        catalog: &crate::catalog::CatalogModelSnapshot,
13    ) -> HealthReport {
14        self.health_with_runtime_diagnostics(self.inner.db.health_from_catalog(catalog))
15    }
16
17    fn health_with_runtime_diagnostics(&self, mut report: HealthReport) -> HealthReport {
18        // Pool counters need a short observation, not a lock held across
19        // catalog inspection and checkpoint publication waits.
20        let (active_connections, idle_connections) = {
21            let pool = self
22                .inner
23                .pool
24                .lock()
25                .unwrap_or_else(|poisoned| poisoned.into_inner());
26            (pool.active, pool.idle.len())
27        };
28        let (readiness_for_query, readiness_for_write, readiness_for_repair) =
29            self.inner.db.readiness_flags_from_health(&report);
30        report = report.with_diagnostic(
31            "runtime.mode",
32            if self.inner.layout.is_persistent() {
33                "persistent"
34            } else {
35                "in-memory"
36            },
37        );
38        report =
39            report.with_diagnostic("runtime.active_connections", active_connections.to_string());
40        report = report.with_diagnostic("runtime.idle_connections", idle_connections.to_string());
41        report = report.with_diagnostic("readiness_for_query", readiness_for_query.to_string());
42        report = report.with_diagnostic("readiness_for_write", readiness_for_write.to_string());
43        report = report.with_diagnostic("readiness_for_repair", readiness_for_repair.to_string());
44        report.with_diagnostic(
45            "runtime.max_connections",
46            self.inner.pool_config.max_connections.to_string(),
47        )
48    }
49}
50
51impl RuntimeConnection {
52    pub fn id(&self) -> u64 {
53        self.id
54    }
55
56    pub fn db(&self) -> Arc<RedDB> {
57        Arc::clone(&self.inner.db)
58    }
59
60    pub fn scan_collection(
61        &self,
62        collection: &str,
63        cursor: Option<ScanCursor>,
64        limit: usize,
65    ) -> RedDBResult<ScanPage> {
66        RedDBRuntime {
67            background_tasks: self.background_tasks.clone(),
68            inner: Arc::clone(&self.inner),
69        }
70        .scan_collection(collection, cursor, limit)
71    }
72}
73
74impl Drop for RuntimeConnection {
75    fn drop(&mut self) {
76        let runtime = RedDBRuntime {
77            background_tasks: self.background_tasks.clone(),
78            inner: Arc::clone(&self.inner),
79        };
80        // Cleanup targets this lease, independently of the dropping thread's
81        // current connection. Never recycle a lease whose cleanup failed.
82        let reusable = match runtime.rollback_connection(self.id) {
83            Ok(_) => true,
84            Err(error) => {
85                tracing::error!(connection_id = self.id, %error, "connection rollback failed");
86                false
87            }
88        };
89        let mut pool = self
90            .inner
91            .pool
92            .lock()
93            .unwrap_or_else(|poisoned| poisoned.into_inner());
94        pool.active = pool.active.saturating_sub(1);
95        if self.uses_pool_slot {
96            pool.pooled_active = pool.pooled_active.saturating_sub(1);
97        }
98        if reusable && pool.idle.len() < self.inner.pool_config.max_idle {
99            pool.idle.push(self.id);
100        }
101        drop(pool);
102        // Issue #1245 — record the disconnect after releasing the pool lock.
103        self.inner.node_load_telemetry.record_disconnect();
104    }
105}