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 with_pool(
174 options: RedDBOptions,
175 pool_config: ConnectionPoolConfig,
176 ) -> RedDBResult<Self> {
177 let boot_open_start_ms = std::time::SystemTime::now()
185 .duration_since(std::time::UNIX_EPOCH)
186 .map(|d| d.as_millis() as u64)
187 .unwrap_or(0);
188 let memory_budget = crate::storage::memory_budget::resolve_for_boot(
192 options.storage_profile.deploy_profile,
193 options.memory_budget_bytes,
194 )
195 .map_err(|err| RedDBError::InvalidConfig(err.to_string()))?;
196 crate::storage::memory_budget::log_resolved_once(&memory_budget);
197 let embedded_single_file = options.storage_profile.deploy_profile
198 == crate::storage::DeployProfile::Embedded
199 && options.storage_profile.packaging == crate::storage::StoragePackaging::SingleFile;
200 let db = Arc::new(RedDB::open_with_options(&options).map_err(|err| {
201 match err.downcast::<crate::storage::StoreError>() {
202 Ok(store_err) => RedDBError::from(*store_err),
203 Err(err) => RedDBError::Internal(err.to_string()),
204 }
205 })?);
206 let result_blob_cache_config = if embedded_single_file {
207 crate::storage::cache::BlobCacheConfig::default()
208 } else {
209 crate::storage::cache::BlobCacheConfig::default().with_l2_path(
210 reddb_file::layout::result_cache_l2_path(
211 &options.resolved_path(reddb_file::default_database_path()),
212 ),
213 )
214 };
215 let result_blob_cache =
216 crate::storage::cache::BlobCache::open_with_l2(result_blob_cache_config).map_err(
217 |err| RedDBError::Internal(format!("open result Blob Cache L2 failed: {err:?}")),
218 )?;
219 let storage_ready_ms = std::time::SystemTime::now()
220 .duration_since(std::time::UNIX_EPOCH)
221 .map(|d| d.as_millis() as u64)
222 .unwrap_or(0);
223
224 let runtime = Self {
225 inner: Arc::new(RuntimeInner {
226 db: db.clone(),
227 layout: PhysicalLayout::from_options(&options),
228 embedded_single_file,
229 memory_budget,
230 indices: IndexCatalog::register_default_vector_graph(
231 options.has_capability(crate::api::Capability::Table),
232 options.has_capability(crate::api::Capability::Graph),
233 ),
234 pool_config,
235 pool: Mutex::new(PoolState::default()),
236 started_at_unix_ms: SystemTime::now()
237 .duration_since(UNIX_EPOCH)
238 .unwrap_or_default()
239 .as_millis(),
240 probabilistic: super::probabilistic_store::ProbabilisticStore::new(),
241 index_store: super::index_store::IndexStore::new(),
242 cdc: crate::replication::cdc::CdcBuffer::new(100_000),
243 checkpoint_projection_stats: super::CheckpointProjectionStats::default(),
244 checkpoint_columnar_emission_budget_chunks: options
245 .checkpoint_columnar_emission_budget_chunks,
246 columnar_projection_size_floor_rows: options.columnar_projection_size_floor_rows,
247 backup_scheduler: crate::replication::scheduler::BackupScheduler::new(3600),
248 query_cache: parking_lot::RwLock::new(
249 crate::storage::query::planner::cache::PlanCache::new(1000),
250 ),
251 result_cache: parking_lot::RwLock::new((
252 HashMap::new(),
253 std::collections::VecDeque::new(),
254 )),
255 result_blob_cache,
256 result_blob_entries: parking_lot::RwLock::new((
257 HashMap::new(),
258 std::collections::VecDeque::new(),
259 )),
260 ask_answer_cache_entries: parking_lot::RwLock::new((
261 HashSet::new(),
262 std::collections::VecDeque::new(),
263 )),
264 result_cache_shadow_divergences: std::sync::atomic::AtomicU64::new(0),
265 result_cache_hits: std::sync::atomic::AtomicU64::new(0),
266 result_cache_misses: std::sync::atomic::AtomicU64::new(0),
267 result_cache_evictions: std::sync::atomic::AtomicU64::new(0),
268 ask_daily_spend: parking_lot::RwLock::new(HashMap::new()),
269 queue_message_locks: parking_lot::RwLock::new(HashMap::new()),
270 rmw_locks: RmwLockTable::new(),
271 planner_dirty_tables: parking_lot::RwLock::new(HashSet::new()),
272 ec_registry: Arc::new(crate::ec::config::EcRegistry::new()),
273 config_registry: Arc::new(crate::auth::registry::ConfigRegistry::new()),
274 ec_worker: crate::ec::worker::EcWorker::new(),
275 auth_store: parking_lot::RwLock::new(None),
276 oauth_validator: parking_lot::RwLock::new(None),
277 browser_token_authority: parking_lot::RwLock::new(None),
278 views: parking_lot::RwLock::new(HashMap::new()),
279 materialized_views: parking_lot::RwLock::new(
280 crate::storage::cache::result::MaterializedViewCache::new(),
281 ),
282 retention_sweeper: parking_lot::RwLock::new(
283 crate::runtime::retention_sweeper::RetentionSweeperState::new(),
284 ),
285 snapshot_manager: Arc::new(
286 crate::storage::transaction::snapshot::SnapshotManager::new(),
287 ),
288 tx_contexts: parking_lot::RwLock::new(HashMap::new()),
289 tx_local_tenants: parking_lot::RwLock::new(HashMap::new()),
290 env_config_overrides: crate::runtime::config_overlay::collect_env_overrides(),
291 lock_manager: Arc::new({
292 let env = crate::runtime::config_overlay::collect_env_overrides();
297 let timeout_ms = env
298 .get("concurrency.locking.deadlock_timeout_ms")
299 .and_then(|raw| raw.parse::<u64>().ok())
300 .unwrap_or_else(|| {
301 match crate::runtime::config_matrix::default_for(
302 "concurrency.locking.deadlock_timeout_ms",
303 ) {
304 Some(crate::serde_json::Value::Number(n)) => n as u64,
305 _ => 5000,
306 }
307 });
308 let cfg = crate::runtime::lock_manager::LockConfig {
309 default_timeout: std::time::Duration::from_millis(timeout_ms),
310 ..Default::default()
311 };
312 crate::runtime::lock_manager::LockManager::new(cfg)
313 }),
314 rls_policies: parking_lot::RwLock::new(HashMap::new()),
315 rls_enabled_tables: parking_lot::RwLock::new(HashSet::new()),
316 foreign_tables: Arc::new(crate::storage::fdw::ForeignTableRegistry::with_builtins()),
317 pending_tombstones: parking_lot::RwLock::new(HashMap::new()),
318 pending_versioned_updates: parking_lot::RwLock::new(HashMap::new()),
319 pending_queue_dedup: parking_lot::RwLock::new(HashMap::new()),
320 pending_kv_watch_events: parking_lot::RwLock::new(HashMap::new()),
321 pending_store_wal_actions: parking_lot::RwLock::new(HashMap::new()),
322 pending_claim_locks: parking_lot::RwLock::new(HashMap::new()),
323 queue_wait_registry: std::sync::Arc::new(
324 crate::runtime::queue_wait_registry::QueueWaitRegistry::new(),
325 ),
326 pending_queue_wakes: parking_lot::RwLock::new(HashMap::new()),
327 tenant_tables: parking_lot::RwLock::new(HashMap::new()),
328 ddl_epoch: std::sync::atomic::AtomicU64::new(0),
329 write_gate: Arc::new(crate::runtime::write_gate::WriteGate::from_options(
330 &options,
331 )),
332 lifecycle: crate::runtime::lifecycle::Lifecycle::new(),
333 resource_limits: crate::runtime::resource_limits::ResourceLimits::from_env(),
334 audit_log: {
335 let data_path = if embedded_single_file {
351 std::env::temp_dir()
352 .join("reddb-embedded-runtime")
353 .join(format!("audit-{}", std::process::id()))
354 } else {
355 options
356 .data_path
357 .clone()
358 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
359 };
360 let (audit_dest, _) = crate::api::tier_wiring::current_log_destinations();
361 if !matches!(audit_dest, crate::storage::layout::LogDestination::File(_))
362 && (embedded_single_file
363 || options
364 .metadata
365 .contains_key(crate::api::EPHEMERAL_RUNTIME_METADATA_KEY))
366 {
367 let audit_path = reddb_file::layout::sibling_path(
377 &data_path,
378 &reddb_file::layout::sidecar_file_name(&data_path, "audit.log"),
379 );
380 Arc::new(crate::runtime::audit_log::AuditLogger::with_path(
381 audit_path,
382 ))
383 } else {
384 Arc::new(crate::runtime::audit_log::AuditLogger::for_destination(
385 &audit_dest,
386 &data_path,
387 ))
388 }
389 },
390 control_event_ledger: parking_lot::RwLock::new(Arc::new(
391 crate::runtime::control_events::RuntimeLedger::new(db.store()),
392 )),
393 control_event_config: options.control_events,
394 query_audit: Arc::new(crate::runtime::query_audit::QueryAuditStream::new(
395 db.store(),
396 options.query_audit.clone(),
397 )),
398 lease_lifecycle: std::sync::OnceLock::new(),
399 replica_apply_metrics: std::sync::Arc::new(
400 crate::replication::logical::ReplicaApplyMetrics::default(),
401 ),
402 replica_link_metrics: std::sync::Arc::new(
403 crate::replication::reconnect::ReplicaLinkMetrics::default(),
404 ),
405 quota_bucket: crate::runtime::quota_bucket::QuotaBucket::from_env(),
406 schema_vocabulary: parking_lot::RwLock::new(
407 crate::runtime::schema_vocabulary::SchemaVocabulary::new(),
408 ),
409 slow_query_logger: {
410 let fallback_dir = if embedded_single_file {
427 std::env::temp_dir()
428 .join("reddb-embedded-runtime")
429 .join(format!("slow-{}", std::process::id()))
430 } else {
431 options
432 .data_path
433 .as_ref()
434 .and_then(|p| p.parent().map(std::path::PathBuf::from))
435 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
436 };
437 let threshold_ms = std::env::var("RED_SLOW_QUERY_THRESHOLD_MS")
438 .ok()
439 .and_then(|s| s.parse::<u64>().ok())
440 .unwrap_or(1000);
441 let sample_pct = std::env::var("RED_SLOW_QUERY_SAMPLE_PCT")
442 .ok()
443 .and_then(|s| s.parse::<u8>().ok())
444 .unwrap_or(100);
445 let (_, slow_dest) = crate::api::tier_wiring::current_log_destinations();
446 crate::telemetry::slow_query_logger::SlowQueryLogger::for_destination(
447 &slow_dest,
448 &fallback_dir,
449 threshold_ms,
450 sample_pct,
451 )
452 },
453 slow_query_store: crate::telemetry::slow_query_store::SlowQueryStore::new(
454 crate::telemetry::slow_query_store::DEFAULT_CAP,
455 ),
456 kv_stats: crate::runtime::KvStatsCounters::default(),
457 metrics_ingest_stats: crate::runtime::MetricsIngestCounters::default(),
458 metrics_tenant_activity_stats:
459 crate::runtime::MetricsTenantActivityCounters::default(),
460 claim_telemetry: Arc::new(
461 crate::runtime::claim_telemetry::ClaimTelemetryCounters::default(),
462 ),
463 queue_telemetry: Arc::new(
464 crate::runtime::queue_telemetry::QueueTelemetryCounters::default(),
465 ),
466 query_latency_telemetry: Arc::new(
467 crate::runtime::query_latency_telemetry::QueryLatencyTelemetry::default(),
468 ),
469 occupancy_sampler: Arc::new(
470 crate::runtime::occupancy_sampler::OccupancySampler::new(),
471 ),
472 node_load_telemetry: Arc::new(
473 crate::runtime::node_load_telemetry::NodeLoadTelemetry::default(),
474 ),
475 queue_presence: Arc::new(
476 crate::storage::queue::presence::ConsumerPresenceRegistry::new(),
477 ),
478 vector_introspection: Arc::new(
479 crate::storage::vector::introspection::VectorIntrospectionRegistry::new(),
480 ),
481 kv_tag_index: crate::runtime::KvTagIndex::default(),
482 chain_tip_cache: parking_lot::Mutex::new(HashMap::new()),
483 chain_integrity_broken: parking_lot::Mutex::new(HashMap::new()),
484 integrity_tombstones: parking_lot::Mutex::new(Vec::new()),
485 integrity_tombstones_state: std::sync::atomic::AtomicU8::new(0),
486 }),
487 };
488
489 crate::telemetry::operator_event::install_global_audit_sink(Arc::clone(
495 &runtime.inner.audit_log,
496 ));
497
498 runtime
501 .inner
502 .slow_query_logger
503 .attach_store(Arc::clone(&runtime.inner.slow_query_store));
504
505 runtime
513 .inner
514 .lifecycle
515 .set_restore_started_at_ms(boot_open_start_ms);
516 runtime
517 .inner
518 .lifecycle
519 .set_restore_ready_at_ms(storage_ready_ms);
520 runtime
521 .inner
522 .lifecycle
523 .set_wal_replay_started_at_ms(boot_open_start_ms);
524 runtime
525 .inner
526 .lifecycle
527 .set_wal_replay_ready_at_ms(storage_ready_ms);
528
529 let restored_cdc_lsn = runtime
530 .inner
531 .db
532 .replication
533 .as_ref()
534 .map(|repl| {
535 repl.logical_wal_spool
536 .as_ref()
537 .map(|spool| spool.current_lsn())
538 .unwrap_or(0)
539 })
540 .unwrap_or(0)
541 .max(runtime.config_u64("red.config.timeline.last_archived_lsn", 0));
542 runtime.inner.cdc.set_current_lsn(restored_cdc_lsn);
543 runtime.rehydrate_snapshot_xid_floor();
544 runtime
545 .bootstrap_system_keyed_collections()
546 .map_err(|err| RedDBError::Internal(format!("bootstrap system collections: {err}")))?;
547 runtime.rehydrate_declared_column_schemas();
548 runtime.rehydrate_runtime_index_registry()?;
549 runtime
550 .load_probabilistic_state()
551 .map_err(|err| RedDBError::Internal(format!("load probabilistic state: {err}")))?;
552
553 runtime.rehydrate_tenant_tables();
557 runtime.rehydrate_materialized_view_descriptors();
562 if let Some(repl) = &runtime.inner.db.replication {
563 repl.wal_buffer.set_current_lsn(restored_cdc_lsn);
564 }
565
566 {
568 let sys = SystemInfo::collect();
569 runtime.inner.db.store().set_config_tree(
570 "red.system",
571 &crate::serde_json::json!({
572 "pid": sys.pid,
573 "cpu_cores": sys.cpu_cores,
574 "total_memory_bytes": sys.total_memory_bytes,
575 "available_memory_bytes": sys.available_memory_bytes,
576 "os": sys.os,
577 "arch": sys.arch,
578 "hostname": sys.hostname,
579 "started_at": SystemTime::now()
580 .duration_since(UNIX_EPOCH)
581 .unwrap_or_default()
582 .as_millis() as u64
583 }),
584 );
585
586 let store = runtime.inner.db.store();
588 if store
589 .get_collection("red_config")
590 .map(|m| m.query_all(|_| true).len())
591 .unwrap_or(0)
592 <= 10
593 {
594 store.set_config_tree("red.ai", &crate::json!({
595 "default": crate::json!({
596 "provider": "openai",
597 "model": crate::ai::DEFAULT_OPENAI_PROMPT_MODEL
598 }),
599 "max_embedding_inputs": 256,
600 "max_prompt_batch": 256,
601 "timeout": crate::json!({ "connect_secs": 10, "read_secs": 90, "write_secs": 30 })
602 }));
603 store.set_config_tree(
604 "red.server",
605 &crate::json!({
606 "max_scan_limit": 1000,
607 "max_body_size": 1048576,
608 "read_timeout_ms": 5000,
609 "write_timeout_ms": 5000
610 }),
611 );
612 store.set_config_tree(
613 "red.storage",
614 &crate::json!({
615 "page_size": 4096,
616 "page_cache_capacity": 100000,
617 "auto_checkpoint_pages": 1000,
618 "snapshot_retention": 16,
619 "verify_checksums": true,
620 "segment": crate::json!({
621 "max_entities": 100000,
622 "max_bytes": 268435456_u64,
623 "compression_level": 6
624 }),
625 "hnsw": crate::json!({ "m": 16, "ef_construction": 100, "ef_search": 50 }),
626 "ivf": crate::json!({ "n_lists": 100, "n_probes": 10 }),
627 "bm25": crate::json!({ "k1": 1.2, "b": 0.75 })
628 }),
629 );
630 store.set_config_tree(
631 "red.search",
632 &crate::json!({
633 "rag": crate::json!({
634 "max_chunks_per_source": 10,
635 "max_total_chunks": 25,
636 "similarity_threshold": 0.8,
637 "graph_depth": 2,
638 "min_relevance": 0.3
639 }),
640 "fusion": crate::json!({
641 "vector_weight": 0.5,
642 "graph_weight": 0.3,
643 "table_weight": 0.2,
644 "dedup_threshold": 0.85
645 })
646 }),
647 );
648 store.set_config_tree(
649 "red.auth",
650 &crate::json!({
651 "enabled": false,
652 "session_ttl_secs": 3600,
653 "require_auth": false
654 }),
655 );
656 store.set_config_tree(
657 "red.query",
658 &crate::json!({
659 "connection_pool": crate::json!({ "max_connections": 64, "max_idle": 16 }),
660 "max_recursion_depth": 1000
661 }),
662 );
663 store.set_config_tree(
664 "red.indexes",
665 &crate::json!({
666 "auto_select": true,
667 "bloom_filter": crate::json!({
668 "enabled": true,
669 "false_positive_rate": 0.01,
670 "prune_on_scan": true
671 }),
672 "hash": crate::json!({ "enabled": true }),
673 "bitmap": crate::json!({ "enabled": true, "max_cardinality": 1000 }),
674 "spatial": crate::json!({ "enabled": true })
675 }),
676 );
677 store.set_config_tree(
678 "red.probabilistic",
679 &crate::json!({
680 "hll_registers": 16384,
681 "sketch_default_width": 1000,
682 "sketch_default_depth": 5,
683 "filter_default_capacity": 100000
684 }),
685 );
686 store.set_config_tree(
687 "red.timeseries",
688 &crate::json!({
689 "default_chunk_size": 1024,
690 "compression": crate::json!({
691 "timestamps": "delta_of_delta",
692 "values": "gorilla_xor"
693 }),
694 "default_retention_days": 0
695 }),
696 );
697 store.set_config_tree(
698 "red.queue",
699 &crate::json!({
700 "default_max_size": 0,
701 "default_max_attempts": 3,
702 "visibility_timeout_ms": 30000,
703 "consumer_idle_timeout_ms": 60000
704 }),
705 );
706 store.set_config_tree(
707 "red.backup",
708 &crate::json!({
709 "enabled": false,
710 "interval_secs": 3600,
711 "retention_count": 24,
712 "upload": false,
713 "backend": "local"
714 }),
715 );
716 store.set_config_tree(
717 "red.wal",
718 &crate::json!({
719 "archive": crate::json!({
720 "enabled": false,
721 "retention_hours": 168,
722 "prefix": reddb_file::backup_wal_prefix("")
723 })
724 }),
725 );
726 store.set_config_tree(
727 "red.cdc",
728 &crate::json!({
729 "enabled": true,
730 "buffer_size": 100000
731 }),
732 );
733 store.set_config_tree(
734 "red.config.secret",
735 &crate::json!({
736 "auto_encrypt": true,
737 "auto_decrypt": true
738 }),
739 );
740 }
741
742 crate::runtime::config_matrix::heal_critical_keys(store.as_ref());
749 seed_storage_deploy_config(store.as_ref(), options.storage_profile);
750
751 let lehman_yao = runtime.config_bool("storage.btree.lehman_yao", true);
758 crate::storage::engine::btree::lehman_yao::set_enabled(lehman_yao);
759 if lehman_yao {
760 tracing::info!(
761 "storage.btree.lehman_yao=true — lock-free concurrent descent enabled"
762 );
763 }
764
765 let overlay_path = crate::runtime::config_overlay::config_file_path();
770 let _ =
771 crate::runtime::config_overlay::apply_config_file(store.as_ref(), &overlay_path);
772 }
773
774 {
778 let store = runtime.inner.db.store();
779 for name in crate::application::vcs_collections::ALL {
780 let _ = store.get_or_create_collection(*name);
781 }
782 store.set_config_tree(
785 crate::application::vcs_collections::CONFIG_NAMESPACE,
786 &crate::json!({
787 "default_branch": "main",
788 "author": crate::json!({
789 "name": "reddb",
790 "email": "reddb@localhost"
791 }),
792 "protected_branches": crate::json!(["main"]),
793 "closure": crate::json!({
794 "enabled": true,
795 "lazy": true
796 }),
797 "merge": crate::json!({
798 "default_strategy": "auto",
799 "fast_forward": true
800 })
801 }),
802 );
803 }
804
805 {
808 let store = runtime.inner.db.store();
809 for name in crate::application::migration_collections::ALL {
810 let _ = store.get_or_create_collection(*name);
811 }
812 }
813
814 let _ = crate::application::topology_collections::ensure(&runtime);
818
819 runtime
827 .inner
828 .db
829 .store()
830 .register_entity_id(crate::storage::EntityId::new(
831 crate::storage::FIRST_USER_ENTITY_ID - 1,
832 ));
833
834 {
849 let weak = Arc::downgrade(&runtime.inner);
850 std::thread::Builder::new()
851 .name("reddb-maintenance".into())
852 .spawn(move || {
853 let tick = std::time::Duration::from_millis(200);
854 let work_interval = std::time::Duration::from_secs(60);
855 let mut last_work = std::time::Instant::now();
856 loop {
857 std::thread::sleep(tick);
858 let Some(inner) = weak.upgrade() else {
859 break;
862 };
863 if last_work.elapsed() >= work_interval {
864 let _stats = inner.db.store().context_index().stats();
865 last_work = std::time::Instant::now();
866 }
867 }
868 })
869 .ok();
870 }
871
872 {
874 let store = runtime.inner.db.store();
875 let mut backup_enabled = false;
876 let mut backup_interval = 3600u64;
877
878 if let Some(manager) = store.get_collection("red_config") {
879 manager.for_each_entity(|entity| {
880 if let Some(row) = entity.data.as_row() {
881 let key = row.get_field("key").and_then(|v| match v {
882 crate::storage::schema::Value::Text(s) => Some(s.as_ref()),
883 _ => None,
884 });
885 let val = row.get_field("value");
886 if key == Some("red.config.backup.enabled") {
887 backup_enabled = match val {
888 Some(crate::storage::schema::Value::Boolean(true)) => true,
889 Some(crate::storage::schema::Value::Text(s)) => &**s == "true",
890 _ => false,
891 };
892 } else if key == Some("red.config.backup.interval_secs") {
893 if let Some(crate::storage::schema::Value::Integer(n)) = val {
894 backup_interval = *n as u64;
895 }
896 }
897 }
898 true
899 });
900 }
901
902 if backup_enabled {
903 runtime.inner.backup_scheduler.set_interval(backup_interval);
904 let rt = runtime.clone();
905 runtime
906 .inner
907 .backup_scheduler
908 .start(move || rt.trigger_backup().map_err(|e| format!("{}", e)));
909 }
910 }
911
912 {
914 runtime
915 .inner
916 .ec_registry
917 .load_from_config_store(runtime.inner.db.store().as_ref());
918 if !runtime.inner.ec_registry.async_configs().is_empty() {
919 runtime.inner.ec_worker.start(
920 Arc::clone(&runtime.inner.ec_registry),
921 Arc::clone(&runtime.inner.db.store()),
922 );
923 }
924 }
925
926 if let crate::replication::ReplicationRole::Replica { primary_addr } =
927 runtime.inner.db.options().replication.role.clone()
928 {
929 let rt = runtime.clone();
930 std::thread::Builder::new()
931 .name("reddb-replica".into())
932 .spawn(move || rt.run_replica_loop(primary_addr))
933 .ok();
934 }
935
936 runtime.inner.lifecycle.mark_ready();
941
942 {
951 let weak_inner = Arc::downgrade(&runtime.inner);
952 std::thread::Builder::new()
953 .name("reddb-mv-scheduler".into())
954 .stack_size(8 * 1024 * 1024)
959 .spawn(move || loop {
960 std::thread::sleep(std::time::Duration::from_millis(50));
961 let Some(inner) = weak_inner.upgrade() else {
962 break;
963 };
964 let rt = RedDBRuntime { inner };
965 rt.refresh_due_materialized_views();
966 })
967 .ok();
968 }
969
970 if !runtime.write_gate().is_read_only() {
980 let weak_inner = Arc::downgrade(&runtime.inner);
981 std::thread::Builder::new()
982 .name("reddb-retention-sweeper".into())
983 .spawn(move || loop {
984 std::thread::sleep(std::time::Duration::from_millis(500));
985 let Some(inner) = weak_inner.upgrade() else {
986 break;
987 };
988 let rt = RedDBRuntime { inner };
989 rt.sweep_retention_tick(
990 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
991 );
992 })
993 .ok();
994 }
995
996 Ok(runtime)
997 }
998
999 fn rehydrate_snapshot_xid_floor(&self) {
1000 let store = self.inner.db.store();
1001 for collection in store.list_collections() {
1002 let Some(manager) = store.get_collection(&collection) else {
1003 continue;
1004 };
1005 for entity in manager.query_all(|_| true) {
1006 self.inner
1007 .snapshot_manager
1008 .observe_committed_xid(entity.xmin);
1009 self.inner
1010 .snapshot_manager
1011 .observe_committed_xid(entity.xmax);
1012 }
1013 }
1014 }
1015
1016 pub(crate) fn ensure_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1029 let store = self.inner.db.store();
1030 let mut changed = false;
1031 if store.get_collection(name).is_none() {
1032 store.get_or_create_collection(name);
1033 changed = true;
1034 }
1035 if self.inner.db.collection_contract(name).is_none() {
1036 self.inner
1037 .db
1038 .save_collection_contract(system_keyed_collection_contract(
1039 name,
1040 crate::catalog::CollectionModel::Table,
1041 ))
1042 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1043 changed = true;
1044 }
1045 if changed {
1046 self.inner
1047 .db
1048 .persist_metadata()
1049 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1050 }
1051 Ok(())
1052 }
1053
1054 pub(crate) fn drop_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1059 let store = self.inner.db.store();
1060 if store.get_collection(name).is_none() {
1061 return Ok(());
1062 }
1063 store
1064 .drop_collection(name)
1065 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1066 if self.inner.db.collection_contract(name).is_some() {
1069 self.inner
1070 .db
1071 .remove_collection_contract(name)
1072 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1073 }
1074 self.invalidate_result_cache();
1075 self.inner
1076 .db
1077 .persist_metadata()
1078 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1079 Ok(())
1080 }
1081
1082 fn bootstrap_system_keyed_collections(&self) -> RedDBResult<()> {
1083 let mut changed = false;
1084 for (name, model) in [
1085 ("red.config", crate::catalog::CollectionModel::Config),
1086 ("red.vault", crate::catalog::CollectionModel::Vault),
1087 (
1091 crate::runtime::continuous_materialized_view::CATALOG_COLLECTION,
1092 crate::catalog::CollectionModel::Config,
1093 ),
1094 ] {
1095 if self.inner.db.store().get_collection(name).is_none() {
1096 self.inner.db.store().get_or_create_collection(name);
1097 changed = true;
1098 }
1099 if self.inner.db.collection_contract(name).is_none() {
1100 self.inner
1101 .db
1102 .save_collection_contract(system_keyed_collection_contract(name, model))
1103 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1104 changed = true;
1105 }
1106 }
1107 if changed {
1108 self.inner
1109 .db
1110 .persist_metadata()
1111 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1112 }
1113 Ok(())
1114 }
1115
1116 pub fn db(&self) -> Arc<RedDB> {
1117 Arc::clone(&self.inner.db)
1118 }
1119
1120 pub fn index_store_ref(&self) -> &super::index_store::IndexStore {
1125 &self.inner.index_store
1126 }
1127
1128 pub(crate) fn schema_vocabulary_apply(
1133 &self,
1134 event: crate::runtime::schema_vocabulary::DdlEvent,
1135 ) {
1136 self.inner.schema_vocabulary.write().on_ddl(event);
1137 }
1138
1139 pub fn schema_vocabulary_lookup(
1144 &self,
1145 token: &str,
1146 ) -> Vec<crate::runtime::schema_vocabulary::VocabHit> {
1147 self.inner.schema_vocabulary.read().lookup(token).to_vec()
1148 }
1149
1150 pub fn set_auth_store(&self, store: Arc<crate::auth::store::AuthStore>) {
1154 *self.inner.auth_store.write() = Some(store);
1155 }
1156
1157 pub fn auth_store(&self) -> Option<Arc<crate::auth::store::AuthStore>> {
1160 self.inner.auth_store.read().clone()
1161 }
1162
1163 pub fn set_oauth_validator(&self, validator: Option<Arc<crate::auth::oauth::OAuthValidator>>) {
1167 *self.inner.oauth_validator.write() = validator;
1168 }
1169
1170 pub fn oauth_validator(&self) -> Option<Arc<crate::auth::oauth::OAuthValidator>> {
1174 self.inner.oauth_validator.read().clone()
1175 }
1176
1177 pub fn set_browser_token_authority(
1183 &self,
1184 authority: Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>,
1185 ) {
1186 *self.inner.browser_token_authority.write() = authority;
1187 }
1188
1189 pub fn browser_token_authority(
1193 &self,
1194 ) -> Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>> {
1195 self.inner.browser_token_authority.read().clone()
1196 }
1197}