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 let store = self.inner.db.store();
118 let publication = store.vault_publication_lock.lock();
119 self.inner
120 .db
121 .flush_local_only()
122 .map_err(|err| RedDBError::Internal(err.to_string()))?;
123 self.index_store_ref().clear_paged_btree_overlays();
124 drop(publication);
125 self.inner
126 .db
127 .upload_to_remote_backend()
128 .map_err(|err| RedDBError::Internal(err.to_string()))
129 }
130
131 pub fn lock_manager(&self) -> std::sync::Arc<crate::runtime::lock_manager::LockManager> {
135 self.inner.lock_manager.clone()
136 }
137
138 pub fn config_registry(&self) -> std::sync::Arc<crate::auth::registry::ConfigRegistry> {
140 self.inner.config_registry.clone()
141 }
142
143 pub fn query_audit(&self) -> std::sync::Arc<crate::runtime::query_audit::QueryAuditStream> {
144 self.inner.query_audit.clone()
145 }
146
147 pub fn control_events_require_persistence(&self) -> bool {
148 self.inner.control_event_config.require_persistence()
149 }
150
151 pub fn control_event_config(&self) -> crate::runtime::control_events::ControlEventConfig {
152 self.inner.control_event_config
153 }
154
155 pub fn control_event_ledger(
156 &self,
157 ) -> Arc<dyn crate::runtime::control_events::ControlEventLedger> {
158 self.inner.control_event_ledger.read().clone()
159 }
160
161 #[doc(hidden)]
162 pub fn replace_control_event_ledger_for_tests(
163 &self,
164 ledger: Arc<dyn crate::runtime::control_events::ControlEventLedger>,
165 ) {
166 *self.inner.control_event_ledger.write() = ledger;
167 }
168
169 #[inline(never)]
170 pub fn with_options(options: RedDBOptions) -> RedDBResult<Self> {
171 Self::with_pool(options, ConnectionPoolConfig::default())
172 }
173
174 pub fn memory_budget(&self) -> crate::storage::memory_budget::MemoryBudget {
178 self.inner.memory_budget
179 }
180
181 pub fn memory_accounting(&self) -> &crate::storage::memory_pools::MemoryAccounting {
185 &self.inner.memory_accounting
186 }
187
188 pub fn with_pool(
189 options: RedDBOptions,
190 pool_config: ConnectionPoolConfig,
191 ) -> RedDBResult<Self> {
192 Self::with_pool_and_limits(
193 options,
194 pool_config,
195 super::resource_limits::ResourceLimits::from_env(),
196 )
197 }
198
199 pub(super) fn with_pool_and_limits(
200 options: RedDBOptions,
201 pool_config: ConnectionPoolConfig,
202 resource_limits: super::resource_limits::ResourceLimits,
203 ) -> RedDBResult<Self> {
204 options
205 .replication
206 .validate_failover_profile()
207 .map_err(RedDBError::InvalidConfig)?;
208 let boot_open_start_ms = std::time::SystemTime::now()
216 .duration_since(std::time::UNIX_EPOCH)
217 .map(|d| d.as_millis() as u64)
218 .unwrap_or(0);
219 let memory_budget = crate::storage::memory_budget::resolve_for_boot(
223 options.storage_profile.deploy_profile,
224 options.memory_budget_bytes,
225 )
226 .map_err(|err| RedDBError::InvalidConfig(err.to_string()))?;
227 crate::storage::memory_budget::log_resolved_once(&memory_budget);
228 let memory_shares = crate::storage::memory_pools::BudgetShares::resolve(
232 memory_budget,
233 options.storage_profile.deploy_profile,
234 );
235 memory_shares.log_once();
236 let embedded_single_file = options.storage_profile.deploy_profile
237 == crate::storage::DeployProfile::Embedded
238 && options.storage_profile.packaging == crate::storage::StoragePackaging::SingleFile;
239 let db = Arc::new(RedDB::open_with_options(&options).map_err(|err| {
240 match err.downcast::<crate::storage::StoreError>() {
241 Ok(store_err) => RedDBError::from(*store_err),
242 Err(err) => RedDBError::Internal(err.to_string()),
243 }
244 })?);
245 let result_blob_cache_config = crate::storage::cache::BlobCacheConfig::default()
248 .with_l1_bytes_max(memory_shares.blob_cache_l1_bytes());
249 let result_blob_cache =
250 crate::storage::cache::BlobCache::open_with_l2(result_blob_cache_config).map_err(
251 |err| RedDBError::Internal(format!("open result Blob Cache L2 failed: {err:?}")),
252 )?;
253 let storage_ready_ms = std::time::SystemTime::now()
254 .duration_since(std::time::UNIX_EPOCH)
255 .map(|d| d.as_millis() as u64)
256 .unwrap_or(0);
257
258 let no_auth_boot = crate::service_cli::no_auth_active(&options);
259 let runtime = Self {
260 background_tasks: Some(Arc::new(background_tasks::RuntimeBackgroundTasks::default())),
261 inner: Arc::new(RuntimeInner {
262 functions: parking_lot::RwLock::new(
263 super::function_catalog::FunctionCatalog::load(&db)?,
264 ),
265 db: db.clone(),
266 layout: PhysicalLayout::from_options(&options),
267 embedded_single_file,
268 memory_budget,
269 memory_accounting: Arc::new(
270 crate::storage::memory_pools::MemoryAccounting::from_shares(
271 memory_budget,
272 memory_shares,
273 ),
274 ),
275 memory_reservations: parking_lot::Mutex::new(
276 super::memory_admission::MemoryReservations::default(),
277 ),
278 mvcc_maintenance: parking_lot::Mutex::new(Default::default()),
279 indices: IndexCatalog::register_default_vector_graph(
280 options.has_capability(crate::api::Capability::Table),
281 options.has_capability(crate::api::Capability::Graph),
282 ),
283 pool_config,
284 pool: Mutex::new(PoolState::default()),
285 execution_admission: parking_lot::RwLock::new(
286 crate::transport::execution_admission::ExecutionAdmission::for_host(),
287 ),
288 execution_queue_timeout_ms: AtomicU64::new(30_000),
289 started_at_unix_ms: SystemTime::now()
290 .duration_since(UNIX_EPOCH)
291 .unwrap_or_default()
292 .as_millis(),
293 probabilistic: super::probabilistic_store::ProbabilisticStore::new(),
294 index_store: super::index_store::IndexStore::new(),
295 cdc: crate::replication::cdc::CdcBuffer::new(100_000),
296 checkpoint_projection_stats: super::CheckpointProjectionStats::default(),
297 scrub_state: parking_lot::Mutex::new(super::ScrubRuntimeState::default()),
298 checkpoint_columnar_emission_budget_chunks: options
299 .checkpoint_columnar_emission_budget_chunks,
300 columnar_projection_size_floor_rows: options.columnar_projection_size_floor_rows,
301 backup_scheduler: crate::replication::scheduler::BackupScheduler::new(3600),
302 query_cache: parking_lot::RwLock::new(
303 crate::storage::query::planner::cache::PlanCache::new(1000),
304 ),
305 result_cache: parking_lot::RwLock::new((
306 HashMap::new(),
307 std::collections::VecDeque::new(),
308 )),
309 result_blob_cache,
310 result_blob_entries: parking_lot::RwLock::new((
311 HashMap::new(),
312 std::collections::VecDeque::new(),
313 )),
314 ask_answer_cache_entries: parking_lot::RwLock::new((
315 HashSet::new(),
316 std::collections::VecDeque::new(),
317 )),
318 result_cache_shadow_divergences: std::sync::atomic::AtomicU64::new(0),
319 result_cache_hits: std::sync::atomic::AtomicU64::new(0),
320 result_cache_misses: std::sync::atomic::AtomicU64::new(0),
321 result_cache_evictions: std::sync::atomic::AtomicU64::new(0),
322 ask_daily_spend: parking_lot::RwLock::new(HashMap::new()),
323 queue_message_locks: parking_lot::RwLock::new(HashMap::new()),
324 rmw_locks: RmwLockTable::new(),
325 planner_dirty_tables: parking_lot::RwLock::new(HashSet::new()),
326 ec_registry: Arc::new(crate::ec::config::EcRegistry::new()),
327 config_registry: Arc::new(crate::auth::registry::ConfigRegistry::new()),
328 ec_worker: crate::ec::worker::EcWorker::new(),
329 auth_store: parking_lot::RwLock::new(None),
330 oauth_validator: parking_lot::RwLock::new(None),
331 browser_token_authority: parking_lot::RwLock::new(None),
332 views: parking_lot::RwLock::new(HashMap::new()),
333 materialized_views: parking_lot::RwLock::new(
334 crate::storage::cache::result::MaterializedViewCache::new(),
335 ),
336 retention_sweeper: parking_lot::RwLock::new(
337 crate::runtime::retention_sweeper::RetentionSweeperState::new(),
338 ),
339 transaction_state: crate::runtime::transaction_state::TransactionState::new(),
340 env_config_overrides: crate::runtime::config_overlay::collect_env_overrides(),
341 lock_manager: Arc::new({
342 let env = crate::runtime::config_overlay::collect_env_overrides();
347 let timeout_ms = env
348 .get("concurrency.locking.deadlock_timeout_ms")
349 .and_then(|raw| raw.parse::<u64>().ok())
350 .unwrap_or_else(|| {
351 match crate::runtime::config_matrix::default_for(
352 "concurrency.locking.deadlock_timeout_ms",
353 ) {
354 Some(crate::serde_json::Value::Number(n)) => n as u64,
355 _ => 5000,
356 }
357 });
358 let cfg = crate::runtime::lock_manager::LockConfig {
359 default_timeout: std::time::Duration::from_millis(timeout_ms),
360 ..Default::default()
361 };
362 crate::runtime::lock_manager::LockManager::new(cfg)
363 }),
364 rls_policies: parking_lot::RwLock::new(HashMap::new()),
365 rls_enabled_tables: parking_lot::RwLock::new(HashSet::new()),
366 foreign_tables: Arc::new(crate::storage::fdw::ForeignTableRegistry::with_builtins()),
367 pending_tombstones: parking_lot::RwLock::new(HashMap::new()),
368 pending_versioned_updates: parking_lot::RwLock::new(HashMap::new()),
369 pending_queue_dedup: parking_lot::RwLock::new(HashMap::new()),
370 pending_kv_watch_events: parking_lot::RwLock::new(HashMap::new()),
371 pending_vault_writes: parking_lot::RwLock::new(HashMap::new()),
372 pending_store_wal_actions: parking_lot::RwLock::new(HashMap::new()),
373 pending_claim_locks: parking_lot::RwLock::new(HashMap::new()),
374 queue_wait_registry: std::sync::Arc::new(
375 crate::runtime::queue_wait_registry::QueueWaitRegistry::new(),
376 ),
377 pending_queue_wakes: parking_lot::RwLock::new(HashMap::new()),
378 tenant_tables: parking_lot::RwLock::new(HashMap::new()),
379 ddl_epoch: std::sync::atomic::AtomicU64::new(0),
380 write_gate: Arc::new(crate::runtime::write_gate::WriteGate::from_options(
381 &options,
382 )),
383 lifecycle: crate::runtime::lifecycle::Lifecycle::new(),
384 resource_limits,
385 query_budget_slots: AtomicU64::new(0),
386 audit_log: {
387 let data_path = if embedded_single_file || options.read_only {
403 std::env::temp_dir()
404 .join("reddb-embedded-runtime")
405 .join(format!("audit-{}", std::process::id()))
406 } else {
407 options
408 .data_path
409 .clone()
410 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
411 };
412 let (audit_dest, _) = crate::api::tier_wiring::current_log_destinations();
413 let audit_dest = if options.read_only {
414 crate::storage::layout::LogDestination::Stderr
415 } else {
416 audit_dest
417 };
418 if !matches!(audit_dest, crate::storage::layout::LogDestination::File(_))
419 && (embedded_single_file
420 || options.read_only
421 || options
422 .metadata
423 .contains_key(crate::api::EPHEMERAL_RUNTIME_METADATA_KEY))
424 {
425 let audit_path = reddb_file::layout::sibling_path(
435 &data_path,
436 &reddb_file::layout::sidecar_file_name(&data_path, "audit.log"),
437 );
438 Arc::new(crate::runtime::audit_log::AuditLogger::with_path(
439 audit_path,
440 ))
441 } else {
442 Arc::new(crate::runtime::audit_log::AuditLogger::for_destination(
443 &audit_dest,
444 &data_path,
445 ))
446 }
447 },
448 control_event_ledger: parking_lot::RwLock::new(Arc::new(
449 crate::runtime::control_events::RuntimeLedger::new(db.store()),
450 )),
451 control_event_config: options.control_events,
452 query_audit: Arc::new(crate::runtime::query_audit::QueryAuditStream::new(
453 db.store(),
454 options.query_audit.clone(),
455 )),
456 lease_lifecycle: std::sync::OnceLock::new(),
457 routing_primary_endpoints: parking_lot::RwLock::new(Default::default()),
458 replica_apply_metrics: std::sync::Arc::new(
459 crate::replication::logical::ReplicaApplyMetrics::default(),
460 ),
461 replica_link_metrics: std::sync::Arc::new(
462 crate::replication::reconnect::ReplicaLinkMetrics::default(),
463 ),
464 quota_bucket: crate::runtime::quota_bucket::QuotaBucket::from_env(),
465 schema_vocabulary: parking_lot::RwLock::new(
466 crate::runtime::schema_vocabulary::SchemaVocabulary::new(),
467 ),
468 slow_query_logger: {
469 let fallback_dir = if embedded_single_file || options.read_only {
486 std::env::temp_dir()
487 .join("reddb-embedded-runtime")
488 .join(format!("slow-{}", std::process::id()))
489 } else {
490 options
491 .data_path
492 .as_ref()
493 .and_then(|p| p.parent().map(std::path::PathBuf::from))
494 .unwrap_or_else(|| std::env::temp_dir().join("reddb"))
495 };
496 let threshold_ms = std::env::var("RED_SLOW_QUERY_THRESHOLD_MS")
497 .ok()
498 .and_then(|s| s.parse::<u64>().ok())
499 .unwrap_or(1000);
500 let sample_pct = std::env::var("RED_SLOW_QUERY_SAMPLE_PCT")
501 .ok()
502 .and_then(|s| s.parse::<u8>().ok())
503 .unwrap_or(100);
504 let (_, slow_dest) = crate::api::tier_wiring::current_log_destinations();
505 let slow_dest = if options.read_only {
506 crate::storage::layout::LogDestination::Stderr
507 } else {
508 slow_dest
509 };
510 crate::telemetry::slow_query_logger::SlowQueryLogger::for_destination(
511 &slow_dest,
512 &fallback_dir,
513 threshold_ms,
514 sample_pct,
515 )
516 },
517 slow_query_store: crate::telemetry::slow_query_store::SlowQueryStore::new(
518 crate::telemetry::slow_query_store::DEFAULT_CAP,
519 ),
520 kv_stats: crate::runtime::KvStatsCounters::default(),
521 metrics_ingest_stats: crate::runtime::MetricsIngestCounters::default(),
522 metrics_tenant_activity_stats:
523 crate::runtime::MetricsTenantActivityCounters::default(),
524 claim_telemetry: Arc::new(
525 crate::runtime::claim_telemetry::ClaimTelemetryCounters::default(),
526 ),
527 queue_telemetry: Arc::new(
528 crate::runtime::queue_telemetry::QueueTelemetryCounters::default(),
529 ),
530 query_latency_telemetry: Arc::new(
531 crate::runtime::query_latency_telemetry::QueryLatencyTelemetry::default(),
532 ),
533 occupancy_sampler: Arc::new(
534 crate::runtime::occupancy_sampler::OccupancySampler::new(),
535 ),
536 node_load_telemetry: Arc::new(
537 crate::runtime::node_load_telemetry::NodeLoadTelemetry::default(),
538 ),
539 queue_presence: Arc::new(
540 crate::storage::queue::presence::ConsumerPresenceRegistry::new(),
541 ),
542 vector_introspection: Arc::new(
543 crate::storage::vector::introspection::VectorIntrospectionRegistry::new(),
544 ),
545 kv_tag_index: crate::runtime::KvTagIndex::default(),
546 chain_tip_cache: parking_lot::Mutex::new(HashMap::new()),
547 chain_integrity_broken: parking_lot::Mutex::new(HashMap::new()),
548 integrity_tombstones: parking_lot::Mutex::new(Vec::new()),
549 integrity_tombstones_state: std::sync::atomic::AtomicU8::new(0),
550 }),
551 };
552
553 runtime.install_hash_candidate_admission();
554
555 crate::telemetry::operator_event::install_global_audit_sink(Arc::clone(
561 &runtime.inner.audit_log,
562 ));
563
564 runtime
567 .inner
568 .slow_query_logger
569 .attach_store(Arc::clone(&runtime.inner.slow_query_store));
570
571 runtime
579 .inner
580 .lifecycle
581 .set_restore_started_at_ms(boot_open_start_ms);
582 runtime
583 .inner
584 .lifecycle
585 .set_restore_ready_at_ms(storage_ready_ms);
586 runtime
587 .inner
588 .lifecycle
589 .set_wal_replay_started_at_ms(boot_open_start_ms);
590 runtime
591 .inner
592 .lifecycle
593 .set_wal_replay_ready_at_ms(storage_ready_ms);
594
595 let restored_cdc_lsn = runtime
596 .inner
597 .db
598 .replication
599 .as_ref()
600 .map(|repl| {
601 repl.logical_wal_spool
602 .as_ref()
603 .map(|spool| spool.current_lsn())
604 .unwrap_or(0)
605 })
606 .unwrap_or(0)
607 .max(runtime.config_u64("red.config.timeline.last_archived_lsn", 0));
608 runtime.inner.cdc.set_current_lsn(restored_cdc_lsn);
609 runtime.rehydrate_snapshot_xid_floor()?;
610 assert!(runtime
611 .inner
612 .db
613 .runtime
614 .set(super::WeakRuntime(Arc::downgrade(&runtime.inner)))
615 .is_ok());
616 assert!(
617 runtime
618 .inner
619 .db
620 .store()
621 .snapshot_manager
622 .set(runtime.snapshot_manager())
623 .is_ok(),
624 "a new runtime owns the store transaction outcomes"
625 );
626 runtime
627 .bootstrap_system_keyed_collections()
628 .map_err(|err| RedDBError::Internal(format!("bootstrap system collections: {err}")))?;
629 runtime.rehydrate_declared_column_schemas();
630 super::control_event_index::prepare(&runtime)?;
633 super::queue_storage_index::prepare(&runtime)?;
634 runtime.rehydrate_runtime_index_registry()?;
635 if runtime
636 .db()
637 .store()
638 .config()
639 .resident_memory_bytes_max
640 .is_some()
641 {
642 runtime
643 .db()
644 .store()
645 .checkpoint_missing_secondary_indices()?;
646 }
647 runtime.rehydrate_kv_tags()?;
648 runtime
649 .load_probabilistic_state()
650 .map_err(|err| RedDBError::Internal(format!("load probabilistic state: {err}")))?;
651
652 runtime.rehydrate_tenant_tables();
656 runtime.rehydrate_materialized_view_descriptors()?;
661 runtime.recover_store_forks()?;
662 if let Some(repl) = &runtime.inner.db.replication {
663 repl.wal_buffer.set_current_lsn(restored_cdc_lsn);
664 }
665
666 {
668 let sys = SystemInfo::collect();
669 runtime.inner.db.store().set_config_tree(
670 "red.system",
671 &crate::serde_json::json!({
672 "pid": sys.pid,
673 "cpu_cores": sys.cpu_cores,
674 "total_memory_bytes": sys.total_memory_bytes,
675 "available_memory_bytes": sys.available_memory_bytes,
676 "os": sys.os,
677 "arch": sys.arch,
678 "hostname": sys.hostname,
679 "started_at": SystemTime::now()
680 .duration_since(UNIX_EPOCH)
681 .unwrap_or_default()
682 .as_millis() as u64
683 }),
684 );
685
686 let store = runtime.inner.db.store();
688 if store
689 .get_collection("red_config")
690 .map(|m| m.query_all(|_| true).len())
691 .unwrap_or(0)
692 <= 10
693 {
694 store.set_config_tree("red.ai", &crate::json!({
695 "default": crate::json!({
696 "provider": "openai",
697 "model": crate::ai::DEFAULT_OPENAI_PROMPT_MODEL
698 }),
699 "max_embedding_inputs": 256,
700 "max_prompt_batch": 256,
701 "timeout": crate::json!({ "connect_secs": 10, "read_secs": 90, "write_secs": 30 })
702 }));
703 store.set_config_tree(
704 "red.server",
705 &crate::json!({
706 "max_scan_limit": 1000,
707 "max_body_size": 1048576,
708 "read_timeout_ms": 5000,
709 "write_timeout_ms": 5000
710 }),
711 );
712 store.set_config_tree(
713 "red.storage",
714 &crate::json!({
715 "page_size": 4096,
716 "page_cache_capacity": 100000,
717 "auto_checkpoint_pages": 1000,
718 "snapshot_retention": 16,
719 "verify_checksums": true,
720 "segment": crate::json!({
721 "max_entities": 100000,
722 "max_bytes": 268435456_u64,
723 "compression_level": 6
724 }),
725 "hnsw": crate::json!({ "m": 16, "ef_construction": 100, "ef_search": 50 }),
726 "ivf": crate::json!({ "n_lists": 100, "n_probes": 10 }),
727 "bm25": crate::json!({ "k1": 1.2, "b": 0.75 })
728 }),
729 );
730 store.set_config_tree(
731 "red.search",
732 &crate::json!({
733 "rag": crate::json!({
734 "max_chunks_per_source": 10,
735 "max_total_chunks": 25,
736 "similarity_threshold": 0.8,
737 "graph_depth": 2,
738 "min_relevance": 0.3
739 }),
740 "fusion": crate::json!({
741 "vector_weight": 0.5,
742 "graph_weight": 0.3,
743 "table_weight": 0.2,
744 "dedup_threshold": 0.85
745 })
746 }),
747 );
748 store.set_config_tree(
749 "red.auth",
750 &crate::json!({
751 "enabled": false,
752 "session_ttl_secs": 3600,
753 "require_auth": false
754 }),
755 );
756 store.set_config_tree(
757 "red.query",
758 &crate::json!({
759 "connection_pool": crate::json!({ "max_connections": 64, "max_idle": 16 }),
760 "max_recursion_depth": 1000
761 }),
762 );
763 store.set_config_tree(
764 "red.indexes",
765 &crate::json!({
766 "auto_select": true,
767 "bloom_filter": crate::json!({
768 "enabled": true,
769 "false_positive_rate": 0.01,
770 "prune_on_scan": true
771 }),
772 "hash": crate::json!({ "enabled": true }),
773 "bitmap": crate::json!({ "enabled": true, "max_cardinality": 1000 }),
774 "spatial": crate::json!({ "enabled": true })
775 }),
776 );
777 store.set_config_tree(
778 "red.probabilistic",
779 &crate::json!({
780 "hll_registers": 16384,
781 "sketch_default_width": 1000,
782 "sketch_default_depth": 5,
783 "filter_default_capacity": 100000
784 }),
785 );
786 store.set_config_tree(
787 "red.timeseries",
788 &crate::json!({
789 "default_chunk_size": 1024,
790 "compression": crate::json!({
791 "timestamps": "delta_of_delta",
792 "values": "gorilla_xor"
793 }),
794 "default_retention_days": 0
795 }),
796 );
797 store.set_config_tree(
798 "red.queue",
799 &crate::json!({
800 "default_max_size": 0,
801 "default_max_attempts": 3,
802 "visibility_timeout_ms": 30000,
803 "consumer_idle_timeout_ms": 60000
804 }),
805 );
806 store.set_config_tree(
807 "red.backup",
808 &crate::json!({
809 "enabled": false,
810 "interval_secs": 3600,
811 "retention_count": 24,
812 "upload": false,
813 "backend": "local"
814 }),
815 );
816 store.set_config_tree(
817 "red.wal",
818 &crate::json!({
819 "archive": crate::json!({
820 "enabled": false,
821 "retention_hours": 168,
822 "prefix": reddb_file::backup_wal_prefix("")
823 })
824 }),
825 );
826 store.set_config_tree(
827 "red.cdc",
828 &crate::json!({
829 "enabled": true,
830 "buffer_size": 100000
831 }),
832 );
833 store.set_config_tree(
834 "red.config.secret",
835 &crate::json!({
836 "auto_encrypt": true,
837 "auto_decrypt": true
838 }),
839 );
840 }
841
842 crate::runtime::config_matrix::heal_critical_keys(store.as_ref());
849 seed_storage_deploy_config(store.as_ref(), options.storage_profile);
850
851 let lehman_yao = runtime.config_bool("storage.btree.lehman_yao", true);
858 crate::storage::engine::btree::lehman_yao::set_enabled(lehman_yao);
859 if lehman_yao {
860 tracing::info!(
861 "storage.btree.lehman_yao=true — lock-free concurrent descent enabled"
862 );
863 }
864
865 let overlay_path = crate::runtime::config_overlay::config_file_path();
870 let _ =
871 crate::runtime::config_overlay::apply_config_file(store.as_ref(), &overlay_path);
872 }
873
874 {
878 let store = runtime.inner.db.store();
879 for name in crate::application::vcs_collections::ALL {
880 let _ = store.get_or_create_collection(*name);
881 }
882 store.set_config_tree(
885 crate::application::vcs_collections::CONFIG_NAMESPACE,
886 &crate::json!({
887 "default_branch": "main",
888 "author": crate::json!({
889 "name": "reddb",
890 "email": "reddb@localhost"
891 }),
892 "protected_branches": crate::json!(["main"]),
893 "closure": crate::json!({
894 "enabled": true,
895 "lazy": true
896 }),
897 "merge": crate::json!({
898 "default_strategy": "auto",
899 "fast_forward": true
900 })
901 }),
902 );
903 }
904
905 {
908 let store = runtime.inner.db.store();
909 for name in crate::application::migration_collections::ALL {
910 let _ = store.get_or_create_collection(*name);
911 }
912 }
913
914 let _ = crate::application::topology_collections::ensure(&runtime);
918
919 runtime.prepare_paged_counts()?;
924
925 runtime
933 .inner
934 .db
935 .store()
936 .register_entity_id(crate::storage::EntityId::new(
937 crate::storage::FIRST_USER_ENTITY_ID - 1,
938 ));
939
940 {
954 let weak = Arc::downgrade(&runtime.inner);
955 runtime
956 .background_tasks
957 .as_ref()
958 .expect("foreground runtime")
959 .spawn(
960 std::thread::Builder::new().name("reddb-maintenance".into()),
961 move |stopped| {
962 let tick = std::time::Duration::from_millis(200);
963 let work_interval = std::time::Duration::from_secs(60);
964 let mut last_work = std::time::Instant::now();
965 let mut allocator_pressure =
966 super::allocator_pressure::AllocatorPressure::default();
967 loop {
968 std::thread::park_timeout(tick);
969 if stopped.load(AtomicOrdering::Acquire) {
970 break;
971 }
972 let Some(inner) = weak.upgrade() else {
973 break;
976 };
977 if last_work.elapsed() >= work_interval {
978 let _stats = inner.db.store().context_index().stats();
979 last_work = std::time::Instant::now();
980 }
981 let rt = RedDBRuntime {
982 background_tasks: None,
983 inner,
984 };
985 rt.try_reclaim_history_tick();
986 allocator_pressure.tick();
987 }
988 },
989 );
990 }
991
992 {
994 let store = runtime.inner.db.store();
995 let mut backup_enabled = false;
996 let mut backup_interval = 3600u64;
997
998 if let Some(manager) = store.get_collection("red_config") {
999 manager.for_each_entity(|entity| {
1000 if let Some(row) = entity.data.as_row() {
1001 let key = row.get_field("key").and_then(|v| match v {
1002 reddb_types::Value::Text(s) => Some(s.as_ref()),
1003 _ => None,
1004 });
1005 let val = row.get_field("value");
1006 if key == Some("red.config.backup.enabled") {
1007 backup_enabled = match val {
1008 Some(reddb_types::Value::Boolean(true)) => true,
1009 Some(reddb_types::Value::Text(s)) => &**s == "true",
1010 _ => false,
1011 };
1012 } else if key == Some("red.config.backup.interval_secs") {
1013 if let Some(reddb_types::Value::Integer(n)) = val {
1014 backup_interval = *n as u64;
1015 }
1016 }
1017 }
1018 true
1019 });
1020 }
1021
1022 if backup_enabled {
1023 runtime.inner.backup_scheduler.set_interval(backup_interval);
1024 let rt = runtime.clone();
1025 runtime
1026 .inner
1027 .backup_scheduler
1028 .start(move || rt.trigger_backup().map_err(|e| format!("{}", e)));
1029 }
1030 }
1031
1032 {
1034 runtime
1035 .inner
1036 .ec_registry
1037 .load_from_config_store(runtime.inner.db.store().as_ref());
1038 if !runtime.inner.db.options().read_only
1039 && !runtime.inner.ec_registry.async_configs().is_empty()
1040 {
1041 runtime.inner.ec_worker.start(
1042 Arc::clone(&runtime.inner.ec_registry),
1043 Arc::clone(&runtime.inner.db.store()),
1044 );
1045 }
1046 }
1047
1048 if let (true, crate::replication::ReplicationRole::Replica { primary_addr }) = (
1049 runtime.inner.db.options().replica_loop_enabled
1050 && !runtime.inner.db.options().read_only,
1051 runtime.inner.db.options().replication.role.clone(),
1052 ) {
1053 let rt = runtime.clone();
1054 std::thread::Builder::new()
1055 .name("reddb-replica".into())
1056 .spawn(move || rt.run_replica_loop(primary_addr))
1057 .ok();
1058 }
1059
1060 runtime.inner.lifecycle.mark_ready();
1065
1066 #[cfg(not(test))]
1067 if runtime
1068 .db()
1069 .store()
1070 .config()
1071 .resident_memory_bytes_max
1072 .is_some()
1073 && !runtime.db().options().read_only
1074 {
1075 runtime.start_count_projection_worker();
1076 }
1077
1078 {
1087 let weak_inner = Arc::downgrade(&runtime.inner);
1088 runtime
1089 .background_tasks
1090 .as_ref()
1091 .expect("foreground runtime")
1092 .spawn(
1093 std::thread::Builder::new()
1094 .name("reddb-mv-scheduler".into())
1095 .stack_size(8 * 1024 * 1024),
1100 move |stopped| loop {
1101 std::thread::park_timeout(std::time::Duration::from_millis(50));
1102 if stopped.load(AtomicOrdering::Acquire) {
1103 break;
1104 }
1105 let Some(inner) = weak_inner.upgrade() else {
1106 break;
1107 };
1108 let rt = RedDBRuntime {
1109 background_tasks: None,
1110 inner,
1111 };
1112 rt.refresh_due_materialized_views();
1113 },
1114 );
1115 }
1116
1117 if !runtime.write_gate().is_read_only() {
1127 let weak_inner = Arc::downgrade(&runtime.inner);
1128 runtime
1129 .background_tasks
1130 .as_ref()
1131 .expect("foreground runtime")
1132 .spawn(
1133 std::thread::Builder::new().name("reddb-retention-sweeper".into()),
1134 move |stopped| loop {
1135 std::thread::park_timeout(std::time::Duration::from_millis(500));
1136 if stopped.load(AtomicOrdering::Acquire) {
1137 break;
1138 }
1139 let Some(inner) = weak_inner.upgrade() else {
1140 break;
1141 };
1142 let rt = RedDBRuntime {
1143 background_tasks: None,
1144 inner,
1145 };
1146 rt.sweep_retention_tick(
1147 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
1148 );
1149 rt.sweep_individual_ttl_tick(
1150 crate::runtime::retention_sweeper::DEFAULT_SWEEPER_BATCH,
1151 );
1152 },
1153 );
1154 }
1155
1156 if no_auth_boot {
1160 use crate::runtime::control_events::{
1161 ActorRef, ControlEvent, ControlEventCtx, EventKind, Outcome,
1162 };
1163 let ledger = std::sync::Arc::clone(&runtime.inner.control_event_ledger.read());
1164 let ctx = ControlEventCtx {
1165 actor: ActorRef::System("boot"),
1166 scope: None,
1167 request_id: None,
1168 trace_id: None,
1169 };
1170 let event = ControlEvent {
1171 kind: EventKind::AuthDisabled,
1172 outcome: Outcome::Allowed,
1173 action: std::borrow::Cow::Borrowed("auth.disabled"),
1174 resource: Some("server".to_string()),
1175 reason: Some("--no-auth / --dev".to_string()),
1176 matched_policy_id: None,
1177 fields: std::collections::HashMap::new(),
1178 };
1179 if let Err(err) = ledger.emit(&ctx, event) {
1180 tracing::warn!(
1181 target: "reddb::security",
1182 error = %err,
1183 "could not record the auth-disabled boot in the control-event ledger"
1184 );
1185 }
1186 }
1187
1188 Ok(runtime)
1189 }
1190
1191 fn rehydrate_snapshot_xid_floor(&self) -> RedDBResult<()> {
1192 let store = self.inner.db.store();
1193 let snapshot_manager = self.inner.transaction_state.snapshot_manager();
1194 let base_high_water_mark = store.startup_xid_high_water_mark();
1195 if let Some(high_water_mark) = base_high_water_mark {
1196 snapshot_manager.observe_committed_xid(high_water_mark);
1197 }
1198 for collection in store.list_collections() {
1199 let Some(manager) = store.get_collection(&collection) else {
1200 continue;
1201 };
1202 let observe = |entity: &crate::storage::UnifiedEntity| {
1203 snapshot_manager.observe_committed_xid(entity.xmin);
1204 snapshot_manager.observe_committed_xid(entity.xmax);
1205 true
1206 };
1207 if base_high_water_mark.is_some() {
1208 manager.for_each_entity(observe);
1211 } else {
1212 manager.try_for_each_entity(usize::MAX, observe)?;
1215 }
1216 }
1217 if base_high_water_mark.is_none() {
1218 store.remember_startup_xid_high_water_mark(
1219 snapshot_manager.peek_next_xid().saturating_sub(1),
1220 );
1221 }
1222 Ok(())
1223 }
1224
1225 pub(crate) fn ensure_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1238 let store = self.inner.db.store();
1239 let mut changed = false;
1240 if store.get_collection(name).is_none() {
1241 store.get_or_create_collection(name);
1242 changed = true;
1243 }
1244 if self.inner.db.collection_contract(name).is_none() {
1245 self.inner
1246 .db
1247 .save_collection_contract(system_keyed_collection_contract(
1248 name,
1249 crate::catalog::CollectionModel::Table,
1250 ))
1251 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1252 changed = true;
1253 }
1254 if changed {
1255 self.inner
1256 .db
1257 .persist_metadata()
1258 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1259 }
1260 Ok(())
1261 }
1262
1263 pub(crate) fn drop_materialized_view_backing(&self, name: &str) -> RedDBResult<()> {
1268 let store = self.inner.db.store();
1269 if store.get_collection(name).is_none() {
1270 return Ok(());
1271 }
1272 store
1273 .drop_collection(name)
1274 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1275 if self.inner.db.collection_contract(name).is_some() {
1278 self.inner
1279 .db
1280 .remove_collection_contract(name)
1281 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1282 }
1283 self.invalidate_result_cache();
1284 self.inner
1285 .db
1286 .persist_metadata()
1287 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1288 Ok(())
1289 }
1290
1291 fn bootstrap_system_keyed_collections(&self) -> RedDBResult<()> {
1292 let mut changed = false;
1293 for (name, model) in [
1294 ("red.config", crate::catalog::CollectionModel::Config),
1295 ("red.vault", crate::catalog::CollectionModel::Vault),
1296 (
1300 crate::runtime::continuous_materialized_view::CATALOG_COLLECTION,
1301 crate::catalog::CollectionModel::Config,
1302 ),
1303 ] {
1304 if self.inner.db.store().get_collection(name).is_none() {
1305 self.inner.db.store().get_or_create_collection(name);
1306 changed = true;
1307 }
1308 if self.inner.db.collection_contract(name).is_none() {
1309 let contract = system_keyed_collection_contract(name, model);
1310 if self.inner.db.options().read_only {
1311 self.inner.db.cache_read_only_bootstrap_contract(contract);
1312 } else {
1313 self.inner
1314 .db
1315 .save_collection_contract(contract)
1316 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1317 }
1318 changed = true;
1319 }
1320 }
1321 if changed {
1322 self.inner
1323 .db
1324 .persist_metadata()
1325 .map_err(|err| RedDBError::Internal(err.to_string()))?;
1326 }
1327 Ok(())
1328 }
1329
1330 pub fn db(&self) -> Arc<RedDB> {
1331 Arc::clone(&self.inner.db)
1332 }
1333
1334 pub fn index_store_ref(&self) -> &super::index_store::IndexStore {
1339 &self.inner.index_store
1340 }
1341
1342 pub(crate) fn schema_vocabulary_apply(
1347 &self,
1348 event: crate::runtime::schema_vocabulary::DdlEvent,
1349 ) {
1350 self.inner.schema_vocabulary.write().on_ddl(event);
1351 }
1352
1353 pub fn schema_vocabulary_lookup(
1358 &self,
1359 token: &str,
1360 ) -> Vec<crate::runtime::schema_vocabulary::VocabHit> {
1361 self.inner.schema_vocabulary.read().lookup(token).to_vec()
1362 }
1363
1364 pub fn set_auth_store(&self, store: Arc<crate::auth::store::AuthStore>) {
1368 *self.inner.auth_store.write() = Some(store);
1369 }
1370
1371 pub fn auth_store(&self) -> Option<Arc<crate::auth::store::AuthStore>> {
1374 self.inner.auth_store.read().clone()
1375 }
1376
1377 pub fn set_oauth_validator(&self, validator: Option<Arc<crate::auth::oauth::OAuthValidator>>) {
1381 *self.inner.oauth_validator.write() = validator;
1382 }
1383
1384 pub fn oauth_validator(&self) -> Option<Arc<crate::auth::oauth::OAuthValidator>> {
1388 self.inner.oauth_validator.read().clone()
1389 }
1390
1391 pub fn set_browser_token_authority(
1397 &self,
1398 authority: Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>,
1399 ) {
1400 *self.inner.browser_token_authority.write() = authority;
1401 }
1402
1403 pub fn browser_token_authority(
1407 &self,
1408 ) -> Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>> {
1409 self.inner.browser_token_authority.read().clone()
1410 }
1411}
1412
1413#[cfg(test)]
1414mod xid_resume_tests {
1415 use super::*;
1416 use reddb_engine::storage::collection_read::{CollectionCursor, CollectionRead, EncodedRecord};
1417
1418 struct Unreadable;
1419
1420 fn unavailable() -> crate::storage::StoreError {
1421 crate::storage::StoreError::Io(std::io::Error::other("XID bootstrap read a base payload"))
1422 }
1423
1424 impl CollectionRead for Unreadable {
1425 fn get_record(
1426 &self,
1427 id: crate::storage::EntityId,
1428 _: usize,
1429 ) -> Result<Option<EncodedRecord>, crate::storage::StoreError> {
1430 if id.raw() == 999 {
1431 Ok(None)
1432 } else {
1433 Err(unavailable())
1434 }
1435 }
1436 fn cursor(&self) -> Result<Box<dyn CollectionCursor + '_>, crate::storage::StoreError> {
1437 Err(unavailable())
1438 }
1439 fn owned_cursor(&self) -> Result<Box<dyn CollectionCursor>, crate::storage::StoreError> {
1440 Err(unavailable())
1441 }
1442 }
1443
1444 #[test]
1445 fn xid_resume_survives_pending_checkpoint_process_exit() {
1446 const FIXTURE: &str = "REDDB_XID_RESUME_CRASH_FIXTURE";
1447 let open = |path: std::path::PathBuf| {
1448 let options = RedDBOptions::persistent(path)
1449 .with_memory_budget(64 * 1024 * 1024)
1450 .with_storage_profile(
1451 crate::storage::profile::StorageDeployPreset::PrimaryReplicaDev.selection(),
1452 )
1453 .expect("profile");
1454 crate::storage::RedDB::with_disk_residency_for_qualification(|| {
1455 RedDBRuntime::with_options(options)
1456 })
1457 .expect("crash recovery runtime")
1458 };
1459 if let Some(root) = std::env::var_os(FIXTURE) {
1460 let root = std::path::PathBuf::from(root);
1461 assert_eq!(
1462 std::fs::read_to_string(root.join("owned-fixture")).expect("owned fixture"),
1463 "xid-resume-crash-v1"
1464 );
1465 let runtime = open(root.join("crash.rdb"));
1466 for sql in [
1467 "CREATE TABLE crash_rows (id INTEGER, payload TEXT)",
1468 "INSERT INTO crash_rows (id, payload) VALUES (1, 'committed')",
1469 "BEGIN",
1470 "UPDATE crash_rows SET payload = 'pending' WHERE id = 1",
1471 "INSERT INTO crash_rows (id, payload) VALUES (2, 'pending')",
1472 ] {
1473 runtime.execute_query(sql).expect("crash fixture statement");
1474 }
1475 runtime
1476 .flush()
1477 .expect("checkpoint with pending transaction");
1478 std::fs::write(
1479 root.join("checkpoint-complete"),
1480 "pending transaction remains open",
1481 )
1482 .expect("acknowledged fixture checkpoint");
1483 std::process::exit(0);
1486 }
1487 let directory = tempfile::tempdir().expect("owned crash fixture");
1488 std::fs::write(
1489 directory.path().join("owned-fixture"),
1490 "xid-resume-crash-v1",
1491 )
1492 .expect("ownership marker");
1493 let child = std::process::Command::new(std::env::current_exe().expect("test executable"))
1494 .args([
1495 "--exact",
1496 "runtime::impl_lifecycle::xid_resume_tests::xid_resume_survives_pending_checkpoint_process_exit",
1497 "--test-threads=1",
1498 ])
1499 .env(FIXTURE, directory.path())
1500 .output()
1501 .expect("crashing owned child");
1502 assert!(
1503 child.status.success(),
1504 "{}",
1505 String::from_utf8_lossy(&child.stderr)
1506 );
1507 assert!(directory.path().join("checkpoint-complete").is_file());
1508 let path = directory.path().join("crash.rdb");
1509 assert!(path.is_file());
1510 let runtime = open(path.clone());
1511 let result = runtime
1512 .execute_query("SELECT id, payload FROM crash_rows ORDER BY id")
1513 .expect("committed preimage");
1514 assert_eq!(
1515 result.result.records.len(),
1516 1,
1517 "pending insert must stay invisible"
1518 );
1519 assert_eq!(result.result.records[0].get("id"), Some(&Value::Integer(1)));
1520 assert_eq!(
1521 result.result.records[0].get("payload"),
1522 Some(&Value::text("committed"))
1523 );
1524 runtime
1525 .execute_query("INSERT INTO crash_rows (id, payload) VALUES (3, 'after')")
1526 .expect("new writer after recovery");
1527 runtime.flush().expect("recovered checkpoint");
1528 drop(runtime);
1529 let reopened = open(path);
1530 let result = reopened
1531 .execute_query("SELECT id FROM crash_rows ORDER BY id")
1532 .expect("second recovery");
1533 assert_eq!(result.result.records.len(), 2);
1534 assert_eq!(result.result.records[0].get("id"), Some(&Value::Integer(1)));
1535 assert_eq!(result.result.records[1].get("id"), Some(&Value::Integer(3)));
1536 }
1537
1538 #[test]
1539 fn xid_resume_skips_known_base_payloads_but_rejects_an_unproven_root() {
1540 let directory = tempfile::tempdir().expect("resume fixture");
1541 let options = RedDBOptions::persistent(directory.path().join("resume.rdb"))
1542 .with_memory_budget(64 * 1024 * 1024)
1543 .with_storage_profile(
1544 crate::storage::profile::StorageDeployPreset::PrimaryReplicaDev.selection(),
1545 )
1546 .expect("profile");
1547 let runtime = crate::storage::RedDB::with_disk_residency_for_qualification(|| {
1548 RedDBRuntime::with_options(options)
1549 })
1550 .expect("runtime");
1551 runtime
1552 .execute_query("CREATE TABLE resume_rows (id INTEGER)")
1553 .expect("table");
1554 runtime
1555 .execute_query("INSERT INTO resume_rows (id) VALUES (1)")
1556 .expect("row");
1557 runtime.flush().expect("publish complete XID summary");
1558 let store = runtime.db().store();
1559 assert!(store.startup_xid_high_water_mark().is_some());
1560 let root = store
1561 .collection_root_page("resume_rows")
1562 .expect("published root");
1563 let manager = store.get_collection("resume_rows").expect("manager");
1564 for (candidate, proven) in [(root, true), (root + 1, false)] {
1565 manager.install_backing(
1566 Arc::new(Unreadable),
1567 candidate,
1568 store.format_version(),
1569 4096,
1570 1,
1571 );
1572 let mut overlay = crate::storage::UnifiedEntity::table_row(
1573 crate::storage::EntityId::new(999),
1574 "resume_rows",
1575 999,
1576 vec![Value::Integer(2)],
1577 );
1578 overlay.xmin = 500_000;
1579 overlay.xmax = 600_000;
1580 manager.insert(overlay).expect("resident WAL overlay");
1581 let result = runtime.rehydrate_snapshot_xid_floor();
1582 assert_eq!(result.is_ok(), proven, "root={candidate}: {result:?}");
1583 if proven {
1584 assert!(runtime.snapshot_manager().peek_next_xid() > 600_000);
1585 }
1586 }
1587 }
1588}