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 = if embedded_single_file {
241 crate::storage::cache::BlobCacheConfig::default()
242 } else {
243 crate::storage::cache::BlobCacheConfig::default().with_l2_path(
244 reddb_file::layout::result_cache_l2_path(
245 &options.resolved_path(reddb_file::default_database_path()),
246 ),
247 )
248 }
249 .with_l1_bytes_max(memory_shares.blob_cache_l1_bytes());
250 let result_blob_cache =
251 crate::storage::cache::BlobCache::open_with_l2(result_blob_cache_config).map_err(
252 |err| RedDBError::Internal(format!("open result Blob Cache L2 failed: {err:?}")),
253 )?;
254 let storage_ready_ms = std::time::SystemTime::now()
255 .duration_since(std::time::UNIX_EPOCH)
256 .map(|d| d.as_millis() as u64)
257 .unwrap_or(0);
258
259 let no_auth_boot = crate::service_cli::no_auth_active(&options);
260 let runtime = Self {
261 background_tasks: Some(Arc::new(background_tasks::RuntimeBackgroundTasks::default())),
262 inner: Arc::new(RuntimeInner {
263 functions: parking_lot::RwLock::new(
264 super::function_catalog::FunctionCatalog::load(&db)?,
265 ),
266 db: db.clone(),
267 layout: PhysicalLayout::from_options(&options),
268 embedded_single_file,
269 memory_budget,
270 memory_accounting: Arc::new(
271 crate::storage::memory_pools::MemoryAccounting::from_shares(
272 memory_budget,
273 memory_shares,
274 ),
275 ),
276 memory_reservations: parking_lot::Mutex::new(
277 super::memory_admission::MemoryReservations::default(),
278 ),
279 mvcc_maintenance: parking_lot::Mutex::new(Default::default()),
280 indices: IndexCatalog::register_default_vector_graph(
281 options.has_capability(crate::api::Capability::Table),
282 options.has_capability(crate::api::Capability::Graph),
283 ),
284 pool_config,
285 pool: Mutex::new(PoolState::default()),
286 execution_admission: parking_lot::RwLock::new(
287 crate::transport::execution_admission::ExecutionAdmission::for_host(),
288 ),
289 execution_queue_timeout_ms: AtomicU64::new(30_000),
290 started_at_unix_ms: SystemTime::now()
291 .duration_since(UNIX_EPOCH)
292 .unwrap_or_default()
293 .as_millis(),
294 probabilistic: super::probabilistic_store::ProbabilisticStore::new(),
295 index_store: super::index_store::IndexStore::new(),
296 cdc: crate::replication::cdc::CdcBuffer::new(100_000),
297 checkpoint_projection_stats: super::CheckpointProjectionStats::default(),
298 scrub_state: parking_lot::Mutex::new(super::ScrubRuntimeState::default()),
299 checkpoint_columnar_emission_budget_chunks: options
300 .checkpoint_columnar_emission_budget_chunks,
301 columnar_projection_size_floor_rows: options.columnar_projection_size_floor_rows,
302 backup_scheduler: crate::replication::scheduler::BackupScheduler::new(3600),
303 query_cache: parking_lot::RwLock::new(
304 crate::storage::query::planner::cache::PlanCache::new(1000),
305 ),
306 result_cache: parking_lot::RwLock::new((
307 HashMap::new(),
308 std::collections::VecDeque::new(),
309 )),
310 result_blob_cache,
311 result_blob_entries: parking_lot::RwLock::new((
312 HashMap::new(),
313 std::collections::VecDeque::new(),
314 )),
315 ask_answer_cache_entries: parking_lot::RwLock::new((
316 HashSet::new(),
317 std::collections::VecDeque::new(),
318 )),
319 result_cache_shadow_divergences: std::sync::atomic::AtomicU64::new(0),
320 result_cache_hits: std::sync::atomic::AtomicU64::new(0),
321 result_cache_misses: std::sync::atomic::AtomicU64::new(0),
322 result_cache_evictions: std::sync::atomic::AtomicU64::new(0),
323 ask_daily_spend: parking_lot::RwLock::new(HashMap::new()),
324 queue_message_locks: parking_lot::RwLock::new(HashMap::new()),
325 rmw_locks: RmwLockTable::new(),
326 planner_dirty_tables: parking_lot::RwLock::new(HashSet::new()),
327 ec_registry: Arc::new(crate::ec::config::EcRegistry::new()),
328 config_registry: Arc::new(crate::auth::registry::ConfigRegistry::new()),
329 ec_worker: crate::ec::worker::EcWorker::new(),
330 auth_store: parking_lot::RwLock::new(None),
331 oauth_validator: parking_lot::RwLock::new(None),
332 browser_token_authority: parking_lot::RwLock::new(None),
333 views: parking_lot::RwLock::new(HashMap::new()),
334 materialized_views: parking_lot::RwLock::new(
335 crate::storage::cache::result::MaterializedViewCache::new(),
336 ),
337 retention_sweeper: parking_lot::RwLock::new(
338 crate::runtime::retention_sweeper::RetentionSweeperState::new(),
339 ),
340 append_only_revalidation: parking_lot::Mutex::new(Default::default()),
341 transaction_state: crate::runtime::transaction_state::TransactionState::new(),
342 env_config_overrides: crate::runtime::config_overlay::collect_env_overrides(),
343 lock_manager: Arc::new({
344 let env = crate::runtime::config_overlay::collect_env_overrides();
349 let timeout_ms = env
350 .get("concurrency.locking.deadlock_timeout_ms")
351 .and_then(|raw| raw.parse::<u64>().ok())
352 .unwrap_or_else(|| {
353 match crate::runtime::config_matrix::default_for(
354 "concurrency.locking.deadlock_timeout_ms",
355 ) {
356 Some(crate::serde_json::Value::Number(n)) => n as u64,
357 _ => 5000,
358 }
359 });
360 let cfg = crate::runtime::lock_manager::LockConfig {
361 default_timeout: std::time::Duration::from_millis(timeout_ms),
362 ..Default::default()
363 };
364 crate::runtime::lock_manager::LockManager::new(cfg)
365 }),
366 rls_policies: parking_lot::RwLock::new(HashMap::new()),
367 rls_enabled_tables: parking_lot::RwLock::new(HashSet::new()),
368 foreign_tables: Arc::new(crate::storage::fdw::ForeignTableRegistry::with_builtins()),
369 pending_tombstones: parking_lot::RwLock::new(HashMap::new()),
370 pending_versioned_updates: parking_lot::RwLock::new(HashMap::new()),
371 pending_queue_dedup: parking_lot::RwLock::new(HashMap::new()),
372 pending_kv_watch_events: parking_lot::RwLock::new(HashMap::new()),
373 pending_vault_writes: parking_lot::RwLock::new(HashMap::new()),
374 pending_store_wal_actions: parking_lot::RwLock::new(HashMap::new()),
375 pending_claim_locks: parking_lot::RwLock::new(HashMap::new()),
376 queue_wait_registry: std::sync::Arc::new(
377 crate::runtime::queue_wait_registry::QueueWaitRegistry::new(),
378 ),
379 pending_queue_wakes: parking_lot::RwLock::new(HashMap::new()),
380 tenant_tables: parking_lot::RwLock::new(HashMap::new()),
381 ddl_epoch: std::sync::atomic::AtomicU64::new(0),
382 write_gate: Arc::new(crate::runtime::write_gate::WriteGate::from_options(
383 &options,
384 )),
385 lifecycle: crate::runtime::lifecycle::Lifecycle::new(),
386 resource_limits,
387 query_budget_slots: AtomicU64::new(0),
388 audit_log: {
389 let data_path = if embedded_single_file {
405 std::env::temp_dir()
406 .join("reddb-embedded-runtime")
407 .join(format!("audit-{}", std::process::id()))
408 } else {
409 options
410 .data_path
411 .clone()
412 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
413 };
414 let (audit_dest, _) = crate::api::tier_wiring::current_log_destinations();
415 if !matches!(audit_dest, crate::storage::layout::LogDestination::File(_))
416 && (embedded_single_file
417 || options
418 .metadata
419 .contains_key(crate::api::EPHEMERAL_RUNTIME_METADATA_KEY))
420 {
421 let audit_path = reddb_file::layout::sibling_path(
431 &data_path,
432 &reddb_file::layout::sidecar_file_name(&data_path, "audit.log"),
433 );
434 Arc::new(crate::runtime::audit_log::AuditLogger::with_path(
435 audit_path,
436 ))
437 } else {
438 Arc::new(crate::runtime::audit_log::AuditLogger::for_destination(
439 &audit_dest,
440 &data_path,
441 ))
442 }
443 },
444 control_event_ledger: parking_lot::RwLock::new(Arc::new(
445 crate::runtime::control_events::RuntimeLedger::new(db.store()),
446 )),
447 control_event_config: options.control_events,
448 query_audit: Arc::new(crate::runtime::query_audit::QueryAuditStream::new(
449 db.store(),
450 options.query_audit.clone(),
451 )),
452 lease_lifecycle: std::sync::OnceLock::new(),
453 replica_apply_metrics: std::sync::Arc::new(
454 crate::replication::logical::ReplicaApplyMetrics::default(),
455 ),
456 replica_link_metrics: std::sync::Arc::new(
457 crate::replication::reconnect::ReplicaLinkMetrics::default(),
458 ),
459 quota_bucket: crate::runtime::quota_bucket::QuotaBucket::from_env(),
460 schema_vocabulary: parking_lot::RwLock::new(
461 crate::runtime::schema_vocabulary::SchemaVocabulary::new(),
462 ),
463 slow_query_logger: {
464 let fallback_dir = if embedded_single_file {
481 std::env::temp_dir()
482 .join("reddb-embedded-runtime")
483 .join(format!("slow-{}", std::process::id()))
484 } else {
485 options
486 .data_path
487 .as_ref()
488 .and_then(|p| p.parent().map(std::path::PathBuf::from))
489 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
490 };
491 let threshold_ms = std::env::var("RED_SLOW_QUERY_THRESHOLD_MS")
492 .ok()
493 .and_then(|s| s.parse::<u64>().ok())
494 .unwrap_or(1000);
495 let sample_pct = std::env::var("RED_SLOW_QUERY_SAMPLE_PCT")
496 .ok()
497 .and_then(|s| s.parse::<u8>().ok())
498 .unwrap_or(100);
499 let (_, slow_dest) = crate::api::tier_wiring::current_log_destinations();
500 crate::telemetry::slow_query_logger::SlowQueryLogger::for_destination(
501 &slow_dest,
502 &fallback_dir,
503 threshold_ms,
504 sample_pct,
505 )
506 },
507 slow_query_store: crate::telemetry::slow_query_store::SlowQueryStore::new(
508 crate::telemetry::slow_query_store::DEFAULT_CAP,
509 ),
510 kv_stats: crate::runtime::KvStatsCounters::default(),
511 metrics_ingest_stats: crate::runtime::MetricsIngestCounters::default(),
512 metrics_tenant_activity_stats:
513 crate::runtime::MetricsTenantActivityCounters::default(),
514 claim_telemetry: Arc::new(
515 crate::runtime::claim_telemetry::ClaimTelemetryCounters::default(),
516 ),
517 queue_telemetry: Arc::new(
518 crate::runtime::queue_telemetry::QueueTelemetryCounters::default(),
519 ),
520 query_latency_telemetry: Arc::new(
521 crate::runtime::query_latency_telemetry::QueryLatencyTelemetry::default(),
522 ),
523 occupancy_sampler: Arc::new(
524 crate::runtime::occupancy_sampler::OccupancySampler::new(),
525 ),
526 node_load_telemetry: Arc::new(
527 crate::runtime::node_load_telemetry::NodeLoadTelemetry::default(),
528 ),
529 queue_presence: Arc::new(
530 crate::storage::queue::presence::ConsumerPresenceRegistry::new(),
531 ),
532 vector_introspection: Arc::new(
533 crate::storage::vector::introspection::VectorIntrospectionRegistry::new(),
534 ),
535 kv_tag_index: crate::runtime::KvTagIndex::default(),
536 chain_tip_cache: parking_lot::Mutex::new(HashMap::new()),
537 chain_integrity_broken: parking_lot::Mutex::new(HashMap::new()),
538 integrity_tombstones: parking_lot::Mutex::new(Vec::new()),
539 integrity_tombstones_state: std::sync::atomic::AtomicU8::new(0),
540 }),
541 };
542
543 runtime.install_hash_candidate_admission();
544
545 crate::telemetry::operator_event::install_global_audit_sink(Arc::clone(
551 &runtime.inner.audit_log,
552 ));
553
554 runtime
557 .inner
558 .slow_query_logger
559 .attach_store(Arc::clone(&runtime.inner.slow_query_store));
560
561 runtime
569 .inner
570 .lifecycle
571 .set_restore_started_at_ms(boot_open_start_ms);
572 runtime
573 .inner
574 .lifecycle
575 .set_restore_ready_at_ms(storage_ready_ms);
576 runtime
577 .inner
578 .lifecycle
579 .set_wal_replay_started_at_ms(boot_open_start_ms);
580 runtime
581 .inner
582 .lifecycle
583 .set_wal_replay_ready_at_ms(storage_ready_ms);
584
585 let restored_cdc_lsn = runtime
586 .inner
587 .db
588 .replication
589 .as_ref()
590 .map(|repl| {
591 repl.logical_wal_spool
592 .as_ref()
593 .map(|spool| spool.current_lsn())
594 .unwrap_or(0)
595 })
596 .unwrap_or(0)
597 .max(runtime.config_u64("red.config.timeline.last_archived_lsn", 0));
598 runtime.inner.cdc.set_current_lsn(restored_cdc_lsn);
599 runtime.rehydrate_snapshot_xid_floor()?;
600 assert!(runtime
601 .inner
602 .db
603 .runtime
604 .set(super::WeakRuntime(Arc::downgrade(&runtime.inner)))
605 .is_ok());
606 assert!(
607 runtime
608 .inner
609 .db
610 .store()
611 .snapshot_manager
612 .set(runtime.snapshot_manager())
613 .is_ok(),
614 "a new runtime owns the store transaction outcomes"
615 );
616 runtime
617 .bootstrap_system_keyed_collections()
618 .map_err(|err| RedDBError::Internal(format!("bootstrap system collections: {err}")))?;
619 runtime.rehydrate_declared_column_schemas();
620 runtime.prepare_paged_counts()?;
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 if let Some(repl) = &runtime.inner.db.replication {
653 repl.wal_buffer.set_current_lsn(restored_cdc_lsn);
654 }
655
656 {
658 let sys = SystemInfo::collect();
659 runtime.inner.db.store().set_config_tree(
660 "red.system",
661 &crate::serde_json::json!({
662 "pid": sys.pid,
663 "cpu_cores": sys.cpu_cores,
664 "total_memory_bytes": sys.total_memory_bytes,
665 "available_memory_bytes": sys.available_memory_bytes,
666 "os": sys.os,
667 "arch": sys.arch,
668 "hostname": sys.hostname,
669 "started_at": SystemTime::now()
670 .duration_since(UNIX_EPOCH)
671 .unwrap_or_default()
672 .as_millis() as u64
673 }),
674 );
675
676 let store = runtime.inner.db.store();
678 if store
679 .get_collection("red_config")
680 .map(|m| m.query_all(|_| true).len())
681 .unwrap_or(0)
682 <= 10
683 {
684 store.set_config_tree("red.ai", &crate::json!({
685 "default": crate::json!({
686 "provider": "openai",
687 "model": crate::ai::DEFAULT_OPENAI_PROMPT_MODEL
688 }),
689 "max_embedding_inputs": 256,
690 "max_prompt_batch": 256,
691 "timeout": crate::json!({ "connect_secs": 10, "read_secs": 90, "write_secs": 30 })
692 }));
693 store.set_config_tree(
694 "red.server",
695 &crate::json!({
696 "max_scan_limit": 1000,
697 "max_body_size": 1048576,
698 "read_timeout_ms": 5000,
699 "write_timeout_ms": 5000
700 }),
701 );
702 store.set_config_tree(
703 "red.storage",
704 &crate::json!({
705 "page_size": 4096,
706 "page_cache_capacity": 100000,
707 "auto_checkpoint_pages": 1000,
708 "snapshot_retention": 16,
709 "verify_checksums": true,
710 "segment": crate::json!({
711 "max_entities": 100000,
712 "max_bytes": 268435456_u64,
713 "compression_level": 6
714 }),
715 "hnsw": crate::json!({ "m": 16, "ef_construction": 100, "ef_search": 50 }),
716 "ivf": crate::json!({ "n_lists": 100, "n_probes": 10 }),
717 "bm25": crate::json!({ "k1": 1.2, "b": 0.75 })
718 }),
719 );
720 store.set_config_tree(
721 "red.search",
722 &crate::json!({
723 "rag": crate::json!({
724 "max_chunks_per_source": 10,
725 "max_total_chunks": 25,
726 "similarity_threshold": 0.8,
727 "graph_depth": 2,
728 "min_relevance": 0.3
729 }),
730 "fusion": crate::json!({
731 "vector_weight": 0.5,
732 "graph_weight": 0.3,
733 "table_weight": 0.2,
734 "dedup_threshold": 0.85
735 })
736 }),
737 );
738 store.set_config_tree(
739 "red.auth",
740 &crate::json!({
741 "enabled": false,
742 "session_ttl_secs": 3600,
743 "require_auth": false
744 }),
745 );
746 store.set_config_tree(
747 "red.query",
748 &crate::json!({
749 "connection_pool": crate::json!({ "max_connections": 64, "max_idle": 16 }),
750 "max_recursion_depth": 1000
751 }),
752 );
753 store.set_config_tree(
754 "red.indexes",
755 &crate::json!({
756 "auto_select": true,
757 "bloom_filter": crate::json!({
758 "enabled": true,
759 "false_positive_rate": 0.01,
760 "prune_on_scan": true
761 }),
762 "hash": crate::json!({ "enabled": true }),
763 "bitmap": crate::json!({ "enabled": true, "max_cardinality": 1000 }),
764 "spatial": crate::json!({ "enabled": true })
765 }),
766 );
767 store.set_config_tree(
768 "red.probabilistic",
769 &crate::json!({
770 "hll_registers": 16384,
771 "sketch_default_width": 1000,
772 "sketch_default_depth": 5,
773 "filter_default_capacity": 100000
774 }),
775 );
776 store.set_config_tree(
777 "red.timeseries",
778 &crate::json!({
779 "default_chunk_size": 1024,
780 "compression": crate::json!({
781 "timestamps": "delta_of_delta",
782 "values": "gorilla_xor"
783 }),
784 "default_retention_days": 0
785 }),
786 );
787 store.set_config_tree(
788 "red.queue",
789 &crate::json!({
790 "default_max_size": 0,
791 "default_max_attempts": 3,
792 "visibility_timeout_ms": 30000,
793 "consumer_idle_timeout_ms": 60000
794 }),
795 );
796 store.set_config_tree(
797 "red.backup",
798 &crate::json!({
799 "enabled": false,
800 "interval_secs": 3600,
801 "retention_count": 24,
802 "upload": false,
803 "backend": "local"
804 }),
805 );
806 store.set_config_tree(
807 "red.wal",
808 &crate::json!({
809 "archive": crate::json!({
810 "enabled": false,
811 "retention_hours": 168,
812 "prefix": reddb_file::backup_wal_prefix("")
813 })
814 }),
815 );
816 store.set_config_tree(
817 "red.cdc",
818 &crate::json!({
819 "enabled": true,
820 "buffer_size": 100000
821 }),
822 );
823 store.set_config_tree(
824 "red.config.secret",
825 &crate::json!({
826 "auto_encrypt": true,
827 "auto_decrypt": true
828 }),
829 );
830 }
831
832 crate::runtime::config_matrix::heal_critical_keys(store.as_ref());
839 seed_storage_deploy_config(store.as_ref(), options.storage_profile);
840
841 let lehman_yao = runtime.config_bool("storage.btree.lehman_yao", true);
848 crate::storage::engine::btree::lehman_yao::set_enabled(lehman_yao);
849 if lehman_yao {
850 tracing::info!(
851 "storage.btree.lehman_yao=true — lock-free concurrent descent enabled"
852 );
853 }
854
855 let overlay_path = crate::runtime::config_overlay::config_file_path();
860 let _ =
861 crate::runtime::config_overlay::apply_config_file(store.as_ref(), &overlay_path);
862 }
863
864 {
868 let store = runtime.inner.db.store();
869 for name in crate::application::vcs_collections::ALL {
870 let _ = store.get_or_create_collection(*name);
871 }
872 store.set_config_tree(
875 crate::application::vcs_collections::CONFIG_NAMESPACE,
876 &crate::json!({
877 "default_branch": "main",
878 "author": crate::json!({
879 "name": "reddb",
880 "email": "reddb@localhost"
881 }),
882 "protected_branches": crate::json!(["main"]),
883 "closure": crate::json!({
884 "enabled": true,
885 "lazy": true
886 }),
887 "merge": crate::json!({
888 "default_strategy": "auto",
889 "fast_forward": true
890 })
891 }),
892 );
893 }
894
895 {
898 let store = runtime.inner.db.store();
899 for name in crate::application::migration_collections::ALL {
900 let _ = store.get_or_create_collection(*name);
901 }
902 }
903
904 let _ = crate::application::topology_collections::ensure(&runtime);
908
909 runtime
917 .inner
918 .db
919 .store()
920 .register_entity_id(crate::storage::EntityId::new(
921 crate::storage::FIRST_USER_ENTITY_ID - 1,
922 ));
923
924 {
938 let weak = Arc::downgrade(&runtime.inner);
939 runtime
940 .background_tasks
941 .as_ref()
942 .expect("foreground runtime")
943 .spawn(
944 std::thread::Builder::new().name("reddb-maintenance".into()),
945 move |stopped| {
946 let tick = std::time::Duration::from_millis(200);
947 let work_interval = std::time::Duration::from_secs(60);
948 let mut last_work = std::time::Instant::now();
949 let mut allocator_pressure =
950 super::allocator_pressure::AllocatorPressure::default();
951 loop {
952 std::thread::park_timeout(tick);
953 if stopped.load(AtomicOrdering::Acquire) {
954 break;
955 }
956 let Some(inner) = weak.upgrade() else {
957 break;
960 };
961 if last_work.elapsed() >= work_interval {
962 let _stats = inner.db.store().context_index().stats();
963 last_work = std::time::Instant::now();
964 }
965 let rt = RedDBRuntime {
966 background_tasks: None,
967 inner,
968 };
969 rt.try_reclaim_history_tick();
970 allocator_pressure.tick();
971 }
972 },
973 );
974 }
975
976 {
978 let store = runtime.inner.db.store();
979 let mut backup_enabled = false;
980 let mut backup_interval = 3600u64;
981
982 if let Some(manager) = store.get_collection("red_config") {
983 manager.for_each_entity(|entity| {
984 if let Some(row) = entity.data.as_row() {
985 let key = row.get_field("key").and_then(|v| match v {
986 reddb_types::Value::Text(s) => Some(s.as_ref()),
987 _ => None,
988 });
989 let val = row.get_field("value");
990 if key == Some("red.config.backup.enabled") {
991 backup_enabled = match val {
992 Some(reddb_types::Value::Boolean(true)) => true,
993 Some(reddb_types::Value::Text(s)) => &**s == "true",
994 _ => false,
995 };
996 } else if key == Some("red.config.backup.interval_secs") {
997 if let Some(reddb_types::Value::Integer(n)) = val {
998 backup_interval = *n as u64;
999 }
1000 }
1001 }
1002 true
1003 });
1004 }
1005
1006 if backup_enabled {
1007 runtime.inner.backup_scheduler.set_interval(backup_interval);
1008 let rt = runtime.clone();
1009 runtime
1010 .inner
1011 .backup_scheduler
1012 .start(move || rt.trigger_backup().map_err(|e| format!("{}", e)));
1013 }
1014 }
1015
1016 {
1018 runtime
1019 .inner
1020 .ec_registry
1021 .load_from_config_store(runtime.inner.db.store().as_ref());
1022 if !runtime.inner.ec_registry.async_configs().is_empty() {
1023 runtime.inner.ec_worker.start(
1024 Arc::clone(&runtime.inner.ec_registry),
1025 Arc::clone(&runtime.inner.db.store()),
1026 );
1027 }
1028 }
1029
1030 if let (true, crate::replication::ReplicationRole::Replica { primary_addr }) = (
1031 runtime.inner.db.options().replica_loop_enabled,
1032 runtime.inner.db.options().replication.role.clone(),
1033 ) {
1034 let rt = runtime.clone();
1035 std::thread::Builder::new()
1036 .name("reddb-replica".into())
1037 .spawn(move || rt.run_replica_loop(primary_addr))
1038 .ok();
1039 }
1040
1041 runtime.inner.lifecycle.mark_ready();
1046
1047 {
1056 let weak_inner = Arc::downgrade(&runtime.inner);
1057 runtime
1058 .background_tasks
1059 .as_ref()
1060 .expect("foreground runtime")
1061 .spawn(
1062 std::thread::Builder::new()
1063 .name("reddb-mv-scheduler".into())
1064 .stack_size(8 * 1024 * 1024),
1069 move |stopped| loop {
1070 std::thread::park_timeout(std::time::Duration::from_millis(50));
1071 if stopped.load(AtomicOrdering::Acquire) {
1072 break;
1073 }
1074 let Some(inner) = weak_inner.upgrade() else {
1075 break;
1076 };
1077 let rt = RedDBRuntime {
1078 background_tasks: None,
1079 inner,
1080 };
1081 rt.refresh_due_materialized_views();
1082 },
1083 );
1084 }
1085
1086 if !runtime.write_gate().is_read_only() {
1096 let weak_inner = Arc::downgrade(&runtime.inner);
1097 runtime
1098 .background_tasks
1099 .as_ref()
1100 .expect("foreground runtime")
1101 .spawn(
1102 std::thread::Builder::new().name("reddb-retention-sweeper".into()),
1103 move |stopped| loop {
1104 std::thread::park_timeout(std::time::Duration::from_millis(500));
1105 if stopped.load(AtomicOrdering::Acquire) {
1106 break;
1107 }
1108 let Some(inner) = weak_inner.upgrade() else {
1109 break;
1110 };
1111 let rt = RedDBRuntime {
1112 background_tasks: None,
1113 inner,
1114 };
1115 rt.sweep_retention_tick(
1116 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
1117 );
1118 },
1119 );
1120 }
1121
1122 if no_auth_boot {
1126 use crate::runtime::control_events::{
1127 ActorRef, ControlEvent, ControlEventCtx, EventKind, Outcome,
1128 };
1129 let ledger = std::sync::Arc::clone(&runtime.inner.control_event_ledger.read());
1130 let ctx = ControlEventCtx {
1131 actor: ActorRef::System("boot"),
1132 scope: None,
1133 request_id: None,
1134 trace_id: None,
1135 };
1136 let event = ControlEvent {
1137 kind: EventKind::AuthDisabled,
1138 outcome: Outcome::Allowed,
1139 action: std::borrow::Cow::Borrowed("auth.disabled"),
1140 resource: Some("server".to_string()),
1141 reason: Some("--no-auth / --dev".to_string()),
1142 matched_policy_id: None,
1143 fields: std::collections::HashMap::new(),
1144 };
1145 if let Err(err) = ledger.emit(&ctx, event) {
1146 tracing::warn!(
1147 target: "reddb::security",
1148 error = %err,
1149 "could not record the auth-disabled boot in the control-event ledger"
1150 );
1151 }
1152 }
1153
1154 Ok(runtime)
1155 }
1156
1157 fn rehydrate_snapshot_xid_floor(&self) -> RedDBResult<()> {
1158 let store = self.inner.db.store();
1159 let snapshot_manager = self.inner.transaction_state.snapshot_manager();
1160 let base_high_water_mark = store.startup_xid_high_water_mark();
1161 if let Some(high_water_mark) = base_high_water_mark {
1162 snapshot_manager.observe_committed_xid(high_water_mark);
1163 }
1164 for collection in store.list_collections() {
1165 let Some(manager) = store.get_collection(&collection) else {
1166 continue;
1167 };
1168 let observe = |entity: &crate::storage::UnifiedEntity| {
1169 snapshot_manager.observe_committed_xid(entity.xmin);
1170 snapshot_manager.observe_committed_xid(entity.xmax);
1171 true
1172 };
1173 if base_high_water_mark.is_some() {
1174 manager.for_each_entity(observe);
1177 } else {
1178 manager.try_for_each_entity(usize::MAX, observe)?;
1181 }
1182 }
1183 if base_high_water_mark.is_none() {
1184 store.remember_startup_xid_high_water_mark(
1185 snapshot_manager.peek_next_xid().saturating_sub(1),
1186 );
1187 }
1188 Ok(())
1189 }
1190
1191 pub(crate) fn ensure_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1204 let store = self.inner.db.store();
1205 let mut changed = false;
1206 if store.get_collection(name).is_none() {
1207 store.get_or_create_collection(name);
1208 changed = true;
1209 }
1210 if self.inner.db.collection_contract(name).is_none() {
1211 self.inner
1212 .db
1213 .save_collection_contract(system_keyed_collection_contract(
1214 name,
1215 crate::catalog::CollectionModel::Table,
1216 ))
1217 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1218 changed = true;
1219 }
1220 if changed {
1221 self.inner
1222 .db
1223 .persist_metadata()
1224 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1225 }
1226 Ok(())
1227 }
1228
1229 pub(crate) fn drop_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1234 let store = self.inner.db.store();
1235 if store.get_collection(name).is_none() {
1236 return Ok(());
1237 }
1238 store
1239 .drop_collection(name)
1240 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1241 if self.inner.db.collection_contract(name).is_some() {
1244 self.inner
1245 .db
1246 .remove_collection_contract(name)
1247 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1248 }
1249 self.invalidate_result_cache();
1250 self.inner
1251 .db
1252 .persist_metadata()
1253 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1254 Ok(())
1255 }
1256
1257 fn bootstrap_system_keyed_collections(&self) -> RedDBResult<()> {
1258 let mut changed = false;
1259 for (name, model) in [
1260 ("red.config", crate::catalog::CollectionModel::Config),
1261 ("red.vault", crate::catalog::CollectionModel::Vault),
1262 (
1266 crate::runtime::continuous_materialized_view::CATALOG_COLLECTION,
1267 crate::catalog::CollectionModel::Config,
1268 ),
1269 ] {
1270 if self.inner.db.store().get_collection(name).is_none() {
1271 self.inner.db.store().get_or_create_collection(name);
1272 changed = true;
1273 }
1274 if self.inner.db.collection_contract(name).is_none() {
1275 self.inner
1276 .db
1277 .save_collection_contract(system_keyed_collection_contract(name, model))
1278 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1279 changed = true;
1280 }
1281 }
1282 if changed {
1283 self.inner
1284 .db
1285 .persist_metadata()
1286 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1287 }
1288 Ok(())
1289 }
1290
1291 pub fn db(&self) -> Arc<RedDB> {
1292 Arc::clone(&self.inner.db)
1293 }
1294
1295 pub fn index_store_ref(&self) -> &super::index_store::IndexStore {
1300 &self.inner.index_store
1301 }
1302
1303 pub(crate) fn schema_vocabulary_apply(
1308 &self,
1309 event: crate::runtime::schema_vocabulary::DdlEvent,
1310 ) {
1311 self.inner.schema_vocabulary.write().on_ddl(event);
1312 }
1313
1314 pub fn schema_vocabulary_lookup(
1319 &self,
1320 token: &str,
1321 ) -> Vec<crate::runtime::schema_vocabulary::VocabHit> {
1322 self.inner.schema_vocabulary.read().lookup(token).to_vec()
1323 }
1324
1325 pub fn set_auth_store(&self, store: Arc<crate::auth::store::AuthStore>) {
1329 *self.inner.auth_store.write() = Some(store);
1330 }
1331
1332 pub fn auth_store(&self) -> Option<Arc<crate::auth::store::AuthStore>> {
1335 self.inner.auth_store.read().clone()
1336 }
1337
1338 pub fn set_oauth_validator(&self, validator: Option<Arc<crate::auth::oauth::OAuthValidator>>) {
1342 *self.inner.oauth_validator.write() = validator;
1343 }
1344
1345 pub fn oauth_validator(&self) -> Option<Arc<crate::auth::oauth::OAuthValidator>> {
1349 self.inner.oauth_validator.read().clone()
1350 }
1351
1352 pub fn set_browser_token_authority(
1358 &self,
1359 authority: Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>,
1360 ) {
1361 *self.inner.browser_token_authority.write() = authority;
1362 }
1363
1364 pub fn browser_token_authority(
1368 &self,
1369 ) -> Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>> {
1370 self.inner.browser_token_authority.read().clone()
1371 }
1372}
1373
1374#[cfg(test)]
1375mod xid_resume_tests {
1376 use super::*;
1377 use reddb_engine::storage::collection_read::{CollectionCursor, CollectionRead, EncodedRecord};
1378
1379 struct Unreadable;
1380
1381 fn unavailable() -> crate::storage::StoreError {
1382 crate::storage::StoreError::Io(std::io::Error::other("XID bootstrap read a base payload"))
1383 }
1384
1385 impl CollectionRead for Unreadable {
1386 fn get_record(
1387 &self,
1388 id: crate::storage::EntityId,
1389 _: usize,
1390 ) -> Result<Option<EncodedRecord>, crate::storage::StoreError> {
1391 if id.raw() == 999 {
1392 Ok(None)
1393 } else {
1394 Err(unavailable())
1395 }
1396 }
1397 fn cursor(&self) -> Result<Box<dyn CollectionCursor + '_>, crate::storage::StoreError> {
1398 Err(unavailable())
1399 }
1400 fn owned_cursor(&self) -> Result<Box<dyn CollectionCursor>, crate::storage::StoreError> {
1401 Err(unavailable())
1402 }
1403 }
1404
1405 #[test]
1406 fn xid_resume_survives_pending_checkpoint_process_exit() {
1407 const FIXTURE: &str = "REDDB_XID_RESUME_CRASH_FIXTURE";
1408 let open = |path: std::path::PathBuf| {
1409 let options = RedDBOptions::persistent(path)
1410 .with_memory_budget(64 * 1024 * 1024)
1411 .with_storage_profile(
1412 crate::storage::profile::StorageDeployPreset::PrimaryReplicaDev.selection(),
1413 )
1414 .expect("profile");
1415 crate::storage::RedDB::with_disk_residency_for_qualification(|| {
1416 RedDBRuntime::with_options(options)
1417 })
1418 .expect("crash recovery runtime")
1419 };
1420 if let Some(root) = std::env::var_os(FIXTURE) {
1421 let root = std::path::PathBuf::from(root);
1422 assert_eq!(
1423 std::fs::read_to_string(root.join("owned-fixture")).expect("owned fixture"),
1424 "xid-resume-crash-v1"
1425 );
1426 let runtime = open(root.join("crash.rdb"));
1427 for sql in [
1428 "CREATE TABLE crash_rows (id INTEGER, payload TEXT)",
1429 "INSERT INTO crash_rows (id, payload) VALUES (1, 'committed')",
1430 "BEGIN",
1431 "UPDATE crash_rows SET payload = 'pending' WHERE id = 1",
1432 "INSERT INTO crash_rows (id, payload) VALUES (2, 'pending')",
1433 ] {
1434 runtime.execute_query(sql).expect("crash fixture statement");
1435 }
1436 runtime
1437 .flush()
1438 .expect("checkpoint with pending transaction");
1439 std::fs::write(
1440 root.join("checkpoint-complete"),
1441 "pending transaction remains open",
1442 )
1443 .expect("acknowledged fixture checkpoint");
1444 std::process::exit(0);
1447 }
1448 let directory = tempfile::tempdir().expect("owned crash fixture");
1449 std::fs::write(
1450 directory.path().join("owned-fixture"),
1451 "xid-resume-crash-v1",
1452 )
1453 .expect("ownership marker");
1454 let child = std::process::Command::new(std::env::current_exe().expect("test executable"))
1455 .args([
1456 "--exact",
1457 "runtime::impl_lifecycle::xid_resume_tests::xid_resume_survives_pending_checkpoint_process_exit",
1458 "--test-threads=1",
1459 ])
1460 .env(FIXTURE, directory.path())
1461 .output()
1462 .expect("crashing owned child");
1463 assert!(
1464 child.status.success(),
1465 "{}",
1466 String::from_utf8_lossy(&child.stderr)
1467 );
1468 assert!(directory.path().join("checkpoint-complete").is_file());
1469 let path = directory.path().join("crash.rdb");
1470 assert!(path.is_file());
1471 let runtime = open(path.clone());
1472 let result = runtime
1473 .execute_query("SELECT id, payload FROM crash_rows ORDER BY id")
1474 .expect("committed preimage");
1475 assert_eq!(
1476 result.result.records.len(),
1477 1,
1478 "pending insert must stay invisible"
1479 );
1480 assert_eq!(result.result.records[0].get("id"), Some(&Value::Integer(1)));
1481 assert_eq!(
1482 result.result.records[0].get("payload"),
1483 Some(&Value::text("committed"))
1484 );
1485 runtime
1486 .execute_query("INSERT INTO crash_rows (id, payload) VALUES (3, 'after')")
1487 .expect("new writer after recovery");
1488 runtime.flush().expect("recovered checkpoint");
1489 drop(runtime);
1490 let reopened = open(path);
1491 let result = reopened
1492 .execute_query("SELECT id FROM crash_rows ORDER BY id")
1493 .expect("second recovery");
1494 assert_eq!(result.result.records.len(), 2);
1495 assert_eq!(result.result.records[0].get("id"), Some(&Value::Integer(1)));
1496 assert_eq!(result.result.records[1].get("id"), Some(&Value::Integer(3)));
1497 }
1498
1499 #[test]
1500 fn xid_resume_skips_known_base_payloads_but_rejects_an_unproven_root() {
1501 let directory = tempfile::tempdir().expect("resume fixture");
1502 let options = RedDBOptions::persistent(directory.path().join("resume.rdb"))
1503 .with_memory_budget(64 * 1024 * 1024)
1504 .with_storage_profile(
1505 crate::storage::profile::StorageDeployPreset::PrimaryReplicaDev.selection(),
1506 )
1507 .expect("profile");
1508 let runtime = crate::storage::RedDB::with_disk_residency_for_qualification(|| {
1509 RedDBRuntime::with_options(options)
1510 })
1511 .expect("runtime");
1512 runtime
1513 .execute_query("CREATE TABLE resume_rows (id INTEGER)")
1514 .expect("table");
1515 runtime
1516 .execute_query("INSERT INTO resume_rows (id) VALUES (1)")
1517 .expect("row");
1518 runtime.flush().expect("publish complete XID summary");
1519 let store = runtime.db().store();
1520 assert!(store.startup_xid_high_water_mark().is_some());
1521 let root = store
1522 .collection_root_page("resume_rows")
1523 .expect("published root");
1524 let manager = store.get_collection("resume_rows").expect("manager");
1525 for (candidate, proven) in [(root, true), (root + 1, false)] {
1526 manager.install_backing(
1527 Arc::new(Unreadable),
1528 candidate,
1529 store.format_version(),
1530 4096,
1531 1,
1532 );
1533 let mut overlay = crate::storage::UnifiedEntity::table_row(
1534 crate::storage::EntityId::new(999),
1535 "resume_rows",
1536 999,
1537 vec![Value::Integer(2)],
1538 );
1539 overlay.xmin = 500_000;
1540 overlay.xmax = 600_000;
1541 manager.insert(overlay).expect("resident WAL overlay");
1542 let result = runtime.rehydrate_snapshot_xid_floor();
1543 assert_eq!(result.is_ok(), proven, "root={candidate}: {result:?}");
1544 if proven {
1545 assert!(runtime.snapshot_manager().peek_next_xid() > 600_000);
1546 }
1547 }
1548 }
1549}