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 self.inner.db.flush_local_only().map_err(|err| {
57 let msg = err.to_string();
62 crate::telemetry::operator_event::OperatorEvent::CheckpointFailed {
63 lsn: 0,
64 error: msg.clone(),
65 }
66 .emit_global();
67 crate::telemetry::operator_event::OperatorEvent::WalFsyncFailed {
68 path: "<flush_local_only>".to_string(),
69 error: msg.clone(),
70 }
71 .emit_global();
72 RedDBError::Engine(msg)
73 })?;
74 if let Err(err) = self.assert_remote_write_allowed("checkpoint") {
75 tracing::warn!(
76 target: "reddb::serverless::lease",
77 error = %err,
78 "checkpoint: skipping remote upload — lease not held"
79 );
80 return Ok(());
81 }
82 self.inner
83 .db
84 .upload_to_remote_backend()
85 .map_err(|err| RedDBError::Engine(err.to_string()))
86 }
87
88 pub(crate) fn assert_remote_write_allowed(&self, action: &str) -> RedDBResult<()> {
95 if self.inner.db.remote_backend.is_none() {
96 return Ok(());
97 }
98 match self.inner.write_gate.lease_state() {
99 crate::runtime::write_gate::LeaseGateState::NotHeld => {
100 self.inner.audit_log.record(
101 action,
102 "system",
103 "remote_backend",
104 "err: writer lease not held",
105 crate::json::Value::Null,
106 );
107 Err(RedDBError::ReadOnly(format!(
108 "writer lease not held — {action} blocked (serverless fence)"
109 )))
110 }
111 _ => Ok(()),
112 }
113 }
114
115 pub fn run_maintenance(&self) -> RedDBResult<()> {
116 self.inner
117 .db
118 .run_maintenance()
119 .map_err(|err| RedDBError::Internal(err.to_string()))
120 }
121
122 pub fn scan_collection(
123 &self,
124 collection: &str,
125 cursor: Option<ScanCursor>,
126 limit: usize,
127 ) -> RedDBResult<ScanPage> {
128 let store = self.inner.db.store();
129 let manager = store
130 .get_collection(collection)
131 .ok_or_else(|| RedDBError::NotFound(collection.to_string()))?;
132
133 let mut entities = manager.query_all(|_| true);
134 entities.sort_by_key(|entity| entity.id.raw());
135
136 let offset = cursor.map(|cursor| cursor.offset).unwrap_or(0);
137 let total = entities.len();
138 let end = total.min(offset.saturating_add(limit.max(1)));
139 let items = if offset >= total {
140 Vec::new()
141 } else {
142 entities[offset..end].to_vec()
143 };
144 let next = (end < total).then_some(ScanCursor { offset: end });
145
146 Ok(ScanPage {
147 collection: collection.to_string(),
148 items,
149 next,
150 total,
151 })
152 }
153
154 pub fn catalog(&self) -> CatalogModelSnapshot {
155 self.inner.db.catalog_model_snapshot()
156 }
157
158 pub fn catalog_consistency_report(&self) -> crate::catalog::CatalogConsistencyReport {
159 self.inner.db.catalog_consistency_report()
160 }
161
162 pub fn catalog_attention_summary(&self) -> CatalogAttentionSummary {
163 crate::catalog::attention_summary(&self.catalog())
164 }
165
166 pub fn collection_attention(&self) -> Vec<CollectionDescriptor> {
167 crate::catalog::collection_attention(&self.catalog())
168 }
169
170 pub fn index_attention(&self) -> Vec<CatalogIndexStatus> {
171 crate::catalog::index_attention(&self.catalog())
172 }
173
174 pub fn graph_projection_attention(&self) -> Vec<CatalogGraphProjectionStatus> {
175 crate::catalog::graph_projection_attention(&self.catalog())
176 }
177
178 pub fn analytics_job_attention(&self) -> Vec<CatalogAnalyticsJobStatus> {
179 crate::catalog::analytics_job_attention(&self.catalog())
180 }
181
182 pub fn stats(&self) -> RuntimeStats {
183 let pool = runtime_pool_lock(self);
184 RuntimeStats {
185 active_connections: pool.active,
186 idle_connections: pool.idle.len(),
187 total_checkouts: pool.total_checkouts,
188 paged_mode: self.inner.db.is_paged(),
189 started_at_unix_ms: self.inner.started_at_unix_ms,
190 store: self.inner.db.stats(),
191 system: SystemInfo::collect(),
192 result_blob_cache: self.inner.result_blob_cache.stats(),
193 kv: self.inner.kv_stats.snapshot(),
194 metrics_ingest: self.inner.metrics_ingest_stats.snapshot(),
195 }
196 }
197}