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