reddb_server/runtime/
impl_catalog_accessors.rs1use super::*;
8
9fn runtime_pool_lock(runtime: &RedDBRuntime) -> std::sync::MutexGuard<'_, PoolState> {
10 runtime
11 .inner
12 .pool
13 .lock()
14 .unwrap_or_else(|poisoned| poisoned.into_inner())
15}
16
17impl RedDBRuntime {
18 pub fn acquire(&self) -> RedDBResult<RuntimeConnection> {
19 let mut pool = self
20 .inner
21 .pool
22 .lock()
23 .map_err(|e| RedDBError::Internal(format!("connection pool lock poisoned: {e}")))?;
24 if pool.active >= self.inner.pool_config.max_connections {
25 return Err(RedDBError::Internal(
26 "connection pool exhausted".to_string(),
27 ));
28 }
29
30 let id = if let Some(id) = pool.idle.pop() {
31 id
32 } else {
33 let id = pool.next_id;
34 pool.next_id += 1;
35 id
36 };
37 pool.active += 1;
38 pool.total_checkouts += 1;
39 drop(pool);
40
41 self.inner.node_load_telemetry.record_connect();
44
45 Ok(RuntimeConnection {
46 id,
47 inner: Arc::clone(&self.inner),
48 })
49 }
50
51 pub fn checkpoint(&self) -> RedDBResult<()> {
52 let started = std::time::Instant::now();
57 self.seal_hypertable_chunks_for_checkpoint(
58 self.inner.checkpoint_columnar_emission_budget_chunks,
59 )?;
60 self.persist_probabilistic_snapshots()?;
61 self.inner.db.flush_local_only().map_err(|err| {
62 let msg = err.to_string();
67 crate::telemetry::operator_event::OperatorEvent::CheckpointFailed {
68 lsn: 0,
69 error: msg.clone(),
70 }
71 .emit_global();
72 crate::telemetry::operator_event::OperatorEvent::WalFsyncFailed {
73 path: "<flush_local_only>".to_string(),
74 error: msg.clone(),
75 }
76 .emit_global();
77 RedDBError::Engine(msg)
78 })?;
79 let checkpoint_lsn = self.cdc_current_lsn();
80 self.inner
81 .checkpoint_projection_stats
82 .record_checkpoint(checkpoint_lsn, started.elapsed().as_millis() as u64);
83 if let Err(err) = self.assert_remote_write_allowed("checkpoint") {
84 tracing::warn!(
85 target: "reddb::serverless::lease",
86 error = %err,
87 "checkpoint: skipping remote upload — lease not held"
88 );
89 return Ok(());
90 }
91 self.inner
92 .db
93 .upload_to_remote_backend()
94 .map_err(|err| RedDBError::Engine(err.to_string()))
95 }
96
97 pub(crate) fn assert_remote_write_allowed(&self, action: &str) -> RedDBResult<()> {
104 if self.inner.db.remote_backend.is_none() {
105 return Ok(());
106 }
107 match self.inner.write_gate.lease_state() {
108 crate::runtime::write_gate::LeaseGateState::NotHeld => {
109 self.inner.audit_log.record(
110 action,
111 "system",
112 "remote_backend",
113 "err: writer lease not held",
114 crate::json::Value::Null,
115 );
116 Err(RedDBError::ReadOnly(format!(
117 "writer lease not held — {action} blocked (serverless fence)"
118 )))
119 }
120 _ => Ok(()),
121 }
122 }
123
124 pub fn run_maintenance(&self) -> RedDBResult<()> {
125 self.inner
126 .db
127 .run_maintenance()
128 .map_err(|err| RedDBError::Internal(err.to_string()))
129 }
130
131 pub fn scan_collection(
132 &self,
133 collection: &str,
134 cursor: Option<ScanCursor>,
135 limit: usize,
136 ) -> RedDBResult<ScanPage> {
137 let store = self.inner.db.store();
138 let manager = store
139 .get_collection(collection)
140 .ok_or_else(|| RedDBError::NotFound(collection.to_string()))?;
141
142 let mut entities = manager.query_all(|_| true);
143 entities.sort_by_key(|entity| entity.id.raw());
144
145 let offset = cursor.map(|cursor| cursor.offset).unwrap_or(0);
146 let total = entities.len();
147 let end = total.min(offset.saturating_add(limit.max(1)));
148 let items = if offset >= total {
149 Vec::new()
150 } else {
151 entities[offset..end].to_vec()
152 };
153 let next = (end < total).then_some(ScanCursor { offset: end });
154
155 Ok(ScanPage {
156 collection: collection.to_string(),
157 items,
158 next,
159 total,
160 })
161 }
162
163 pub fn catalog(&self) -> CatalogModelSnapshot {
164 self.inner.db.catalog_model_snapshot()
165 }
166
167 pub fn catalog_consistency_report(&self) -> crate::catalog::CatalogConsistencyReport {
168 self.inner.db.catalog_consistency_report()
169 }
170
171 pub fn catalog_attention_summary(&self) -> CatalogAttentionSummary {
172 crate::catalog::attention_summary(&self.catalog())
173 }
174
175 pub fn collection_attention(&self) -> Vec<CollectionDescriptor> {
176 crate::catalog::collection_attention(&self.catalog())
177 }
178
179 pub fn index_attention(&self) -> Vec<CatalogIndexStatus> {
180 crate::catalog::index_attention(&self.catalog())
181 }
182
183 pub fn graph_projection_attention(&self) -> Vec<CatalogGraphProjectionStatus> {
184 crate::catalog::graph_projection_attention(&self.catalog())
185 }
186
187 pub fn analytics_job_attention(&self) -> Vec<CatalogAnalyticsJobStatus> {
188 crate::catalog::analytics_job_attention(&self.catalog())
189 }
190
191 pub fn stats(&self) -> RuntimeStats {
192 let pool = runtime_pool_lock(self);
193 RuntimeStats {
194 active_connections: pool.active,
195 idle_connections: pool.idle.len(),
196 total_checkouts: pool.total_checkouts,
197 paged_mode: self.inner.db.is_paged(),
198 started_at_unix_ms: self.inner.started_at_unix_ms,
199 store: self.inner.db.stats(),
200 system: SystemInfo::collect(),
201 result_blob_cache: self.inner.result_blob_cache.stats(),
202 kv: self.inner.kv_stats.snapshot(),
203 metrics_ingest: self.inner.metrics_ingest_stats.snapshot(),
204 }
205 }
206}