1use super::impl_config_secret::seed_storage_deploy_config;
13use super::*;
14
15pub(crate) fn view_records_to_entities(
24 table: &str,
25 records: &[crate::storage::query::unified::UnifiedRecord],
26) -> Vec<crate::storage::UnifiedEntity> {
27 use std::collections::HashMap;
28 let table_arc: std::sync::Arc<str> = std::sync::Arc::from(table);
29 let mut out = Vec::with_capacity(records.len());
30 for record in records {
31 let mut named: HashMap<String, reddb_types::Value> = HashMap::new();
32 for (name, value) in record.iter_fields() {
33 named.insert(name.to_string(), value.clone());
34 }
35 let entity = crate::storage::UnifiedEntity::new(
36 crate::storage::EntityId::new(0),
37 crate::storage::EntityKind::TableRow {
38 table: std::sync::Arc::clone(&table_arc),
39 row_id: 0,
40 },
41 crate::storage::EntityData::Row(crate::storage::RowData {
42 columns: Vec::new(),
43 named: Some(named),
44 schema: None,
45 }),
46 );
47 out.push(entity);
48 }
49 out
50}
51
52fn system_keyed_collection_contract(
53 name: &str,
54 model: crate::catalog::CollectionModel,
55) -> crate::physical::CollectionContract {
56 let now = crate::utils::now_unix_millis() as u128;
57 crate::physical::CollectionContract {
58 name: name.to_string(),
59 declared_model: model,
60 schema_mode: crate::catalog::SchemaMode::Dynamic,
61 origin: crate::physical::ContractOrigin::Implicit,
62 version: 1,
63 created_at_unix_ms: now,
64 updated_at_unix_ms: now,
65 default_ttl_ms: None,
66 vector_dimension: None,
67 vector_metric: None,
68 context_index_fields: Vec::new(),
69 declared_columns: Vec::new(),
70 table_def: None,
71 timestamps_enabled: false,
72 context_index_enabled: false,
73 metrics_raw_retention_ms: None,
74 metrics_rollup_policies: Vec::new(),
75 metrics_tenant_identity: None,
76 metrics_namespace: None,
77 append_only: false,
78 subscriptions: Vec::new(),
79 analytics_config: Vec::new(),
80 session_key: None,
81 session_gap_ms: None,
82 retention_duration_ms: None,
83 analytical_storage: None,
84
85 ai_policy: None,
86 }
87}
88
89pub(crate) fn table_row_index_fields(
90 entity: &crate::storage::unified::entity::UnifiedEntity,
91) -> Vec<(String, reddb_types::Value)> {
92 let crate::storage::EntityData::Row(row) = &entity.data else {
93 return Vec::new();
94 };
95 if let Some(named) = &row.named {
96 return named
97 .iter()
98 .map(|(name, value)| (name.clone(), value.clone()))
99 .collect();
100 }
101 if let Some(schema) = &row.schema {
102 return schema
103 .iter()
104 .zip(row.columns.iter())
105 .map(|(name, value)| (name.clone(), value.clone()))
106 .collect();
107 }
108 Vec::new()
109}
110
111impl RedDBRuntime {
112 pub fn in_memory() -> RedDBResult<Self> {
113 Self::with_options(RedDBOptions::in_memory())
114 }
115
116 pub fn flush(&self) -> RedDBResult<()> {
117 self.inner
118 .db
119 .flush()
120 .map_err(|err| RedDBError::Internal(err.to_string()))
121 }
122
123 pub fn lock_manager(&self) -> std::sync::Arc<crate::runtime::lock_manager::LockManager> {
127 self.inner.lock_manager.clone()
128 }
129
130 pub fn config_registry(&self) -> std::sync::Arc<crate::auth::registry::ConfigRegistry> {
132 self.inner.config_registry.clone()
133 }
134
135 pub fn query_audit(&self) -> std::sync::Arc<crate::runtime::query_audit::QueryAuditStream> {
136 self.inner.query_audit.clone()
137 }
138
139 pub fn control_events_require_persistence(&self) -> bool {
140 self.inner.control_event_config.require_persistence()
141 }
142
143 pub fn control_event_config(&self) -> crate::runtime::control_events::ControlEventConfig {
144 self.inner.control_event_config
145 }
146
147 pub fn control_event_ledger(
148 &self,
149 ) -> Arc<dyn crate::runtime::control_events::ControlEventLedger> {
150 self.inner.control_event_ledger.read().clone()
151 }
152
153 #[doc(hidden)]
154 pub fn replace_control_event_ledger_for_tests(
155 &self,
156 ledger: Arc<dyn crate::runtime::control_events::ControlEventLedger>,
157 ) {
158 *self.inner.control_event_ledger.write() = ledger;
159 }
160
161 #[inline(never)]
162 pub fn with_options(options: RedDBOptions) -> RedDBResult<Self> {
163 Self::with_pool(options, ConnectionPoolConfig::default())
164 }
165
166 pub fn memory_budget(&self) -> crate::storage::memory_budget::MemoryBudget {
170 self.inner.memory_budget
171 }
172
173 pub fn memory_accounting(&self) -> &crate::storage::memory_pools::MemoryAccounting {
177 &self.inner.memory_accounting
178 }
179
180 pub fn with_pool(
181 options: RedDBOptions,
182 pool_config: ConnectionPoolConfig,
183 ) -> RedDBResult<Self> {
184 Self::with_pool_and_limits(
185 options,
186 pool_config,
187 super::resource_limits::ResourceLimits::from_env(),
188 )
189 }
190
191 pub(super) fn with_pool_and_limits(
192 options: RedDBOptions,
193 pool_config: ConnectionPoolConfig,
194 resource_limits: super::resource_limits::ResourceLimits,
195 ) -> RedDBResult<Self> {
196 options
197 .replication
198 .validate_failover_profile()
199 .map_err(RedDBError::InvalidConfig)?;
200 let boot_open_start_ms = std::time::SystemTime::now()
208 .duration_since(std::time::UNIX_EPOCH)
209 .map(|d| d.as_millis() as u64)
210 .unwrap_or(0);
211 let memory_budget = crate::storage::memory_budget::resolve_for_boot(
215 options.storage_profile.deploy_profile,
216 options.memory_budget_bytes,
217 )
218 .map_err(|err| RedDBError::InvalidConfig(err.to_string()))?;
219 crate::storage::memory_budget::log_resolved_once(&memory_budget);
220 let memory_shares = crate::storage::memory_pools::BudgetShares::resolve(
224 memory_budget,
225 options.storage_profile.deploy_profile,
226 );
227 memory_shares.log_once();
228 let embedded_single_file = options.storage_profile.deploy_profile
229 == crate::storage::DeployProfile::Embedded
230 && options.storage_profile.packaging == crate::storage::StoragePackaging::SingleFile;
231 let db = Arc::new(RedDB::open_with_options(&options).map_err(|err| {
232 match err.downcast::<crate::storage::StoreError>() {
233 Ok(store_err) => RedDBError::from(*store_err),
234 Err(err) => RedDBError::Internal(err.to_string()),
235 }
236 })?);
237 let result_blob_cache_config = crate::storage::cache::BlobCacheConfig::default()
240 .with_l1_bytes_max(memory_shares.blob_cache_l1_bytes());
241 let result_blob_cache =
242 crate::storage::cache::BlobCache::open_with_l2(result_blob_cache_config).map_err(
243 |err| RedDBError::Internal(format!("open result Blob Cache L2 failed: {err:?}")),
244 )?;
245 let storage_ready_ms = std::time::SystemTime::now()
246 .duration_since(std::time::UNIX_EPOCH)
247 .map(|d| d.as_millis() as u64)
248 .unwrap_or(0);
249
250 let no_auth_boot = crate::service_cli::no_auth_active(&options);
251 let runtime = Self {
252 background_tasks: Some(Arc::new(background_tasks::RuntimeBackgroundTasks::default())),
253 inner: Arc::new(RuntimeInner {
254 functions: parking_lot::RwLock::new(
255 super::function_catalog::FunctionCatalog::load(&db)?,
256 ),
257 db: db.clone(),
258 layout: PhysicalLayout::from_options(&options),
259 embedded_single_file,
260 memory_budget,
261 memory_accounting: Arc::new(
262 crate::storage::memory_pools::MemoryAccounting::from_shares(
263 memory_budget,
264 memory_shares,
265 ),
266 ),
267 memory_reservations: parking_lot::Mutex::new(
268 super::memory_admission::MemoryReservations::default(),
269 ),
270 mvcc_maintenance: parking_lot::Mutex::new(Default::default()),
271 indices: IndexCatalog::register_default_vector_graph(
272 options.has_capability(crate::api::Capability::Table),
273 options.has_capability(crate::api::Capability::Graph),
274 ),
275 pool_config,
276 pool: Mutex::new(PoolState::default()),
277 execution_admission: parking_lot::RwLock::new(
278 crate::transport::execution_admission::ExecutionAdmission::for_host(),
279 ),
280 execution_queue_timeout_ms: AtomicU64::new(30_000),
281 started_at_unix_ms: SystemTime::now()
282 .duration_since(UNIX_EPOCH)
283 .unwrap_or_default()
284 .as_millis(),
285 probabilistic: super::probabilistic_store::ProbabilisticStore::new(),
286 index_store: super::index_store::IndexStore::new(),
287 cdc: crate::replication::cdc::CdcBuffer::new(100_000),
288 checkpoint_projection_stats: super::CheckpointProjectionStats::default(),
289 scrub_state: parking_lot::Mutex::new(super::ScrubRuntimeState::default()),
290 checkpoint_columnar_emission_budget_chunks: options
291 .checkpoint_columnar_emission_budget_chunks,
292 columnar_projection_size_floor_rows: options.columnar_projection_size_floor_rows,
293 backup_scheduler: crate::replication::scheduler::BackupScheduler::new(3600),
294 query_cache: parking_lot::RwLock::new(
295 crate::storage::query::planner::cache::PlanCache::new(1000),
296 ),
297 result_cache: parking_lot::RwLock::new((
298 HashMap::new(),
299 std::collections::VecDeque::new(),
300 )),
301 result_blob_cache,
302 result_blob_entries: parking_lot::RwLock::new((
303 HashMap::new(),
304 std::collections::VecDeque::new(),
305 )),
306 ask_answer_cache_entries: parking_lot::RwLock::new((
307 HashSet::new(),
308 std::collections::VecDeque::new(),
309 )),
310 result_cache_shadow_divergences: std::sync::atomic::AtomicU64::new(0),
311 result_cache_hits: std::sync::atomic::AtomicU64::new(0),
312 result_cache_misses: std::sync::atomic::AtomicU64::new(0),
313 result_cache_evictions: std::sync::atomic::AtomicU64::new(0),
314 ask_daily_spend: parking_lot::RwLock::new(HashMap::new()),
315 queue_message_locks: parking_lot::RwLock::new(HashMap::new()),
316 rmw_locks: RmwLockTable::new(),
317 planner_dirty_tables: parking_lot::RwLock::new(HashSet::new()),
318 ec_registry: Arc::new(crate::ec::config::EcRegistry::new()),
319 config_registry: Arc::new(crate::auth::registry::ConfigRegistry::new()),
320 ec_worker: crate::ec::worker::EcWorker::new(),
321 auth_store: parking_lot::RwLock::new(None),
322 oauth_validator: parking_lot::RwLock::new(None),
323 browser_token_authority: parking_lot::RwLock::new(None),
324 views: parking_lot::RwLock::new(HashMap::new()),
325 materialized_views: parking_lot::RwLock::new(
326 crate::storage::cache::result::MaterializedViewCache::new(),
327 ),
328 retention_sweeper: parking_lot::RwLock::new(
329 crate::runtime::retention_sweeper::RetentionSweeperState::new(),
330 ),
331 transaction_state: crate::runtime::transaction_state::TransactionState::new(),
332 env_config_overrides: crate::runtime::config_overlay::collect_env_overrides(),
333 lock_manager: Arc::new({
334 let env = crate::runtime::config_overlay::collect_env_overrides();
339 let timeout_ms = env
340 .get("concurrency.locking.deadlock_timeout_ms")
341 .and_then(|raw| raw.parse::<u64>().ok())
342 .unwrap_or_else(|| {
343 match crate::runtime::config_matrix::default_for(
344 "concurrency.locking.deadlock_timeout_ms",
345 ) {
346 Some(crate::serde_json::Value::Number(n)) => n as u64,
347 _ => 5000,
348 }
349 });
350 let cfg = crate::runtime::lock_manager::LockConfig {
351 default_timeout: std::time::Duration::from_millis(timeout_ms),
352 ..Default::default()
353 };
354 crate::runtime::lock_manager::LockManager::new(cfg)
355 }),
356 rls_policies: parking_lot::RwLock::new(HashMap::new()),
357 rls_enabled_tables: parking_lot::RwLock::new(HashSet::new()),
358 foreign_tables: Arc::new(crate::storage::fdw::ForeignTableRegistry::with_builtins()),
359 pending_tombstones: parking_lot::RwLock::new(HashMap::new()),
360 pending_versioned_updates: parking_lot::RwLock::new(HashMap::new()),
361 pending_queue_dedup: parking_lot::RwLock::new(HashMap::new()),
362 pending_kv_watch_events: parking_lot::RwLock::new(HashMap::new()),
363 pending_vault_writes: parking_lot::RwLock::new(HashMap::new()),
364 pending_store_wal_actions: parking_lot::RwLock::new(HashMap::new()),
365 pending_claim_locks: parking_lot::RwLock::new(HashMap::new()),
366 queue_wait_registry: std::sync::Arc::new(
367 crate::runtime::queue_wait_registry::QueueWaitRegistry::new(),
368 ),
369 pending_queue_wakes: parking_lot::RwLock::new(HashMap::new()),
370 tenant_tables: parking_lot::RwLock::new(HashMap::new()),
371 ddl_epoch: std::sync::atomic::AtomicU64::new(0),
372 write_gate: Arc::new(crate::runtime::write_gate::WriteGate::from_options(
373 &options,
374 )),
375 lifecycle: crate::runtime::lifecycle::Lifecycle::new(),
376 resource_limits,
377 query_budget_slots: AtomicU64::new(0),
378 audit_log: {
379 let data_path = if embedded_single_file || options.read_only {
395 std::env::temp_dir()
396 .join("reddb-embedded-runtime")
397 .join(format!("audit-{}", std::process::id()))
398 } else {
399 options
400 .data_path
401 .clone()
402 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
403 };
404 let (audit_dest, _) = crate::api::tier_wiring::current_log_destinations();
405 let audit_dest = if options.read_only {
406 crate::storage::layout::LogDestination::Stderr
407 } else {
408 audit_dest
409 };
410 if !matches!(audit_dest, crate::storage::layout::LogDestination::File(_))
411 && (embedded_single_file
412 || options.read_only
413 || options
414 .metadata
415 .contains_key(crate::api::EPHEMERAL_RUNTIME_METADATA_KEY))
416 {
417 let audit_path = reddb_file::layout::sibling_path(
427 &data_path,
428 &reddb_file::layout::sidecar_file_name(&data_path, "audit.log"),
429 );
430 Arc::new(crate::runtime::audit_log::AuditLogger::with_path(
431 audit_path,
432 ))
433 } else {
434 Arc::new(crate::runtime::audit_log::AuditLogger::for_destination(
435 &audit_dest,
436 &data_path,
437 ))
438 }
439 },
440 control_event_ledger: parking_lot::RwLock::new(Arc::new(
441 crate::runtime::control_events::RuntimeLedger::new(db.store()),
442 )),
443 control_event_config: options.control_events,
444 query_audit: Arc::new(crate::runtime::query_audit::QueryAuditStream::new(
445 db.store(),
446 options.query_audit.clone(),
447 )),
448 lease_lifecycle: std::sync::OnceLock::new(),
449 replica_apply_metrics: std::sync::Arc::new(
450 crate::replication::logical::ReplicaApplyMetrics::default(),
451 ),
452 replica_link_metrics: std::sync::Arc::new(
453 crate::replication::reconnect::ReplicaLinkMetrics::default(),
454 ),
455 quota_bucket: crate::runtime::quota_bucket::QuotaBucket::from_env(),
456 schema_vocabulary: parking_lot::RwLock::new(
457 crate::runtime::schema_vocabulary::SchemaVocabulary::new(),
458 ),
459 slow_query_logger: {
460 let fallback_dir = if embedded_single_file || options.read_only {
477 std::env::temp_dir()
478 .join("reddb-embedded-runtime")
479 .join(format!("slow-{}", std::process::id()))
480 } else {
481 options
482 .data_path
483 .as_ref()
484 .and_then(|p| p.parent().map(std::path::PathBuf::from))
485 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
486 };
487 let threshold_ms = std::env::var("RED_SLOW_QUERY_THRESHOLD_MS")
488 .ok()
489 .and_then(|s| s.parse::<u64>().ok())
490 .unwrap_or(1000);
491 let sample_pct = std::env::var("RED_SLOW_QUERY_SAMPLE_PCT")
492 .ok()
493 .and_then(|s| s.parse::<u8>().ok())
494 .unwrap_or(100);
495 let (_, slow_dest) = crate::api::tier_wiring::current_log_destinations();
496 let slow_dest = if options.read_only {
497 crate::storage::layout::LogDestination::Stderr
498 } else {
499 slow_dest
500 };
501 crate::telemetry::slow_query_logger::SlowQueryLogger::for_destination(
502 &slow_dest,
503 &fallback_dir,
504 threshold_ms,
505 sample_pct,
506 )
507 },
508 slow_query_store: crate::telemetry::slow_query_store::SlowQueryStore::new(
509 crate::telemetry::slow_query_store::DEFAULT_CAP,
510 ),
511 kv_stats: crate::runtime::KvStatsCounters::default(),
512 metrics_ingest_stats: crate::runtime::MetricsIngestCounters::default(),
513 metrics_tenant_activity_stats:
514 crate::runtime::MetricsTenantActivityCounters::default(),
515 claim_telemetry: Arc::new(
516 crate::runtime::claim_telemetry::ClaimTelemetryCounters::default(),
517 ),
518 queue_telemetry: Arc::new(
519 crate::runtime::queue_telemetry::QueueTelemetryCounters::default(),
520 ),
521 query_latency_telemetry: Arc::new(
522 crate::runtime::query_latency_telemetry::QueryLatencyTelemetry::default(),
523 ),
524 occupancy_sampler: Arc::new(
525 crate::runtime::occupancy_sampler::OccupancySampler::new(),
526 ),
527 node_load_telemetry: Arc::new(
528 crate::runtime::node_load_telemetry::NodeLoadTelemetry::default(),
529 ),
530 queue_presence: Arc::new(
531 crate::storage::queue::presence::ConsumerPresenceRegistry::new(),
532 ),
533 vector_introspection: Arc::new(
534 crate::storage::vector::introspection::VectorIntrospectionRegistry::new(),
535 ),
536 kv_tag_index: crate::runtime::KvTagIndex::default(),
537 chain_tip_cache: parking_lot::Mutex::new(HashMap::new()),
538 chain_integrity_broken: parking_lot::Mutex::new(HashMap::new()),
539 integrity_tombstones: parking_lot::Mutex::new(Vec::new()),
540 integrity_tombstones_state: std::sync::atomic::AtomicU8::new(0),
541 }),
542 };
543
544 runtime.install_hash_candidate_admission();
545
546 crate::telemetry::operator_event::install_global_audit_sink(Arc::clone(
552 &runtime.inner.audit_log,
553 ));
554
555 runtime
558 .inner
559 .slow_query_logger
560 .attach_store(Arc::clone(&runtime.inner.slow_query_store));
561
562 runtime
570 .inner
571 .lifecycle
572 .set_restore_started_at_ms(boot_open_start_ms);
573 runtime
574 .inner
575 .lifecycle
576 .set_restore_ready_at_ms(storage_ready_ms);
577 runtime
578 .inner
579 .lifecycle
580 .set_wal_replay_started_at_ms(boot_open_start_ms);
581 runtime
582 .inner
583 .lifecycle
584 .set_wal_replay_ready_at_ms(storage_ready_ms);
585
586 let restored_cdc_lsn = runtime
587 .inner
588 .db
589 .replication
590 .as_ref()
591 .map(|repl| {
592 repl.logical_wal_spool
593 .as_ref()
594 .map(|spool| spool.current_lsn())
595 .unwrap_or(0)
596 })
597 .unwrap_or(0)
598 .max(runtime.config_u64("red.config.timeline.last_archived_lsn", 0));
599 runtime.inner.cdc.set_current_lsn(restored_cdc_lsn);
600 runtime.rehydrate_snapshot_xid_floor()?;
601 assert!(runtime
602 .inner
603 .db
604 .runtime
605 .set(super::WeakRuntime(Arc::downgrade(&runtime.inner)))
606 .is_ok());
607 assert!(
608 runtime
609 .inner
610 .db
611 .store()
612 .snapshot_manager
613 .set(runtime.snapshot_manager())
614 .is_ok(),
615 "a new runtime owns the store transaction outcomes"
616 );
617 runtime
618 .bootstrap_system_keyed_collections()
619 .map_err(|err| RedDBError::Internal(format!("bootstrap system collections: {err}")))?;
620 runtime.rehydrate_declared_column_schemas();
621 super::control_event_index::prepare(&runtime)?;
624 super::queue_storage_index::prepare(&runtime)?;
625 runtime.rehydrate_runtime_index_registry()?;
626 if runtime
627 .db()
628 .store()
629 .config()
630 .resident_memory_bytes_max
631 .is_some()
632 {
633 runtime
634 .db()
635 .store()
636 .checkpoint_missing_secondary_indices()?;
637 }
638 runtime.rehydrate_kv_tags()?;
639 runtime
640 .load_probabilistic_state()
641 .map_err(|err| RedDBError::Internal(format!("load probabilistic state: {err}")))?;
642
643 runtime.rehydrate_tenant_tables();
647 runtime.rehydrate_materialized_view_descriptors()?;
652 runtime.recover_store_forks()?;
653 if let Some(repl) = &runtime.inner.db.replication {
654 repl.wal_buffer.set_current_lsn(restored_cdc_lsn);
655 }
656
657 {
659 let sys = SystemInfo::collect();
660 runtime.inner.db.store().set_config_tree(
661 "red.system",
662 &crate::serde_json::json!({
663 "pid": sys.pid,
664 "cpu_cores": sys.cpu_cores,
665 "total_memory_bytes": sys.total_memory_bytes,
666 "available_memory_bytes": sys.available_memory_bytes,
667 "os": sys.os,
668 "arch": sys.arch,
669 "hostname": sys.hostname,
670 "started_at": SystemTime::now()
671 .duration_since(UNIX_EPOCH)
672 .unwrap_or_default()
673 .as_millis() as u64
674 }),
675 );
676
677 let store = runtime.inner.db.store();
679 if store
680 .get_collection("red_config")
681 .map(|m| m.query_all(|_| true).len())
682 .unwrap_or(0)
683 <= 10
684 {
685 store.set_config_tree("red.ai", &crate::json!({
686 "default": crate::json!({
687 "provider": "openai",
688 "model": crate::ai::DEFAULT_OPENAI_PROMPT_MODEL
689 }),
690 "max_embedding_inputs": 256,
691 "max_prompt_batch": 256,
692 "timeout": crate::json!({ "connect_secs": 10, "read_secs": 90, "write_secs": 30 })
693 }));
694 store.set_config_tree(
695 "red.server",
696 &crate::json!({
697 "max_scan_limit": 1000,
698 "max_body_size": 1048576,
699 "read_timeout_ms": 5000,
700 "write_timeout_ms": 5000
701 }),
702 );
703 store.set_config_tree(
704 "red.storage",
705 &crate::json!({
706 "page_size": 4096,
707 "page_cache_capacity": 100000,
708 "auto_checkpoint_pages": 1000,
709 "snapshot_retention": 16,
710 "verify_checksums": true,
711 "segment": crate::json!({
712 "max_entities": 100000,
713 "max_bytes": 268435456_u64,
714 "compression_level": 6
715 }),
716 "hnsw": crate::json!({ "m": 16, "ef_construction": 100, "ef_search": 50 }),
717 "ivf": crate::json!({ "n_lists": 100, "n_probes": 10 }),
718 "bm25": crate::json!({ "k1": 1.2, "b": 0.75 })
719 }),
720 );
721 store.set_config_tree(
722 "red.search",
723 &crate::json!({
724 "rag": crate::json!({
725 "max_chunks_per_source": 10,
726 "max_total_chunks": 25,
727 "similarity_threshold": 0.8,
728 "graph_depth": 2,
729 "min_relevance": 0.3
730 }),
731 "fusion": crate::json!({
732 "vector_weight": 0.5,
733 "graph_weight": 0.3,
734 "table_weight": 0.2,
735 "dedup_threshold": 0.85
736 })
737 }),
738 );
739 store.set_config_tree(
740 "red.auth",
741 &crate::json!({
742 "enabled": false,
743 "session_ttl_secs": 3600,
744 "require_auth": false
745 }),
746 );
747 store.set_config_tree(
748 "red.query",
749 &crate::json!({
750 "connection_pool": crate::json!({ "max_connections": 64, "max_idle": 16 }),
751 "max_recursion_depth": 1000
752 }),
753 );
754 store.set_config_tree(
755 "red.indexes",
756 &crate::json!({
757 "auto_select": true,
758 "bloom_filter": crate::json!({
759 "enabled": true,
760 "false_positive_rate": 0.01,
761 "prune_on_scan": true
762 }),
763 "hash": crate::json!({ "enabled": true }),
764 "bitmap": crate::json!({ "enabled": true, "max_cardinality": 1000 }),
765 "spatial": crate::json!({ "enabled": true })
766 }),
767 );
768 store.set_config_tree(
769 "red.probabilistic",
770 &crate::json!({
771 "hll_registers": 16384,
772 "sketch_default_width": 1000,
773 "sketch_default_depth": 5,
774 "filter_default_capacity": 100000
775 }),
776 );
777 store.set_config_tree(
778 "red.timeseries",
779 &crate::json!({
780 "default_chunk_size": 1024,
781 "compression": crate::json!({
782 "timestamps": "delta_of_delta",
783 "values": "gorilla_xor"
784 }),
785 "default_retention_days": 0
786 }),
787 );
788 store.set_config_tree(
789 "red.queue",
790 &crate::json!({
791 "default_max_size": 0,
792 "default_max_attempts": 3,
793 "visibility_timeout_ms": 30000,
794 "consumer_idle_timeout_ms": 60000
795 }),
796 );
797 store.set_config_tree(
798 "red.backup",
799 &crate::json!({
800 "enabled": false,
801 "interval_secs": 3600,
802 "retention_count": 24,
803 "upload": false,
804 "backend": "local"
805 }),
806 );
807 store.set_config_tree(
808 "red.wal",
809 &crate::json!({
810 "archive": crate::json!({
811 "enabled": false,
812 "retention_hours": 168,
813 "prefix": reddb_file::backup_wal_prefix("")
814 })
815 }),
816 );
817 store.set_config_tree(
818 "red.cdc",
819 &crate::json!({
820 "enabled": true,
821 "buffer_size": 100000
822 }),
823 );
824 store.set_config_tree(
825 "red.config.secret",
826 &crate::json!({
827 "auto_encrypt": true,
828 "auto_decrypt": true
829 }),
830 );
831 }
832
833 crate::runtime::config_matrix::heal_critical_keys(store.as_ref());
840 seed_storage_deploy_config(store.as_ref(), options.storage_profile);
841
842 let lehman_yao = runtime.config_bool("storage.btree.lehman_yao", true);
849 crate::storage::engine::btree::lehman_yao::set_enabled(lehman_yao);
850 if lehman_yao {
851 tracing::info!(
852 "storage.btree.lehman_yao=true — lock-free concurrent descent enabled"
853 );
854 }
855
856 let overlay_path = crate::runtime::config_overlay::config_file_path();
861 let _ =
862 crate::runtime::config_overlay::apply_config_file(store.as_ref(), &overlay_path);
863 }
864
865 {
869 let store = runtime.inner.db.store();
870 for name in crate::application::vcs_collections::ALL {
871 let _ = store.get_or_create_collection(*name);
872 }
873 store.set_config_tree(
876 crate::application::vcs_collections::CONFIG_NAMESPACE,
877 &crate::json!({
878 "default_branch": "main",
879 "author": crate::json!({
880 "name": "reddb",
881 "email": "reddb@localhost"
882 }),
883 "protected_branches": crate::json!(["main"]),
884 "closure": crate::json!({
885 "enabled": true,
886 "lazy": true
887 }),
888 "merge": crate::json!({
889 "default_strategy": "auto",
890 "fast_forward": true
891 })
892 }),
893 );
894 }
895
896 {
899 let store = runtime.inner.db.store();
900 for name in crate::application::migration_collections::ALL {
901 let _ = store.get_or_create_collection(*name);
902 }
903 }
904
905 let _ = crate::application::topology_collections::ensure(&runtime);
909
910 runtime.prepare_paged_counts()?;
915
916 runtime
924 .inner
925 .db
926 .store()
927 .register_entity_id(crate::storage::EntityId::new(
928 crate::storage::FIRST_USER_ENTITY_ID - 1,
929 ));
930
931 {
945 let weak = Arc::downgrade(&runtime.inner);
946 runtime
947 .background_tasks
948 .as_ref()
949 .expect("foreground runtime")
950 .spawn(
951 std::thread::Builder::new().name("reddb-maintenance".into()),
952 move |stopped| {
953 let tick = std::time::Duration::from_millis(200);
954 let work_interval = std::time::Duration::from_secs(60);
955 let mut last_work = std::time::Instant::now();
956 let mut allocator_pressure =
957 super::allocator_pressure::AllocatorPressure::default();
958 loop {
959 std::thread::park_timeout(tick);
960 if stopped.load(AtomicOrdering::Acquire) {
961 break;
962 }
963 let Some(inner) = weak.upgrade() else {
964 break;
967 };
968 if last_work.elapsed() >= work_interval {
969 let _stats = inner.db.store().context_index().stats();
970 last_work = std::time::Instant::now();
971 }
972 let rt = RedDBRuntime {
973 background_tasks: None,
974 inner,
975 };
976 rt.try_reclaim_history_tick();
977 allocator_pressure.tick();
978 }
979 },
980 );
981 }
982
983 {
985 let store = runtime.inner.db.store();
986 let mut backup_enabled = false;
987 let mut backup_interval = 3600u64;
988
989 if let Some(manager) = store.get_collection("red_config") {
990 manager.for_each_entity(|entity| {
991 if let Some(row) = entity.data.as_row() {
992 let key = row.get_field("key").and_then(|v| match v {
993 reddb_types::Value::Text(s) => Some(s.as_ref()),
994 _ => None,
995 });
996 let val = row.get_field("value");
997 if key == Some("red.config.backup.enabled") {
998 backup_enabled = match val {
999 Some(reddb_types::Value::Boolean(true)) => true,
1000 Some(reddb_types::Value::Text(s)) => &**s == "true",
1001 _ => false,
1002 };
1003 } else if key == Some("red.config.backup.interval_secs") {
1004 if let Some(reddb_types::Value::Integer(n)) = val {
1005 backup_interval = *n as u64;
1006 }
1007 }
1008 }
1009 true
1010 });
1011 }
1012
1013 if backup_enabled {
1014 runtime.inner.backup_scheduler.set_interval(backup_interval);
1015 let rt = runtime.clone();
1016 runtime
1017 .inner
1018 .backup_scheduler
1019 .start(move || rt.trigger_backup().map_err(|e| format!("{}", e)));
1020 }
1021 }
1022
1023 {
1025 runtime
1026 .inner
1027 .ec_registry
1028 .load_from_config_store(runtime.inner.db.store().as_ref());
1029 if !runtime.inner.db.options().read_only
1030 && !runtime.inner.ec_registry.async_configs().is_empty()
1031 {
1032 runtime.inner.ec_worker.start(
1033 Arc::clone(&runtime.inner.ec_registry),
1034 Arc::clone(&runtime.inner.db.store()),
1035 );
1036 }
1037 }
1038
1039 if let (true, crate::replication::ReplicationRole::Replica { primary_addr }) = (
1040 runtime.inner.db.options().replica_loop_enabled
1041 && !runtime.inner.db.options().read_only,
1042 runtime.inner.db.options().replication.role.clone(),
1043 ) {
1044 let rt = runtime.clone();
1045 std::thread::Builder::new()
1046 .name("reddb-replica".into())
1047 .spawn(move || rt.run_replica_loop(primary_addr))
1048 .ok();
1049 }
1050
1051 runtime.inner.lifecycle.mark_ready();
1056
1057 #[cfg(not(test))]
1058 if runtime
1059 .db()
1060 .store()
1061 .config()
1062 .resident_memory_bytes_max
1063 .is_some()
1064 && !runtime.db().options().read_only
1065 {
1066 runtime.start_count_projection_worker();
1067 }
1068
1069 {
1078 let weak_inner = Arc::downgrade(&runtime.inner);
1079 runtime
1080 .background_tasks
1081 .as_ref()
1082 .expect("foreground runtime")
1083 .spawn(
1084 std::thread::Builder::new()
1085 .name("reddb-mv-scheduler".into())
1086 .stack_size(8 * 1024 * 1024),
1091 move |stopped| loop {
1092 std::thread::park_timeout(std::time::Duration::from_millis(50));
1093 if stopped.load(AtomicOrdering::Acquire) {
1094 break;
1095 }
1096 let Some(inner) = weak_inner.upgrade() else {
1097 break;
1098 };
1099 let rt = RedDBRuntime {
1100 background_tasks: None,
1101 inner,
1102 };
1103 rt.refresh_due_materialized_views();
1104 },
1105 );
1106 }
1107
1108 if !runtime.write_gate().is_read_only() {
1118 let weak_inner = Arc::downgrade(&runtime.inner);
1119 runtime
1120 .background_tasks
1121 .as_ref()
1122 .expect("foreground runtime")
1123 .spawn(
1124 std::thread::Builder::new().name("reddb-retention-sweeper".into()),
1125 move |stopped| loop {
1126 std::thread::park_timeout(std::time::Duration::from_millis(500));
1127 if stopped.load(AtomicOrdering::Acquire) {
1128 break;
1129 }
1130 let Some(inner) = weak_inner.upgrade() else {
1131 break;
1132 };
1133 let rt = RedDBRuntime {
1134 background_tasks: None,
1135 inner,
1136 };
1137 rt.sweep_retention_tick(
1138 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
1139 );
1140 },
1141 );
1142 }
1143
1144 if no_auth_boot {
1148 use crate::runtime::control_events::{
1149 ActorRef, ControlEvent, ControlEventCtx, EventKind, Outcome,
1150 };
1151 let ledger = std::sync::Arc::clone(&runtime.inner.control_event_ledger.read());
1152 let ctx = ControlEventCtx {
1153 actor: ActorRef::System("boot"),
1154 scope: None,
1155 request_id: None,
1156 trace_id: None,
1157 };
1158 let event = ControlEvent {
1159 kind: EventKind::AuthDisabled,
1160 outcome: Outcome::Allowed,
1161 action: std::borrow::Cow::Borrowed("auth.disabled"),
1162 resource: Some("server".to_string()),
1163 reason: Some("--no-auth / --dev".to_string()),
1164 matched_policy_id: None,
1165 fields: std::collections::HashMap::new(),
1166 };
1167 if let Err(err) = ledger.emit(&ctx, event) {
1168 tracing::warn!(
1169 target: "reddb::security",
1170 error = %err,
1171 "could not record the auth-disabled boot in the control-event ledger"
1172 );
1173 }
1174 }
1175
1176 Ok(runtime)
1177 }
1178
1179 fn rehydrate_snapshot_xid_floor(&self) -> RedDBResult<()> {
1180 let store = self.inner.db.store();
1181 let snapshot_manager = self.inner.transaction_state.snapshot_manager();
1182 let base_high_water_mark = store.startup_xid_high_water_mark();
1183 if let Some(high_water_mark) = base_high_water_mark {
1184 snapshot_manager.observe_committed_xid(high_water_mark);
1185 }
1186 for collection in store.list_collections() {
1187 let Some(manager) = store.get_collection(&collection) else {
1188 continue;
1189 };
1190 let observe = |entity: &crate::storage::UnifiedEntity| {
1191 snapshot_manager.observe_committed_xid(entity.xmin);
1192 snapshot_manager.observe_committed_xid(entity.xmax);
1193 true
1194 };
1195 if base_high_water_mark.is_some() {
1196 manager.for_each_entity(observe);
1199 } else {
1200 manager.try_for_each_entity(usize::MAX, observe)?;
1203 }
1204 }
1205 if base_high_water_mark.is_none() {
1206 store.remember_startup_xid_high_water_mark(
1207 snapshot_manager.peek_next_xid().saturating_sub(1),
1208 );
1209 }
1210 Ok(())
1211 }
1212
1213 pub(crate) fn ensure_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1226 let store = self.inner.db.store();
1227 let mut changed = false;
1228 if store.get_collection(name).is_none() {
1229 store.get_or_create_collection(name);
1230 changed = true;
1231 }
1232 if self.inner.db.collection_contract(name).is_none() {
1233 self.inner
1234 .db
1235 .save_collection_contract(system_keyed_collection_contract(
1236 name,
1237 crate::catalog::CollectionModel::Table,
1238 ))
1239 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1240 changed = true;
1241 }
1242 if changed {
1243 self.inner
1244 .db
1245 .persist_metadata()
1246 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1247 }
1248 Ok(())
1249 }
1250
1251 pub(crate) fn drop_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1256 let store = self.inner.db.store();
1257 if store.get_collection(name).is_none() {
1258 return Ok(());
1259 }
1260 store
1261 .drop_collection(name)
1262 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1263 if self.inner.db.collection_contract(name).is_some() {
1266 self.inner
1267 .db
1268 .remove_collection_contract(name)
1269 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1270 }
1271 self.invalidate_result_cache();
1272 self.inner
1273 .db
1274 .persist_metadata()
1275 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1276 Ok(())
1277 }
1278
1279 fn bootstrap_system_keyed_collections(&self) -> RedDBResult<()> {
1280 let mut changed = false;
1281 for (name, model) in [
1282 ("red.config", crate::catalog::CollectionModel::Config),
1283 ("red.vault", crate::catalog::CollectionModel::Vault),
1284 (
1288 crate::runtime::continuous_materialized_view::CATALOG_COLLECTION,
1289 crate::catalog::CollectionModel::Config,
1290 ),
1291 ] {
1292 if self.inner.db.store().get_collection(name).is_none() {
1293 self.inner.db.store().get_or_create_collection(name);
1294 changed = true;
1295 }
1296 if self.inner.db.collection_contract(name).is_none() {
1297 let contract = system_keyed_collection_contract(name, model);
1298 if self.inner.db.options().read_only {
1299 self.inner.db.cache_read_only_bootstrap_contract(contract);
1300 } else {
1301 self.inner
1302 .db
1303 .save_collection_contract(contract)
1304 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1305 }
1306 changed = true;
1307 }
1308 }
1309 if changed {
1310 self.inner
1311 .db
1312 .persist_metadata()
1313 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1314 }
1315 Ok(())
1316 }
1317
1318 pub fn db(&self) -> Arc<RedDB> {
1319 Arc::clone(&self.inner.db)
1320 }
1321
1322 pub fn index_store_ref(&self) -> &super::index_store::IndexStore {
1327 &self.inner.index_store
1328 }
1329
1330 pub(crate) fn schema_vocabulary_apply(
1335 &self,
1336 event: crate::runtime::schema_vocabulary::DdlEvent,
1337 ) {
1338 self.inner.schema_vocabulary.write().on_ddl(event);
1339 }
1340
1341 pub fn schema_vocabulary_lookup(
1346 &self,
1347 token: &str,
1348 ) -> Vec<crate::runtime::schema_vocabulary::VocabHit> {
1349 self.inner.schema_vocabulary.read().lookup(token).to_vec()
1350 }
1351
1352 pub fn set_auth_store(&self, store: Arc<crate::auth::store::AuthStore>) {
1356 *self.inner.auth_store.write() = Some(store);
1357 }
1358
1359 pub fn auth_store(&self) -> Option<Arc<crate::auth::store::AuthStore>> {
1362 self.inner.auth_store.read().clone()
1363 }
1364
1365 pub fn set_oauth_validator(&self, validator: Option<Arc<crate::auth::oauth::OAuthValidator>>) {
1369 *self.inner.oauth_validator.write() = validator;
1370 }
1371
1372 pub fn oauth_validator(&self) -> Option<Arc<crate::auth::oauth::OAuthValidator>> {
1376 self.inner.oauth_validator.read().clone()
1377 }
1378
1379 pub fn set_browser_token_authority(
1385 &self,
1386 authority: Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>,
1387 ) {
1388 *self.inner.browser_token_authority.write() = authority;
1389 }
1390
1391 pub fn browser_token_authority(
1395 &self,
1396 ) -> Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>> {
1397 self.inner.browser_token_authority.read().clone()
1398 }
1399}
1400
1401#[cfg(test)]
1402mod xid_resume_tests {
1403 use super::*;
1404 use reddb_engine::storage::collection_read::{CollectionCursor, CollectionRead, EncodedRecord};
1405
1406 struct Unreadable;
1407
1408 fn unavailable() -> crate::storage::StoreError {
1409 crate::storage::StoreError::Io(std::io::Error::other("XID bootstrap read a base payload"))
1410 }
1411
1412 impl CollectionRead for Unreadable {
1413 fn get_record(
1414 &self,
1415 id: crate::storage::EntityId,
1416 _: usize,
1417 ) -> Result<Option<EncodedRecord>, crate::storage::StoreError> {
1418 if id.raw() == 999 {
1419 Ok(None)
1420 } else {
1421 Err(unavailable())
1422 }
1423 }
1424 fn cursor(&self) -> Result<Box<dyn CollectionCursor + '_>, crate::storage::StoreError> {
1425 Err(unavailable())
1426 }
1427 fn owned_cursor(&self) -> Result<Box<dyn CollectionCursor>, crate::storage::StoreError> {
1428 Err(unavailable())
1429 }
1430 }
1431
1432 #[test]
1433 fn xid_resume_survives_pending_checkpoint_process_exit() {
1434 const FIXTURE: &str = "REDDB_XID_RESUME_CRASH_FIXTURE";
1435 let open = |path: std::path::PathBuf| {
1436 let options = RedDBOptions::persistent(path)
1437 .with_memory_budget(64 * 1024 * 1024)
1438 .with_storage_profile(
1439 crate::storage::profile::StorageDeployPreset::PrimaryReplicaDev.selection(),
1440 )
1441 .expect("profile");
1442 crate::storage::RedDB::with_disk_residency_for_qualification(|| {
1443 RedDBRuntime::with_options(options)
1444 })
1445 .expect("crash recovery runtime")
1446 };
1447 if let Some(root) = std::env::var_os(FIXTURE) {
1448 let root = std::path::PathBuf::from(root);
1449 assert_eq!(
1450 std::fs::read_to_string(root.join("owned-fixture")).expect("owned fixture"),
1451 "xid-resume-crash-v1"
1452 );
1453 let runtime = open(root.join("crash.rdb"));
1454 for sql in [
1455 "CREATE TABLE crash_rows (id INTEGER, payload TEXT)",
1456 "INSERT INTO crash_rows (id, payload) VALUES (1, 'committed')",
1457 "BEGIN",
1458 "UPDATE crash_rows SET payload = 'pending' WHERE id = 1",
1459 "INSERT INTO crash_rows (id, payload) VALUES (2, 'pending')",
1460 ] {
1461 runtime.execute_query(sql).expect("crash fixture statement");
1462 }
1463 runtime
1464 .flush()
1465 .expect("checkpoint with pending transaction");
1466 std::fs::write(
1467 root.join("checkpoint-complete"),
1468 "pending transaction remains open",
1469 )
1470 .expect("acknowledged fixture checkpoint");
1471 std::process::exit(0);
1474 }
1475 let directory = tempfile::tempdir().expect("owned crash fixture");
1476 std::fs::write(
1477 directory.path().join("owned-fixture"),
1478 "xid-resume-crash-v1",
1479 )
1480 .expect("ownership marker");
1481 let child = std::process::Command::new(std::env::current_exe().expect("test executable"))
1482 .args([
1483 "--exact",
1484 "runtime::impl_lifecycle::xid_resume_tests::xid_resume_survives_pending_checkpoint_process_exit",
1485 "--test-threads=1",
1486 ])
1487 .env(FIXTURE, directory.path())
1488 .output()
1489 .expect("crashing owned child");
1490 assert!(
1491 child.status.success(),
1492 "{}",
1493 String::from_utf8_lossy(&child.stderr)
1494 );
1495 assert!(directory.path().join("checkpoint-complete").is_file());
1496 let path = directory.path().join("crash.rdb");
1497 assert!(path.is_file());
1498 let runtime = open(path.clone());
1499 let result = runtime
1500 .execute_query("SELECT id, payload FROM crash_rows ORDER BY id")
1501 .expect("committed preimage");
1502 assert_eq!(
1503 result.result.records.len(),
1504 1,
1505 "pending insert must stay invisible"
1506 );
1507 assert_eq!(result.result.records[0].get("id"), Some(&Value::Integer(1)));
1508 assert_eq!(
1509 result.result.records[0].get("payload"),
1510 Some(&Value::text("committed"))
1511 );
1512 runtime
1513 .execute_query("INSERT INTO crash_rows (id, payload) VALUES (3, 'after')")
1514 .expect("new writer after recovery");
1515 runtime.flush().expect("recovered checkpoint");
1516 drop(runtime);
1517 let reopened = open(path);
1518 let result = reopened
1519 .execute_query("SELECT id FROM crash_rows ORDER BY id")
1520 .expect("second recovery");
1521 assert_eq!(result.result.records.len(), 2);
1522 assert_eq!(result.result.records[0].get("id"), Some(&Value::Integer(1)));
1523 assert_eq!(result.result.records[1].get("id"), Some(&Value::Integer(3)));
1524 }
1525
1526 #[test]
1527 fn xid_resume_skips_known_base_payloads_but_rejects_an_unproven_root() {
1528 let directory = tempfile::tempdir().expect("resume fixture");
1529 let options = RedDBOptions::persistent(directory.path().join("resume.rdb"))
1530 .with_memory_budget(64 * 1024 * 1024)
1531 .with_storage_profile(
1532 crate::storage::profile::StorageDeployPreset::PrimaryReplicaDev.selection(),
1533 )
1534 .expect("profile");
1535 let runtime = crate::storage::RedDB::with_disk_residency_for_qualification(|| {
1536 RedDBRuntime::with_options(options)
1537 })
1538 .expect("runtime");
1539 runtime
1540 .execute_query("CREATE TABLE resume_rows (id INTEGER)")
1541 .expect("table");
1542 runtime
1543 .execute_query("INSERT INTO resume_rows (id) VALUES (1)")
1544 .expect("row");
1545 runtime.flush().expect("publish complete XID summary");
1546 let store = runtime.db().store();
1547 assert!(store.startup_xid_high_water_mark().is_some());
1548 let root = store
1549 .collection_root_page("resume_rows")
1550 .expect("published root");
1551 let manager = store.get_collection("resume_rows").expect("manager");
1552 for (candidate, proven) in [(root, true), (root + 1, false)] {
1553 manager.install_backing(
1554 Arc::new(Unreadable),
1555 candidate,
1556 store.format_version(),
1557 4096,
1558 1,
1559 );
1560 let mut overlay = crate::storage::UnifiedEntity::table_row(
1561 crate::storage::EntityId::new(999),
1562 "resume_rows",
1563 999,
1564 vec![Value::Integer(2)],
1565 );
1566 overlay.xmin = 500_000;
1567 overlay.xmax = 600_000;
1568 manager.insert(overlay).expect("resident WAL overlay");
1569 let result = runtime.rehydrate_snapshot_xid_floor();
1570 assert_eq!(result.is_ok(), proven, "root={candidate}: {result:?}");
1571 if proven {
1572 assert!(runtime.snapshot_manager().peek_next_xid() > 600_000);
1573 }
1574 }
1575 }
1576}