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!(
601 runtime
602 .inner
603 .db
604 .store()
605 .snapshot_manager
606 .set(runtime.snapshot_manager())
607 .is_ok(),
608 "a new runtime owns the store transaction outcomes"
609 );
610 runtime
611 .bootstrap_system_keyed_collections()
612 .map_err(|err| RedDBError::Internal(format!("bootstrap system collections: {err}")))?;
613 runtime.rehydrate_declared_column_schemas();
614 runtime.prepare_paged_counts()?;
615 super::control_event_index::prepare(&runtime)?;
618 super::queue_storage_index::prepare(&runtime)?;
619 runtime.rehydrate_runtime_index_registry()?;
620 if runtime
621 .db()
622 .store()
623 .config()
624 .resident_memory_bytes_max
625 .is_some()
626 {
627 runtime
628 .db()
629 .store()
630 .checkpoint_missing_secondary_indices()?;
631 }
632 runtime.rehydrate_kv_tags()?;
633 runtime
634 .load_probabilistic_state()
635 .map_err(|err| RedDBError::Internal(format!("load probabilistic state: {err}")))?;
636
637 runtime.rehydrate_tenant_tables();
641 runtime.rehydrate_materialized_view_descriptors();
646 if let Some(repl) = &runtime.inner.db.replication {
647 repl.wal_buffer.set_current_lsn(restored_cdc_lsn);
648 }
649
650 {
652 let sys = SystemInfo::collect();
653 runtime.inner.db.store().set_config_tree(
654 "red.system",
655 &crate::serde_json::json!({
656 "pid": sys.pid,
657 "cpu_cores": sys.cpu_cores,
658 "total_memory_bytes": sys.total_memory_bytes,
659 "available_memory_bytes": sys.available_memory_bytes,
660 "os": sys.os,
661 "arch": sys.arch,
662 "hostname": sys.hostname,
663 "started_at": SystemTime::now()
664 .duration_since(UNIX_EPOCH)
665 .unwrap_or_default()
666 .as_millis() as u64
667 }),
668 );
669
670 let store = runtime.inner.db.store();
672 if store
673 .get_collection("red_config")
674 .map(|m| m.query_all(|_| true).len())
675 .unwrap_or(0)
676 <= 10
677 {
678 store.set_config_tree("red.ai", &crate::json!({
679 "default": crate::json!({
680 "provider": "openai",
681 "model": crate::ai::DEFAULT_OPENAI_PROMPT_MODEL
682 }),
683 "max_embedding_inputs": 256,
684 "max_prompt_batch": 256,
685 "timeout": crate::json!({ "connect_secs": 10, "read_secs": 90, "write_secs": 30 })
686 }));
687 store.set_config_tree(
688 "red.server",
689 &crate::json!({
690 "max_scan_limit": 1000,
691 "max_body_size": 1048576,
692 "read_timeout_ms": 5000,
693 "write_timeout_ms": 5000
694 }),
695 );
696 store.set_config_tree(
697 "red.storage",
698 &crate::json!({
699 "page_size": 4096,
700 "page_cache_capacity": 100000,
701 "auto_checkpoint_pages": 1000,
702 "snapshot_retention": 16,
703 "verify_checksums": true,
704 "segment": crate::json!({
705 "max_entities": 100000,
706 "max_bytes": 268435456_u64,
707 "compression_level": 6
708 }),
709 "hnsw": crate::json!({ "m": 16, "ef_construction": 100, "ef_search": 50 }),
710 "ivf": crate::json!({ "n_lists": 100, "n_probes": 10 }),
711 "bm25": crate::json!({ "k1": 1.2, "b": 0.75 })
712 }),
713 );
714 store.set_config_tree(
715 "red.search",
716 &crate::json!({
717 "rag": crate::json!({
718 "max_chunks_per_source": 10,
719 "max_total_chunks": 25,
720 "similarity_threshold": 0.8,
721 "graph_depth": 2,
722 "min_relevance": 0.3
723 }),
724 "fusion": crate::json!({
725 "vector_weight": 0.5,
726 "graph_weight": 0.3,
727 "table_weight": 0.2,
728 "dedup_threshold": 0.85
729 })
730 }),
731 );
732 store.set_config_tree(
733 "red.auth",
734 &crate::json!({
735 "enabled": false,
736 "session_ttl_secs": 3600,
737 "require_auth": false
738 }),
739 );
740 store.set_config_tree(
741 "red.query",
742 &crate::json!({
743 "connection_pool": crate::json!({ "max_connections": 64, "max_idle": 16 }),
744 "max_recursion_depth": 1000
745 }),
746 );
747 store.set_config_tree(
748 "red.indexes",
749 &crate::json!({
750 "auto_select": true,
751 "bloom_filter": crate::json!({
752 "enabled": true,
753 "false_positive_rate": 0.01,
754 "prune_on_scan": true
755 }),
756 "hash": crate::json!({ "enabled": true }),
757 "bitmap": crate::json!({ "enabled": true, "max_cardinality": 1000 }),
758 "spatial": crate::json!({ "enabled": true })
759 }),
760 );
761 store.set_config_tree(
762 "red.probabilistic",
763 &crate::json!({
764 "hll_registers": 16384,
765 "sketch_default_width": 1000,
766 "sketch_default_depth": 5,
767 "filter_default_capacity": 100000
768 }),
769 );
770 store.set_config_tree(
771 "red.timeseries",
772 &crate::json!({
773 "default_chunk_size": 1024,
774 "compression": crate::json!({
775 "timestamps": "delta_of_delta",
776 "values": "gorilla_xor"
777 }),
778 "default_retention_days": 0
779 }),
780 );
781 store.set_config_tree(
782 "red.queue",
783 &crate::json!({
784 "default_max_size": 0,
785 "default_max_attempts": 3,
786 "visibility_timeout_ms": 30000,
787 "consumer_idle_timeout_ms": 60000
788 }),
789 );
790 store.set_config_tree(
791 "red.backup",
792 &crate::json!({
793 "enabled": false,
794 "interval_secs": 3600,
795 "retention_count": 24,
796 "upload": false,
797 "backend": "local"
798 }),
799 );
800 store.set_config_tree(
801 "red.wal",
802 &crate::json!({
803 "archive": crate::json!({
804 "enabled": false,
805 "retention_hours": 168,
806 "prefix": reddb_file::backup_wal_prefix("")
807 })
808 }),
809 );
810 store.set_config_tree(
811 "red.cdc",
812 &crate::json!({
813 "enabled": true,
814 "buffer_size": 100000
815 }),
816 );
817 store.set_config_tree(
818 "red.config.secret",
819 &crate::json!({
820 "auto_encrypt": true,
821 "auto_decrypt": true
822 }),
823 );
824 }
825
826 crate::runtime::config_matrix::heal_critical_keys(store.as_ref());
833 seed_storage_deploy_config(store.as_ref(), options.storage_profile);
834
835 let lehman_yao = runtime.config_bool("storage.btree.lehman_yao", true);
842 crate::storage::engine::btree::lehman_yao::set_enabled(lehman_yao);
843 if lehman_yao {
844 tracing::info!(
845 "storage.btree.lehman_yao=true — lock-free concurrent descent enabled"
846 );
847 }
848
849 let overlay_path = crate::runtime::config_overlay::config_file_path();
854 let _ =
855 crate::runtime::config_overlay::apply_config_file(store.as_ref(), &overlay_path);
856 }
857
858 {
862 let store = runtime.inner.db.store();
863 for name in crate::application::vcs_collections::ALL {
864 let _ = store.get_or_create_collection(*name);
865 }
866 store.set_config_tree(
869 crate::application::vcs_collections::CONFIG_NAMESPACE,
870 &crate::json!({
871 "default_branch": "main",
872 "author": crate::json!({
873 "name": "reddb",
874 "email": "reddb@localhost"
875 }),
876 "protected_branches": crate::json!(["main"]),
877 "closure": crate::json!({
878 "enabled": true,
879 "lazy": true
880 }),
881 "merge": crate::json!({
882 "default_strategy": "auto",
883 "fast_forward": true
884 })
885 }),
886 );
887 }
888
889 {
892 let store = runtime.inner.db.store();
893 for name in crate::application::migration_collections::ALL {
894 let _ = store.get_or_create_collection(*name);
895 }
896 }
897
898 let _ = crate::application::topology_collections::ensure(&runtime);
902
903 runtime
911 .inner
912 .db
913 .store()
914 .register_entity_id(crate::storage::EntityId::new(
915 crate::storage::FIRST_USER_ENTITY_ID - 1,
916 ));
917
918 {
932 let weak = Arc::downgrade(&runtime.inner);
933 runtime
934 .background_tasks
935 .as_ref()
936 .expect("foreground runtime")
937 .spawn(
938 std::thread::Builder::new().name("reddb-maintenance".into()),
939 move |stopped| {
940 let tick = std::time::Duration::from_millis(200);
941 let work_interval = std::time::Duration::from_secs(60);
942 let mut last_work = std::time::Instant::now();
943 let mut allocator_pressure =
944 super::allocator_pressure::AllocatorPressure::default();
945 loop {
946 std::thread::park_timeout(tick);
947 if stopped.load(AtomicOrdering::Acquire) {
948 break;
949 }
950 let Some(inner) = weak.upgrade() else {
951 break;
954 };
955 if last_work.elapsed() >= work_interval {
956 let _stats = inner.db.store().context_index().stats();
957 last_work = std::time::Instant::now();
958 }
959 let rt = RedDBRuntime {
960 background_tasks: None,
961 inner,
962 };
963 rt.try_reclaim_history_tick();
964 allocator_pressure.tick();
965 }
966 },
967 );
968 }
969
970 {
972 let store = runtime.inner.db.store();
973 let mut backup_enabled = false;
974 let mut backup_interval = 3600u64;
975
976 if let Some(manager) = store.get_collection("red_config") {
977 manager.for_each_entity(|entity| {
978 if let Some(row) = entity.data.as_row() {
979 let key = row.get_field("key").and_then(|v| match v {
980 reddb_types::Value::Text(s) => Some(s.as_ref()),
981 _ => None,
982 });
983 let val = row.get_field("value");
984 if key == Some("red.config.backup.enabled") {
985 backup_enabled = match val {
986 Some(reddb_types::Value::Boolean(true)) => true,
987 Some(reddb_types::Value::Text(s)) => &**s == "true",
988 _ => false,
989 };
990 } else if key == Some("red.config.backup.interval_secs") {
991 if let Some(reddb_types::Value::Integer(n)) = val {
992 backup_interval = *n as u64;
993 }
994 }
995 }
996 true
997 });
998 }
999
1000 if backup_enabled {
1001 runtime.inner.backup_scheduler.set_interval(backup_interval);
1002 let rt = runtime.clone();
1003 runtime
1004 .inner
1005 .backup_scheduler
1006 .start(move || rt.trigger_backup().map_err(|e| format!("{}", e)));
1007 }
1008 }
1009
1010 {
1012 runtime
1013 .inner
1014 .ec_registry
1015 .load_from_config_store(runtime.inner.db.store().as_ref());
1016 if !runtime.inner.ec_registry.async_configs().is_empty() {
1017 runtime.inner.ec_worker.start(
1018 Arc::clone(&runtime.inner.ec_registry),
1019 Arc::clone(&runtime.inner.db.store()),
1020 );
1021 }
1022 }
1023
1024 if let (true, crate::replication::ReplicationRole::Replica { primary_addr }) = (
1025 runtime.inner.db.options().replica_loop_enabled,
1026 runtime.inner.db.options().replication.role.clone(),
1027 ) {
1028 let rt = runtime.clone();
1029 std::thread::Builder::new()
1030 .name("reddb-replica".into())
1031 .spawn(move || rt.run_replica_loop(primary_addr))
1032 .ok();
1033 }
1034
1035 runtime.inner.lifecycle.mark_ready();
1040
1041 {
1050 let weak_inner = Arc::downgrade(&runtime.inner);
1051 runtime
1052 .background_tasks
1053 .as_ref()
1054 .expect("foreground runtime")
1055 .spawn(
1056 std::thread::Builder::new()
1057 .name("reddb-mv-scheduler".into())
1058 .stack_size(8 * 1024 * 1024),
1063 move |stopped| loop {
1064 std::thread::park_timeout(std::time::Duration::from_millis(50));
1065 if stopped.load(AtomicOrdering::Acquire) {
1066 break;
1067 }
1068 let Some(inner) = weak_inner.upgrade() else {
1069 break;
1070 };
1071 let rt = RedDBRuntime {
1072 background_tasks: None,
1073 inner,
1074 };
1075 rt.refresh_due_materialized_views();
1076 },
1077 );
1078 }
1079
1080 if !runtime.write_gate().is_read_only() {
1090 let weak_inner = Arc::downgrade(&runtime.inner);
1091 runtime
1092 .background_tasks
1093 .as_ref()
1094 .expect("foreground runtime")
1095 .spawn(
1096 std::thread::Builder::new().name("reddb-retention-sweeper".into()),
1097 move |stopped| loop {
1098 std::thread::park_timeout(std::time::Duration::from_millis(500));
1099 if stopped.load(AtomicOrdering::Acquire) {
1100 break;
1101 }
1102 let Some(inner) = weak_inner.upgrade() else {
1103 break;
1104 };
1105 let rt = RedDBRuntime {
1106 background_tasks: None,
1107 inner,
1108 };
1109 rt.sweep_retention_tick(
1110 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
1111 );
1112 },
1113 );
1114 }
1115
1116 if no_auth_boot {
1120 use crate::runtime::control_events::{
1121 ActorRef, ControlEvent, ControlEventCtx, EventKind, Outcome,
1122 };
1123 let ledger = std::sync::Arc::clone(&runtime.inner.control_event_ledger.read());
1124 let ctx = ControlEventCtx {
1125 actor: ActorRef::System("boot"),
1126 scope: None,
1127 request_id: None,
1128 trace_id: None,
1129 };
1130 let event = ControlEvent {
1131 kind: EventKind::AuthDisabled,
1132 outcome: Outcome::Allowed,
1133 action: std::borrow::Cow::Borrowed("auth.disabled"),
1134 resource: Some("server".to_string()),
1135 reason: Some("--no-auth / --dev".to_string()),
1136 matched_policy_id: None,
1137 fields: std::collections::HashMap::new(),
1138 };
1139 if let Err(err) = ledger.emit(&ctx, event) {
1140 tracing::warn!(
1141 target: "reddb::security",
1142 error = %err,
1143 "could not record the auth-disabled boot in the control-event ledger"
1144 );
1145 }
1146 }
1147
1148 Ok(runtime)
1149 }
1150
1151 fn rehydrate_snapshot_xid_floor(&self) -> RedDBResult<()> {
1152 let store = self.inner.db.store();
1153 let snapshot_manager = self.inner.transaction_state.snapshot_manager();
1154 let base_high_water_mark = store.startup_xid_high_water_mark();
1155 if let Some(high_water_mark) = base_high_water_mark {
1156 snapshot_manager.observe_committed_xid(high_water_mark);
1157 }
1158 for collection in store.list_collections() {
1159 let Some(manager) = store.get_collection(&collection) else {
1160 continue;
1161 };
1162 let observe = |entity: &crate::storage::UnifiedEntity| {
1163 snapshot_manager.observe_committed_xid(entity.xmin);
1164 snapshot_manager.observe_committed_xid(entity.xmax);
1165 true
1166 };
1167 if base_high_water_mark.is_some() {
1168 manager.for_each_entity(observe);
1171 } else {
1172 manager.try_for_each_entity(usize::MAX, observe)?;
1175 }
1176 }
1177 if base_high_water_mark.is_none() {
1178 store.remember_startup_xid_high_water_mark(
1179 snapshot_manager.peek_next_xid().saturating_sub(1),
1180 );
1181 }
1182 Ok(())
1183 }
1184
1185 pub(crate) fn ensure_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1198 let store = self.inner.db.store();
1199 let mut changed = false;
1200 if store.get_collection(name).is_none() {
1201 store.get_or_create_collection(name);
1202 changed = true;
1203 }
1204 if self.inner.db.collection_contract(name).is_none() {
1205 self.inner
1206 .db
1207 .save_collection_contract(system_keyed_collection_contract(
1208 name,
1209 crate::catalog::CollectionModel::Table,
1210 ))
1211 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1212 changed = true;
1213 }
1214 if changed {
1215 self.inner
1216 .db
1217 .persist_metadata()
1218 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1219 }
1220 Ok(())
1221 }
1222
1223 pub(crate) fn drop_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1228 let store = self.inner.db.store();
1229 if store.get_collection(name).is_none() {
1230 return Ok(());
1231 }
1232 store
1233 .drop_collection(name)
1234 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1235 if self.inner.db.collection_contract(name).is_some() {
1238 self.inner
1239 .db
1240 .remove_collection_contract(name)
1241 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1242 }
1243 self.invalidate_result_cache();
1244 self.inner
1245 .db
1246 .persist_metadata()
1247 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1248 Ok(())
1249 }
1250
1251 fn bootstrap_system_keyed_collections(&self) -> RedDBResult<()> {
1252 let mut changed = false;
1253 for (name, model) in [
1254 ("red.config", crate::catalog::CollectionModel::Config),
1255 ("red.vault", crate::catalog::CollectionModel::Vault),
1256 (
1260 crate::runtime::continuous_materialized_view::CATALOG_COLLECTION,
1261 crate::catalog::CollectionModel::Config,
1262 ),
1263 ] {
1264 if self.inner.db.store().get_collection(name).is_none() {
1265 self.inner.db.store().get_or_create_collection(name);
1266 changed = true;
1267 }
1268 if self.inner.db.collection_contract(name).is_none() {
1269 self.inner
1270 .db
1271 .save_collection_contract(system_keyed_collection_contract(name, model))
1272 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1273 changed = true;
1274 }
1275 }
1276 if changed {
1277 self.inner
1278 .db
1279 .persist_metadata()
1280 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1281 }
1282 Ok(())
1283 }
1284
1285 pub fn db(&self) -> Arc<RedDB> {
1286 Arc::clone(&self.inner.db)
1287 }
1288
1289 pub fn index_store_ref(&self) -> &super::index_store::IndexStore {
1294 &self.inner.index_store
1295 }
1296
1297 pub(crate) fn schema_vocabulary_apply(
1302 &self,
1303 event: crate::runtime::schema_vocabulary::DdlEvent,
1304 ) {
1305 self.inner.schema_vocabulary.write().on_ddl(event);
1306 }
1307
1308 pub fn schema_vocabulary_lookup(
1313 &self,
1314 token: &str,
1315 ) -> Vec<crate::runtime::schema_vocabulary::VocabHit> {
1316 self.inner.schema_vocabulary.read().lookup(token).to_vec()
1317 }
1318
1319 pub fn set_auth_store(&self, store: Arc<crate::auth::store::AuthStore>) {
1323 *self.inner.auth_store.write() = Some(store);
1324 }
1325
1326 pub fn auth_store(&self) -> Option<Arc<crate::auth::store::AuthStore>> {
1329 self.inner.auth_store.read().clone()
1330 }
1331
1332 pub fn set_oauth_validator(&self, validator: Option<Arc<crate::auth::oauth::OAuthValidator>>) {
1336 *self.inner.oauth_validator.write() = validator;
1337 }
1338
1339 pub fn oauth_validator(&self) -> Option<Arc<crate::auth::oauth::OAuthValidator>> {
1343 self.inner.oauth_validator.read().clone()
1344 }
1345
1346 pub fn set_browser_token_authority(
1352 &self,
1353 authority: Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>,
1354 ) {
1355 *self.inner.browser_token_authority.write() = authority;
1356 }
1357
1358 pub fn browser_token_authority(
1362 &self,
1363 ) -> Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>> {
1364 self.inner.browser_token_authority.read().clone()
1365 }
1366}
1367
1368#[cfg(test)]
1369mod xid_resume_tests {
1370 use super::*;
1371 use reddb_engine::storage::collection_read::{CollectionCursor, CollectionRead, EncodedRecord};
1372
1373 struct Unreadable;
1374
1375 fn unavailable() -> crate::storage::StoreError {
1376 crate::storage::StoreError::Io(std::io::Error::other("XID bootstrap read a base payload"))
1377 }
1378
1379 impl CollectionRead for Unreadable {
1380 fn get_record(
1381 &self,
1382 id: crate::storage::EntityId,
1383 _: usize,
1384 ) -> Result<Option<EncodedRecord>, crate::storage::StoreError> {
1385 if id.raw() == 999 {
1386 Ok(None)
1387 } else {
1388 Err(unavailable())
1389 }
1390 }
1391 fn cursor(&self) -> Result<Box<dyn CollectionCursor + '_>, crate::storage::StoreError> {
1392 Err(unavailable())
1393 }
1394 fn owned_cursor(&self) -> Result<Box<dyn CollectionCursor>, crate::storage::StoreError> {
1395 Err(unavailable())
1396 }
1397 }
1398
1399 #[test]
1400 fn xid_resume_survives_pending_checkpoint_process_exit() {
1401 const FIXTURE: &str = "REDDB_XID_RESUME_CRASH_FIXTURE";
1402 let open = |path: std::path::PathBuf| {
1403 let options = RedDBOptions::persistent(path)
1404 .with_memory_budget(64 * 1024 * 1024)
1405 .with_storage_profile(
1406 crate::storage::profile::StorageDeployPreset::PrimaryReplicaDev.selection(),
1407 )
1408 .expect("profile");
1409 crate::storage::RedDB::with_disk_residency_for_qualification(|| {
1410 RedDBRuntime::with_options(options)
1411 })
1412 .expect("crash recovery runtime")
1413 };
1414 if let Some(root) = std::env::var_os(FIXTURE) {
1415 let root = std::path::PathBuf::from(root);
1416 assert_eq!(
1417 std::fs::read_to_string(root.join("owned-fixture")).expect("owned fixture"),
1418 "xid-resume-crash-v1"
1419 );
1420 let runtime = open(root.join("crash.rdb"));
1421 for sql in [
1422 "CREATE TABLE crash_rows (id INTEGER, payload TEXT)",
1423 "INSERT INTO crash_rows (id, payload) VALUES (1, 'committed')",
1424 "BEGIN",
1425 "UPDATE crash_rows SET payload = 'pending' WHERE id = 1",
1426 "INSERT INTO crash_rows (id, payload) VALUES (2, 'pending')",
1427 ] {
1428 runtime.execute_query(sql).expect("crash fixture statement");
1429 }
1430 runtime
1431 .flush()
1432 .expect("checkpoint with pending transaction");
1433 std::fs::write(
1434 root.join("checkpoint-complete"),
1435 "pending transaction remains open",
1436 )
1437 .expect("acknowledged fixture checkpoint");
1438 std::process::exit(0);
1441 }
1442 let directory = tempfile::tempdir().expect("owned crash fixture");
1443 std::fs::write(
1444 directory.path().join("owned-fixture"),
1445 "xid-resume-crash-v1",
1446 )
1447 .expect("ownership marker");
1448 let child = std::process::Command::new(std::env::current_exe().expect("test executable"))
1449 .args([
1450 "--exact",
1451 "runtime::impl_lifecycle::xid_resume_tests::xid_resume_survives_pending_checkpoint_process_exit",
1452 "--test-threads=1",
1453 ])
1454 .env(FIXTURE, directory.path())
1455 .output()
1456 .expect("crashing owned child");
1457 assert!(
1458 child.status.success(),
1459 "{}",
1460 String::from_utf8_lossy(&child.stderr)
1461 );
1462 assert!(directory.path().join("checkpoint-complete").is_file());
1463 let path = directory.path().join("crash.rdb");
1464 assert!(path.is_file());
1465 let runtime = open(path.clone());
1466 let result = runtime
1467 .execute_query("SELECT id, payload FROM crash_rows ORDER BY id")
1468 .expect("committed preimage");
1469 assert_eq!(
1470 result.result.records.len(),
1471 1,
1472 "pending insert must stay invisible"
1473 );
1474 assert_eq!(result.result.records[0].get("id"), Some(&Value::Integer(1)));
1475 assert_eq!(
1476 result.result.records[0].get("payload"),
1477 Some(&Value::text("committed"))
1478 );
1479 runtime
1480 .execute_query("INSERT INTO crash_rows (id, payload) VALUES (3, 'after')")
1481 .expect("new writer after recovery");
1482 runtime.flush().expect("recovered checkpoint");
1483 drop(runtime);
1484 let reopened = open(path);
1485 let result = reopened
1486 .execute_query("SELECT id FROM crash_rows ORDER BY id")
1487 .expect("second recovery");
1488 assert_eq!(result.result.records.len(), 2);
1489 assert_eq!(result.result.records[0].get("id"), Some(&Value::Integer(1)));
1490 assert_eq!(result.result.records[1].get("id"), Some(&Value::Integer(3)));
1491 }
1492
1493 #[test]
1494 fn xid_resume_skips_known_base_payloads_but_rejects_an_unproven_root() {
1495 let directory = tempfile::tempdir().expect("resume fixture");
1496 let options = RedDBOptions::persistent(directory.path().join("resume.rdb"))
1497 .with_memory_budget(64 * 1024 * 1024)
1498 .with_storage_profile(
1499 crate::storage::profile::StorageDeployPreset::PrimaryReplicaDev.selection(),
1500 )
1501 .expect("profile");
1502 let runtime = crate::storage::RedDB::with_disk_residency_for_qualification(|| {
1503 RedDBRuntime::with_options(options)
1504 })
1505 .expect("runtime");
1506 runtime
1507 .execute_query("CREATE TABLE resume_rows (id INTEGER)")
1508 .expect("table");
1509 runtime
1510 .execute_query("INSERT INTO resume_rows (id) VALUES (1)")
1511 .expect("row");
1512 runtime.flush().expect("publish complete XID summary");
1513 let store = runtime.db().store();
1514 assert!(store.startup_xid_high_water_mark().is_some());
1515 let root = store
1516 .collection_root_page("resume_rows")
1517 .expect("published root");
1518 let manager = store.get_collection("resume_rows").expect("manager");
1519 for (candidate, proven) in [(root, true), (root + 1, false)] {
1520 manager.install_backing(
1521 Arc::new(Unreadable),
1522 candidate,
1523 store.format_version(),
1524 4096,
1525 1,
1526 );
1527 let mut overlay = crate::storage::UnifiedEntity::table_row(
1528 crate::storage::EntityId::new(999),
1529 "resume_rows",
1530 999,
1531 vec![Value::Integer(2)],
1532 );
1533 overlay.xmin = 500_000;
1534 overlay.xmax = 600_000;
1535 manager.insert(overlay).expect("resident WAL overlay");
1536 let result = runtime.rehydrate_snapshot_xid_floor();
1537 assert_eq!(result.is_ok(), proven, "root={candidate}: {result:?}");
1538 if proven {
1539 assert!(runtime.snapshot_manager().peek_next_xid() > 600_000);
1540 }
1541 }
1542 }
1543}