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_store_wal_actions: parking_lot::RwLock::new(HashMap::new()),
355 pending_claim_locks: parking_lot::RwLock::new(HashMap::new()),
356 queue_wait_registry: std::sync::Arc::new(
357 crate::runtime::queue_wait_registry::QueueWaitRegistry::new(),
358 ),
359 pending_queue_wakes: parking_lot::RwLock::new(HashMap::new()),
360 tenant_tables: parking_lot::RwLock::new(HashMap::new()),
361 ddl_epoch: std::sync::atomic::AtomicU64::new(0),
362 write_gate: Arc::new(crate::runtime::write_gate::WriteGate::from_options(
363 &options,
364 )),
365 lifecycle: crate::runtime::lifecycle::Lifecycle::new(),
366 resource_limits: crate::runtime::resource_limits::ResourceLimits::from_env(),
367 audit_log: {
368 let data_path = if embedded_single_file {
384 std::env::temp_dir()
385 .join("reddb-embedded-runtime")
386 .join(format!("audit-{}", std::process::id()))
387 } else {
388 options
389 .data_path
390 .clone()
391 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
392 };
393 let (audit_dest, _) = crate::api::tier_wiring::current_log_destinations();
394 if !matches!(audit_dest, crate::storage::layout::LogDestination::File(_))
395 && (embedded_single_file
396 || options
397 .metadata
398 .contains_key(crate::api::EPHEMERAL_RUNTIME_METADATA_KEY))
399 {
400 let audit_path = reddb_file::layout::sibling_path(
410 &data_path,
411 &reddb_file::layout::sidecar_file_name(&data_path, "audit.log"),
412 );
413 Arc::new(crate::runtime::audit_log::AuditLogger::with_path(
414 audit_path,
415 ))
416 } else {
417 Arc::new(crate::runtime::audit_log::AuditLogger::for_destination(
418 &audit_dest,
419 &data_path,
420 ))
421 }
422 },
423 control_event_ledger: parking_lot::RwLock::new(Arc::new(
424 crate::runtime::control_events::RuntimeLedger::new(db.store()),
425 )),
426 control_event_config: options.control_events,
427 query_audit: Arc::new(crate::runtime::query_audit::QueryAuditStream::new(
428 db.store(),
429 options.query_audit.clone(),
430 )),
431 lease_lifecycle: std::sync::OnceLock::new(),
432 replica_apply_metrics: std::sync::Arc::new(
433 crate::replication::logical::ReplicaApplyMetrics::default(),
434 ),
435 replica_link_metrics: std::sync::Arc::new(
436 crate::replication::reconnect::ReplicaLinkMetrics::default(),
437 ),
438 quota_bucket: crate::runtime::quota_bucket::QuotaBucket::from_env(),
439 schema_vocabulary: parking_lot::RwLock::new(
440 crate::runtime::schema_vocabulary::SchemaVocabulary::new(),
441 ),
442 slow_query_logger: {
443 let fallback_dir = if embedded_single_file {
460 std::env::temp_dir()
461 .join("reddb-embedded-runtime")
462 .join(format!("slow-{}", std::process::id()))
463 } else {
464 options
465 .data_path
466 .as_ref()
467 .and_then(|p| p.parent().map(std::path::PathBuf::from))
468 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
469 };
470 let threshold_ms = std::env::var("RED_SLOW_QUERY_THRESHOLD_MS")
471 .ok()
472 .and_then(|s| s.parse::<u64>().ok())
473 .unwrap_or(1000);
474 let sample_pct = std::env::var("RED_SLOW_QUERY_SAMPLE_PCT")
475 .ok()
476 .and_then(|s| s.parse::<u8>().ok())
477 .unwrap_or(100);
478 let (_, slow_dest) = crate::api::tier_wiring::current_log_destinations();
479 crate::telemetry::slow_query_logger::SlowQueryLogger::for_destination(
480 &slow_dest,
481 &fallback_dir,
482 threshold_ms,
483 sample_pct,
484 )
485 },
486 slow_query_store: crate::telemetry::slow_query_store::SlowQueryStore::new(
487 crate::telemetry::slow_query_store::DEFAULT_CAP,
488 ),
489 kv_stats: crate::runtime::KvStatsCounters::default(),
490 metrics_ingest_stats: crate::runtime::MetricsIngestCounters::default(),
491 metrics_tenant_activity_stats:
492 crate::runtime::MetricsTenantActivityCounters::default(),
493 claim_telemetry: Arc::new(
494 crate::runtime::claim_telemetry::ClaimTelemetryCounters::default(),
495 ),
496 queue_telemetry: Arc::new(
497 crate::runtime::queue_telemetry::QueueTelemetryCounters::default(),
498 ),
499 query_latency_telemetry: Arc::new(
500 crate::runtime::query_latency_telemetry::QueryLatencyTelemetry::default(),
501 ),
502 occupancy_sampler: Arc::new(
503 crate::runtime::occupancy_sampler::OccupancySampler::new(),
504 ),
505 node_load_telemetry: Arc::new(
506 crate::runtime::node_load_telemetry::NodeLoadTelemetry::default(),
507 ),
508 queue_presence: Arc::new(
509 crate::storage::queue::presence::ConsumerPresenceRegistry::new(),
510 ),
511 vector_introspection: Arc::new(
512 crate::storage::vector::introspection::VectorIntrospectionRegistry::new(),
513 ),
514 kv_tag_index: crate::runtime::KvTagIndex::default(),
515 chain_tip_cache: parking_lot::Mutex::new(HashMap::new()),
516 chain_integrity_broken: parking_lot::Mutex::new(HashMap::new()),
517 integrity_tombstones: parking_lot::Mutex::new(Vec::new()),
518 integrity_tombstones_state: std::sync::atomic::AtomicU8::new(0),
519 }),
520 };
521
522 crate::telemetry::operator_event::install_global_audit_sink(Arc::clone(
528 &runtime.inner.audit_log,
529 ));
530
531 runtime
534 .inner
535 .slow_query_logger
536 .attach_store(Arc::clone(&runtime.inner.slow_query_store));
537
538 runtime
546 .inner
547 .lifecycle
548 .set_restore_started_at_ms(boot_open_start_ms);
549 runtime
550 .inner
551 .lifecycle
552 .set_restore_ready_at_ms(storage_ready_ms);
553 runtime
554 .inner
555 .lifecycle
556 .set_wal_replay_started_at_ms(boot_open_start_ms);
557 runtime
558 .inner
559 .lifecycle
560 .set_wal_replay_ready_at_ms(storage_ready_ms);
561
562 let restored_cdc_lsn = runtime
563 .inner
564 .db
565 .replication
566 .as_ref()
567 .map(|repl| {
568 repl.logical_wal_spool
569 .as_ref()
570 .map(|spool| spool.current_lsn())
571 .unwrap_or(0)
572 })
573 .unwrap_or(0)
574 .max(runtime.config_u64("red.config.timeline.last_archived_lsn", 0));
575 runtime.inner.cdc.set_current_lsn(restored_cdc_lsn);
576 runtime.rehydrate_snapshot_xid_floor();
577 assert!(
578 runtime
579 .inner
580 .db
581 .store()
582 .snapshot_manager
583 .set(runtime.snapshot_manager())
584 .is_ok(),
585 "a new runtime owns the store transaction outcomes"
586 );
587 runtime
588 .bootstrap_system_keyed_collections()
589 .map_err(|err| RedDBError::Internal(format!("bootstrap system collections: {err}")))?;
590 runtime.rehydrate_declared_column_schemas();
591 runtime.rehydrate_runtime_index_registry()?;
592 runtime
593 .load_probabilistic_state()
594 .map_err(|err| RedDBError::Internal(format!("load probabilistic state: {err}")))?;
595
596 runtime.rehydrate_tenant_tables();
600 runtime.rehydrate_materialized_view_descriptors();
605 if let Some(repl) = &runtime.inner.db.replication {
606 repl.wal_buffer.set_current_lsn(restored_cdc_lsn);
607 }
608
609 {
611 let sys = SystemInfo::collect();
612 runtime.inner.db.store().set_config_tree(
613 "red.system",
614 &crate::serde_json::json!({
615 "pid": sys.pid,
616 "cpu_cores": sys.cpu_cores,
617 "total_memory_bytes": sys.total_memory_bytes,
618 "available_memory_bytes": sys.available_memory_bytes,
619 "os": sys.os,
620 "arch": sys.arch,
621 "hostname": sys.hostname,
622 "started_at": SystemTime::now()
623 .duration_since(UNIX_EPOCH)
624 .unwrap_or_default()
625 .as_millis() as u64
626 }),
627 );
628
629 let store = runtime.inner.db.store();
631 if store
632 .get_collection("red_config")
633 .map(|m| m.query_all(|_| true).len())
634 .unwrap_or(0)
635 <= 10
636 {
637 store.set_config_tree("red.ai", &crate::json!({
638 "default": crate::json!({
639 "provider": "openai",
640 "model": crate::ai::DEFAULT_OPENAI_PROMPT_MODEL
641 }),
642 "max_embedding_inputs": 256,
643 "max_prompt_batch": 256,
644 "timeout": crate::json!({ "connect_secs": 10, "read_secs": 90, "write_secs": 30 })
645 }));
646 store.set_config_tree(
647 "red.server",
648 &crate::json!({
649 "max_scan_limit": 1000,
650 "max_body_size": 1048576,
651 "read_timeout_ms": 5000,
652 "write_timeout_ms": 5000
653 }),
654 );
655 store.set_config_tree(
656 "red.storage",
657 &crate::json!({
658 "page_size": 4096,
659 "page_cache_capacity": 100000,
660 "auto_checkpoint_pages": 1000,
661 "snapshot_retention": 16,
662 "verify_checksums": true,
663 "segment": crate::json!({
664 "max_entities": 100000,
665 "max_bytes": 268435456_u64,
666 "compression_level": 6
667 }),
668 "hnsw": crate::json!({ "m": 16, "ef_construction": 100, "ef_search": 50 }),
669 "ivf": crate::json!({ "n_lists": 100, "n_probes": 10 }),
670 "bm25": crate::json!({ "k1": 1.2, "b": 0.75 })
671 }),
672 );
673 store.set_config_tree(
674 "red.search",
675 &crate::json!({
676 "rag": crate::json!({
677 "max_chunks_per_source": 10,
678 "max_total_chunks": 25,
679 "similarity_threshold": 0.8,
680 "graph_depth": 2,
681 "min_relevance": 0.3
682 }),
683 "fusion": crate::json!({
684 "vector_weight": 0.5,
685 "graph_weight": 0.3,
686 "table_weight": 0.2,
687 "dedup_threshold": 0.85
688 })
689 }),
690 );
691 store.set_config_tree(
692 "red.auth",
693 &crate::json!({
694 "enabled": false,
695 "session_ttl_secs": 3600,
696 "require_auth": false
697 }),
698 );
699 store.set_config_tree(
700 "red.query",
701 &crate::json!({
702 "connection_pool": crate::json!({ "max_connections": 64, "max_idle": 16 }),
703 "max_recursion_depth": 1000
704 }),
705 );
706 store.set_config_tree(
707 "red.indexes",
708 &crate::json!({
709 "auto_select": true,
710 "bloom_filter": crate::json!({
711 "enabled": true,
712 "false_positive_rate": 0.01,
713 "prune_on_scan": true
714 }),
715 "hash": crate::json!({ "enabled": true }),
716 "bitmap": crate::json!({ "enabled": true, "max_cardinality": 1000 }),
717 "spatial": crate::json!({ "enabled": true })
718 }),
719 );
720 store.set_config_tree(
721 "red.probabilistic",
722 &crate::json!({
723 "hll_registers": 16384,
724 "sketch_default_width": 1000,
725 "sketch_default_depth": 5,
726 "filter_default_capacity": 100000
727 }),
728 );
729 store.set_config_tree(
730 "red.timeseries",
731 &crate::json!({
732 "default_chunk_size": 1024,
733 "compression": crate::json!({
734 "timestamps": "delta_of_delta",
735 "values": "gorilla_xor"
736 }),
737 "default_retention_days": 0
738 }),
739 );
740 store.set_config_tree(
741 "red.queue",
742 &crate::json!({
743 "default_max_size": 0,
744 "default_max_attempts": 3,
745 "visibility_timeout_ms": 30000,
746 "consumer_idle_timeout_ms": 60000
747 }),
748 );
749 store.set_config_tree(
750 "red.backup",
751 &crate::json!({
752 "enabled": false,
753 "interval_secs": 3600,
754 "retention_count": 24,
755 "upload": false,
756 "backend": "local"
757 }),
758 );
759 store.set_config_tree(
760 "red.wal",
761 &crate::json!({
762 "archive": crate::json!({
763 "enabled": false,
764 "retention_hours": 168,
765 "prefix": reddb_file::backup_wal_prefix("")
766 })
767 }),
768 );
769 store.set_config_tree(
770 "red.cdc",
771 &crate::json!({
772 "enabled": true,
773 "buffer_size": 100000
774 }),
775 );
776 store.set_config_tree(
777 "red.config.secret",
778 &crate::json!({
779 "auto_encrypt": true,
780 "auto_decrypt": true
781 }),
782 );
783 }
784
785 crate::runtime::config_matrix::heal_critical_keys(store.as_ref());
792 seed_storage_deploy_config(store.as_ref(), options.storage_profile);
793
794 let lehman_yao = runtime.config_bool("storage.btree.lehman_yao", true);
801 crate::storage::engine::btree::lehman_yao::set_enabled(lehman_yao);
802 if lehman_yao {
803 tracing::info!(
804 "storage.btree.lehman_yao=true — lock-free concurrent descent enabled"
805 );
806 }
807
808 let overlay_path = crate::runtime::config_overlay::config_file_path();
813 let _ =
814 crate::runtime::config_overlay::apply_config_file(store.as_ref(), &overlay_path);
815 }
816
817 {
821 let store = runtime.inner.db.store();
822 for name in crate::application::vcs_collections::ALL {
823 let _ = store.get_or_create_collection(*name);
824 }
825 store.set_config_tree(
828 crate::application::vcs_collections::CONFIG_NAMESPACE,
829 &crate::json!({
830 "default_branch": "main",
831 "author": crate::json!({
832 "name": "reddb",
833 "email": "reddb@localhost"
834 }),
835 "protected_branches": crate::json!(["main"]),
836 "closure": crate::json!({
837 "enabled": true,
838 "lazy": true
839 }),
840 "merge": crate::json!({
841 "default_strategy": "auto",
842 "fast_forward": true
843 })
844 }),
845 );
846 }
847
848 {
851 let store = runtime.inner.db.store();
852 for name in crate::application::migration_collections::ALL {
853 let _ = store.get_or_create_collection(*name);
854 }
855 }
856
857 let _ = crate::application::topology_collections::ensure(&runtime);
861
862 runtime
870 .inner
871 .db
872 .store()
873 .register_entity_id(crate::storage::EntityId::new(
874 crate::storage::FIRST_USER_ENTITY_ID - 1,
875 ));
876
877 {
892 let weak = Arc::downgrade(&runtime.inner);
893 std::thread::Builder::new()
894 .name("reddb-maintenance".into())
895 .spawn(move || {
896 let tick = std::time::Duration::from_millis(200);
897 let work_interval = std::time::Duration::from_secs(60);
898 let mut last_work = std::time::Instant::now();
899 loop {
900 std::thread::sleep(tick);
901 let Some(inner) = weak.upgrade() else {
902 break;
905 };
906 if last_work.elapsed() >= work_interval {
907 let _stats = inner.db.store().context_index().stats();
908 last_work = std::time::Instant::now();
909 }
910 }
911 })
912 .ok();
913 }
914
915 {
917 let store = runtime.inner.db.store();
918 let mut backup_enabled = false;
919 let mut backup_interval = 3600u64;
920
921 if let Some(manager) = store.get_collection("red_config") {
922 manager.for_each_entity(|entity| {
923 if let Some(row) = entity.data.as_row() {
924 let key = row.get_field("key").and_then(|v| match v {
925 reddb_types::Value::Text(s) => Some(s.as_ref()),
926 _ => None,
927 });
928 let val = row.get_field("value");
929 if key == Some("red.config.backup.enabled") {
930 backup_enabled = match val {
931 Some(reddb_types::Value::Boolean(true)) => true,
932 Some(reddb_types::Value::Text(s)) => &**s == "true",
933 _ => false,
934 };
935 } else if key == Some("red.config.backup.interval_secs") {
936 if let Some(reddb_types::Value::Integer(n)) = val {
937 backup_interval = *n as u64;
938 }
939 }
940 }
941 true
942 });
943 }
944
945 if backup_enabled {
946 runtime.inner.backup_scheduler.set_interval(backup_interval);
947 let rt = runtime.clone();
948 runtime
949 .inner
950 .backup_scheduler
951 .start(move || rt.trigger_backup().map_err(|e| format!("{}", e)));
952 }
953 }
954
955 {
957 runtime
958 .inner
959 .ec_registry
960 .load_from_config_store(runtime.inner.db.store().as_ref());
961 if !runtime.inner.ec_registry.async_configs().is_empty() {
962 runtime.inner.ec_worker.start(
963 Arc::clone(&runtime.inner.ec_registry),
964 Arc::clone(&runtime.inner.db.store()),
965 );
966 }
967 }
968
969 if let (true, crate::replication::ReplicationRole::Replica { primary_addr }) = (
970 runtime.inner.db.options().replica_loop_enabled,
971 runtime.inner.db.options().replication.role.clone(),
972 ) {
973 let rt = runtime.clone();
974 std::thread::Builder::new()
975 .name("reddb-replica".into())
976 .spawn(move || rt.run_replica_loop(primary_addr))
977 .ok();
978 }
979
980 runtime.inner.lifecycle.mark_ready();
985
986 {
995 let weak_inner = Arc::downgrade(&runtime.inner);
996 std::thread::Builder::new()
997 .name("reddb-mv-scheduler".into())
998 .stack_size(8 * 1024 * 1024)
1003 .spawn(move || loop {
1004 std::thread::sleep(std::time::Duration::from_millis(50));
1005 let Some(inner) = weak_inner.upgrade() else {
1006 break;
1007 };
1008 let rt = RedDBRuntime { inner };
1009 rt.refresh_due_materialized_views();
1010 })
1011 .ok();
1012 }
1013
1014 if !runtime.write_gate().is_read_only() {
1024 let weak_inner = Arc::downgrade(&runtime.inner);
1025 std::thread::Builder::new()
1026 .name("reddb-retention-sweeper".into())
1027 .spawn(move || loop {
1028 std::thread::sleep(std::time::Duration::from_millis(500));
1029 let Some(inner) = weak_inner.upgrade() else {
1030 break;
1031 };
1032 let rt = RedDBRuntime { inner };
1033 rt.sweep_retention_tick(
1034 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
1035 );
1036 })
1037 .ok();
1038 }
1039
1040 if no_auth_boot {
1044 use crate::runtime::control_events::{
1045 ActorRef, ControlEvent, ControlEventCtx, EventKind, Outcome,
1046 };
1047 let ledger = std::sync::Arc::clone(&runtime.inner.control_event_ledger.read());
1048 let ctx = ControlEventCtx {
1049 actor: ActorRef::System("boot"),
1050 scope: None,
1051 request_id: None,
1052 trace_id: None,
1053 };
1054 let event = ControlEvent {
1055 kind: EventKind::AuthDisabled,
1056 outcome: Outcome::Allowed,
1057 action: std::borrow::Cow::Borrowed("auth.disabled"),
1058 resource: Some("server".to_string()),
1059 reason: Some("--no-auth / --dev".to_string()),
1060 matched_policy_id: None,
1061 fields: std::collections::HashMap::new(),
1062 };
1063 if let Err(err) = ledger.emit(&ctx, event) {
1064 tracing::warn!(
1065 target: "reddb::security",
1066 error = %err,
1067 "could not record the auth-disabled boot in the control-event ledger"
1068 );
1069 }
1070 }
1071
1072 Ok(runtime)
1073 }
1074
1075 fn rehydrate_snapshot_xid_floor(&self) {
1076 let store = self.inner.db.store();
1077 for collection in store.list_collections() {
1078 let Some(manager) = store.get_collection(&collection) else {
1079 continue;
1080 };
1081 for entity in manager.query_all(|_| true) {
1082 let snapshot_manager = self.inner.transaction_state.snapshot_manager();
1083 snapshot_manager.observe_committed_xid(entity.xmin);
1084 snapshot_manager.observe_committed_xid(entity.xmax);
1085 }
1086 }
1087 }
1088
1089 pub(crate) fn ensure_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1102 let store = self.inner.db.store();
1103 let mut changed = false;
1104 if store.get_collection(name).is_none() {
1105 store.get_or_create_collection(name);
1106 changed = true;
1107 }
1108 if self.inner.db.collection_contract(name).is_none() {
1109 self.inner
1110 .db
1111 .save_collection_contract(system_keyed_collection_contract(
1112 name,
1113 crate::catalog::CollectionModel::Table,
1114 ))
1115 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1116 changed = true;
1117 }
1118 if changed {
1119 self.inner
1120 .db
1121 .persist_metadata()
1122 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1123 }
1124 Ok(())
1125 }
1126
1127 pub(crate) fn drop_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1132 let store = self.inner.db.store();
1133 if store.get_collection(name).is_none() {
1134 return Ok(());
1135 }
1136 store
1137 .drop_collection(name)
1138 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1139 if self.inner.db.collection_contract(name).is_some() {
1142 self.inner
1143 .db
1144 .remove_collection_contract(name)
1145 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1146 }
1147 self.invalidate_result_cache();
1148 self.inner
1149 .db
1150 .persist_metadata()
1151 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1152 Ok(())
1153 }
1154
1155 fn bootstrap_system_keyed_collections(&self) -> RedDBResult<()> {
1156 let mut changed = false;
1157 for (name, model) in [
1158 ("red.config", crate::catalog::CollectionModel::Config),
1159 ("red.vault", crate::catalog::CollectionModel::Vault),
1160 (
1164 crate::runtime::continuous_materialized_view::CATALOG_COLLECTION,
1165 crate::catalog::CollectionModel::Config,
1166 ),
1167 ] {
1168 if self.inner.db.store().get_collection(name).is_none() {
1169 self.inner.db.store().get_or_create_collection(name);
1170 changed = true;
1171 }
1172 if self.inner.db.collection_contract(name).is_none() {
1173 self.inner
1174 .db
1175 .save_collection_contract(system_keyed_collection_contract(name, model))
1176 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1177 changed = true;
1178 }
1179 }
1180 if changed {
1181 self.inner
1182 .db
1183 .persist_metadata()
1184 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1185 }
1186 Ok(())
1187 }
1188
1189 pub fn db(&self) -> Arc<RedDB> {
1190 Arc::clone(&self.inner.db)
1191 }
1192
1193 pub fn index_store_ref(&self) -> &super::index_store::IndexStore {
1198 &self.inner.index_store
1199 }
1200
1201 pub(crate) fn schema_vocabulary_apply(
1206 &self,
1207 event: crate::runtime::schema_vocabulary::DdlEvent,
1208 ) {
1209 self.inner.schema_vocabulary.write().on_ddl(event);
1210 }
1211
1212 pub fn schema_vocabulary_lookup(
1217 &self,
1218 token: &str,
1219 ) -> Vec<crate::runtime::schema_vocabulary::VocabHit> {
1220 self.inner.schema_vocabulary.read().lookup(token).to_vec()
1221 }
1222
1223 pub fn set_auth_store(&self, store: Arc<crate::auth::store::AuthStore>) {
1227 *self.inner.auth_store.write() = Some(store);
1228 }
1229
1230 pub fn auth_store(&self) -> Option<Arc<crate::auth::store::AuthStore>> {
1233 self.inner.auth_store.read().clone()
1234 }
1235
1236 pub fn set_oauth_validator(&self, validator: Option<Arc<crate::auth::oauth::OAuthValidator>>) {
1240 *self.inner.oauth_validator.write() = validator;
1241 }
1242
1243 pub fn oauth_validator(&self) -> Option<Arc<crate::auth::oauth::OAuthValidator>> {
1247 self.inner.oauth_validator.read().clone()
1248 }
1249
1250 pub fn set_browser_token_authority(
1256 &self,
1257 authority: Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>,
1258 ) {
1259 *self.inner.browser_token_authority.write() = authority;
1260 }
1261
1262 pub fn browser_token_authority(
1266 &self,
1267 ) -> Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>> {
1268 self.inner.browser_token_authority.read().clone()
1269 }
1270}