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 with_pool(
167 options: RedDBOptions,
168 pool_config: ConnectionPoolConfig,
169 ) -> RedDBResult<Self> {
170 let boot_open_start_ms = std::time::SystemTime::now()
178 .duration_since(std::time::UNIX_EPOCH)
179 .map(|d| d.as_millis() as u64)
180 .unwrap_or(0);
181 let embedded_single_file = options.storage_profile.deploy_profile
182 == crate::storage::DeployProfile::Embedded
183 && options.storage_profile.packaging == crate::storage::StoragePackaging::SingleFile;
184 let db = Arc::new(
185 RedDB::open_with_options(&options)
186 .map_err(|err| RedDBError::Internal(err.to_string()))?,
187 );
188 let result_blob_cache_config = if embedded_single_file {
189 crate::storage::cache::BlobCacheConfig::default()
190 } else {
191 crate::storage::cache::BlobCacheConfig::default().with_l2_path(
192 reddb_file::layout::result_cache_l2_path(
193 &options.resolved_path(reddb_file::default_database_path()),
194 ),
195 )
196 };
197 let result_blob_cache =
198 crate::storage::cache::BlobCache::open_with_l2(result_blob_cache_config).map_err(
199 |err| RedDBError::Internal(format!("open result Blob Cache L2 failed: {err:?}")),
200 )?;
201 let storage_ready_ms = std::time::SystemTime::now()
202 .duration_since(std::time::UNIX_EPOCH)
203 .map(|d| d.as_millis() as u64)
204 .unwrap_or(0);
205
206 let runtime = Self {
207 inner: Arc::new(RuntimeInner {
208 db: db.clone(),
209 layout: PhysicalLayout::from_options(&options),
210 embedded_single_file,
211 indices: IndexCatalog::register_default_vector_graph(
212 options.has_capability(crate::api::Capability::Table),
213 options.has_capability(crate::api::Capability::Graph),
214 ),
215 pool_config,
216 pool: Mutex::new(PoolState::default()),
217 started_at_unix_ms: SystemTime::now()
218 .duration_since(UNIX_EPOCH)
219 .unwrap_or_default()
220 .as_millis(),
221 probabilistic: super::probabilistic_store::ProbabilisticStore::new(),
222 index_store: super::index_store::IndexStore::new(),
223 cdc: crate::replication::cdc::CdcBuffer::new(100_000),
224 backup_scheduler: crate::replication::scheduler::BackupScheduler::new(3600),
225 query_cache: parking_lot::RwLock::new(
226 crate::storage::query::planner::cache::PlanCache::new(1000),
227 ),
228 result_cache: parking_lot::RwLock::new((
229 HashMap::new(),
230 std::collections::VecDeque::new(),
231 )),
232 result_blob_cache,
233 result_blob_entries: parking_lot::RwLock::new((
234 HashMap::new(),
235 std::collections::VecDeque::new(),
236 )),
237 ask_answer_cache_entries: parking_lot::RwLock::new((
238 HashSet::new(),
239 std::collections::VecDeque::new(),
240 )),
241 result_cache_shadow_divergences: std::sync::atomic::AtomicU64::new(0),
242 result_cache_hits: std::sync::atomic::AtomicU64::new(0),
243 result_cache_misses: std::sync::atomic::AtomicU64::new(0),
244 result_cache_evictions: std::sync::atomic::AtomicU64::new(0),
245 ask_daily_spend: parking_lot::RwLock::new(HashMap::new()),
246 queue_message_locks: parking_lot::RwLock::new(HashMap::new()),
247 rmw_locks: RmwLockTable::new(),
248 planner_dirty_tables: parking_lot::RwLock::new(HashSet::new()),
249 ec_registry: Arc::new(crate::ec::config::EcRegistry::new()),
250 config_registry: Arc::new(crate::auth::registry::ConfigRegistry::new()),
251 ec_worker: crate::ec::worker::EcWorker::new(),
252 auth_store: parking_lot::RwLock::new(None),
253 oauth_validator: parking_lot::RwLock::new(None),
254 browser_token_authority: parking_lot::RwLock::new(None),
255 views: parking_lot::RwLock::new(HashMap::new()),
256 materialized_views: parking_lot::RwLock::new(
257 crate::storage::cache::result::MaterializedViewCache::new(),
258 ),
259 retention_sweeper: parking_lot::RwLock::new(
260 crate::runtime::retention_sweeper::RetentionSweeperState::new(),
261 ),
262 snapshot_manager: Arc::new(
263 crate::storage::transaction::snapshot::SnapshotManager::new(),
264 ),
265 tx_contexts: parking_lot::RwLock::new(HashMap::new()),
266 tx_local_tenants: parking_lot::RwLock::new(HashMap::new()),
267 env_config_overrides: crate::runtime::config_overlay::collect_env_overrides(),
268 lock_manager: Arc::new({
269 let env = crate::runtime::config_overlay::collect_env_overrides();
274 let timeout_ms = env
275 .get("concurrency.locking.deadlock_timeout_ms")
276 .and_then(|raw| raw.parse::<u64>().ok())
277 .unwrap_or_else(|| {
278 match crate::runtime::config_matrix::default_for(
279 "concurrency.locking.deadlock_timeout_ms",
280 ) {
281 Some(crate::serde_json::Value::Number(n)) => n as u64,
282 _ => 5000,
283 }
284 });
285 let cfg = crate::runtime::lock_manager::LockConfig {
286 default_timeout: std::time::Duration::from_millis(timeout_ms),
287 ..Default::default()
288 };
289 crate::runtime::lock_manager::LockManager::new(cfg)
290 }),
291 rls_policies: parking_lot::RwLock::new(HashMap::new()),
292 rls_enabled_tables: parking_lot::RwLock::new(HashSet::new()),
293 foreign_tables: Arc::new(crate::storage::fdw::ForeignTableRegistry::with_builtins()),
294 pending_tombstones: parking_lot::RwLock::new(HashMap::new()),
295 pending_versioned_updates: parking_lot::RwLock::new(HashMap::new()),
296 pending_kv_watch_events: parking_lot::RwLock::new(HashMap::new()),
297 pending_store_wal_actions: parking_lot::RwLock::new(HashMap::new()),
298 pending_claim_locks: parking_lot::RwLock::new(HashMap::new()),
299 queue_wait_registry: std::sync::Arc::new(
300 crate::runtime::queue_wait_registry::QueueWaitRegistry::new(),
301 ),
302 pending_queue_wakes: parking_lot::RwLock::new(HashMap::new()),
303 tenant_tables: parking_lot::RwLock::new(HashMap::new()),
304 ddl_epoch: std::sync::atomic::AtomicU64::new(0),
305 write_gate: Arc::new(crate::runtime::write_gate::WriteGate::from_options(
306 &options,
307 )),
308 lifecycle: crate::runtime::lifecycle::Lifecycle::new(),
309 resource_limits: crate::runtime::resource_limits::ResourceLimits::from_env(),
310 audit_log: {
311 let data_path = if embedded_single_file {
327 std::env::temp_dir()
328 .join("reddb-embedded-runtime")
329 .join(format!("audit-{}", std::process::id()))
330 } else {
331 options
332 .data_path
333 .clone()
334 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
335 };
336 let (audit_dest, _) = crate::api::tier_wiring::current_log_destinations();
337 if !matches!(audit_dest, crate::storage::layout::LogDestination::File(_))
338 && (embedded_single_file
339 || options
340 .metadata
341 .contains_key(crate::api::EPHEMERAL_RUNTIME_METADATA_KEY))
342 {
343 let audit_path = reddb_file::layout::sibling_path(
353 &data_path,
354 &reddb_file::layout::sidecar_file_name(&data_path, "audit.log"),
355 );
356 Arc::new(crate::runtime::audit_log::AuditLogger::with_path(
357 audit_path,
358 ))
359 } else {
360 Arc::new(crate::runtime::audit_log::AuditLogger::for_destination(
361 &audit_dest,
362 &data_path,
363 ))
364 }
365 },
366 control_event_ledger: parking_lot::RwLock::new(Arc::new(
367 crate::runtime::control_events::RuntimeLedger::new(db.store()),
368 )),
369 control_event_config: options.control_events,
370 query_audit: Arc::new(crate::runtime::query_audit::QueryAuditStream::new(
371 db.store(),
372 options.query_audit.clone(),
373 )),
374 lease_lifecycle: std::sync::OnceLock::new(),
375 replica_apply_metrics: std::sync::Arc::new(
376 crate::replication::logical::ReplicaApplyMetrics::default(),
377 ),
378 replica_link_metrics: std::sync::Arc::new(
379 crate::replication::reconnect::ReplicaLinkMetrics::default(),
380 ),
381 quota_bucket: crate::runtime::quota_bucket::QuotaBucket::from_env(),
382 schema_vocabulary: parking_lot::RwLock::new(
383 crate::runtime::schema_vocabulary::SchemaVocabulary::new(),
384 ),
385 slow_query_logger: {
386 let fallback_dir = if embedded_single_file {
403 std::env::temp_dir()
404 .join("reddb-embedded-runtime")
405 .join(format!("slow-{}", std::process::id()))
406 } else {
407 options
408 .data_path
409 .as_ref()
410 .and_then(|p| p.parent().map(std::path::PathBuf::from))
411 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
412 };
413 let threshold_ms = std::env::var("RED_SLOW_QUERY_THRESHOLD_MS")
414 .ok()
415 .and_then(|s| s.parse::<u64>().ok())
416 .unwrap_or(1000);
417 let sample_pct = std::env::var("RED_SLOW_QUERY_SAMPLE_PCT")
418 .ok()
419 .and_then(|s| s.parse::<u8>().ok())
420 .unwrap_or(100);
421 let (_, slow_dest) = crate::api::tier_wiring::current_log_destinations();
422 crate::telemetry::slow_query_logger::SlowQueryLogger::for_destination(
423 &slow_dest,
424 &fallback_dir,
425 threshold_ms,
426 sample_pct,
427 )
428 },
429 slow_query_store: crate::telemetry::slow_query_store::SlowQueryStore::new(
430 crate::telemetry::slow_query_store::DEFAULT_CAP,
431 ),
432 kv_stats: crate::runtime::KvStatsCounters::default(),
433 metrics_ingest_stats: crate::runtime::MetricsIngestCounters::default(),
434 metrics_tenant_activity_stats:
435 crate::runtime::MetricsTenantActivityCounters::default(),
436 claim_telemetry: Arc::new(
437 crate::runtime::claim_telemetry::ClaimTelemetryCounters::default(),
438 ),
439 queue_telemetry: Arc::new(
440 crate::runtime::queue_telemetry::QueueTelemetryCounters::default(),
441 ),
442 query_latency_telemetry: Arc::new(
443 crate::runtime::query_latency_telemetry::QueryLatencyTelemetry::default(),
444 ),
445 occupancy_sampler: Arc::new(
446 crate::runtime::occupancy_sampler::OccupancySampler::new(),
447 ),
448 node_load_telemetry: Arc::new(
449 crate::runtime::node_load_telemetry::NodeLoadTelemetry::default(),
450 ),
451 queue_presence: Arc::new(
452 crate::storage::queue::presence::ConsumerPresenceRegistry::new(),
453 ),
454 vector_introspection: Arc::new(
455 crate::storage::vector::introspection::VectorIntrospectionRegistry::new(),
456 ),
457 kv_tag_index: crate::runtime::KvTagIndex::default(),
458 chain_tip_cache: parking_lot::Mutex::new(HashMap::new()),
459 chain_integrity_broken: parking_lot::Mutex::new(HashMap::new()),
460 integrity_tombstones: parking_lot::Mutex::new(Vec::new()),
461 integrity_tombstones_state: std::sync::atomic::AtomicU8::new(0),
462 }),
463 };
464
465 crate::telemetry::operator_event::install_global_audit_sink(Arc::clone(
471 &runtime.inner.audit_log,
472 ));
473
474 runtime
477 .inner
478 .slow_query_logger
479 .attach_store(Arc::clone(&runtime.inner.slow_query_store));
480
481 runtime
489 .inner
490 .lifecycle
491 .set_restore_started_at_ms(boot_open_start_ms);
492 runtime
493 .inner
494 .lifecycle
495 .set_restore_ready_at_ms(storage_ready_ms);
496 runtime
497 .inner
498 .lifecycle
499 .set_wal_replay_started_at_ms(boot_open_start_ms);
500 runtime
501 .inner
502 .lifecycle
503 .set_wal_replay_ready_at_ms(storage_ready_ms);
504
505 let restored_cdc_lsn = runtime
506 .inner
507 .db
508 .replication
509 .as_ref()
510 .map(|repl| {
511 repl.logical_wal_spool
512 .as_ref()
513 .map(|spool| spool.current_lsn())
514 .unwrap_or(0)
515 })
516 .unwrap_or(0)
517 .max(runtime.config_u64("red.config.timeline.last_archived_lsn", 0));
518 runtime.inner.cdc.set_current_lsn(restored_cdc_lsn);
519 runtime.rehydrate_snapshot_xid_floor();
520 runtime
521 .bootstrap_system_keyed_collections()
522 .map_err(|err| RedDBError::Internal(format!("bootstrap system collections: {err}")))?;
523 runtime.rehydrate_declared_column_schemas();
524 runtime.rehydrate_runtime_index_registry()?;
525 runtime
526 .load_probabilistic_state()
527 .map_err(|err| RedDBError::Internal(format!("load probabilistic state: {err}")))?;
528
529 runtime.rehydrate_tenant_tables();
533 runtime.rehydrate_materialized_view_descriptors();
538 if let Some(repl) = &runtime.inner.db.replication {
539 repl.wal_buffer.set_current_lsn(restored_cdc_lsn);
540 }
541
542 {
544 let sys = SystemInfo::collect();
545 runtime.inner.db.store().set_config_tree(
546 "red.system",
547 &crate::serde_json::json!({
548 "pid": sys.pid,
549 "cpu_cores": sys.cpu_cores,
550 "total_memory_bytes": sys.total_memory_bytes,
551 "available_memory_bytes": sys.available_memory_bytes,
552 "os": sys.os,
553 "arch": sys.arch,
554 "hostname": sys.hostname,
555 "started_at": SystemTime::now()
556 .duration_since(UNIX_EPOCH)
557 .unwrap_or_default()
558 .as_millis() as u64
559 }),
560 );
561
562 let store = runtime.inner.db.store();
564 if store
565 .get_collection("red_config")
566 .map(|m| m.query_all(|_| true).len())
567 .unwrap_or(0)
568 <= 10
569 {
570 store.set_config_tree("red.ai", &crate::json!({
571 "default": crate::json!({
572 "provider": "openai",
573 "model": crate::ai::DEFAULT_OPENAI_PROMPT_MODEL
574 }),
575 "max_embedding_inputs": 256,
576 "max_prompt_batch": 256,
577 "timeout": crate::json!({ "connect_secs": 10, "read_secs": 90, "write_secs": 30 })
578 }));
579 store.set_config_tree(
580 "red.server",
581 &crate::json!({
582 "max_scan_limit": 1000,
583 "max_body_size": 1048576,
584 "read_timeout_ms": 5000,
585 "write_timeout_ms": 5000
586 }),
587 );
588 store.set_config_tree(
589 "red.storage",
590 &crate::json!({
591 "page_size": 4096,
592 "page_cache_capacity": 100000,
593 "auto_checkpoint_pages": 1000,
594 "snapshot_retention": 16,
595 "verify_checksums": true,
596 "segment": crate::json!({
597 "max_entities": 100000,
598 "max_bytes": 268435456_u64,
599 "compression_level": 6
600 }),
601 "hnsw": crate::json!({ "m": 16, "ef_construction": 100, "ef_search": 50 }),
602 "ivf": crate::json!({ "n_lists": 100, "n_probes": 10 }),
603 "bm25": crate::json!({ "k1": 1.2, "b": 0.75 })
604 }),
605 );
606 store.set_config_tree(
607 "red.search",
608 &crate::json!({
609 "rag": crate::json!({
610 "max_chunks_per_source": 10,
611 "max_total_chunks": 25,
612 "similarity_threshold": 0.8,
613 "graph_depth": 2,
614 "min_relevance": 0.3
615 }),
616 "fusion": crate::json!({
617 "vector_weight": 0.5,
618 "graph_weight": 0.3,
619 "table_weight": 0.2,
620 "dedup_threshold": 0.85
621 })
622 }),
623 );
624 store.set_config_tree(
625 "red.auth",
626 &crate::json!({
627 "enabled": false,
628 "session_ttl_secs": 3600,
629 "require_auth": false
630 }),
631 );
632 store.set_config_tree(
633 "red.query",
634 &crate::json!({
635 "connection_pool": crate::json!({ "max_connections": 64, "max_idle": 16 }),
636 "max_recursion_depth": 1000
637 }),
638 );
639 store.set_config_tree(
640 "red.indexes",
641 &crate::json!({
642 "auto_select": true,
643 "bloom_filter": crate::json!({
644 "enabled": true,
645 "false_positive_rate": 0.01,
646 "prune_on_scan": true
647 }),
648 "hash": crate::json!({ "enabled": true }),
649 "bitmap": crate::json!({ "enabled": true, "max_cardinality": 1000 }),
650 "spatial": crate::json!({ "enabled": true })
651 }),
652 );
653 store.set_config_tree(
654 "red.memtable",
655 &crate::json!({
656 "enabled": true,
657 "max_bytes": 67108864_u64,
658 "flush_threshold": 0.75
659 }),
660 );
661 store.set_config_tree(
662 "red.probabilistic",
663 &crate::json!({
664 "hll_registers": 16384,
665 "sketch_default_width": 1000,
666 "sketch_default_depth": 5,
667 "filter_default_capacity": 100000
668 }),
669 );
670 store.set_config_tree(
671 "red.timeseries",
672 &crate::json!({
673 "default_chunk_size": 1024,
674 "compression": crate::json!({
675 "timestamps": "delta_of_delta",
676 "values": "gorilla_xor"
677 }),
678 "default_retention_days": 0
679 }),
680 );
681 store.set_config_tree(
682 "red.queue",
683 &crate::json!({
684 "default_max_size": 0,
685 "default_max_attempts": 3,
686 "visibility_timeout_ms": 30000,
687 "consumer_idle_timeout_ms": 60000
688 }),
689 );
690 store.set_config_tree(
691 "red.backup",
692 &crate::json!({
693 "enabled": false,
694 "interval_secs": 3600,
695 "retention_count": 24,
696 "upload": false,
697 "backend": "local"
698 }),
699 );
700 store.set_config_tree(
701 "red.wal",
702 &crate::json!({
703 "archive": crate::json!({
704 "enabled": false,
705 "retention_hours": 168,
706 "prefix": reddb_file::backup_wal_prefix("")
707 })
708 }),
709 );
710 store.set_config_tree(
711 "red.cdc",
712 &crate::json!({
713 "enabled": true,
714 "buffer_size": 100000
715 }),
716 );
717 store.set_config_tree(
718 "red.config.secret",
719 &crate::json!({
720 "auto_encrypt": true,
721 "auto_decrypt": true
722 }),
723 );
724 }
725
726 crate::runtime::config_matrix::heal_critical_keys(store.as_ref());
733 seed_storage_deploy_config(store.as_ref(), options.storage_profile);
734
735 let lehman_yao = runtime.config_bool("storage.btree.lehman_yao", true);
742 crate::storage::engine::btree::lehman_yao::set_enabled(lehman_yao);
743 if lehman_yao {
744 tracing::info!(
745 "storage.btree.lehman_yao=true — lock-free concurrent descent enabled"
746 );
747 }
748
749 let overlay_path = crate::runtime::config_overlay::config_file_path();
754 let _ =
755 crate::runtime::config_overlay::apply_config_file(store.as_ref(), &overlay_path);
756 }
757
758 {
762 let store = runtime.inner.db.store();
763 for name in crate::application::vcs_collections::ALL {
764 let _ = store.get_or_create_collection(*name);
765 }
766 store.set_config_tree(
769 crate::application::vcs_collections::CONFIG_NAMESPACE,
770 &crate::json!({
771 "default_branch": "main",
772 "author": crate::json!({
773 "name": "reddb",
774 "email": "reddb@localhost"
775 }),
776 "protected_branches": crate::json!(["main"]),
777 "closure": crate::json!({
778 "enabled": true,
779 "lazy": true
780 }),
781 "merge": crate::json!({
782 "default_strategy": "auto",
783 "fast_forward": true
784 })
785 }),
786 );
787 }
788
789 {
792 let store = runtime.inner.db.store();
793 for name in crate::application::migration_collections::ALL {
794 let _ = store.get_or_create_collection(*name);
795 }
796 }
797
798 let _ = crate::application::topology_collections::ensure(&runtime);
802
803 runtime
811 .inner
812 .db
813 .store()
814 .register_entity_id(crate::storage::EntityId::new(
815 crate::storage::FIRST_USER_ENTITY_ID - 1,
816 ));
817
818 {
833 let weak = Arc::downgrade(&runtime.inner);
834 std::thread::Builder::new()
835 .name("reddb-maintenance".into())
836 .spawn(move || {
837 let tick = std::time::Duration::from_millis(200);
838 let work_interval = std::time::Duration::from_secs(60);
839 let mut last_work = std::time::Instant::now();
840 loop {
841 std::thread::sleep(tick);
842 let Some(inner) = weak.upgrade() else {
843 break;
846 };
847 if last_work.elapsed() >= work_interval {
848 let _stats = inner.db.store().context_index().stats();
849 last_work = std::time::Instant::now();
850 }
851 }
852 })
853 .ok();
854 }
855
856 {
858 let store = runtime.inner.db.store();
859 let mut backup_enabled = false;
860 let mut backup_interval = 3600u64;
861
862 if let Some(manager) = store.get_collection("red_config") {
863 manager.for_each_entity(|entity| {
864 if let Some(row) = entity.data.as_row() {
865 let key = row.get_field("key").and_then(|v| match v {
866 crate::storage::schema::Value::Text(s) => Some(s.as_ref()),
867 _ => None,
868 });
869 let val = row.get_field("value");
870 if key == Some("red.config.backup.enabled") {
871 backup_enabled = match val {
872 Some(crate::storage::schema::Value::Boolean(true)) => true,
873 Some(crate::storage::schema::Value::Text(s)) => &**s == "true",
874 _ => false,
875 };
876 } else if key == Some("red.config.backup.interval_secs") {
877 if let Some(crate::storage::schema::Value::Integer(n)) = val {
878 backup_interval = *n as u64;
879 }
880 }
881 }
882 true
883 });
884 }
885
886 if backup_enabled {
887 runtime.inner.backup_scheduler.set_interval(backup_interval);
888 let rt = runtime.clone();
889 runtime
890 .inner
891 .backup_scheduler
892 .start(move || rt.trigger_backup().map_err(|e| format!("{}", e)));
893 }
894 }
895
896 {
898 runtime
899 .inner
900 .ec_registry
901 .load_from_config_store(runtime.inner.db.store().as_ref());
902 if !runtime.inner.ec_registry.async_configs().is_empty() {
903 runtime.inner.ec_worker.start(
904 Arc::clone(&runtime.inner.ec_registry),
905 Arc::clone(&runtime.inner.db.store()),
906 );
907 }
908 }
909
910 if let crate::replication::ReplicationRole::Replica { primary_addr } =
911 runtime.inner.db.options().replication.role.clone()
912 {
913 let rt = runtime.clone();
914 std::thread::Builder::new()
915 .name("reddb-replica".into())
916 .spawn(move || rt.run_replica_loop(primary_addr))
917 .ok();
918 }
919
920 runtime.inner.lifecycle.mark_ready();
925
926 {
935 let weak_inner = Arc::downgrade(&runtime.inner);
936 std::thread::Builder::new()
937 .name("reddb-mv-scheduler".into())
938 .stack_size(8 * 1024 * 1024)
943 .spawn(move || loop {
944 std::thread::sleep(std::time::Duration::from_millis(50));
945 let Some(inner) = weak_inner.upgrade() else {
946 break;
947 };
948 let rt = RedDBRuntime { inner };
949 rt.refresh_due_materialized_views();
950 })
951 .ok();
952 }
953
954 if !runtime.write_gate().is_read_only() {
964 let weak_inner = Arc::downgrade(&runtime.inner);
965 std::thread::Builder::new()
966 .name("reddb-retention-sweeper".into())
967 .spawn(move || loop {
968 std::thread::sleep(std::time::Duration::from_millis(500));
969 let Some(inner) = weak_inner.upgrade() else {
970 break;
971 };
972 let rt = RedDBRuntime { inner };
973 rt.sweep_retention_tick(
974 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
975 );
976 })
977 .ok();
978 }
979
980 Ok(runtime)
981 }
982
983 fn rehydrate_snapshot_xid_floor(&self) {
984 let store = self.inner.db.store();
985 for collection in store.list_collections() {
986 let Some(manager) = store.get_collection(&collection) else {
987 continue;
988 };
989 for entity in manager.query_all(|_| true) {
990 self.inner
991 .snapshot_manager
992 .observe_committed_xid(entity.xmin);
993 self.inner
994 .snapshot_manager
995 .observe_committed_xid(entity.xmax);
996 }
997 }
998 }
999
1000 pub(crate) fn ensure_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1013 let store = self.inner.db.store();
1014 let mut changed = false;
1015 if store.get_collection(name).is_none() {
1016 store.get_or_create_collection(name);
1017 changed = true;
1018 }
1019 if self.inner.db.collection_contract(name).is_none() {
1020 self.inner
1021 .db
1022 .save_collection_contract(system_keyed_collection_contract(
1023 name,
1024 crate::catalog::CollectionModel::Table,
1025 ))
1026 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1027 changed = true;
1028 }
1029 if changed {
1030 self.inner
1031 .db
1032 .persist_metadata()
1033 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1034 }
1035 Ok(())
1036 }
1037
1038 pub(crate) fn drop_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1043 let store = self.inner.db.store();
1044 if store.get_collection(name).is_none() {
1045 return Ok(());
1046 }
1047 store
1048 .drop_collection(name)
1049 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1050 if self.inner.db.collection_contract(name).is_some() {
1053 self.inner
1054 .db
1055 .remove_collection_contract(name)
1056 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1057 }
1058 self.invalidate_result_cache();
1059 self.inner
1060 .db
1061 .persist_metadata()
1062 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1063 Ok(())
1064 }
1065
1066 fn bootstrap_system_keyed_collections(&self) -> RedDBResult<()> {
1067 let mut changed = false;
1068 for (name, model) in [
1069 ("red.config", crate::catalog::CollectionModel::Config),
1070 ("red.vault", crate::catalog::CollectionModel::Vault),
1071 (
1075 crate::runtime::continuous_materialized_view::CATALOG_COLLECTION,
1076 crate::catalog::CollectionModel::Config,
1077 ),
1078 ] {
1079 if self.inner.db.store().get_collection(name).is_none() {
1080 self.inner.db.store().get_or_create_collection(name);
1081 changed = true;
1082 }
1083 if self.inner.db.collection_contract(name).is_none() {
1084 self.inner
1085 .db
1086 .save_collection_contract(system_keyed_collection_contract(name, model))
1087 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1088 changed = true;
1089 }
1090 }
1091 if changed {
1092 self.inner
1093 .db
1094 .persist_metadata()
1095 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1096 }
1097 Ok(())
1098 }
1099
1100 pub fn db(&self) -> Arc<RedDB> {
1101 Arc::clone(&self.inner.db)
1102 }
1103
1104 pub fn index_store_ref(&self) -> &super::index_store::IndexStore {
1109 &self.inner.index_store
1110 }
1111
1112 pub(crate) fn schema_vocabulary_apply(
1117 &self,
1118 event: crate::runtime::schema_vocabulary::DdlEvent,
1119 ) {
1120 self.inner.schema_vocabulary.write().on_ddl(event);
1121 }
1122
1123 pub fn schema_vocabulary_lookup(
1128 &self,
1129 token: &str,
1130 ) -> Vec<crate::runtime::schema_vocabulary::VocabHit> {
1131 self.inner.schema_vocabulary.read().lookup(token).to_vec()
1132 }
1133
1134 pub fn set_auth_store(&self, store: Arc<crate::auth::store::AuthStore>) {
1138 *self.inner.auth_store.write() = Some(store);
1139 }
1140
1141 pub fn auth_store(&self) -> Option<Arc<crate::auth::store::AuthStore>> {
1144 self.inner.auth_store.read().clone()
1145 }
1146
1147 pub fn set_oauth_validator(&self, validator: Option<Arc<crate::auth::oauth::OAuthValidator>>) {
1151 *self.inner.oauth_validator.write() = validator;
1152 }
1153
1154 pub fn oauth_validator(&self) -> Option<Arc<crate::auth::oauth::OAuthValidator>> {
1158 self.inner.oauth_validator.read().clone()
1159 }
1160
1161 pub fn set_browser_token_authority(
1167 &self,
1168 authority: Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>,
1169 ) {
1170 *self.inner.browser_token_authority.write() = authority;
1171 }
1172
1173 pub fn browser_token_authority(
1177 &self,
1178 ) -> Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>> {
1179 self.inner.browser_token_authority.read().clone()
1180 }
1181}