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 options
185 .replication
186 .validate_failover_profile()
187 .map_err(RedDBError::InvalidConfig)?;
188 let boot_open_start_ms = std::time::SystemTime::now()
196 .duration_since(std::time::UNIX_EPOCH)
197 .map(|d| d.as_millis() as u64)
198 .unwrap_or(0);
199 let memory_budget = crate::storage::memory_budget::resolve_for_boot(
203 options.storage_profile.deploy_profile,
204 options.memory_budget_bytes,
205 )
206 .map_err(|err| RedDBError::InvalidConfig(err.to_string()))?;
207 crate::storage::memory_budget::log_resolved_once(&memory_budget);
208 let memory_shares = crate::storage::memory_pools::BudgetShares::resolve(
212 memory_budget,
213 options.storage_profile.deploy_profile,
214 );
215 memory_shares.log_once();
216 let embedded_single_file = options.storage_profile.deploy_profile
217 == crate::storage::DeployProfile::Embedded
218 && options.storage_profile.packaging == crate::storage::StoragePackaging::SingleFile;
219 let db = Arc::new(RedDB::open_with_options(&options).map_err(|err| {
220 match err.downcast::<crate::storage::StoreError>() {
221 Ok(store_err) => RedDBError::from(*store_err),
222 Err(err) => RedDBError::Internal(err.to_string()),
223 }
224 })?);
225 let result_blob_cache_config = if embedded_single_file {
229 crate::storage::cache::BlobCacheConfig::default()
230 } else {
231 crate::storage::cache::BlobCacheConfig::default().with_l2_path(
232 reddb_file::layout::result_cache_l2_path(
233 &options.resolved_path(reddb_file::default_database_path()),
234 ),
235 )
236 }
237 .with_l1_bytes_max(memory_shares.blob_cache_l1_bytes());
238 let result_blob_cache =
239 crate::storage::cache::BlobCache::open_with_l2(result_blob_cache_config).map_err(
240 |err| RedDBError::Internal(format!("open result Blob Cache L2 failed: {err:?}")),
241 )?;
242 let storage_ready_ms = std::time::SystemTime::now()
243 .duration_since(std::time::UNIX_EPOCH)
244 .map(|d| d.as_millis() as u64)
245 .unwrap_or(0);
246
247 let no_auth_boot = crate::service_cli::no_auth_active(&options);
248 let runtime = Self {
249 inner: Arc::new(RuntimeInner {
250 functions: parking_lot::RwLock::new(
251 super::function_catalog::FunctionCatalog::load(&db)?,
252 ),
253 db: db.clone(),
254 layout: PhysicalLayout::from_options(&options),
255 embedded_single_file,
256 memory_budget,
257 memory_accounting: Arc::new(
258 crate::storage::memory_pools::MemoryAccounting::from_shares(
259 memory_budget,
260 memory_shares,
261 ),
262 ),
263 memory_reservations: parking_lot::Mutex::new(
264 super::memory_admission::MemoryReservations::default(),
265 ),
266 indices: IndexCatalog::register_default_vector_graph(
267 options.has_capability(crate::api::Capability::Table),
268 options.has_capability(crate::api::Capability::Graph),
269 ),
270 pool_config,
271 pool: Mutex::new(PoolState::default()),
272 started_at_unix_ms: SystemTime::now()
273 .duration_since(UNIX_EPOCH)
274 .unwrap_or_default()
275 .as_millis(),
276 probabilistic: super::probabilistic_store::ProbabilisticStore::new(),
277 index_store: super::index_store::IndexStore::new(),
278 cdc: crate::replication::cdc::CdcBuffer::new(100_000),
279 checkpoint_projection_stats: super::CheckpointProjectionStats::default(),
280 scrub_state: parking_lot::Mutex::new(super::ScrubRuntimeState::default()),
281 checkpoint_columnar_emission_budget_chunks: options
282 .checkpoint_columnar_emission_budget_chunks,
283 columnar_projection_size_floor_rows: options.columnar_projection_size_floor_rows,
284 backup_scheduler: crate::replication::scheduler::BackupScheduler::new(3600),
285 query_cache: parking_lot::RwLock::new(
286 crate::storage::query::planner::cache::PlanCache::new(1000),
287 ),
288 result_cache: parking_lot::RwLock::new((
289 HashMap::new(),
290 std::collections::VecDeque::new(),
291 )),
292 result_blob_cache,
293 result_blob_entries: parking_lot::RwLock::new((
294 HashMap::new(),
295 std::collections::VecDeque::new(),
296 )),
297 ask_answer_cache_entries: parking_lot::RwLock::new((
298 HashSet::new(),
299 std::collections::VecDeque::new(),
300 )),
301 result_cache_shadow_divergences: std::sync::atomic::AtomicU64::new(0),
302 result_cache_hits: std::sync::atomic::AtomicU64::new(0),
303 result_cache_misses: std::sync::atomic::AtomicU64::new(0),
304 result_cache_evictions: std::sync::atomic::AtomicU64::new(0),
305 ask_daily_spend: parking_lot::RwLock::new(HashMap::new()),
306 queue_message_locks: parking_lot::RwLock::new(HashMap::new()),
307 rmw_locks: RmwLockTable::new(),
308 planner_dirty_tables: parking_lot::RwLock::new(HashSet::new()),
309 ec_registry: Arc::new(crate::ec::config::EcRegistry::new()),
310 config_registry: Arc::new(crate::auth::registry::ConfigRegistry::new()),
311 ec_worker: crate::ec::worker::EcWorker::new(),
312 auth_store: parking_lot::RwLock::new(None),
313 oauth_validator: parking_lot::RwLock::new(None),
314 browser_token_authority: parking_lot::RwLock::new(None),
315 views: parking_lot::RwLock::new(HashMap::new()),
316 materialized_views: parking_lot::RwLock::new(
317 crate::storage::cache::result::MaterializedViewCache::new(),
318 ),
319 retention_sweeper: parking_lot::RwLock::new(
320 crate::runtime::retention_sweeper::RetentionSweeperState::new(),
321 ),
322 transaction_state: crate::runtime::transaction_state::TransactionState::new(),
323 env_config_overrides: crate::runtime::config_overlay::collect_env_overrides(),
324 lock_manager: Arc::new({
325 let env = crate::runtime::config_overlay::collect_env_overrides();
330 let timeout_ms = env
331 .get("concurrency.locking.deadlock_timeout_ms")
332 .and_then(|raw| raw.parse::<u64>().ok())
333 .unwrap_or_else(|| {
334 match crate::runtime::config_matrix::default_for(
335 "concurrency.locking.deadlock_timeout_ms",
336 ) {
337 Some(crate::serde_json::Value::Number(n)) => n as u64,
338 _ => 5000,
339 }
340 });
341 let cfg = crate::runtime::lock_manager::LockConfig {
342 default_timeout: std::time::Duration::from_millis(timeout_ms),
343 ..Default::default()
344 };
345 crate::runtime::lock_manager::LockManager::new(cfg)
346 }),
347 rls_policies: parking_lot::RwLock::new(HashMap::new()),
348 rls_enabled_tables: parking_lot::RwLock::new(HashSet::new()),
349 foreign_tables: Arc::new(crate::storage::fdw::ForeignTableRegistry::with_builtins()),
350 pending_tombstones: parking_lot::RwLock::new(HashMap::new()),
351 pending_versioned_updates: parking_lot::RwLock::new(HashMap::new()),
352 pending_queue_dedup: parking_lot::RwLock::new(HashMap::new()),
353 pending_kv_watch_events: parking_lot::RwLock::new(HashMap::new()),
354 pending_vault_writes: parking_lot::RwLock::new(HashMap::new()),
355 pending_store_wal_actions: parking_lot::RwLock::new(HashMap::new()),
356 pending_claim_locks: parking_lot::RwLock::new(HashMap::new()),
357 queue_wait_registry: std::sync::Arc::new(
358 crate::runtime::queue_wait_registry::QueueWaitRegistry::new(),
359 ),
360 pending_queue_wakes: parking_lot::RwLock::new(HashMap::new()),
361 tenant_tables: parking_lot::RwLock::new(HashMap::new()),
362 ddl_epoch: std::sync::atomic::AtomicU64::new(0),
363 write_gate: Arc::new(crate::runtime::write_gate::WriteGate::from_options(
364 &options,
365 )),
366 lifecycle: crate::runtime::lifecycle::Lifecycle::new(),
367 resource_limits: crate::runtime::resource_limits::ResourceLimits::from_env(),
368 audit_log: {
369 let data_path = if embedded_single_file {
385 std::env::temp_dir()
386 .join("reddb-embedded-runtime")
387 .join(format!("audit-{}", std::process::id()))
388 } else {
389 options
390 .data_path
391 .clone()
392 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
393 };
394 let (audit_dest, _) = crate::api::tier_wiring::current_log_destinations();
395 if !matches!(audit_dest, crate::storage::layout::LogDestination::File(_))
396 && (embedded_single_file
397 || options
398 .metadata
399 .contains_key(crate::api::EPHEMERAL_RUNTIME_METADATA_KEY))
400 {
401 let audit_path = reddb_file::layout::sibling_path(
411 &data_path,
412 &reddb_file::layout::sidecar_file_name(&data_path, "audit.log"),
413 );
414 Arc::new(crate::runtime::audit_log::AuditLogger::with_path(
415 audit_path,
416 ))
417 } else {
418 Arc::new(crate::runtime::audit_log::AuditLogger::for_destination(
419 &audit_dest,
420 &data_path,
421 ))
422 }
423 },
424 control_event_ledger: parking_lot::RwLock::new(Arc::new(
425 crate::runtime::control_events::RuntimeLedger::new(db.store()),
426 )),
427 control_event_config: options.control_events,
428 query_audit: Arc::new(crate::runtime::query_audit::QueryAuditStream::new(
429 db.store(),
430 options.query_audit.clone(),
431 )),
432 lease_lifecycle: std::sync::OnceLock::new(),
433 replica_apply_metrics: std::sync::Arc::new(
434 crate::replication::logical::ReplicaApplyMetrics::default(),
435 ),
436 replica_link_metrics: std::sync::Arc::new(
437 crate::replication::reconnect::ReplicaLinkMetrics::default(),
438 ),
439 quota_bucket: crate::runtime::quota_bucket::QuotaBucket::from_env(),
440 schema_vocabulary: parking_lot::RwLock::new(
441 crate::runtime::schema_vocabulary::SchemaVocabulary::new(),
442 ),
443 slow_query_logger: {
444 let fallback_dir = if embedded_single_file {
461 std::env::temp_dir()
462 .join("reddb-embedded-runtime")
463 .join(format!("slow-{}", std::process::id()))
464 } else {
465 options
466 .data_path
467 .as_ref()
468 .and_then(|p| p.parent().map(std::path::PathBuf::from))
469 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
470 };
471 let threshold_ms = std::env::var("RED_SLOW_QUERY_THRESHOLD_MS")
472 .ok()
473 .and_then(|s| s.parse::<u64>().ok())
474 .unwrap_or(1000);
475 let sample_pct = std::env::var("RED_SLOW_QUERY_SAMPLE_PCT")
476 .ok()
477 .and_then(|s| s.parse::<u8>().ok())
478 .unwrap_or(100);
479 let (_, slow_dest) = crate::api::tier_wiring::current_log_destinations();
480 crate::telemetry::slow_query_logger::SlowQueryLogger::for_destination(
481 &slow_dest,
482 &fallback_dir,
483 threshold_ms,
484 sample_pct,
485 )
486 },
487 slow_query_store: crate::telemetry::slow_query_store::SlowQueryStore::new(
488 crate::telemetry::slow_query_store::DEFAULT_CAP,
489 ),
490 kv_stats: crate::runtime::KvStatsCounters::default(),
491 metrics_ingest_stats: crate::runtime::MetricsIngestCounters::default(),
492 metrics_tenant_activity_stats:
493 crate::runtime::MetricsTenantActivityCounters::default(),
494 claim_telemetry: Arc::new(
495 crate::runtime::claim_telemetry::ClaimTelemetryCounters::default(),
496 ),
497 queue_telemetry: Arc::new(
498 crate::runtime::queue_telemetry::QueueTelemetryCounters::default(),
499 ),
500 query_latency_telemetry: Arc::new(
501 crate::runtime::query_latency_telemetry::QueryLatencyTelemetry::default(),
502 ),
503 occupancy_sampler: Arc::new(
504 crate::runtime::occupancy_sampler::OccupancySampler::new(),
505 ),
506 node_load_telemetry: Arc::new(
507 crate::runtime::node_load_telemetry::NodeLoadTelemetry::default(),
508 ),
509 queue_presence: Arc::new(
510 crate::storage::queue::presence::ConsumerPresenceRegistry::new(),
511 ),
512 vector_introspection: Arc::new(
513 crate::storage::vector::introspection::VectorIntrospectionRegistry::new(),
514 ),
515 kv_tag_index: crate::runtime::KvTagIndex::default(),
516 chain_tip_cache: parking_lot::Mutex::new(HashMap::new()),
517 chain_integrity_broken: parking_lot::Mutex::new(HashMap::new()),
518 integrity_tombstones: parking_lot::Mutex::new(Vec::new()),
519 integrity_tombstones_state: std::sync::atomic::AtomicU8::new(0),
520 }),
521 };
522
523 crate::telemetry::operator_event::install_global_audit_sink(Arc::clone(
529 &runtime.inner.audit_log,
530 ));
531
532 runtime
535 .inner
536 .slow_query_logger
537 .attach_store(Arc::clone(&runtime.inner.slow_query_store));
538
539 runtime
547 .inner
548 .lifecycle
549 .set_restore_started_at_ms(boot_open_start_ms);
550 runtime
551 .inner
552 .lifecycle
553 .set_restore_ready_at_ms(storage_ready_ms);
554 runtime
555 .inner
556 .lifecycle
557 .set_wal_replay_started_at_ms(boot_open_start_ms);
558 runtime
559 .inner
560 .lifecycle
561 .set_wal_replay_ready_at_ms(storage_ready_ms);
562
563 let restored_cdc_lsn = runtime
564 .inner
565 .db
566 .replication
567 .as_ref()
568 .map(|repl| {
569 repl.logical_wal_spool
570 .as_ref()
571 .map(|spool| spool.current_lsn())
572 .unwrap_or(0)
573 })
574 .unwrap_or(0)
575 .max(runtime.config_u64("red.config.timeline.last_archived_lsn", 0));
576 runtime.inner.cdc.set_current_lsn(restored_cdc_lsn);
577 runtime.rehydrate_snapshot_xid_floor();
578 assert!(
579 runtime
580 .inner
581 .db
582 .store()
583 .snapshot_manager
584 .set(runtime.snapshot_manager())
585 .is_ok(),
586 "a new runtime owns the store transaction outcomes"
587 );
588 runtime
589 .bootstrap_system_keyed_collections()
590 .map_err(|err| RedDBError::Internal(format!("bootstrap system collections: {err}")))?;
591 runtime.rehydrate_declared_column_schemas();
592 runtime.rehydrate_runtime_index_registry()?;
593 runtime
594 .load_probabilistic_state()
595 .map_err(|err| RedDBError::Internal(format!("load probabilistic state: {err}")))?;
596
597 runtime.rehydrate_tenant_tables();
601 runtime.rehydrate_materialized_view_descriptors();
606 if let Some(repl) = &runtime.inner.db.replication {
607 repl.wal_buffer.set_current_lsn(restored_cdc_lsn);
608 }
609
610 {
612 let sys = SystemInfo::collect();
613 runtime.inner.db.store().set_config_tree(
614 "red.system",
615 &crate::serde_json::json!({
616 "pid": sys.pid,
617 "cpu_cores": sys.cpu_cores,
618 "total_memory_bytes": sys.total_memory_bytes,
619 "available_memory_bytes": sys.available_memory_bytes,
620 "os": sys.os,
621 "arch": sys.arch,
622 "hostname": sys.hostname,
623 "started_at": SystemTime::now()
624 .duration_since(UNIX_EPOCH)
625 .unwrap_or_default()
626 .as_millis() as u64
627 }),
628 );
629
630 let store = runtime.inner.db.store();
632 if store
633 .get_collection("red_config")
634 .map(|m| m.query_all(|_| true).len())
635 .unwrap_or(0)
636 <= 10
637 {
638 store.set_config_tree("red.ai", &crate::json!({
639 "default": crate::json!({
640 "provider": "openai",
641 "model": crate::ai::DEFAULT_OPENAI_PROMPT_MODEL
642 }),
643 "max_embedding_inputs": 256,
644 "max_prompt_batch": 256,
645 "timeout": crate::json!({ "connect_secs": 10, "read_secs": 90, "write_secs": 30 })
646 }));
647 store.set_config_tree(
648 "red.server",
649 &crate::json!({
650 "max_scan_limit": 1000,
651 "max_body_size": 1048576,
652 "read_timeout_ms": 5000,
653 "write_timeout_ms": 5000
654 }),
655 );
656 store.set_config_tree(
657 "red.storage",
658 &crate::json!({
659 "page_size": 4096,
660 "page_cache_capacity": 100000,
661 "auto_checkpoint_pages": 1000,
662 "snapshot_retention": 16,
663 "verify_checksums": true,
664 "segment": crate::json!({
665 "max_entities": 100000,
666 "max_bytes": 268435456_u64,
667 "compression_level": 6
668 }),
669 "hnsw": crate::json!({ "m": 16, "ef_construction": 100, "ef_search": 50 }),
670 "ivf": crate::json!({ "n_lists": 100, "n_probes": 10 }),
671 "bm25": crate::json!({ "k1": 1.2, "b": 0.75 })
672 }),
673 );
674 store.set_config_tree(
675 "red.search",
676 &crate::json!({
677 "rag": crate::json!({
678 "max_chunks_per_source": 10,
679 "max_total_chunks": 25,
680 "similarity_threshold": 0.8,
681 "graph_depth": 2,
682 "min_relevance": 0.3
683 }),
684 "fusion": crate::json!({
685 "vector_weight": 0.5,
686 "graph_weight": 0.3,
687 "table_weight": 0.2,
688 "dedup_threshold": 0.85
689 })
690 }),
691 );
692 store.set_config_tree(
693 "red.auth",
694 &crate::json!({
695 "enabled": false,
696 "session_ttl_secs": 3600,
697 "require_auth": false
698 }),
699 );
700 store.set_config_tree(
701 "red.query",
702 &crate::json!({
703 "connection_pool": crate::json!({ "max_connections": 64, "max_idle": 16 }),
704 "max_recursion_depth": 1000
705 }),
706 );
707 store.set_config_tree(
708 "red.indexes",
709 &crate::json!({
710 "auto_select": true,
711 "bloom_filter": crate::json!({
712 "enabled": true,
713 "false_positive_rate": 0.01,
714 "prune_on_scan": true
715 }),
716 "hash": crate::json!({ "enabled": true }),
717 "bitmap": crate::json!({ "enabled": true, "max_cardinality": 1000 }),
718 "spatial": crate::json!({ "enabled": true })
719 }),
720 );
721 store.set_config_tree(
722 "red.probabilistic",
723 &crate::json!({
724 "hll_registers": 16384,
725 "sketch_default_width": 1000,
726 "sketch_default_depth": 5,
727 "filter_default_capacity": 100000
728 }),
729 );
730 store.set_config_tree(
731 "red.timeseries",
732 &crate::json!({
733 "default_chunk_size": 1024,
734 "compression": crate::json!({
735 "timestamps": "delta_of_delta",
736 "values": "gorilla_xor"
737 }),
738 "default_retention_days": 0
739 }),
740 );
741 store.set_config_tree(
742 "red.queue",
743 &crate::json!({
744 "default_max_size": 0,
745 "default_max_attempts": 3,
746 "visibility_timeout_ms": 30000,
747 "consumer_idle_timeout_ms": 60000
748 }),
749 );
750 store.set_config_tree(
751 "red.backup",
752 &crate::json!({
753 "enabled": false,
754 "interval_secs": 3600,
755 "retention_count": 24,
756 "upload": false,
757 "backend": "local"
758 }),
759 );
760 store.set_config_tree(
761 "red.wal",
762 &crate::json!({
763 "archive": crate::json!({
764 "enabled": false,
765 "retention_hours": 168,
766 "prefix": reddb_file::backup_wal_prefix("")
767 })
768 }),
769 );
770 store.set_config_tree(
771 "red.cdc",
772 &crate::json!({
773 "enabled": true,
774 "buffer_size": 100000
775 }),
776 );
777 store.set_config_tree(
778 "red.config.secret",
779 &crate::json!({
780 "auto_encrypt": true,
781 "auto_decrypt": true
782 }),
783 );
784 }
785
786 crate::runtime::config_matrix::heal_critical_keys(store.as_ref());
793 seed_storage_deploy_config(store.as_ref(), options.storage_profile);
794
795 let lehman_yao = runtime.config_bool("storage.btree.lehman_yao", true);
802 crate::storage::engine::btree::lehman_yao::set_enabled(lehman_yao);
803 if lehman_yao {
804 tracing::info!(
805 "storage.btree.lehman_yao=true — lock-free concurrent descent enabled"
806 );
807 }
808
809 let overlay_path = crate::runtime::config_overlay::config_file_path();
814 let _ =
815 crate::runtime::config_overlay::apply_config_file(store.as_ref(), &overlay_path);
816 }
817
818 {
822 let store = runtime.inner.db.store();
823 for name in crate::application::vcs_collections::ALL {
824 let _ = store.get_or_create_collection(*name);
825 }
826 store.set_config_tree(
829 crate::application::vcs_collections::CONFIG_NAMESPACE,
830 &crate::json!({
831 "default_branch": "main",
832 "author": crate::json!({
833 "name": "reddb",
834 "email": "reddb@localhost"
835 }),
836 "protected_branches": crate::json!(["main"]),
837 "closure": crate::json!({
838 "enabled": true,
839 "lazy": true
840 }),
841 "merge": crate::json!({
842 "default_strategy": "auto",
843 "fast_forward": true
844 })
845 }),
846 );
847 }
848
849 {
852 let store = runtime.inner.db.store();
853 for name in crate::application::migration_collections::ALL {
854 let _ = store.get_or_create_collection(*name);
855 }
856 }
857
858 let _ = crate::application::topology_collections::ensure(&runtime);
862
863 runtime
871 .inner
872 .db
873 .store()
874 .register_entity_id(crate::storage::EntityId::new(
875 crate::storage::FIRST_USER_ENTITY_ID - 1,
876 ));
877
878 {
893 let weak = Arc::downgrade(&runtime.inner);
894 std::thread::Builder::new()
895 .name("reddb-maintenance".into())
896 .spawn(move || {
897 let tick = std::time::Duration::from_millis(200);
898 let work_interval = std::time::Duration::from_secs(60);
899 let mut last_work = std::time::Instant::now();
900 loop {
901 std::thread::sleep(tick);
902 let Some(inner) = weak.upgrade() else {
903 break;
906 };
907 if last_work.elapsed() >= work_interval {
908 let _stats = inner.db.store().context_index().stats();
909 last_work = std::time::Instant::now();
910 }
911 }
912 })
913 .ok();
914 }
915
916 {
918 let store = runtime.inner.db.store();
919 let mut backup_enabled = false;
920 let mut backup_interval = 3600u64;
921
922 if let Some(manager) = store.get_collection("red_config") {
923 manager.for_each_entity(|entity| {
924 if let Some(row) = entity.data.as_row() {
925 let key = row.get_field("key").and_then(|v| match v {
926 reddb_types::Value::Text(s) => Some(s.as_ref()),
927 _ => None,
928 });
929 let val = row.get_field("value");
930 if key == Some("red.config.backup.enabled") {
931 backup_enabled = match val {
932 Some(reddb_types::Value::Boolean(true)) => true,
933 Some(reddb_types::Value::Text(s)) => &**s == "true",
934 _ => false,
935 };
936 } else if key == Some("red.config.backup.interval_secs") {
937 if let Some(reddb_types::Value::Integer(n)) = val {
938 backup_interval = *n as u64;
939 }
940 }
941 }
942 true
943 });
944 }
945
946 if backup_enabled {
947 runtime.inner.backup_scheduler.set_interval(backup_interval);
948 let rt = runtime.clone();
949 runtime
950 .inner
951 .backup_scheduler
952 .start(move || rt.trigger_backup().map_err(|e| format!("{}", e)));
953 }
954 }
955
956 {
958 runtime
959 .inner
960 .ec_registry
961 .load_from_config_store(runtime.inner.db.store().as_ref());
962 if !runtime.inner.ec_registry.async_configs().is_empty() {
963 runtime.inner.ec_worker.start(
964 Arc::clone(&runtime.inner.ec_registry),
965 Arc::clone(&runtime.inner.db.store()),
966 );
967 }
968 }
969
970 if let (true, crate::replication::ReplicationRole::Replica { primary_addr }) = (
971 runtime.inner.db.options().replica_loop_enabled,
972 runtime.inner.db.options().replication.role.clone(),
973 ) {
974 let rt = runtime.clone();
975 std::thread::Builder::new()
976 .name("reddb-replica".into())
977 .spawn(move || rt.run_replica_loop(primary_addr))
978 .ok();
979 }
980
981 runtime.inner.lifecycle.mark_ready();
986
987 {
996 let weak_inner = Arc::downgrade(&runtime.inner);
997 std::thread::Builder::new()
998 .name("reddb-mv-scheduler".into())
999 .stack_size(8 * 1024 * 1024)
1004 .spawn(move || loop {
1005 std::thread::sleep(std::time::Duration::from_millis(50));
1006 let Some(inner) = weak_inner.upgrade() else {
1007 break;
1008 };
1009 let rt = RedDBRuntime { inner };
1010 rt.refresh_due_materialized_views();
1011 })
1012 .ok();
1013 }
1014
1015 if !runtime.write_gate().is_read_only() {
1025 let weak_inner = Arc::downgrade(&runtime.inner);
1026 std::thread::Builder::new()
1027 .name("reddb-retention-sweeper".into())
1028 .spawn(move || loop {
1029 std::thread::sleep(std::time::Duration::from_millis(500));
1030 let Some(inner) = weak_inner.upgrade() else {
1031 break;
1032 };
1033 let rt = RedDBRuntime { inner };
1034 rt.sweep_retention_tick(
1035 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
1036 );
1037 })
1038 .ok();
1039 }
1040
1041 if no_auth_boot {
1045 use crate::runtime::control_events::{
1046 ActorRef, ControlEvent, ControlEventCtx, EventKind, Outcome,
1047 };
1048 let ledger = std::sync::Arc::clone(&runtime.inner.control_event_ledger.read());
1049 let ctx = ControlEventCtx {
1050 actor: ActorRef::System("boot"),
1051 scope: None,
1052 request_id: None,
1053 trace_id: None,
1054 };
1055 let event = ControlEvent {
1056 kind: EventKind::AuthDisabled,
1057 outcome: Outcome::Allowed,
1058 action: std::borrow::Cow::Borrowed("auth.disabled"),
1059 resource: Some("server".to_string()),
1060 reason: Some("--no-auth / --dev".to_string()),
1061 matched_policy_id: None,
1062 fields: std::collections::HashMap::new(),
1063 };
1064 if let Err(err) = ledger.emit(&ctx, event) {
1065 tracing::warn!(
1066 target: "reddb::security",
1067 error = %err,
1068 "could not record the auth-disabled boot in the control-event ledger"
1069 );
1070 }
1071 }
1072
1073 Ok(runtime)
1074 }
1075
1076 fn rehydrate_snapshot_xid_floor(&self) {
1077 let store = self.inner.db.store();
1078 for collection in store.list_collections() {
1079 let Some(manager) = store.get_collection(&collection) else {
1080 continue;
1081 };
1082 for entity in manager.query_all(|_| true) {
1083 let snapshot_manager = self.inner.transaction_state.snapshot_manager();
1084 snapshot_manager.observe_committed_xid(entity.xmin);
1085 snapshot_manager.observe_committed_xid(entity.xmax);
1086 }
1087 }
1088 }
1089
1090 pub(crate) fn ensure_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1103 let store = self.inner.db.store();
1104 let mut changed = false;
1105 if store.get_collection(name).is_none() {
1106 store.get_or_create_collection(name);
1107 changed = true;
1108 }
1109 if self.inner.db.collection_contract(name).is_none() {
1110 self.inner
1111 .db
1112 .save_collection_contract(system_keyed_collection_contract(
1113 name,
1114 crate::catalog::CollectionModel::Table,
1115 ))
1116 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1117 changed = true;
1118 }
1119 if changed {
1120 self.inner
1121 .db
1122 .persist_metadata()
1123 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1124 }
1125 Ok(())
1126 }
1127
1128 pub(crate) fn drop_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1133 let store = self.inner.db.store();
1134 if store.get_collection(name).is_none() {
1135 return Ok(());
1136 }
1137 store
1138 .drop_collection(name)
1139 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1140 if self.inner.db.collection_contract(name).is_some() {
1143 self.inner
1144 .db
1145 .remove_collection_contract(name)
1146 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1147 }
1148 self.invalidate_result_cache();
1149 self.inner
1150 .db
1151 .persist_metadata()
1152 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1153 Ok(())
1154 }
1155
1156 fn bootstrap_system_keyed_collections(&self) -> RedDBResult<()> {
1157 let mut changed = false;
1158 for (name, model) in [
1159 ("red.config", crate::catalog::CollectionModel::Config),
1160 ("red.vault", crate::catalog::CollectionModel::Vault),
1161 (
1165 crate::runtime::continuous_materialized_view::CATALOG_COLLECTION,
1166 crate::catalog::CollectionModel::Config,
1167 ),
1168 ] {
1169 if self.inner.db.store().get_collection(name).is_none() {
1170 self.inner.db.store().get_or_create_collection(name);
1171 changed = true;
1172 }
1173 if self.inner.db.collection_contract(name).is_none() {
1174 self.inner
1175 .db
1176 .save_collection_contract(system_keyed_collection_contract(name, model))
1177 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1178 changed = true;
1179 }
1180 }
1181 if changed {
1182 self.inner
1183 .db
1184 .persist_metadata()
1185 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1186 }
1187 Ok(())
1188 }
1189
1190 pub fn db(&self) -> Arc<RedDB> {
1191 Arc::clone(&self.inner.db)
1192 }
1193
1194 pub fn index_store_ref(&self) -> &super::index_store::IndexStore {
1199 &self.inner.index_store
1200 }
1201
1202 pub(crate) fn schema_vocabulary_apply(
1207 &self,
1208 event: crate::runtime::schema_vocabulary::DdlEvent,
1209 ) {
1210 self.inner.schema_vocabulary.write().on_ddl(event);
1211 }
1212
1213 pub fn schema_vocabulary_lookup(
1218 &self,
1219 token: &str,
1220 ) -> Vec<crate::runtime::schema_vocabulary::VocabHit> {
1221 self.inner.schema_vocabulary.read().lookup(token).to_vec()
1222 }
1223
1224 pub fn set_auth_store(&self, store: Arc<crate::auth::store::AuthStore>) {
1228 *self.inner.auth_store.write() = Some(store);
1229 }
1230
1231 pub fn auth_store(&self) -> Option<Arc<crate::auth::store::AuthStore>> {
1234 self.inner.auth_store.read().clone()
1235 }
1236
1237 pub fn set_oauth_validator(&self, validator: Option<Arc<crate::auth::oauth::OAuthValidator>>) {
1241 *self.inner.oauth_validator.write() = validator;
1242 }
1243
1244 pub fn oauth_validator(&self) -> Option<Arc<crate::auth::oauth::OAuthValidator>> {
1248 self.inner.oauth_validator.read().clone()
1249 }
1250
1251 pub fn set_browser_token_authority(
1257 &self,
1258 authority: Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>,
1259 ) {
1260 *self.inner.browser_token_authority.write() = authority;
1261 }
1262
1263 pub fn browser_token_authority(
1267 &self,
1268 ) -> Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>> {
1269 self.inner.browser_token_authority.read().clone()
1270 }
1271}