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, crate::storage::schema::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, crate::storage::schema::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 runtime = Self {
248 inner: Arc::new(RuntimeInner {
249 db: db.clone(),
250 layout: PhysicalLayout::from_options(&options),
251 embedded_single_file,
252 memory_budget,
253 memory_accounting: Arc::new(
254 crate::storage::memory_pools::MemoryAccounting::from_shares(
255 memory_budget,
256 memory_shares,
257 ),
258 ),
259 indices: IndexCatalog::register_default_vector_graph(
260 options.has_capability(crate::api::Capability::Table),
261 options.has_capability(crate::api::Capability::Graph),
262 ),
263 pool_config,
264 pool: Mutex::new(PoolState::default()),
265 started_at_unix_ms: SystemTime::now()
266 .duration_since(UNIX_EPOCH)
267 .unwrap_or_default()
268 .as_millis(),
269 probabilistic: super::probabilistic_store::ProbabilisticStore::new(),
270 index_store: super::index_store::IndexStore::new(),
271 cdc: crate::replication::cdc::CdcBuffer::new(100_000),
272 checkpoint_projection_stats: super::CheckpointProjectionStats::default(),
273 scrub_state: parking_lot::Mutex::new(super::ScrubRuntimeState::default()),
274 checkpoint_columnar_emission_budget_chunks: options
275 .checkpoint_columnar_emission_budget_chunks,
276 columnar_projection_size_floor_rows: options.columnar_projection_size_floor_rows,
277 backup_scheduler: crate::replication::scheduler::BackupScheduler::new(3600),
278 query_cache: parking_lot::RwLock::new(
279 crate::storage::query::planner::cache::PlanCache::new(1000),
280 ),
281 result_cache: parking_lot::RwLock::new((
282 HashMap::new(),
283 std::collections::VecDeque::new(),
284 )),
285 result_blob_cache,
286 result_blob_entries: parking_lot::RwLock::new((
287 HashMap::new(),
288 std::collections::VecDeque::new(),
289 )),
290 ask_answer_cache_entries: parking_lot::RwLock::new((
291 HashSet::new(),
292 std::collections::VecDeque::new(),
293 )),
294 result_cache_shadow_divergences: std::sync::atomic::AtomicU64::new(0),
295 result_cache_hits: std::sync::atomic::AtomicU64::new(0),
296 result_cache_misses: std::sync::atomic::AtomicU64::new(0),
297 result_cache_evictions: std::sync::atomic::AtomicU64::new(0),
298 ask_daily_spend: parking_lot::RwLock::new(HashMap::new()),
299 queue_message_locks: parking_lot::RwLock::new(HashMap::new()),
300 rmw_locks: RmwLockTable::new(),
301 planner_dirty_tables: parking_lot::RwLock::new(HashSet::new()),
302 ec_registry: Arc::new(crate::ec::config::EcRegistry::new()),
303 config_registry: Arc::new(crate::auth::registry::ConfigRegistry::new()),
304 ec_worker: crate::ec::worker::EcWorker::new(),
305 auth_store: parking_lot::RwLock::new(None),
306 oauth_validator: parking_lot::RwLock::new(None),
307 browser_token_authority: parking_lot::RwLock::new(None),
308 views: parking_lot::RwLock::new(HashMap::new()),
309 materialized_views: parking_lot::RwLock::new(
310 crate::storage::cache::result::MaterializedViewCache::new(),
311 ),
312 retention_sweeper: parking_lot::RwLock::new(
313 crate::runtime::retention_sweeper::RetentionSweeperState::new(),
314 ),
315 snapshot_manager: Arc::new(
316 crate::storage::transaction::snapshot::SnapshotManager::new(),
317 ),
318 tx_contexts: parking_lot::RwLock::new(HashMap::new()),
319 tx_local_tenants: parking_lot::RwLock::new(HashMap::new()),
320 env_config_overrides: crate::runtime::config_overlay::collect_env_overrides(),
321 lock_manager: Arc::new({
322 let env = crate::runtime::config_overlay::collect_env_overrides();
327 let timeout_ms = env
328 .get("concurrency.locking.deadlock_timeout_ms")
329 .and_then(|raw| raw.parse::<u64>().ok())
330 .unwrap_or_else(|| {
331 match crate::runtime::config_matrix::default_for(
332 "concurrency.locking.deadlock_timeout_ms",
333 ) {
334 Some(crate::serde_json::Value::Number(n)) => n as u64,
335 _ => 5000,
336 }
337 });
338 let cfg = crate::runtime::lock_manager::LockConfig {
339 default_timeout: std::time::Duration::from_millis(timeout_ms),
340 ..Default::default()
341 };
342 crate::runtime::lock_manager::LockManager::new(cfg)
343 }),
344 rls_policies: parking_lot::RwLock::new(HashMap::new()),
345 rls_enabled_tables: parking_lot::RwLock::new(HashSet::new()),
346 foreign_tables: Arc::new(crate::storage::fdw::ForeignTableRegistry::with_builtins()),
347 pending_tombstones: parking_lot::RwLock::new(HashMap::new()),
348 pending_versioned_updates: parking_lot::RwLock::new(HashMap::new()),
349 pending_queue_dedup: parking_lot::RwLock::new(HashMap::new()),
350 pending_kv_watch_events: parking_lot::RwLock::new(HashMap::new()),
351 pending_store_wal_actions: parking_lot::RwLock::new(HashMap::new()),
352 pending_claim_locks: parking_lot::RwLock::new(HashMap::new()),
353 queue_wait_registry: std::sync::Arc::new(
354 crate::runtime::queue_wait_registry::QueueWaitRegistry::new(),
355 ),
356 pending_queue_wakes: parking_lot::RwLock::new(HashMap::new()),
357 tenant_tables: parking_lot::RwLock::new(HashMap::new()),
358 ddl_epoch: std::sync::atomic::AtomicU64::new(0),
359 write_gate: Arc::new(crate::runtime::write_gate::WriteGate::from_options(
360 &options,
361 )),
362 lifecycle: crate::runtime::lifecycle::Lifecycle::new(),
363 resource_limits: crate::runtime::resource_limits::ResourceLimits::from_env(),
364 audit_log: {
365 let data_path = if embedded_single_file {
381 std::env::temp_dir()
382 .join("reddb-embedded-runtime")
383 .join(format!("audit-{}", std::process::id()))
384 } else {
385 options
386 .data_path
387 .clone()
388 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
389 };
390 let (audit_dest, _) = crate::api::tier_wiring::current_log_destinations();
391 if !matches!(audit_dest, crate::storage::layout::LogDestination::File(_))
392 && (embedded_single_file
393 || options
394 .metadata
395 .contains_key(crate::api::EPHEMERAL_RUNTIME_METADATA_KEY))
396 {
397 let audit_path = reddb_file::layout::sibling_path(
407 &data_path,
408 &reddb_file::layout::sidecar_file_name(&data_path, "audit.log"),
409 );
410 Arc::new(crate::runtime::audit_log::AuditLogger::with_path(
411 audit_path,
412 ))
413 } else {
414 Arc::new(crate::runtime::audit_log::AuditLogger::for_destination(
415 &audit_dest,
416 &data_path,
417 ))
418 }
419 },
420 control_event_ledger: parking_lot::RwLock::new(Arc::new(
421 crate::runtime::control_events::RuntimeLedger::new(db.store()),
422 )),
423 control_event_config: options.control_events,
424 query_audit: Arc::new(crate::runtime::query_audit::QueryAuditStream::new(
425 db.store(),
426 options.query_audit.clone(),
427 )),
428 lease_lifecycle: std::sync::OnceLock::new(),
429 replica_apply_metrics: std::sync::Arc::new(
430 crate::replication::logical::ReplicaApplyMetrics::default(),
431 ),
432 replica_link_metrics: std::sync::Arc::new(
433 crate::replication::reconnect::ReplicaLinkMetrics::default(),
434 ),
435 quota_bucket: crate::runtime::quota_bucket::QuotaBucket::from_env(),
436 schema_vocabulary: parking_lot::RwLock::new(
437 crate::runtime::schema_vocabulary::SchemaVocabulary::new(),
438 ),
439 slow_query_logger: {
440 let fallback_dir = if embedded_single_file {
457 std::env::temp_dir()
458 .join("reddb-embedded-runtime")
459 .join(format!("slow-{}", std::process::id()))
460 } else {
461 options
462 .data_path
463 .as_ref()
464 .and_then(|p| p.parent().map(std::path::PathBuf::from))
465 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
466 };
467 let threshold_ms = std::env::var("RED_SLOW_QUERY_THRESHOLD_MS")
468 .ok()
469 .and_then(|s| s.parse::<u64>().ok())
470 .unwrap_or(1000);
471 let sample_pct = std::env::var("RED_SLOW_QUERY_SAMPLE_PCT")
472 .ok()
473 .and_then(|s| s.parse::<u8>().ok())
474 .unwrap_or(100);
475 let (_, slow_dest) = crate::api::tier_wiring::current_log_destinations();
476 crate::telemetry::slow_query_logger::SlowQueryLogger::for_destination(
477 &slow_dest,
478 &fallback_dir,
479 threshold_ms,
480 sample_pct,
481 )
482 },
483 slow_query_store: crate::telemetry::slow_query_store::SlowQueryStore::new(
484 crate::telemetry::slow_query_store::DEFAULT_CAP,
485 ),
486 kv_stats: crate::runtime::KvStatsCounters::default(),
487 metrics_ingest_stats: crate::runtime::MetricsIngestCounters::default(),
488 metrics_tenant_activity_stats:
489 crate::runtime::MetricsTenantActivityCounters::default(),
490 claim_telemetry: Arc::new(
491 crate::runtime::claim_telemetry::ClaimTelemetryCounters::default(),
492 ),
493 queue_telemetry: Arc::new(
494 crate::runtime::queue_telemetry::QueueTelemetryCounters::default(),
495 ),
496 query_latency_telemetry: Arc::new(
497 crate::runtime::query_latency_telemetry::QueryLatencyTelemetry::default(),
498 ),
499 occupancy_sampler: Arc::new(
500 crate::runtime::occupancy_sampler::OccupancySampler::new(),
501 ),
502 node_load_telemetry: Arc::new(
503 crate::runtime::node_load_telemetry::NodeLoadTelemetry::default(),
504 ),
505 queue_presence: Arc::new(
506 crate::storage::queue::presence::ConsumerPresenceRegistry::new(),
507 ),
508 vector_introspection: Arc::new(
509 crate::storage::vector::introspection::VectorIntrospectionRegistry::new(),
510 ),
511 kv_tag_index: crate::runtime::KvTagIndex::default(),
512 chain_tip_cache: parking_lot::Mutex::new(HashMap::new()),
513 chain_integrity_broken: parking_lot::Mutex::new(HashMap::new()),
514 integrity_tombstones: parking_lot::Mutex::new(Vec::new()),
515 integrity_tombstones_state: std::sync::atomic::AtomicU8::new(0),
516 }),
517 };
518
519 crate::telemetry::operator_event::install_global_audit_sink(Arc::clone(
525 &runtime.inner.audit_log,
526 ));
527
528 runtime
531 .inner
532 .slow_query_logger
533 .attach_store(Arc::clone(&runtime.inner.slow_query_store));
534
535 runtime
543 .inner
544 .lifecycle
545 .set_restore_started_at_ms(boot_open_start_ms);
546 runtime
547 .inner
548 .lifecycle
549 .set_restore_ready_at_ms(storage_ready_ms);
550 runtime
551 .inner
552 .lifecycle
553 .set_wal_replay_started_at_ms(boot_open_start_ms);
554 runtime
555 .inner
556 .lifecycle
557 .set_wal_replay_ready_at_ms(storage_ready_ms);
558
559 let restored_cdc_lsn = runtime
560 .inner
561 .db
562 .replication
563 .as_ref()
564 .map(|repl| {
565 repl.logical_wal_spool
566 .as_ref()
567 .map(|spool| spool.current_lsn())
568 .unwrap_or(0)
569 })
570 .unwrap_or(0)
571 .max(runtime.config_u64("red.config.timeline.last_archived_lsn", 0));
572 runtime.inner.cdc.set_current_lsn(restored_cdc_lsn);
573 runtime.rehydrate_snapshot_xid_floor();
574 runtime
575 .bootstrap_system_keyed_collections()
576 .map_err(|err| RedDBError::Internal(format!("bootstrap system collections: {err}")))?;
577 runtime.rehydrate_declared_column_schemas();
578 runtime.rehydrate_runtime_index_registry()?;
579 runtime
580 .load_probabilistic_state()
581 .map_err(|err| RedDBError::Internal(format!("load probabilistic state: {err}")))?;
582
583 runtime.rehydrate_tenant_tables();
587 runtime.rehydrate_materialized_view_descriptors();
592 if let Some(repl) = &runtime.inner.db.replication {
593 repl.wal_buffer.set_current_lsn(restored_cdc_lsn);
594 }
595
596 {
598 let sys = SystemInfo::collect();
599 runtime.inner.db.store().set_config_tree(
600 "red.system",
601 &crate::serde_json::json!({
602 "pid": sys.pid,
603 "cpu_cores": sys.cpu_cores,
604 "total_memory_bytes": sys.total_memory_bytes,
605 "available_memory_bytes": sys.available_memory_bytes,
606 "os": sys.os,
607 "arch": sys.arch,
608 "hostname": sys.hostname,
609 "started_at": SystemTime::now()
610 .duration_since(UNIX_EPOCH)
611 .unwrap_or_default()
612 .as_millis() as u64
613 }),
614 );
615
616 let store = runtime.inner.db.store();
618 if store
619 .get_collection("red_config")
620 .map(|m| m.query_all(|_| true).len())
621 .unwrap_or(0)
622 <= 10
623 {
624 store.set_config_tree("red.ai", &crate::json!({
625 "default": crate::json!({
626 "provider": "openai",
627 "model": crate::ai::DEFAULT_OPENAI_PROMPT_MODEL
628 }),
629 "max_embedding_inputs": 256,
630 "max_prompt_batch": 256,
631 "timeout": crate::json!({ "connect_secs": 10, "read_secs": 90, "write_secs": 30 })
632 }));
633 store.set_config_tree(
634 "red.server",
635 &crate::json!({
636 "max_scan_limit": 1000,
637 "max_body_size": 1048576,
638 "read_timeout_ms": 5000,
639 "write_timeout_ms": 5000
640 }),
641 );
642 store.set_config_tree(
643 "red.storage",
644 &crate::json!({
645 "page_size": 4096,
646 "page_cache_capacity": 100000,
647 "auto_checkpoint_pages": 1000,
648 "snapshot_retention": 16,
649 "verify_checksums": true,
650 "segment": crate::json!({
651 "max_entities": 100000,
652 "max_bytes": 268435456_u64,
653 "compression_level": 6
654 }),
655 "hnsw": crate::json!({ "m": 16, "ef_construction": 100, "ef_search": 50 }),
656 "ivf": crate::json!({ "n_lists": 100, "n_probes": 10 }),
657 "bm25": crate::json!({ "k1": 1.2, "b": 0.75 })
658 }),
659 );
660 store.set_config_tree(
661 "red.search",
662 &crate::json!({
663 "rag": crate::json!({
664 "max_chunks_per_source": 10,
665 "max_total_chunks": 25,
666 "similarity_threshold": 0.8,
667 "graph_depth": 2,
668 "min_relevance": 0.3
669 }),
670 "fusion": crate::json!({
671 "vector_weight": 0.5,
672 "graph_weight": 0.3,
673 "table_weight": 0.2,
674 "dedup_threshold": 0.85
675 })
676 }),
677 );
678 store.set_config_tree(
679 "red.auth",
680 &crate::json!({
681 "enabled": false,
682 "session_ttl_secs": 3600,
683 "require_auth": false
684 }),
685 );
686 store.set_config_tree(
687 "red.query",
688 &crate::json!({
689 "connection_pool": crate::json!({ "max_connections": 64, "max_idle": 16 }),
690 "max_recursion_depth": 1000
691 }),
692 );
693 store.set_config_tree(
694 "red.indexes",
695 &crate::json!({
696 "auto_select": true,
697 "bloom_filter": crate::json!({
698 "enabled": true,
699 "false_positive_rate": 0.01,
700 "prune_on_scan": true
701 }),
702 "hash": crate::json!({ "enabled": true }),
703 "bitmap": crate::json!({ "enabled": true, "max_cardinality": 1000 }),
704 "spatial": crate::json!({ "enabled": true })
705 }),
706 );
707 store.set_config_tree(
708 "red.probabilistic",
709 &crate::json!({
710 "hll_registers": 16384,
711 "sketch_default_width": 1000,
712 "sketch_default_depth": 5,
713 "filter_default_capacity": 100000
714 }),
715 );
716 store.set_config_tree(
717 "red.timeseries",
718 &crate::json!({
719 "default_chunk_size": 1024,
720 "compression": crate::json!({
721 "timestamps": "delta_of_delta",
722 "values": "gorilla_xor"
723 }),
724 "default_retention_days": 0
725 }),
726 );
727 store.set_config_tree(
728 "red.queue",
729 &crate::json!({
730 "default_max_size": 0,
731 "default_max_attempts": 3,
732 "visibility_timeout_ms": 30000,
733 "consumer_idle_timeout_ms": 60000
734 }),
735 );
736 store.set_config_tree(
737 "red.backup",
738 &crate::json!({
739 "enabled": false,
740 "interval_secs": 3600,
741 "retention_count": 24,
742 "upload": false,
743 "backend": "local"
744 }),
745 );
746 store.set_config_tree(
747 "red.wal",
748 &crate::json!({
749 "archive": crate::json!({
750 "enabled": false,
751 "retention_hours": 168,
752 "prefix": reddb_file::backup_wal_prefix("")
753 })
754 }),
755 );
756 store.set_config_tree(
757 "red.cdc",
758 &crate::json!({
759 "enabled": true,
760 "buffer_size": 100000
761 }),
762 );
763 store.set_config_tree(
764 "red.config.secret",
765 &crate::json!({
766 "auto_encrypt": true,
767 "auto_decrypt": true
768 }),
769 );
770 }
771
772 crate::runtime::config_matrix::heal_critical_keys(store.as_ref());
779 seed_storage_deploy_config(store.as_ref(), options.storage_profile);
780
781 let lehman_yao = runtime.config_bool("storage.btree.lehman_yao", true);
788 crate::storage::engine::btree::lehman_yao::set_enabled(lehman_yao);
789 if lehman_yao {
790 tracing::info!(
791 "storage.btree.lehman_yao=true — lock-free concurrent descent enabled"
792 );
793 }
794
795 let overlay_path = crate::runtime::config_overlay::config_file_path();
800 let _ =
801 crate::runtime::config_overlay::apply_config_file(store.as_ref(), &overlay_path);
802 }
803
804 {
808 let store = runtime.inner.db.store();
809 for name in crate::application::vcs_collections::ALL {
810 let _ = store.get_or_create_collection(*name);
811 }
812 store.set_config_tree(
815 crate::application::vcs_collections::CONFIG_NAMESPACE,
816 &crate::json!({
817 "default_branch": "main",
818 "author": crate::json!({
819 "name": "reddb",
820 "email": "reddb@localhost"
821 }),
822 "protected_branches": crate::json!(["main"]),
823 "closure": crate::json!({
824 "enabled": true,
825 "lazy": true
826 }),
827 "merge": crate::json!({
828 "default_strategy": "auto",
829 "fast_forward": true
830 })
831 }),
832 );
833 }
834
835 {
838 let store = runtime.inner.db.store();
839 for name in crate::application::migration_collections::ALL {
840 let _ = store.get_or_create_collection(*name);
841 }
842 }
843
844 let _ = crate::application::topology_collections::ensure(&runtime);
848
849 runtime
857 .inner
858 .db
859 .store()
860 .register_entity_id(crate::storage::EntityId::new(
861 crate::storage::FIRST_USER_ENTITY_ID - 1,
862 ));
863
864 {
879 let weak = Arc::downgrade(&runtime.inner);
880 std::thread::Builder::new()
881 .name("reddb-maintenance".into())
882 .spawn(move || {
883 let tick = std::time::Duration::from_millis(200);
884 let work_interval = std::time::Duration::from_secs(60);
885 let mut last_work = std::time::Instant::now();
886 loop {
887 std::thread::sleep(tick);
888 let Some(inner) = weak.upgrade() else {
889 break;
892 };
893 if last_work.elapsed() >= work_interval {
894 let _stats = inner.db.store().context_index().stats();
895 last_work = std::time::Instant::now();
896 }
897 }
898 })
899 .ok();
900 }
901
902 {
904 let store = runtime.inner.db.store();
905 let mut backup_enabled = false;
906 let mut backup_interval = 3600u64;
907
908 if let Some(manager) = store.get_collection("red_config") {
909 manager.for_each_entity(|entity| {
910 if let Some(row) = entity.data.as_row() {
911 let key = row.get_field("key").and_then(|v| match v {
912 crate::storage::schema::Value::Text(s) => Some(s.as_ref()),
913 _ => None,
914 });
915 let val = row.get_field("value");
916 if key == Some("red.config.backup.enabled") {
917 backup_enabled = match val {
918 Some(crate::storage::schema::Value::Boolean(true)) => true,
919 Some(crate::storage::schema::Value::Text(s)) => &**s == "true",
920 _ => false,
921 };
922 } else if key == Some("red.config.backup.interval_secs") {
923 if let Some(crate::storage::schema::Value::Integer(n)) = val {
924 backup_interval = *n as u64;
925 }
926 }
927 }
928 true
929 });
930 }
931
932 if backup_enabled {
933 runtime.inner.backup_scheduler.set_interval(backup_interval);
934 let rt = runtime.clone();
935 runtime
936 .inner
937 .backup_scheduler
938 .start(move || rt.trigger_backup().map_err(|e| format!("{}", e)));
939 }
940 }
941
942 {
944 runtime
945 .inner
946 .ec_registry
947 .load_from_config_store(runtime.inner.db.store().as_ref());
948 if !runtime.inner.ec_registry.async_configs().is_empty() {
949 runtime.inner.ec_worker.start(
950 Arc::clone(&runtime.inner.ec_registry),
951 Arc::clone(&runtime.inner.db.store()),
952 );
953 }
954 }
955
956 if let crate::replication::ReplicationRole::Replica { primary_addr } =
957 runtime.inner.db.options().replication.role.clone()
958 {
959 let rt = runtime.clone();
960 std::thread::Builder::new()
961 .name("reddb-replica".into())
962 .spawn(move || rt.run_replica_loop(primary_addr))
963 .ok();
964 }
965
966 runtime.inner.lifecycle.mark_ready();
971
972 {
981 let weak_inner = Arc::downgrade(&runtime.inner);
982 std::thread::Builder::new()
983 .name("reddb-mv-scheduler".into())
984 .stack_size(8 * 1024 * 1024)
989 .spawn(move || loop {
990 std::thread::sleep(std::time::Duration::from_millis(50));
991 let Some(inner) = weak_inner.upgrade() else {
992 break;
993 };
994 let rt = RedDBRuntime { inner };
995 rt.refresh_due_materialized_views();
996 })
997 .ok();
998 }
999
1000 if !runtime.write_gate().is_read_only() {
1010 let weak_inner = Arc::downgrade(&runtime.inner);
1011 std::thread::Builder::new()
1012 .name("reddb-retention-sweeper".into())
1013 .spawn(move || loop {
1014 std::thread::sleep(std::time::Duration::from_millis(500));
1015 let Some(inner) = weak_inner.upgrade() else {
1016 break;
1017 };
1018 let rt = RedDBRuntime { inner };
1019 rt.sweep_retention_tick(
1020 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
1021 );
1022 })
1023 .ok();
1024 }
1025
1026 Ok(runtime)
1027 }
1028
1029 fn rehydrate_snapshot_xid_floor(&self) {
1030 let store = self.inner.db.store();
1031 for collection in store.list_collections() {
1032 let Some(manager) = store.get_collection(&collection) else {
1033 continue;
1034 };
1035 for entity in manager.query_all(|_| true) {
1036 self.inner
1037 .snapshot_manager
1038 .observe_committed_xid(entity.xmin);
1039 self.inner
1040 .snapshot_manager
1041 .observe_committed_xid(entity.xmax);
1042 }
1043 }
1044 }
1045
1046 pub(crate) fn ensure_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1059 let store = self.inner.db.store();
1060 let mut changed = false;
1061 if store.get_collection(name).is_none() {
1062 store.get_or_create_collection(name);
1063 changed = true;
1064 }
1065 if self.inner.db.collection_contract(name).is_none() {
1066 self.inner
1067 .db
1068 .save_collection_contract(system_keyed_collection_contract(
1069 name,
1070 crate::catalog::CollectionModel::Table,
1071 ))
1072 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1073 changed = true;
1074 }
1075 if changed {
1076 self.inner
1077 .db
1078 .persist_metadata()
1079 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1080 }
1081 Ok(())
1082 }
1083
1084 pub(crate) fn drop_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1089 let store = self.inner.db.store();
1090 if store.get_collection(name).is_none() {
1091 return Ok(());
1092 }
1093 store
1094 .drop_collection(name)
1095 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1096 if self.inner.db.collection_contract(name).is_some() {
1099 self.inner
1100 .db
1101 .remove_collection_contract(name)
1102 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1103 }
1104 self.invalidate_result_cache();
1105 self.inner
1106 .db
1107 .persist_metadata()
1108 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1109 Ok(())
1110 }
1111
1112 fn bootstrap_system_keyed_collections(&self) -> RedDBResult<()> {
1113 let mut changed = false;
1114 for (name, model) in [
1115 ("red.config", crate::catalog::CollectionModel::Config),
1116 ("red.vault", crate::catalog::CollectionModel::Vault),
1117 (
1121 crate::runtime::continuous_materialized_view::CATALOG_COLLECTION,
1122 crate::catalog::CollectionModel::Config,
1123 ),
1124 ] {
1125 if self.inner.db.store().get_collection(name).is_none() {
1126 self.inner.db.store().get_or_create_collection(name);
1127 changed = true;
1128 }
1129 if self.inner.db.collection_contract(name).is_none() {
1130 self.inner
1131 .db
1132 .save_collection_contract(system_keyed_collection_contract(name, model))
1133 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1134 changed = true;
1135 }
1136 }
1137 if changed {
1138 self.inner
1139 .db
1140 .persist_metadata()
1141 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1142 }
1143 Ok(())
1144 }
1145
1146 pub fn db(&self) -> Arc<RedDB> {
1147 Arc::clone(&self.inner.db)
1148 }
1149
1150 pub fn index_store_ref(&self) -> &super::index_store::IndexStore {
1155 &self.inner.index_store
1156 }
1157
1158 pub(crate) fn schema_vocabulary_apply(
1163 &self,
1164 event: crate::runtime::schema_vocabulary::DdlEvent,
1165 ) {
1166 self.inner.schema_vocabulary.write().on_ddl(event);
1167 }
1168
1169 pub fn schema_vocabulary_lookup(
1174 &self,
1175 token: &str,
1176 ) -> Vec<crate::runtime::schema_vocabulary::VocabHit> {
1177 self.inner.schema_vocabulary.read().lookup(token).to_vec()
1178 }
1179
1180 pub fn set_auth_store(&self, store: Arc<crate::auth::store::AuthStore>) {
1184 *self.inner.auth_store.write() = Some(store);
1185 }
1186
1187 pub fn auth_store(&self) -> Option<Arc<crate::auth::store::AuthStore>> {
1190 self.inner.auth_store.read().clone()
1191 }
1192
1193 pub fn set_oauth_validator(&self, validator: Option<Arc<crate::auth::oauth::OAuthValidator>>) {
1197 *self.inner.oauth_validator.write() = validator;
1198 }
1199
1200 pub fn oauth_validator(&self) -> Option<Arc<crate::auth::oauth::OAuthValidator>> {
1204 self.inner.oauth_validator.read().clone()
1205 }
1206
1207 pub fn set_browser_token_authority(
1213 &self,
1214 authority: Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>,
1215 ) {
1216 *self.inner.browser_token_authority.write() = authority;
1217 }
1218
1219 pub fn browser_token_authority(
1223 &self,
1224 ) -> Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>> {
1225 self.inner.browser_token_authority.read().clone()
1226 }
1227}