1use super::impl_config_secret::seed_storage_deploy_config;
13use super::*;
14
15pub(crate) fn view_records_to_entities(
24 table: &str,
25 records: &[crate::storage::query::unified::UnifiedRecord],
26) -> Vec<crate::storage::UnifiedEntity> {
27 use std::collections::HashMap;
28 let table_arc: std::sync::Arc<str> = std::sync::Arc::from(table);
29 let mut out = Vec::with_capacity(records.len());
30 for record in records {
31 let mut named: HashMap<String, reddb_types::Value> = HashMap::new();
32 for (name, value) in record.iter_fields() {
33 named.insert(name.to_string(), value.clone());
34 }
35 let entity = crate::storage::UnifiedEntity::new(
36 crate::storage::EntityId::new(0),
37 crate::storage::EntityKind::TableRow {
38 table: std::sync::Arc::clone(&table_arc),
39 row_id: 0,
40 },
41 crate::storage::EntityData::Row(crate::storage::RowData {
42 columns: Vec::new(),
43 named: Some(named),
44 schema: None,
45 }),
46 );
47 out.push(entity);
48 }
49 out
50}
51
52fn system_keyed_collection_contract(
53 name: &str,
54 model: crate::catalog::CollectionModel,
55) -> crate::physical::CollectionContract {
56 let now = crate::utils::now_unix_millis() as u128;
57 crate::physical::CollectionContract {
58 name: name.to_string(),
59 declared_model: model,
60 schema_mode: crate::catalog::SchemaMode::Dynamic,
61 origin: crate::physical::ContractOrigin::Implicit,
62 version: 1,
63 created_at_unix_ms: now,
64 updated_at_unix_ms: now,
65 default_ttl_ms: None,
66 vector_dimension: None,
67 vector_metric: None,
68 context_index_fields: Vec::new(),
69 declared_columns: Vec::new(),
70 table_def: None,
71 timestamps_enabled: false,
72 context_index_enabled: false,
73 metrics_raw_retention_ms: None,
74 metrics_rollup_policies: Vec::new(),
75 metrics_tenant_identity: None,
76 metrics_namespace: None,
77 append_only: false,
78 subscriptions: Vec::new(),
79 analytics_config: Vec::new(),
80 session_key: None,
81 session_gap_ms: None,
82 retention_duration_ms: None,
83 analytical_storage: None,
84
85 ai_policy: None,
86 }
87}
88
89pub(crate) fn table_row_index_fields(
90 entity: &crate::storage::unified::entity::UnifiedEntity,
91) -> Vec<(String, reddb_types::Value)> {
92 let crate::storage::EntityData::Row(row) = &entity.data else {
93 return Vec::new();
94 };
95 if let Some(named) = &row.named {
96 return named
97 .iter()
98 .map(|(name, value)| (name.clone(), value.clone()))
99 .collect();
100 }
101 if let Some(schema) = &row.schema {
102 return schema
103 .iter()
104 .zip(row.columns.iter())
105 .map(|(name, value)| (name.clone(), value.clone()))
106 .collect();
107 }
108 Vec::new()
109}
110
111impl RedDBRuntime {
112 pub fn in_memory() -> RedDBResult<Self> {
113 Self::with_options(RedDBOptions::in_memory())
114 }
115
116 pub fn flush(&self) -> RedDBResult<()> {
117 self.inner
118 .db
119 .flush()
120 .map_err(|err| RedDBError::Internal(err.to_string()))
121 }
122
123 pub fn lock_manager(&self) -> std::sync::Arc<crate::runtime::lock_manager::LockManager> {
127 self.inner.lock_manager.clone()
128 }
129
130 pub fn config_registry(&self) -> std::sync::Arc<crate::auth::registry::ConfigRegistry> {
132 self.inner.config_registry.clone()
133 }
134
135 pub fn query_audit(&self) -> std::sync::Arc<crate::runtime::query_audit::QueryAuditStream> {
136 self.inner.query_audit.clone()
137 }
138
139 pub fn control_events_require_persistence(&self) -> bool {
140 self.inner.control_event_config.require_persistence()
141 }
142
143 pub fn control_event_config(&self) -> crate::runtime::control_events::ControlEventConfig {
144 self.inner.control_event_config
145 }
146
147 pub fn control_event_ledger(
148 &self,
149 ) -> Arc<dyn crate::runtime::control_events::ControlEventLedger> {
150 self.inner.control_event_ledger.read().clone()
151 }
152
153 #[doc(hidden)]
154 pub fn replace_control_event_ledger_for_tests(
155 &self,
156 ledger: Arc<dyn crate::runtime::control_events::ControlEventLedger>,
157 ) {
158 *self.inner.control_event_ledger.write() = ledger;
159 }
160
161 #[inline(never)]
162 pub fn with_options(options: RedDBOptions) -> RedDBResult<Self> {
163 Self::with_pool(options, ConnectionPoolConfig::default())
164 }
165
166 pub fn memory_budget(&self) -> crate::storage::memory_budget::MemoryBudget {
170 self.inner.memory_budget
171 }
172
173 pub fn memory_accounting(&self) -> &crate::storage::memory_pools::MemoryAccounting {
177 &self.inner.memory_accounting
178 }
179
180 pub fn with_pool(
181 options: RedDBOptions,
182 pool_config: ConnectionPoolConfig,
183 ) -> RedDBResult<Self> {
184 Self::with_pool_and_limits(
185 options,
186 pool_config,
187 super::resource_limits::ResourceLimits::from_env(),
188 )
189 }
190
191 pub(super) fn with_pool_and_limits(
192 options: RedDBOptions,
193 pool_config: ConnectionPoolConfig,
194 resource_limits: super::resource_limits::ResourceLimits,
195 ) -> RedDBResult<Self> {
196 options
197 .replication
198 .validate_failover_profile()
199 .map_err(RedDBError::InvalidConfig)?;
200 let boot_open_start_ms = std::time::SystemTime::now()
208 .duration_since(std::time::UNIX_EPOCH)
209 .map(|d| d.as_millis() as u64)
210 .unwrap_or(0);
211 let memory_budget = crate::storage::memory_budget::resolve_for_boot(
215 options.storage_profile.deploy_profile,
216 options.memory_budget_bytes,
217 )
218 .map_err(|err| RedDBError::InvalidConfig(err.to_string()))?;
219 crate::storage::memory_budget::log_resolved_once(&memory_budget);
220 let memory_shares = crate::storage::memory_pools::BudgetShares::resolve(
224 memory_budget,
225 options.storage_profile.deploy_profile,
226 );
227 memory_shares.log_once();
228 let embedded_single_file = options.storage_profile.deploy_profile
229 == crate::storage::DeployProfile::Embedded
230 && options.storage_profile.packaging == crate::storage::StoragePackaging::SingleFile;
231 let db = Arc::new(RedDB::open_with_options(&options).map_err(|err| {
232 match err.downcast::<crate::storage::StoreError>() {
233 Ok(store_err) => RedDBError::from(*store_err),
234 Err(err) => RedDBError::Internal(err.to_string()),
235 }
236 })?);
237 let result_blob_cache_config = crate::storage::cache::BlobCacheConfig::default()
240 .with_l1_bytes_max(memory_shares.blob_cache_l1_bytes());
241 let result_blob_cache =
242 crate::storage::cache::BlobCache::open_with_l2(result_blob_cache_config).map_err(
243 |err| RedDBError::Internal(format!("open result Blob Cache L2 failed: {err:?}")),
244 )?;
245 let storage_ready_ms = std::time::SystemTime::now()
246 .duration_since(std::time::UNIX_EPOCH)
247 .map(|d| d.as_millis() as u64)
248 .unwrap_or(0);
249
250 let no_auth_boot = crate::service_cli::no_auth_active(&options);
251 let runtime = Self {
252 background_tasks: Some(Arc::new(background_tasks::RuntimeBackgroundTasks::default())),
253 inner: Arc::new(RuntimeInner {
254 functions: parking_lot::RwLock::new(
255 super::function_catalog::FunctionCatalog::load(&db)?,
256 ),
257 db: db.clone(),
258 layout: PhysicalLayout::from_options(&options),
259 embedded_single_file,
260 memory_budget,
261 memory_accounting: Arc::new(
262 crate::storage::memory_pools::MemoryAccounting::from_shares(
263 memory_budget,
264 memory_shares,
265 ),
266 ),
267 memory_reservations: parking_lot::Mutex::new(
268 super::memory_admission::MemoryReservations::default(),
269 ),
270 mvcc_maintenance: parking_lot::Mutex::new(Default::default()),
271 indices: IndexCatalog::register_default_vector_graph(
272 options.has_capability(crate::api::Capability::Table),
273 options.has_capability(crate::api::Capability::Graph),
274 ),
275 pool_config,
276 pool: Mutex::new(PoolState::default()),
277 execution_admission: parking_lot::RwLock::new(
278 crate::transport::execution_admission::ExecutionAdmission::for_host(),
279 ),
280 execution_queue_timeout_ms: AtomicU64::new(30_000),
281 started_at_unix_ms: SystemTime::now()
282 .duration_since(UNIX_EPOCH)
283 .unwrap_or_default()
284 .as_millis(),
285 probabilistic: super::probabilistic_store::ProbabilisticStore::new(),
286 index_store: super::index_store::IndexStore::new(),
287 cdc: crate::replication::cdc::CdcBuffer::new(100_000),
288 checkpoint_projection_stats: super::CheckpointProjectionStats::default(),
289 scrub_state: parking_lot::Mutex::new(super::ScrubRuntimeState::default()),
290 checkpoint_columnar_emission_budget_chunks: options
291 .checkpoint_columnar_emission_budget_chunks,
292 columnar_projection_size_floor_rows: options.columnar_projection_size_floor_rows,
293 backup_scheduler: crate::replication::scheduler::BackupScheduler::new(3600),
294 query_cache: parking_lot::RwLock::new(
295 crate::storage::query::planner::cache::PlanCache::new(1000),
296 ),
297 result_cache: parking_lot::RwLock::new((
298 HashMap::new(),
299 std::collections::VecDeque::new(),
300 )),
301 result_blob_cache,
302 result_blob_entries: parking_lot::RwLock::new((
303 HashMap::new(),
304 std::collections::VecDeque::new(),
305 )),
306 ask_answer_cache_entries: parking_lot::RwLock::new((
307 HashSet::new(),
308 std::collections::VecDeque::new(),
309 )),
310 result_cache_shadow_divergences: std::sync::atomic::AtomicU64::new(0),
311 result_cache_hits: std::sync::atomic::AtomicU64::new(0),
312 result_cache_misses: std::sync::atomic::AtomicU64::new(0),
313 result_cache_evictions: std::sync::atomic::AtomicU64::new(0),
314 ask_daily_spend: parking_lot::RwLock::new(HashMap::new()),
315 queue_message_locks: parking_lot::RwLock::new(HashMap::new()),
316 rmw_locks: RmwLockTable::new(),
317 planner_dirty_tables: parking_lot::RwLock::new(HashSet::new()),
318 ec_registry: Arc::new(crate::ec::config::EcRegistry::new()),
319 config_registry: Arc::new(crate::auth::registry::ConfigRegistry::new()),
320 ec_worker: crate::ec::worker::EcWorker::new(),
321 auth_store: parking_lot::RwLock::new(None),
322 oauth_validator: parking_lot::RwLock::new(None),
323 browser_token_authority: parking_lot::RwLock::new(None),
324 views: parking_lot::RwLock::new(HashMap::new()),
325 materialized_views: parking_lot::RwLock::new(
326 crate::storage::cache::result::MaterializedViewCache::new(),
327 ),
328 retention_sweeper: parking_lot::RwLock::new(
329 crate::runtime::retention_sweeper::RetentionSweeperState::new(),
330 ),
331 transaction_state: crate::runtime::transaction_state::TransactionState::new(),
332 env_config_overrides: crate::runtime::config_overlay::collect_env_overrides(),
333 lock_manager: Arc::new({
334 let env = crate::runtime::config_overlay::collect_env_overrides();
339 let timeout_ms = env
340 .get("concurrency.locking.deadlock_timeout_ms")
341 .and_then(|raw| raw.parse::<u64>().ok())
342 .unwrap_or_else(|| {
343 match crate::runtime::config_matrix::default_for(
344 "concurrency.locking.deadlock_timeout_ms",
345 ) {
346 Some(crate::serde_json::Value::Number(n)) => n as u64,
347 _ => 5000,
348 }
349 });
350 let cfg = crate::runtime::lock_manager::LockConfig {
351 default_timeout: std::time::Duration::from_millis(timeout_ms),
352 ..Default::default()
353 };
354 crate::runtime::lock_manager::LockManager::new(cfg)
355 }),
356 rls_policies: parking_lot::RwLock::new(HashMap::new()),
357 rls_enabled_tables: parking_lot::RwLock::new(HashSet::new()),
358 foreign_tables: Arc::new(crate::storage::fdw::ForeignTableRegistry::with_builtins()),
359 pending_tombstones: parking_lot::RwLock::new(HashMap::new()),
360 pending_versioned_updates: parking_lot::RwLock::new(HashMap::new()),
361 pending_queue_dedup: parking_lot::RwLock::new(HashMap::new()),
362 pending_kv_watch_events: parking_lot::RwLock::new(HashMap::new()),
363 pending_vault_writes: parking_lot::RwLock::new(HashMap::new()),
364 pending_store_wal_actions: parking_lot::RwLock::new(HashMap::new()),
365 pending_claim_locks: parking_lot::RwLock::new(HashMap::new()),
366 queue_wait_registry: std::sync::Arc::new(
367 crate::runtime::queue_wait_registry::QueueWaitRegistry::new(),
368 ),
369 pending_queue_wakes: parking_lot::RwLock::new(HashMap::new()),
370 tenant_tables: parking_lot::RwLock::new(HashMap::new()),
371 ddl_epoch: std::sync::atomic::AtomicU64::new(0),
372 write_gate: Arc::new(crate::runtime::write_gate::WriteGate::from_options(
373 &options,
374 )),
375 lifecycle: crate::runtime::lifecycle::Lifecycle::new(),
376 resource_limits,
377 query_budget_slots: AtomicU64::new(0),
378 audit_log: {
379 let data_path = if embedded_single_file || options.read_only {
395 std::env::temp_dir()
396 .join("reddb-embedded-runtime")
397 .join(format!("audit-{}", std::process::id()))
398 } else {
399 options
400 .data_path
401 .clone()
402 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
403 };
404 let (audit_dest, _) = crate::api::tier_wiring::current_log_destinations();
405 let audit_dest = if options.read_only {
406 crate::storage::layout::LogDestination::Stderr
407 } else {
408 audit_dest
409 };
410 if !matches!(audit_dest, crate::storage::layout::LogDestination::File(_))
411 && (embedded_single_file
412 || options.read_only
413 || options
414 .metadata
415 .contains_key(crate::api::EPHEMERAL_RUNTIME_METADATA_KEY))
416 {
417 let audit_path = reddb_file::layout::sibling_path(
427 &data_path,
428 &reddb_file::layout::sidecar_file_name(&data_path, "audit.log"),
429 );
430 Arc::new(crate::runtime::audit_log::AuditLogger::with_path(
431 audit_path,
432 ))
433 } else {
434 Arc::new(crate::runtime::audit_log::AuditLogger::for_destination(
435 &audit_dest,
436 &data_path,
437 ))
438 }
439 },
440 control_event_ledger: parking_lot::RwLock::new(Arc::new(
441 crate::runtime::control_events::RuntimeLedger::new(db.store()),
442 )),
443 control_event_config: options.control_events,
444 query_audit: Arc::new(crate::runtime::query_audit::QueryAuditStream::new(
445 db.store(),
446 options.query_audit.clone(),
447 )),
448 lease_lifecycle: std::sync::OnceLock::new(),
449 replica_apply_metrics: std::sync::Arc::new(
450 crate::replication::logical::ReplicaApplyMetrics::default(),
451 ),
452 replica_link_metrics: std::sync::Arc::new(
453 crate::replication::reconnect::ReplicaLinkMetrics::default(),
454 ),
455 quota_bucket: crate::runtime::quota_bucket::QuotaBucket::from_env(),
456 schema_vocabulary: parking_lot::RwLock::new(
457 crate::runtime::schema_vocabulary::SchemaVocabulary::new(),
458 ),
459 slow_query_logger: {
460 let fallback_dir = if embedded_single_file || options.read_only {
477 std::env::temp_dir()
478 .join("reddb-embedded-runtime")
479 .join(format!("slow-{}", std::process::id()))
480 } else {
481 options
482 .data_path
483 .as_ref()
484 .and_then(|p| p.parent().map(std::path::PathBuf::from))
485 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
486 };
487 let threshold_ms = std::env::var("RED_SLOW_QUERY_THRESHOLD_MS")
488 .ok()
489 .and_then(|s| s.parse::<u64>().ok())
490 .unwrap_or(1000);
491 let sample_pct = std::env::var("RED_SLOW_QUERY_SAMPLE_PCT")
492 .ok()
493 .and_then(|s| s.parse::<u8>().ok())
494 .unwrap_or(100);
495 let (_, slow_dest) = crate::api::tier_wiring::current_log_destinations();
496 let slow_dest = if options.read_only {
497 crate::storage::layout::LogDestination::Stderr
498 } else {
499 slow_dest
500 };
501 crate::telemetry::slow_query_logger::SlowQueryLogger::for_destination(
502 &slow_dest,
503 &fallback_dir,
504 threshold_ms,
505 sample_pct,
506 )
507 },
508 slow_query_store: crate::telemetry::slow_query_store::SlowQueryStore::new(
509 crate::telemetry::slow_query_store::DEFAULT_CAP,
510 ),
511 kv_stats: crate::runtime::KvStatsCounters::default(),
512 metrics_ingest_stats: crate::runtime::MetricsIngestCounters::default(),
513 metrics_tenant_activity_stats:
514 crate::runtime::MetricsTenantActivityCounters::default(),
515 claim_telemetry: Arc::new(
516 crate::runtime::claim_telemetry::ClaimTelemetryCounters::default(),
517 ),
518 queue_telemetry: Arc::new(
519 crate::runtime::queue_telemetry::QueueTelemetryCounters::default(),
520 ),
521 query_latency_telemetry: Arc::new(
522 crate::runtime::query_latency_telemetry::QueryLatencyTelemetry::default(),
523 ),
524 occupancy_sampler: Arc::new(
525 crate::runtime::occupancy_sampler::OccupancySampler::new(),
526 ),
527 node_load_telemetry: Arc::new(
528 crate::runtime::node_load_telemetry::NodeLoadTelemetry::default(),
529 ),
530 queue_presence: Arc::new(
531 crate::storage::queue::presence::ConsumerPresenceRegistry::new(),
532 ),
533 vector_introspection: Arc::new(
534 crate::storage::vector::introspection::VectorIntrospectionRegistry::new(),
535 ),
536 kv_tag_index: crate::runtime::KvTagIndex::default(),
537 chain_tip_cache: parking_lot::Mutex::new(HashMap::new()),
538 chain_integrity_broken: parking_lot::Mutex::new(HashMap::new()),
539 integrity_tombstones: parking_lot::Mutex::new(Vec::new()),
540 integrity_tombstones_state: std::sync::atomic::AtomicU8::new(0),
541 }),
542 };
543
544 runtime.install_hash_candidate_admission();
545
546 crate::telemetry::operator_event::install_global_audit_sink(Arc::clone(
552 &runtime.inner.audit_log,
553 ));
554
555 runtime
558 .inner
559 .slow_query_logger
560 .attach_store(Arc::clone(&runtime.inner.slow_query_store));
561
562 runtime
570 .inner
571 .lifecycle
572 .set_restore_started_at_ms(boot_open_start_ms);
573 runtime
574 .inner
575 .lifecycle
576 .set_restore_ready_at_ms(storage_ready_ms);
577 runtime
578 .inner
579 .lifecycle
580 .set_wal_replay_started_at_ms(boot_open_start_ms);
581 runtime
582 .inner
583 .lifecycle
584 .set_wal_replay_ready_at_ms(storage_ready_ms);
585
586 let restored_cdc_lsn = runtime
587 .inner
588 .db
589 .replication
590 .as_ref()
591 .map(|repl| {
592 repl.logical_wal_spool
593 .as_ref()
594 .map(|spool| spool.current_lsn())
595 .unwrap_or(0)
596 })
597 .unwrap_or(0)
598 .max(runtime.config_u64("red.config.timeline.last_archived_lsn", 0));
599 runtime.inner.cdc.set_current_lsn(restored_cdc_lsn);
600 runtime.rehydrate_snapshot_xid_floor()?;
601 assert!(runtime
602 .inner
603 .db
604 .runtime
605 .set(super::WeakRuntime(Arc::downgrade(&runtime.inner)))
606 .is_ok());
607 assert!(
608 runtime
609 .inner
610 .db
611 .store()
612 .snapshot_manager
613 .set(runtime.snapshot_manager())
614 .is_ok(),
615 "a new runtime owns the store transaction outcomes"
616 );
617 runtime
618 .bootstrap_system_keyed_collections()
619 .map_err(|err| RedDBError::Internal(format!("bootstrap system collections: {err}")))?;
620 runtime.rehydrate_declared_column_schemas();
621 super::control_event_index::prepare(&runtime)?;
624 super::queue_storage_index::prepare(&runtime)?;
625 runtime.rehydrate_runtime_index_registry()?;
626 if runtime
627 .db()
628 .store()
629 .config()
630 .resident_memory_bytes_max
631 .is_some()
632 {
633 runtime
634 .db()
635 .store()
636 .checkpoint_missing_secondary_indices()?;
637 }
638 runtime.rehydrate_kv_tags()?;
639 runtime
640 .load_probabilistic_state()
641 .map_err(|err| RedDBError::Internal(format!("load probabilistic state: {err}")))?;
642
643 runtime.rehydrate_tenant_tables();
647 runtime.rehydrate_materialized_view_descriptors()?;
652 if let Some(repl) = &runtime.inner.db.replication {
653 repl.wal_buffer.set_current_lsn(restored_cdc_lsn);
654 }
655
656 {
658 let sys = SystemInfo::collect();
659 runtime.inner.db.store().set_config_tree(
660 "red.system",
661 &crate::serde_json::json!({
662 "pid": sys.pid,
663 "cpu_cores": sys.cpu_cores,
664 "total_memory_bytes": sys.total_memory_bytes,
665 "available_memory_bytes": sys.available_memory_bytes,
666 "os": sys.os,
667 "arch": sys.arch,
668 "hostname": sys.hostname,
669 "started_at": SystemTime::now()
670 .duration_since(UNIX_EPOCH)
671 .unwrap_or_default()
672 .as_millis() as u64
673 }),
674 );
675
676 let store = runtime.inner.db.store();
678 if store
679 .get_collection("red_config")
680 .map(|m| m.query_all(|_| true).len())
681 .unwrap_or(0)
682 <= 10
683 {
684 store.set_config_tree("red.ai", &crate::json!({
685 "default": crate::json!({
686 "provider": "openai",
687 "model": crate::ai::DEFAULT_OPENAI_PROMPT_MODEL
688 }),
689 "max_embedding_inputs": 256,
690 "max_prompt_batch": 256,
691 "timeout": crate::json!({ "connect_secs": 10, "read_secs": 90, "write_secs": 30 })
692 }));
693 store.set_config_tree(
694 "red.server",
695 &crate::json!({
696 "max_scan_limit": 1000,
697 "max_body_size": 1048576,
698 "read_timeout_ms": 5000,
699 "write_timeout_ms": 5000
700 }),
701 );
702 store.set_config_tree(
703 "red.storage",
704 &crate::json!({
705 "page_size": 4096,
706 "page_cache_capacity": 100000,
707 "auto_checkpoint_pages": 1000,
708 "snapshot_retention": 16,
709 "verify_checksums": true,
710 "segment": crate::json!({
711 "max_entities": 100000,
712 "max_bytes": 268435456_u64,
713 "compression_level": 6
714 }),
715 "hnsw": crate::json!({ "m": 16, "ef_construction": 100, "ef_search": 50 }),
716 "ivf": crate::json!({ "n_lists": 100, "n_probes": 10 }),
717 "bm25": crate::json!({ "k1": 1.2, "b": 0.75 })
718 }),
719 );
720 store.set_config_tree(
721 "red.search",
722 &crate::json!({
723 "rag": crate::json!({
724 "max_chunks_per_source": 10,
725 "max_total_chunks": 25,
726 "similarity_threshold": 0.8,
727 "graph_depth": 2,
728 "min_relevance": 0.3
729 }),
730 "fusion": crate::json!({
731 "vector_weight": 0.5,
732 "graph_weight": 0.3,
733 "table_weight": 0.2,
734 "dedup_threshold": 0.85
735 })
736 }),
737 );
738 store.set_config_tree(
739 "red.auth",
740 &crate::json!({
741 "enabled": false,
742 "session_ttl_secs": 3600,
743 "require_auth": false
744 }),
745 );
746 store.set_config_tree(
747 "red.query",
748 &crate::json!({
749 "connection_pool": crate::json!({ "max_connections": 64, "max_idle": 16 }),
750 "max_recursion_depth": 1000
751 }),
752 );
753 store.set_config_tree(
754 "red.indexes",
755 &crate::json!({
756 "auto_select": true,
757 "bloom_filter": crate::json!({
758 "enabled": true,
759 "false_positive_rate": 0.01,
760 "prune_on_scan": true
761 }),
762 "hash": crate::json!({ "enabled": true }),
763 "bitmap": crate::json!({ "enabled": true, "max_cardinality": 1000 }),
764 "spatial": crate::json!({ "enabled": true })
765 }),
766 );
767 store.set_config_tree(
768 "red.probabilistic",
769 &crate::json!({
770 "hll_registers": 16384,
771 "sketch_default_width": 1000,
772 "sketch_default_depth": 5,
773 "filter_default_capacity": 100000
774 }),
775 );
776 store.set_config_tree(
777 "red.timeseries",
778 &crate::json!({
779 "default_chunk_size": 1024,
780 "compression": crate::json!({
781 "timestamps": "delta_of_delta",
782 "values": "gorilla_xor"
783 }),
784 "default_retention_days": 0
785 }),
786 );
787 store.set_config_tree(
788 "red.queue",
789 &crate::json!({
790 "default_max_size": 0,
791 "default_max_attempts": 3,
792 "visibility_timeout_ms": 30000,
793 "consumer_idle_timeout_ms": 60000
794 }),
795 );
796 store.set_config_tree(
797 "red.backup",
798 &crate::json!({
799 "enabled": false,
800 "interval_secs": 3600,
801 "retention_count": 24,
802 "upload": false,
803 "backend": "local"
804 }),
805 );
806 store.set_config_tree(
807 "red.wal",
808 &crate::json!({
809 "archive": crate::json!({
810 "enabled": false,
811 "retention_hours": 168,
812 "prefix": reddb_file::backup_wal_prefix("")
813 })
814 }),
815 );
816 store.set_config_tree(
817 "red.cdc",
818 &crate::json!({
819 "enabled": true,
820 "buffer_size": 100000
821 }),
822 );
823 store.set_config_tree(
824 "red.config.secret",
825 &crate::json!({
826 "auto_encrypt": true,
827 "auto_decrypt": true
828 }),
829 );
830 }
831
832 crate::runtime::config_matrix::heal_critical_keys(store.as_ref());
839 seed_storage_deploy_config(store.as_ref(), options.storage_profile);
840
841 let lehman_yao = runtime.config_bool("storage.btree.lehman_yao", true);
848 crate::storage::engine::btree::lehman_yao::set_enabled(lehman_yao);
849 if lehman_yao {
850 tracing::info!(
851 "storage.btree.lehman_yao=true — lock-free concurrent descent enabled"
852 );
853 }
854
855 let overlay_path = crate::runtime::config_overlay::config_file_path();
860 let _ =
861 crate::runtime::config_overlay::apply_config_file(store.as_ref(), &overlay_path);
862 }
863
864 {
868 let store = runtime.inner.db.store();
869 for name in crate::application::vcs_collections::ALL {
870 let _ = store.get_or_create_collection(*name);
871 }
872 store.set_config_tree(
875 crate::application::vcs_collections::CONFIG_NAMESPACE,
876 &crate::json!({
877 "default_branch": "main",
878 "author": crate::json!({
879 "name": "reddb",
880 "email": "reddb@localhost"
881 }),
882 "protected_branches": crate::json!(["main"]),
883 "closure": crate::json!({
884 "enabled": true,
885 "lazy": true
886 }),
887 "merge": crate::json!({
888 "default_strategy": "auto",
889 "fast_forward": true
890 })
891 }),
892 );
893 }
894
895 {
898 let store = runtime.inner.db.store();
899 for name in crate::application::migration_collections::ALL {
900 let _ = store.get_or_create_collection(*name);
901 }
902 }
903
904 let _ = crate::application::topology_collections::ensure(&runtime);
908
909 runtime.prepare_paged_counts()?;
914
915 runtime
923 .inner
924 .db
925 .store()
926 .register_entity_id(crate::storage::EntityId::new(
927 crate::storage::FIRST_USER_ENTITY_ID - 1,
928 ));
929
930 {
944 let weak = Arc::downgrade(&runtime.inner);
945 runtime
946 .background_tasks
947 .as_ref()
948 .expect("foreground runtime")
949 .spawn(
950 std::thread::Builder::new().name("reddb-maintenance".into()),
951 move |stopped| {
952 let tick = std::time::Duration::from_millis(200);
953 let work_interval = std::time::Duration::from_secs(60);
954 let mut last_work = std::time::Instant::now();
955 let mut allocator_pressure =
956 super::allocator_pressure::AllocatorPressure::default();
957 loop {
958 std::thread::park_timeout(tick);
959 if stopped.load(AtomicOrdering::Acquire) {
960 break;
961 }
962 let Some(inner) = weak.upgrade() else {
963 break;
966 };
967 if last_work.elapsed() >= work_interval {
968 let _stats = inner.db.store().context_index().stats();
969 last_work = std::time::Instant::now();
970 }
971 let rt = RedDBRuntime {
972 background_tasks: None,
973 inner,
974 };
975 rt.try_reclaim_history_tick();
976 allocator_pressure.tick();
977 }
978 },
979 );
980 }
981
982 {
984 let store = runtime.inner.db.store();
985 let mut backup_enabled = false;
986 let mut backup_interval = 3600u64;
987
988 if let Some(manager) = store.get_collection("red_config") {
989 manager.for_each_entity(|entity| {
990 if let Some(row) = entity.data.as_row() {
991 let key = row.get_field("key").and_then(|v| match v {
992 reddb_types::Value::Text(s) => Some(s.as_ref()),
993 _ => None,
994 });
995 let val = row.get_field("value");
996 if key == Some("red.config.backup.enabled") {
997 backup_enabled = match val {
998 Some(reddb_types::Value::Boolean(true)) => true,
999 Some(reddb_types::Value::Text(s)) => &**s == "true",
1000 _ => false,
1001 };
1002 } else if key == Some("red.config.backup.interval_secs") {
1003 if let Some(reddb_types::Value::Integer(n)) = val {
1004 backup_interval = *n as u64;
1005 }
1006 }
1007 }
1008 true
1009 });
1010 }
1011
1012 if backup_enabled {
1013 runtime.inner.backup_scheduler.set_interval(backup_interval);
1014 let rt = runtime.clone();
1015 runtime
1016 .inner
1017 .backup_scheduler
1018 .start(move || rt.trigger_backup().map_err(|e| format!("{}", e)));
1019 }
1020 }
1021
1022 {
1024 runtime
1025 .inner
1026 .ec_registry
1027 .load_from_config_store(runtime.inner.db.store().as_ref());
1028 if !runtime.inner.db.options().read_only
1029 && !runtime.inner.ec_registry.async_configs().is_empty()
1030 {
1031 runtime.inner.ec_worker.start(
1032 Arc::clone(&runtime.inner.ec_registry),
1033 Arc::clone(&runtime.inner.db.store()),
1034 );
1035 }
1036 }
1037
1038 if let (true, crate::replication::ReplicationRole::Replica { primary_addr }) = (
1039 runtime.inner.db.options().replica_loop_enabled
1040 && !runtime.inner.db.options().read_only,
1041 runtime.inner.db.options().replication.role.clone(),
1042 ) {
1043 let rt = runtime.clone();
1044 std::thread::Builder::new()
1045 .name("reddb-replica".into())
1046 .spawn(move || rt.run_replica_loop(primary_addr))
1047 .ok();
1048 }
1049
1050 runtime.inner.lifecycle.mark_ready();
1055
1056 {
1065 let weak_inner = Arc::downgrade(&runtime.inner);
1066 runtime
1067 .background_tasks
1068 .as_ref()
1069 .expect("foreground runtime")
1070 .spawn(
1071 std::thread::Builder::new()
1072 .name("reddb-mv-scheduler".into())
1073 .stack_size(8 * 1024 * 1024),
1078 move |stopped| loop {
1079 std::thread::park_timeout(std::time::Duration::from_millis(50));
1080 if stopped.load(AtomicOrdering::Acquire) {
1081 break;
1082 }
1083 let Some(inner) = weak_inner.upgrade() else {
1084 break;
1085 };
1086 let rt = RedDBRuntime {
1087 background_tasks: None,
1088 inner,
1089 };
1090 rt.refresh_due_materialized_views();
1091 },
1092 );
1093 }
1094
1095 if !runtime.write_gate().is_read_only() {
1105 let weak_inner = Arc::downgrade(&runtime.inner);
1106 runtime
1107 .background_tasks
1108 .as_ref()
1109 .expect("foreground runtime")
1110 .spawn(
1111 std::thread::Builder::new().name("reddb-retention-sweeper".into()),
1112 move |stopped| loop {
1113 std::thread::park_timeout(std::time::Duration::from_millis(500));
1114 if stopped.load(AtomicOrdering::Acquire) {
1115 break;
1116 }
1117 let Some(inner) = weak_inner.upgrade() else {
1118 break;
1119 };
1120 let rt = RedDBRuntime {
1121 background_tasks: None,
1122 inner,
1123 };
1124 rt.sweep_retention_tick(
1125 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
1126 );
1127 },
1128 );
1129 }
1130
1131 if no_auth_boot {
1135 use crate::runtime::control_events::{
1136 ActorRef, ControlEvent, ControlEventCtx, EventKind, Outcome,
1137 };
1138 let ledger = std::sync::Arc::clone(&runtime.inner.control_event_ledger.read());
1139 let ctx = ControlEventCtx {
1140 actor: ActorRef::System("boot"),
1141 scope: None,
1142 request_id: None,
1143 trace_id: None,
1144 };
1145 let event = ControlEvent {
1146 kind: EventKind::AuthDisabled,
1147 outcome: Outcome::Allowed,
1148 action: std::borrow::Cow::Borrowed("auth.disabled"),
1149 resource: Some("server".to_string()),
1150 reason: Some("--no-auth / --dev".to_string()),
1151 matched_policy_id: None,
1152 fields: std::collections::HashMap::new(),
1153 };
1154 if let Err(err) = ledger.emit(&ctx, event) {
1155 tracing::warn!(
1156 target: "reddb::security",
1157 error = %err,
1158 "could not record the auth-disabled boot in the control-event ledger"
1159 );
1160 }
1161 }
1162
1163 Ok(runtime)
1164 }
1165
1166 fn rehydrate_snapshot_xid_floor(&self) -> RedDBResult<()> {
1167 let store = self.inner.db.store();
1168 let snapshot_manager = self.inner.transaction_state.snapshot_manager();
1169 let base_high_water_mark = store.startup_xid_high_water_mark();
1170 if let Some(high_water_mark) = base_high_water_mark {
1171 snapshot_manager.observe_committed_xid(high_water_mark);
1172 }
1173 for collection in store.list_collections() {
1174 let Some(manager) = store.get_collection(&collection) else {
1175 continue;
1176 };
1177 let observe = |entity: &crate::storage::UnifiedEntity| {
1178 snapshot_manager.observe_committed_xid(entity.xmin);
1179 snapshot_manager.observe_committed_xid(entity.xmax);
1180 true
1181 };
1182 if base_high_water_mark.is_some() {
1183 manager.for_each_entity(observe);
1186 } else {
1187 manager.try_for_each_entity(usize::MAX, observe)?;
1190 }
1191 }
1192 if base_high_water_mark.is_none() {
1193 store.remember_startup_xid_high_water_mark(
1194 snapshot_manager.peek_next_xid().saturating_sub(1),
1195 );
1196 }
1197 Ok(())
1198 }
1199
1200 pub(crate) fn ensure_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1213 let store = self.inner.db.store();
1214 let mut changed = false;
1215 if store.get_collection(name).is_none() {
1216 store.get_or_create_collection(name);
1217 changed = true;
1218 }
1219 if self.inner.db.collection_contract(name).is_none() {
1220 self.inner
1221 .db
1222 .save_collection_contract(system_keyed_collection_contract(
1223 name,
1224 crate::catalog::CollectionModel::Table,
1225 ))
1226 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1227 changed = true;
1228 }
1229 if changed {
1230 self.inner
1231 .db
1232 .persist_metadata()
1233 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1234 }
1235 Ok(())
1236 }
1237
1238 pub(crate) fn drop_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1243 let store = self.inner.db.store();
1244 if store.get_collection(name).is_none() {
1245 return Ok(());
1246 }
1247 store
1248 .drop_collection(name)
1249 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1250 if self.inner.db.collection_contract(name).is_some() {
1253 self.inner
1254 .db
1255 .remove_collection_contract(name)
1256 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1257 }
1258 self.invalidate_result_cache();
1259 self.inner
1260 .db
1261 .persist_metadata()
1262 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1263 Ok(())
1264 }
1265
1266 fn bootstrap_system_keyed_collections(&self) -> RedDBResult<()> {
1267 let mut changed = false;
1268 for (name, model) in [
1269 ("red.config", crate::catalog::CollectionModel::Config),
1270 ("red.vault", crate::catalog::CollectionModel::Vault),
1271 (
1275 crate::runtime::continuous_materialized_view::CATALOG_COLLECTION,
1276 crate::catalog::CollectionModel::Config,
1277 ),
1278 ] {
1279 if self.inner.db.store().get_collection(name).is_none() {
1280 self.inner.db.store().get_or_create_collection(name);
1281 changed = true;
1282 }
1283 if self.inner.db.collection_contract(name).is_none() {
1284 let contract = system_keyed_collection_contract(name, model);
1285 if self.inner.db.options().read_only {
1286 self.inner.db.cache_read_only_bootstrap_contract(contract);
1287 } else {
1288 self.inner
1289 .db
1290 .save_collection_contract(contract)
1291 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1292 }
1293 changed = true;
1294 }
1295 }
1296 if changed {
1297 self.inner
1298 .db
1299 .persist_metadata()
1300 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1301 }
1302 Ok(())
1303 }
1304
1305 pub fn db(&self) -> Arc<RedDB> {
1306 Arc::clone(&self.inner.db)
1307 }
1308
1309 pub fn index_store_ref(&self) -> &super::index_store::IndexStore {
1314 &self.inner.index_store
1315 }
1316
1317 pub(crate) fn schema_vocabulary_apply(
1322 &self,
1323 event: crate::runtime::schema_vocabulary::DdlEvent,
1324 ) {
1325 self.inner.schema_vocabulary.write().on_ddl(event);
1326 }
1327
1328 pub fn schema_vocabulary_lookup(
1333 &self,
1334 token: &str,
1335 ) -> Vec<crate::runtime::schema_vocabulary::VocabHit> {
1336 self.inner.schema_vocabulary.read().lookup(token).to_vec()
1337 }
1338
1339 pub fn set_auth_store(&self, store: Arc<crate::auth::store::AuthStore>) {
1343 *self.inner.auth_store.write() = Some(store);
1344 }
1345
1346 pub fn auth_store(&self) -> Option<Arc<crate::auth::store::AuthStore>> {
1349 self.inner.auth_store.read().clone()
1350 }
1351
1352 pub fn set_oauth_validator(&self, validator: Option<Arc<crate::auth::oauth::OAuthValidator>>) {
1356 *self.inner.oauth_validator.write() = validator;
1357 }
1358
1359 pub fn oauth_validator(&self) -> Option<Arc<crate::auth::oauth::OAuthValidator>> {
1363 self.inner.oauth_validator.read().clone()
1364 }
1365
1366 pub fn set_browser_token_authority(
1372 &self,
1373 authority: Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>,
1374 ) {
1375 *self.inner.browser_token_authority.write() = authority;
1376 }
1377
1378 pub fn browser_token_authority(
1382 &self,
1383 ) -> Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>> {
1384 self.inner.browser_token_authority.read().clone()
1385 }
1386}
1387
1388#[cfg(test)]
1389mod xid_resume_tests {
1390 use super::*;
1391 use reddb_engine::storage::collection_read::{CollectionCursor, CollectionRead, EncodedRecord};
1392
1393 struct Unreadable;
1394
1395 fn unavailable() -> crate::storage::StoreError {
1396 crate::storage::StoreError::Io(std::io::Error::other("XID bootstrap read a base payload"))
1397 }
1398
1399 impl CollectionRead for Unreadable {
1400 fn get_record(
1401 &self,
1402 id: crate::storage::EntityId,
1403 _: usize,
1404 ) -> Result<Option<EncodedRecord>, crate::storage::StoreError> {
1405 if id.raw() == 999 {
1406 Ok(None)
1407 } else {
1408 Err(unavailable())
1409 }
1410 }
1411 fn cursor(&self) -> Result<Box<dyn CollectionCursor + '_>, crate::storage::StoreError> {
1412 Err(unavailable())
1413 }
1414 fn owned_cursor(&self) -> Result<Box<dyn CollectionCursor>, crate::storage::StoreError> {
1415 Err(unavailable())
1416 }
1417 }
1418
1419 #[test]
1420 fn xid_resume_survives_pending_checkpoint_process_exit() {
1421 const FIXTURE: &str = "REDDB_XID_RESUME_CRASH_FIXTURE";
1422 let open = |path: std::path::PathBuf| {
1423 let options = RedDBOptions::persistent(path)
1424 .with_memory_budget(64 * 1024 * 1024)
1425 .with_storage_profile(
1426 crate::storage::profile::StorageDeployPreset::PrimaryReplicaDev.selection(),
1427 )
1428 .expect("profile");
1429 crate::storage::RedDB::with_disk_residency_for_qualification(|| {
1430 RedDBRuntime::with_options(options)
1431 })
1432 .expect("crash recovery runtime")
1433 };
1434 if let Some(root) = std::env::var_os(FIXTURE) {
1435 let root = std::path::PathBuf::from(root);
1436 assert_eq!(
1437 std::fs::read_to_string(root.join("owned-fixture")).expect("owned fixture"),
1438 "xid-resume-crash-v1"
1439 );
1440 let runtime = open(root.join("crash.rdb"));
1441 for sql in [
1442 "CREATE TABLE crash_rows (id INTEGER, payload TEXT)",
1443 "INSERT INTO crash_rows (id, payload) VALUES (1, 'committed')",
1444 "BEGIN",
1445 "UPDATE crash_rows SET payload = 'pending' WHERE id = 1",
1446 "INSERT INTO crash_rows (id, payload) VALUES (2, 'pending')",
1447 ] {
1448 runtime.execute_query(sql).expect("crash fixture statement");
1449 }
1450 runtime
1451 .flush()
1452 .expect("checkpoint with pending transaction");
1453 std::fs::write(
1454 root.join("checkpoint-complete"),
1455 "pending transaction remains open",
1456 )
1457 .expect("acknowledged fixture checkpoint");
1458 std::process::exit(0);
1461 }
1462 let directory = tempfile::tempdir().expect("owned crash fixture");
1463 std::fs::write(
1464 directory.path().join("owned-fixture"),
1465 "xid-resume-crash-v1",
1466 )
1467 .expect("ownership marker");
1468 let child = std::process::Command::new(std::env::current_exe().expect("test executable"))
1469 .args([
1470 "--exact",
1471 "runtime::impl_lifecycle::xid_resume_tests::xid_resume_survives_pending_checkpoint_process_exit",
1472 "--test-threads=1",
1473 ])
1474 .env(FIXTURE, directory.path())
1475 .output()
1476 .expect("crashing owned child");
1477 assert!(
1478 child.status.success(),
1479 "{}",
1480 String::from_utf8_lossy(&child.stderr)
1481 );
1482 assert!(directory.path().join("checkpoint-complete").is_file());
1483 let path = directory.path().join("crash.rdb");
1484 assert!(path.is_file());
1485 let runtime = open(path.clone());
1486 let result = runtime
1487 .execute_query("SELECT id, payload FROM crash_rows ORDER BY id")
1488 .expect("committed preimage");
1489 assert_eq!(
1490 result.result.records.len(),
1491 1,
1492 "pending insert must stay invisible"
1493 );
1494 assert_eq!(result.result.records[0].get("id"), Some(&Value::Integer(1)));
1495 assert_eq!(
1496 result.result.records[0].get("payload"),
1497 Some(&Value::text("committed"))
1498 );
1499 runtime
1500 .execute_query("INSERT INTO crash_rows (id, payload) VALUES (3, 'after')")
1501 .expect("new writer after recovery");
1502 runtime.flush().expect("recovered checkpoint");
1503 drop(runtime);
1504 let reopened = open(path);
1505 let result = reopened
1506 .execute_query("SELECT id FROM crash_rows ORDER BY id")
1507 .expect("second recovery");
1508 assert_eq!(result.result.records.len(), 2);
1509 assert_eq!(result.result.records[0].get("id"), Some(&Value::Integer(1)));
1510 assert_eq!(result.result.records[1].get("id"), Some(&Value::Integer(3)));
1511 }
1512
1513 #[test]
1514 fn xid_resume_skips_known_base_payloads_but_rejects_an_unproven_root() {
1515 let directory = tempfile::tempdir().expect("resume fixture");
1516 let options = RedDBOptions::persistent(directory.path().join("resume.rdb"))
1517 .with_memory_budget(64 * 1024 * 1024)
1518 .with_storage_profile(
1519 crate::storage::profile::StorageDeployPreset::PrimaryReplicaDev.selection(),
1520 )
1521 .expect("profile");
1522 let runtime = crate::storage::RedDB::with_disk_residency_for_qualification(|| {
1523 RedDBRuntime::with_options(options)
1524 })
1525 .expect("runtime");
1526 runtime
1527 .execute_query("CREATE TABLE resume_rows (id INTEGER)")
1528 .expect("table");
1529 runtime
1530 .execute_query("INSERT INTO resume_rows (id) VALUES (1)")
1531 .expect("row");
1532 runtime.flush().expect("publish complete XID summary");
1533 let store = runtime.db().store();
1534 assert!(store.startup_xid_high_water_mark().is_some());
1535 let root = store
1536 .collection_root_page("resume_rows")
1537 .expect("published root");
1538 let manager = store.get_collection("resume_rows").expect("manager");
1539 for (candidate, proven) in [(root, true), (root + 1, false)] {
1540 manager.install_backing(
1541 Arc::new(Unreadable),
1542 candidate,
1543 store.format_version(),
1544 4096,
1545 1,
1546 );
1547 let mut overlay = crate::storage::UnifiedEntity::table_row(
1548 crate::storage::EntityId::new(999),
1549 "resume_rows",
1550 999,
1551 vec![Value::Integer(2)],
1552 );
1553 overlay.xmin = 500_000;
1554 overlay.xmax = 600_000;
1555 manager.insert(overlay).expect("resident WAL overlay");
1556 let result = runtime.rehydrate_snapshot_xid_floor();
1557 assert_eq!(result.is_ok(), proven, "root={candidate}: {result:?}");
1558 if proven {
1559 assert!(runtime.snapshot_manager().peek_next_xid() > 600_000);
1560 }
1561 }
1562 }
1563}