use super::*;
fn runtime_pool_lock(runtime: &RedDBRuntime) -> std::sync::MutexGuard<'_, PoolState> {
runtime
.inner
.pool
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
impl RedDBRuntime {
pub fn acquire(&self) -> RedDBResult<RuntimeConnection> {
let mut pool = self
.inner
.pool
.lock()
.map_err(|e| RedDBError::Internal(format!("connection pool lock poisoned: {e}")))?;
if pool.active >= self.inner.pool_config.max_connections {
return Err(RedDBError::Internal(
"connection pool exhausted".to_string(),
));
}
let id = if let Some(id) = pool.idle.pop() {
id
} else {
let id = pool.next_id;
pool.next_id += 1;
id
};
pool.active += 1;
pool.total_checkouts += 1;
drop(pool);
self.inner.node_load_telemetry.record_connect();
Ok(RuntimeConnection {
id,
inner: Arc::clone(&self.inner),
})
}
pub fn checkpoint(&self) -> RedDBResult<()> {
let started = std::time::Instant::now();
self.seal_hypertable_chunks_for_checkpoint(
self.inner.checkpoint_columnar_emission_budget_chunks,
)?;
self.persist_probabilistic_snapshots()?;
self.inner.db.flush_local_only().map_err(|err| {
let msg = err.to_string();
crate::telemetry::operator_event::OperatorEvent::CheckpointFailed {
lsn: 0,
error: msg.clone(),
}
.emit_global();
crate::telemetry::operator_event::OperatorEvent::WalFsyncFailed {
path: "<flush_local_only>".to_string(),
error: msg.clone(),
}
.emit_global();
RedDBError::Engine(msg)
})?;
let checkpoint_lsn = self.cdc_current_lsn();
self.inner
.checkpoint_projection_stats
.record_checkpoint(checkpoint_lsn, started.elapsed().as_millis() as u64);
if let Err(err) = self.assert_remote_write_allowed("checkpoint") {
tracing::warn!(
target: "reddb::serverless::lease",
error = %err,
"checkpoint: skipping remote upload — lease not held"
);
return Ok(());
}
self.inner
.db
.upload_to_remote_backend()
.map_err(|err| RedDBError::Engine(err.to_string()))
}
pub(crate) fn assert_remote_write_allowed(&self, action: &str) -> RedDBResult<()> {
if self.inner.db.remote_backend.is_none() {
return Ok(());
}
match self.inner.write_gate.lease_state() {
crate::runtime::write_gate::LeaseGateState::NotHeld => {
self.inner.audit_log.record(
action,
"system",
"remote_backend",
"err: writer lease not held",
crate::json::Value::Null,
);
Err(RedDBError::ReadOnly(format!(
"writer lease not held — {action} blocked (serverless fence)"
)))
}
_ => Ok(()),
}
}
pub fn run_maintenance(&self) -> RedDBResult<()> {
self.inner
.db
.run_maintenance()
.map_err(|err| RedDBError::Internal(err.to_string()))
}
pub fn scan_collection(
&self,
collection: &str,
cursor: Option<ScanCursor>,
limit: usize,
) -> RedDBResult<ScanPage> {
let store = self.inner.db.store();
let manager = store
.get_collection(collection)
.ok_or_else(|| RedDBError::NotFound(collection.to_string()))?;
let mut entities = manager.query_all(|_| true);
entities.sort_by_key(|entity| entity.id.raw());
let offset = cursor.map(|cursor| cursor.offset).unwrap_or(0);
let total = entities.len();
let end = total.min(offset.saturating_add(limit.max(1)));
let items = if offset >= total {
Vec::new()
} else {
entities[offset..end].to_vec()
};
let next = (end < total).then_some(ScanCursor { offset: end });
Ok(ScanPage {
collection: collection.to_string(),
items,
next,
total,
})
}
pub fn catalog(&self) -> CatalogModelSnapshot {
self.inner.db.catalog_model_snapshot()
}
pub fn catalog_consistency_report(&self) -> crate::catalog::CatalogConsistencyReport {
self.inner.db.catalog_consistency_report()
}
pub fn catalog_attention_summary(&self) -> CatalogAttentionSummary {
crate::catalog::attention_summary(&self.catalog())
}
pub fn collection_attention(&self) -> Vec<CollectionDescriptor> {
crate::catalog::collection_attention(&self.catalog())
}
pub fn index_attention(&self) -> Vec<CatalogIndexStatus> {
crate::catalog::index_attention(&self.catalog())
}
pub fn graph_projection_attention(&self) -> Vec<CatalogGraphProjectionStatus> {
crate::catalog::graph_projection_attention(&self.catalog())
}
pub fn analytics_job_attention(&self) -> Vec<CatalogAnalyticsJobStatus> {
crate::catalog::analytics_job_attention(&self.catalog())
}
pub fn stats(&self) -> RuntimeStats {
let pool = runtime_pool_lock(self);
RuntimeStats {
active_connections: pool.active,
idle_connections: pool.idle.len(),
total_checkouts: pool.total_checkouts,
paged_mode: self.inner.db.is_paged(),
started_at_unix_ms: self.inner.started_at_unix_ms,
store: self.inner.db.stats(),
system: SystemInfo::collect(),
result_blob_cache: self.inner.result_blob_cache.stats(),
kv: self.inner.kv_stats.snapshot(),
metrics_ingest: self.inner.metrics_ingest_stats.snapshot(),
}
}
}