1use std::cmp::Ordering;
4use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque};
5use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
6use std::sync::{Arc, Mutex};
7use std::time::{SystemTime, UNIX_EPOCH};
8
9use crate::api::{RedDBError, RedDBOptions, RedDBResult};
10use crate::catalog::{
11 CatalogAnalyticsJobStatus, CatalogAttentionSummary, CatalogGraphProjectionStatus,
12 CatalogIndexStatus, CatalogModelSnapshot, CollectionDescriptor,
13};
14use crate::health::{HealthProvider, HealthReport};
15use crate::index::IndexCatalog;
16use crate::physical::{
17 ExportDescriptor, ManifestEvent, PhysicalAnalyticsJob, PhysicalGraphProjection, PhysicalLayout,
18 SnapshotDescriptor,
19};
20use crate::serde_json::Value as JsonValue;
21use crate::storage::engine::pathfinding::{AStar, BellmanFord, Dijkstra, BFS, DFS};
22use crate::storage::engine::{
23 BetweennessCentrality, ClosenessCentrality, ClusteringCoefficient, ConnectedComponents,
24 CycleDetector, DegreeCentrality, EigenvectorCentrality, GraphStore, IvfConfig, IvfIndex,
25 IvfStats, LabelPropagation, Louvain, MetadataEntry, MetadataFilter as VectorMetadataFilter,
26 MetadataValue as VectorMetadataValue, PageRank, PersonalizedPageRank, PhysicalFileHeader,
27 StoredNode, StronglyConnectedComponents, WeaklyConnectedComponents, HITS,
28};
29use crate::storage::query::ast::{
30 AlterOperation, AlterQueueQuery, AlterTableQuery, CompareOp, CreateCollectionQuery,
31 CreateIndexQuery, CreateQueueQuery, CreateTableQuery, CreateTimeSeriesQuery, CreateTreeQuery,
32 CreateVectorQuery, DeleteQuery, DropCollectionQuery, DropDocumentQuery, DropForkQuery,
33 DropGraphQuery, DropIndexQuery, DropKvQuery, DropQueueQuery, DropTableQuery,
34 DropTimeSeriesQuery, DropTreeQuery, DropVectorQuery, EventsBackfillQuery, ExplainAlterQuery,
35 ExplainFormat, FieldRef, Filter, ForkStoreQuery, FusionStrategy, GraphCommand, HybridQuery,
36 IndexMethod, InsertEntityType, InsertQuery, JoinQuery, JoinType, OrderByClause,
37 ProbabilisticCommand, Projection, PromoteForkQuery, QueryExpr, QueueCommand, QueueSelectQuery,
38 QueueSide, SearchCommand, TableQuery, TreeCommand, TruncateQuery, UpdateQuery, VectorQuery,
39 VectorSource,
40};
41use crate::storage::query::is_universal_entity_source as is_universal_query_source;
42use crate::storage::query::modes::{detect_mode, parse_multi, QueryMode};
43use crate::storage::query::planner::{
44 CanonicalLogicalPlan, CanonicalPlanner, CostEstimator, QueryPlanner,
45};
46use crate::storage::query::unified::{UnifiedRecord, UnifiedResult};
47use crate::storage::schema::Value;
48use crate::storage::unified::dsl::{
49 apply_filters, cosine_similarity, Filter as DslFilter, FilterOp as DslFilterOp,
50 FilterValue as DslFilterValue, GraphPatternDsl, HybridQueryBuilder, MatchComponents,
51 QueryResult as DslQueryResult, ScoredMatch, TextSearchBuilder,
52};
53use crate::storage::unified::store::{
54 NativeCatalogSummary, NativeManifestSummary, NativePhysicalState, NativeRecoverySummary,
55 NativeRegistrySummary,
56};
57use crate::storage::unified::{
58 Metadata, MetadataValue as UnifiedMetadataValue, RefTarget, UnifiedMetadataFilter,
59};
60use crate::storage::{
61 EntityData, EntityId, EntityKind, RedDB, RefType, SimilarResult, StoreStats, UnifiedEntity,
62 UnifiedStore,
63};
64
65#[derive(Debug, Clone)]
66pub struct ConnectionPoolConfig {
67 pub max_connections: usize,
68 pub max_idle: usize,
69}
70
71impl Default for ConnectionPoolConfig {
72 fn default() -> Self {
73 Self {
74 max_connections: 64,
75 max_idle: 16,
76 }
77 }
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub struct ScanCursor {
82 pub offset: usize,
83}
84
85#[derive(Debug, Clone)]
86pub struct ScanPage {
87 pub collection: String,
88 pub items: Vec<UnifiedEntity>,
89 pub next: Option<ScanCursor>,
90 pub total: usize,
91}
92
93#[derive(Debug, Clone)]
94pub struct SystemInfo {
95 pub pid: u32,
96 pub cpu_cores: usize,
97 pub total_memory_bytes: u64,
98 pub available_memory_bytes: u64,
99 pub os: String,
100 pub arch: String,
101 pub hostname: String,
102}
103
104impl SystemInfo {
105 pub fn should_parallelize() -> bool {
108 std::thread::available_parallelism()
109 .map(|p| p.get() > 1)
110 .unwrap_or(false)
111 }
112
113 pub fn collect() -> Self {
114 Self {
115 pid: std::process::id(),
116 cpu_cores: std::thread::available_parallelism()
117 .map(|p| p.get())
118 .unwrap_or(1),
119 total_memory_bytes: Self::read_total_memory(),
120 available_memory_bytes: Self::read_available_memory(),
121 os: std::env::consts::OS.to_string(),
122 arch: std::env::consts::ARCH.to_string(),
123 hostname: std::env::var("HOSTNAME")
124 .or_else(|_| std::env::var("COMPUTERNAME"))
125 .unwrap_or_else(|_| "unknown".to_string()),
126 }
127 }
128
129 #[cfg(target_os = "linux")]
130 fn read_total_memory() -> u64 {
131 std::fs::read_to_string("/proc/meminfo")
132 .ok()
133 .and_then(|s| {
134 s.lines()
135 .find(|l| l.starts_with("MemTotal:"))
136 .and_then(|l| {
137 l.split_whitespace()
138 .nth(1)
139 .and_then(|v| v.parse::<u64>().ok())
140 })
141 .map(|kb| kb * 1024)
142 })
143 .unwrap_or(0)
144 }
145
146 #[cfg(target_os = "linux")]
147 fn read_available_memory() -> u64 {
148 std::fs::read_to_string("/proc/meminfo")
149 .ok()
150 .and_then(|s| {
151 s.lines()
152 .find(|l| l.starts_with("MemAvailable:"))
153 .and_then(|l| {
154 l.split_whitespace()
155 .nth(1)
156 .and_then(|v| v.parse::<u64>().ok())
157 })
158 .map(|kb| kb * 1024)
159 })
160 .unwrap_or(0)
161 }
162
163 #[cfg(not(target_os = "linux"))]
164 fn read_total_memory() -> u64 {
165 0
166 }
167
168 #[cfg(not(target_os = "linux"))]
169 fn read_available_memory() -> u64 {
170 0
171 }
172}
173
174#[derive(Debug, Clone)]
175pub struct RuntimeStats {
176 pub active_connections: usize,
177 pub idle_connections: usize,
178 pub total_checkouts: u64,
179 pub paged_mode: bool,
180 pub started_at_unix_ms: u128,
181 pub store: StoreStats,
182 pub system: SystemInfo,
183 pub result_blob_cache: crate::storage::cache::BlobCacheStats,
184 pub kv: KvStats,
185 pub metrics_ingest: MetricsIngestStats,
186}
187
188#[derive(Debug, Clone, PartialEq, Eq)]
189pub struct MetricsTenantActivityStats {
190 pub tenant: String,
191 pub namespace: String,
192 pub operation: String,
193 pub count: u64,
194}
195
196#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
197pub struct MetricsIngestStats {
198 pub samples_accepted: u64,
199 pub series_accepted: u64,
200 pub samples_rejected: u64,
201 pub series_rejected: u64,
202 pub series_rejected_cardinality_budget: u64,
203}
204
205#[derive(Debug, Default)]
206pub(crate) struct MetricsIngestCounters {
207 samples_accepted: AtomicU64,
208 series_accepted: AtomicU64,
209 samples_rejected: AtomicU64,
210 series_rejected: AtomicU64,
211 series_rejected_cardinality_budget: AtomicU64,
212}
213
214impl MetricsIngestCounters {
215 pub(crate) fn record(
216 &self,
217 accepted_samples: u64,
218 accepted_series: u64,
219 rejected_samples: u64,
220 rejected_series: u64,
221 ) {
222 self.samples_accepted
223 .fetch_add(accepted_samples, AtomicOrdering::Relaxed);
224 self.series_accepted
225 .fetch_add(accepted_series, AtomicOrdering::Relaxed);
226 self.samples_rejected
227 .fetch_add(rejected_samples, AtomicOrdering::Relaxed);
228 self.series_rejected
229 .fetch_add(rejected_series, AtomicOrdering::Relaxed);
230 }
231
232 pub(crate) fn record_cardinality_budget_rejections(&self, rejected_series: u64) {
233 self.series_rejected_cardinality_budget
234 .fetch_add(rejected_series, AtomicOrdering::Relaxed);
235 }
236
237 pub(crate) fn snapshot(&self) -> MetricsIngestStats {
238 MetricsIngestStats {
239 samples_accepted: self.samples_accepted.load(AtomicOrdering::Relaxed),
240 series_accepted: self.series_accepted.load(AtomicOrdering::Relaxed),
241 samples_rejected: self.samples_rejected.load(AtomicOrdering::Relaxed),
242 series_rejected: self.series_rejected.load(AtomicOrdering::Relaxed),
243 series_rejected_cardinality_budget: self
244 .series_rejected_cardinality_budget
245 .load(AtomicOrdering::Relaxed),
246 }
247 }
248}
249
250#[derive(Debug, Default)]
251pub(crate) struct MetricsTenantActivityCounters {
252 inner: Mutex<BTreeMap<(String, String, String), u64>>,
253}
254
255impl MetricsTenantActivityCounters {
256 pub(crate) fn record(&self, tenant: &str, namespace: &str, operation: &str) {
257 let mut inner = self
258 .inner
259 .lock()
260 .unwrap_or_else(|poison| poison.into_inner());
261 let key = (
262 tenant.to_string(),
263 namespace.to_string(),
264 operation.to_string(),
265 );
266 *inner.entry(key).or_insert(0) += 1;
267 }
268
269 pub(crate) fn snapshot(&self) -> Vec<MetricsTenantActivityStats> {
270 let inner = self
271 .inner
272 .lock()
273 .unwrap_or_else(|poison| poison.into_inner());
274 inner
275 .iter()
276 .map(
277 |((tenant, namespace, operation), count)| MetricsTenantActivityStats {
278 tenant: tenant.clone(),
279 namespace: namespace.clone(),
280 operation: operation.clone(),
281 count: *count,
282 },
283 )
284 .collect()
285 }
286}
287
288#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
289pub struct KvStats {
290 pub puts: u64,
291 pub gets: u64,
292 pub deletes: u64,
293 pub incrs: u64,
294 pub cas_success: u64,
295 pub cas_conflict: u64,
296 pub watch_streams_active: u64,
297 pub watch_events_emitted: u64,
298 pub watch_drops: u64,
299}
300
301#[derive(Debug, Default)]
302pub(crate) struct KvStatsCounters {
303 puts: AtomicU64,
304 gets: AtomicU64,
305 deletes: AtomicU64,
306 incrs: AtomicU64,
307 cas_success: AtomicU64,
308 cas_conflict: AtomicU64,
309 watch_streams_active: AtomicU64,
310 watch_events_emitted: AtomicU64,
311 watch_drops: AtomicU64,
312}
313
314#[derive(Debug, Default)]
315pub(crate) struct KvTagIndex {
316 tag_to_entries: parking_lot::RwLock<HashMap<(String, String), HashMap<String, EntityId>>>,
317 key_to_tags: parking_lot::RwLock<HashMap<(String, String), BTreeSet<String>>>,
318}
319
320impl KvTagIndex {
321 pub(crate) fn replace(&self, collection: &str, key: &str, id: EntityId, tags: &[String]) {
322 let entry_key = (collection.to_string(), key.to_string());
323 let new_tags: BTreeSet<String> = tags
324 .iter()
325 .map(|tag| tag.trim())
326 .filter(|tag| !tag.is_empty())
327 .map(ToOwned::to_owned)
328 .collect();
329
330 let old_tags = {
331 let mut key_to_tags = self.key_to_tags.write();
332 if new_tags.is_empty() {
333 key_to_tags.remove(&entry_key)
334 } else {
335 key_to_tags.insert(entry_key.clone(), new_tags.clone())
336 }
337 };
338
339 let mut tag_to_entries = self.tag_to_entries.write();
340 if let Some(old_tags) = old_tags {
341 for tag in old_tags {
342 let scoped = (collection.to_string(), tag);
343 let remove_scoped = if let Some(entries) = tag_to_entries.get_mut(&scoped) {
344 entries.remove(key);
345 entries.is_empty()
346 } else {
347 false
348 };
349 if remove_scoped {
350 tag_to_entries.remove(&scoped);
351 }
352 }
353 }
354
355 for tag in new_tags {
356 tag_to_entries
357 .entry((collection.to_string(), tag))
358 .or_default()
359 .insert(key.to_string(), id);
360 }
361 }
362
363 pub(crate) fn remove(&self, collection: &str, key: &str) {
364 let entry_key = (collection.to_string(), key.to_string());
365 let old_tags = self.key_to_tags.write().remove(&entry_key);
366 let Some(old_tags) = old_tags else {
367 return;
368 };
369
370 let mut tag_to_entries = self.tag_to_entries.write();
371 for tag in old_tags {
372 let scoped = (collection.to_string(), tag);
373 let remove_scoped = if let Some(entries) = tag_to_entries.get_mut(&scoped) {
374 entries.remove(key);
375 entries.is_empty()
376 } else {
377 false
378 };
379 if remove_scoped {
380 tag_to_entries.remove(&scoped);
381 }
382 }
383 }
384
385 pub(crate) fn entries_for_tags(
386 &self,
387 collection: &str,
388 tags: &[String],
389 ) -> Vec<(String, EntityId)> {
390 if tags.is_empty() {
391 return Vec::new();
392 }
393
394 let tag_to_entries = self.tag_to_entries.read();
395 let mut out: HashMap<String, EntityId> = HashMap::new();
396 for tag in tags {
397 let scoped = (collection.to_string(), tag.trim().to_string());
398 if let Some(entries) = tag_to_entries.get(&scoped) {
399 for (key, id) in entries {
400 out.entry(key.clone()).or_insert(*id);
401 }
402 }
403 }
404 out.into_iter().collect()
405 }
406
407 pub(crate) fn tags_for_key(&self, collection: &str, key: &str) -> Vec<String> {
408 self.key_to_tags
409 .read()
410 .get(&(collection.to_string(), key.to_string()))
411 .map(|tags| tags.iter().cloned().collect())
412 .unwrap_or_default()
413 }
414}
415
416impl KvStatsCounters {
417 pub(crate) fn snapshot(&self) -> KvStats {
418 KvStats {
419 puts: self.puts.load(AtomicOrdering::Relaxed),
420 gets: self.gets.load(AtomicOrdering::Relaxed),
421 deletes: self.deletes.load(AtomicOrdering::Relaxed),
422 incrs: self.incrs.load(AtomicOrdering::Relaxed),
423 cas_success: self.cas_success.load(AtomicOrdering::Relaxed),
424 cas_conflict: self.cas_conflict.load(AtomicOrdering::Relaxed),
425 watch_streams_active: self.watch_streams_active.load(AtomicOrdering::Relaxed),
426 watch_events_emitted: self.watch_events_emitted.load(AtomicOrdering::Relaxed),
427 watch_drops: self.watch_drops.load(AtomicOrdering::Relaxed),
428 }
429 }
430
431 pub(crate) fn incr_puts(&self) {
432 self.puts.fetch_add(1, AtomicOrdering::Relaxed);
433 }
434
435 pub(crate) fn incr_gets(&self) {
436 self.gets.fetch_add(1, AtomicOrdering::Relaxed);
437 }
438
439 pub(crate) fn incr_deletes(&self) {
440 self.deletes.fetch_add(1, AtomicOrdering::Relaxed);
441 }
442
443 pub(crate) fn incr_incrs(&self) {
444 self.incrs.fetch_add(1, AtomicOrdering::Relaxed);
445 }
446
447 pub(crate) fn incr_cas_success(&self) {
448 self.cas_success.fetch_add(1, AtomicOrdering::Relaxed);
449 }
450
451 pub(crate) fn incr_cas_conflict(&self) {
452 self.cas_conflict.fetch_add(1, AtomicOrdering::Relaxed);
453 }
454
455 pub(crate) fn incr_watch_streams_active(&self) {
456 self.watch_streams_active
457 .fetch_add(1, AtomicOrdering::Relaxed);
458 }
459
460 pub(crate) fn decr_watch_streams_active(&self) {
461 self.watch_streams_active
462 .fetch_sub(1, AtomicOrdering::Relaxed);
463 }
464
465 pub(crate) fn incr_watch_events_emitted(&self) {
466 self.watch_events_emitted
467 .fetch_add(1, AtomicOrdering::Relaxed);
468 }
469
470 pub(crate) fn add_watch_drops(&self, count: u64) {
471 self.watch_drops.fetch_add(count, AtomicOrdering::Relaxed);
472 }
473}
474
475#[derive(Debug, Clone)]
476pub struct RuntimeQueryResult {
477 pub query: String,
478 pub mode: QueryMode,
479 pub statement: &'static str,
480 pub engine: &'static str,
481 pub result: UnifiedResult,
482 pub affected_rows: u64,
483 pub statement_type: &'static str,
485 pub bookmark: Option<String>,
486 pub notice: Option<String>,
487}
488
489impl RuntimeQueryResult {
490 pub fn dml_result(
492 query: String,
493 affected: u64,
494 statement_type: &'static str,
495 engine: &'static str,
496 ) -> Self {
497 Self {
498 query,
499 mode: QueryMode::Sql,
500 statement: statement_type,
501 engine,
502 result: UnifiedResult::empty(),
503 affected_rows: affected,
504 statement_type,
505 bookmark: None,
506 notice: None,
507 }
508 }
509
510 pub fn ok_message(query: String, message: &str, statement_type: &'static str) -> Self {
512 let mut result = UnifiedResult::empty();
513 let mut record = UnifiedRecord::new();
514 record.set("message", Value::text(message.to_string()));
515 result.push(record);
516 result.columns = vec!["message".to_string()];
517
518 Self {
519 query,
520 mode: QueryMode::Sql,
521 statement: statement_type,
522 engine: "runtime-ddl",
523 result,
524 affected_rows: 0,
525 statement_type,
526 bookmark: None,
527 notice: None,
528 }
529 }
530
531 pub fn ok_records(
535 query: String,
536 columns: Vec<String>,
537 rows: Vec<Vec<(String, Value)>>,
538 statement_type: &'static str,
539 ) -> Self {
540 let mut result = UnifiedResult::empty();
541 for row in rows {
542 let mut record = UnifiedRecord::new();
543 for (k, v) in row {
544 record.set(&k, v);
545 }
546 result.push(record);
547 }
548 result.columns = columns;
549
550 Self {
551 query,
552 mode: QueryMode::Sql,
553 statement: statement_type,
554 engine: "runtime-meta",
555 result,
556 affected_rows: 0,
557 statement_type,
558 bookmark: None,
559 notice: None,
560 }
561 }
562
563 pub fn bookmark_token(&self) -> Option<&str> {
564 self.bookmark.as_deref()
565 }
566}
567
568#[derive(Debug, Clone)]
569pub struct RuntimeQueryExplain {
570 pub query: String,
571 pub mode: QueryMode,
572 pub statement: &'static str,
573 pub is_universal: bool,
574 pub plan_cost: crate::storage::query::planner::PlanCost,
575 pub estimated_rows: f64,
576 pub estimated_selectivity: f64,
577 pub estimated_confidence: f64,
578 pub passes_applied: Vec<String>,
579 pub logical_plan: CanonicalLogicalPlan,
580 pub cte_materializations: Vec<String>,
587}
588
589#[derive(Debug, Clone)]
590pub struct RuntimeIvfMatch {
591 pub entity_id: u64,
592 pub distance: f32,
593 pub entity: Option<UnifiedEntity>,
594}
595
596#[derive(Debug, Clone)]
597pub struct RuntimeIvfSearchResult {
598 pub collection: String,
599 pub k: usize,
600 pub n_lists: usize,
601 pub n_probes: usize,
602 pub stats: IvfStats,
603 pub matches: Vec<RuntimeIvfMatch>,
604}
605
606#[derive(Debug, Clone, Copy, PartialEq, Eq)]
607pub enum RuntimeGraphDirection {
608 Outgoing,
609 Incoming,
610 Both,
611}
612
613#[derive(Debug, Clone, Copy, PartialEq, Eq)]
614pub enum RuntimeGraphTraversalStrategy {
615 Bfs,
616 Dfs,
617}
618
619#[derive(Debug, Clone, Copy, PartialEq, Eq)]
620pub enum RuntimeGraphPathAlgorithm {
621 Bfs,
622 Dijkstra,
623 AStar,
624 BellmanFord,
625}
626
627#[derive(Debug, Clone)]
628pub struct RuntimeGraphNode {
629 pub id: String,
630 pub label: String,
631 pub node_type: String,
632 pub out_edge_count: u32,
633 pub in_edge_count: u32,
634}
635
636#[derive(Debug, Clone)]
637pub struct RuntimeGraphEdge {
638 pub source: String,
639 pub target: String,
640 pub edge_type: String,
641 pub weight: f32,
642}
643
644#[derive(Debug, Clone)]
645pub struct RuntimeGraphVisit {
646 pub depth: usize,
647 pub node: RuntimeGraphNode,
648}
649
650#[derive(Debug, Clone)]
651pub struct RuntimeGraphNeighborhoodResult {
652 pub source: String,
653 pub direction: RuntimeGraphDirection,
654 pub max_depth: usize,
655 pub nodes: Vec<RuntimeGraphVisit>,
656 pub edges: Vec<RuntimeGraphEdge>,
657}
658
659#[derive(Debug, Clone)]
660pub struct RuntimeGraphTraversalResult {
661 pub source: String,
662 pub direction: RuntimeGraphDirection,
663 pub strategy: RuntimeGraphTraversalStrategy,
664 pub max_depth: usize,
665 pub visits: Vec<RuntimeGraphVisit>,
666 pub edges: Vec<RuntimeGraphEdge>,
667}
668
669#[derive(Debug, Clone)]
670pub struct RuntimeGraphPath {
671 pub hop_count: usize,
672 pub total_weight: f64,
673 pub nodes: Vec<RuntimeGraphNode>,
674 pub edges: Vec<RuntimeGraphEdge>,
675}
676
677#[derive(Debug, Clone)]
678pub struct RuntimeGraphPathResult {
679 pub source: String,
680 pub target: String,
681 pub direction: RuntimeGraphDirection,
682 pub algorithm: RuntimeGraphPathAlgorithm,
683 pub nodes_visited: usize,
684 pub negative_cycle_detected: Option<bool>,
685 pub path: Option<RuntimeGraphPath>,
686}
687
688#[derive(Debug, Clone, Copy, PartialEq, Eq)]
689pub enum RuntimeGraphComponentsMode {
690 Connected,
691 Weak,
692 Strong,
693}
694
695#[derive(Debug, Clone, Copy, PartialEq, Eq)]
696pub enum RuntimeGraphCentralityAlgorithm {
697 Degree,
698 Closeness,
699 Betweenness,
700 Eigenvector,
701 PageRank,
702}
703
704#[derive(Debug, Clone, Copy, PartialEq, Eq)]
705pub enum RuntimeGraphCommunityAlgorithm {
706 LabelPropagation,
707 Louvain,
708}
709
710#[derive(Debug, Clone)]
711pub struct RuntimeGraphComponent {
712 pub id: String,
713 pub size: usize,
714 pub nodes: Vec<String>,
715}
716
717#[derive(Debug, Clone)]
718pub struct RuntimeGraphComponentsResult {
719 pub mode: RuntimeGraphComponentsMode,
720 pub count: usize,
721 pub components: Vec<RuntimeGraphComponent>,
722}
723
724#[derive(Debug, Clone)]
725pub struct RuntimeGraphCentralityScore {
726 pub node: RuntimeGraphNode,
727 pub score: f64,
728}
729
730#[derive(Debug, Clone)]
731pub struct RuntimeGraphDegreeScore {
732 pub node: RuntimeGraphNode,
733 pub in_degree: usize,
734 pub out_degree: usize,
735 pub total_degree: usize,
736}
737
738#[derive(Debug, Clone)]
739pub struct RuntimeGraphCentralityResult {
740 pub algorithm: RuntimeGraphCentralityAlgorithm,
741 pub normalized: Option<bool>,
742 pub iterations: Option<usize>,
743 pub converged: Option<bool>,
744 pub scores: Vec<RuntimeGraphCentralityScore>,
745 pub degree_scores: Vec<RuntimeGraphDegreeScore>,
746}
747
748#[derive(Debug, Clone)]
749pub struct RuntimeGraphCommunity {
750 pub id: String,
751 pub size: usize,
752 pub nodes: Vec<String>,
753}
754
755#[derive(Debug, Clone)]
756pub struct RuntimeGraphCommunityResult {
757 pub algorithm: RuntimeGraphCommunityAlgorithm,
758 pub count: usize,
759 pub iterations: Option<usize>,
760 pub converged: Option<bool>,
761 pub modularity: Option<f64>,
762 pub passes: Option<usize>,
763 pub communities: Vec<RuntimeGraphCommunity>,
764}
765
766#[derive(Debug, Clone)]
767pub struct RuntimeGraphClusteringResult {
768 pub global: f64,
769 pub local: Vec<RuntimeGraphCentralityScore>,
770 pub triangle_count: Option<usize>,
771}
772
773#[derive(Debug, Clone)]
774pub struct RuntimeGraphHitsResult {
775 pub iterations: usize,
776 pub converged: bool,
777 pub hubs: Vec<RuntimeGraphCentralityScore>,
778 pub authorities: Vec<RuntimeGraphCentralityScore>,
779}
780
781#[derive(Debug, Clone)]
782pub struct RuntimeGraphCyclesResult {
783 pub limit_reached: bool,
784 pub cycles: Vec<RuntimeGraphPath>,
785}
786
787#[derive(Debug, Clone)]
788pub struct RuntimeGraphTopologicalSortResult {
789 pub acyclic: bool,
790 pub ordered_nodes: Vec<RuntimeGraphNode>,
791}
792
793#[derive(Debug, Clone)]
794pub struct RuntimeGraphPropertiesResult {
795 pub node_count: usize,
796 pub edge_count: usize,
797 pub self_loop_count: usize,
798 pub negative_edge_count: usize,
799 pub connected_component_count: usize,
800 pub weak_component_count: usize,
801 pub strong_component_count: usize,
802 pub is_empty: bool,
803 pub is_connected: bool,
804 pub is_weakly_connected: bool,
805 pub is_strongly_connected: bool,
806 pub is_complete: bool,
807 pub is_complete_directed: bool,
808 pub is_cyclic: bool,
809 pub is_circular: bool,
810 pub is_acyclic: bool,
811 pub is_tree: bool,
812 pub density: f64,
813 pub density_directed: f64,
814}
815
816#[derive(Debug, Clone)]
821pub struct ContextSearchResult {
822 pub query: String,
823 pub tables: Vec<ContextEntity>,
824 pub graph: ContextGraphResult,
825 pub vectors: Vec<ContextEntity>,
826 pub documents: Vec<ContextEntity>,
827 pub key_values: Vec<ContextEntity>,
828 pub connections: Vec<ContextConnection>,
829 pub summary: ContextSummary,
830}
831
832#[derive(Debug, Clone)]
833pub struct ContextEntity {
834 pub entity: UnifiedEntity,
835 pub score: f32,
836 pub discovery: DiscoveryMethod,
837 pub collection: String,
838}
839
840#[derive(Debug, Clone)]
841pub enum DiscoveryMethod {
842 Indexed {
843 field: String,
844 },
845 GlobalScan,
846 CrossReference {
847 source_id: u64,
848 ref_type: String,
849 },
850 GraphTraversal {
851 source_id: u64,
852 edge_type: String,
853 depth: usize,
854 },
855 VectorQuery {
856 similarity: f32,
857 },
858}
859
860#[derive(Debug, Clone)]
861pub struct ContextGraphResult {
862 pub nodes: Vec<ContextEntity>,
863 pub edges: Vec<ContextEntity>,
864}
865
866#[derive(Debug, Clone)]
867pub struct ContextConnection {
868 pub from_id: u64,
869 pub to_id: u64,
870 pub connection_type: ContextConnectionType,
871 pub weight: f32,
872}
873
874#[derive(Debug, Clone)]
875pub enum ContextConnectionType {
876 CrossRef(String),
877 GraphEdge(String),
878 VectorSimilarity(f32),
879}
880
881#[derive(Debug, Clone)]
882pub struct ContextSummary {
883 pub total_entities: usize,
884 pub direct_matches: usize,
885 pub expanded_via_graph: usize,
886 pub expanded_via_cross_refs: usize,
887 pub expanded_via_vector_query: usize,
888 pub collections_searched: usize,
889 pub execution_time_us: u64,
890 pub tiers_used: Vec<String>,
891 pub entities_reindexed: usize,
892}
893
894struct PoolState {
895 next_id: u64,
896 active: usize,
897 idle: Vec<u64>,
898 total_checkouts: u64,
899}
900
901impl Default for PoolState {
902 fn default() -> Self {
903 Self {
904 next_id: 1,
905 active: 0,
906 idle: Vec::new(),
907 total_checkouts: 0,
908 }
909 }
910}
911
912#[derive(Debug, Clone)]
913struct RuntimeResultCacheEntry {
914 result: RuntimeQueryResult,
915 cached_at: std::time::Instant,
916 scopes: HashSet<String>,
917}
918
919pub const METRIC_CACHE_SHADOW_DIVERGENCE_TOTAL: &str = "cache_shadow_divergence_total";
920pub const METRIC_RESULT_CACHE_HIT_TOTAL: &str = "result_cache_hit_total";
925pub const METRIC_RESULT_CACHE_MISS_TOTAL: &str = "result_cache_miss_total";
926pub const METRIC_RESULT_CACHE_EVICT_TOTAL: &str = "result_cache_evict_total";
927pub(crate) const ASK_ANSWER_CACHE_NAMESPACE: &str = "runtime.ask_answer_cache";
928const RMW_LOCK_SHARDS: usize = 64;
929
930struct RmwLockTable {
931 shards: Vec<parking_lot::Mutex<HashMap<String, Arc<parking_lot::Mutex<()>>>>>,
932}
933
934impl RmwLockTable {
935 fn new() -> Self {
936 let shards = (0..RMW_LOCK_SHARDS)
937 .map(|_| parking_lot::Mutex::new(HashMap::new()))
938 .collect();
939 Self { shards }
940 }
941
942 fn lock_for(&self, collection: &str, key: &str) -> Arc<parking_lot::Mutex<()>> {
943 use std::hash::{Hash, Hasher};
944
945 let mut hasher = std::collections::hash_map::DefaultHasher::new();
946 collection.hash(&mut hasher);
947 key.hash(&mut hasher);
948 let shard_idx = (hasher.finish() as usize) % self.shards.len();
949 let map_key = format!("{collection}\u{1f}{key}");
950 let mut shard = self.shards[shard_idx].lock();
951 shard
952 .entry(map_key)
953 .or_insert_with(|| Arc::new(parking_lot::Mutex::new(())))
954 .clone()
955 }
956}
957
958#[derive(Debug, Default)]
959pub(crate) struct CheckpointProjectionStats {
960 last_materialized_lsn: std::sync::atomic::AtomicU64,
961 checkpoints_completed: std::sync::atomic::AtomicU64,
962 last_checkpoint_duration_ms: std::sync::atomic::AtomicU64,
963}
964
965impl CheckpointProjectionStats {
966 pub(crate) fn record_checkpoint(&self, lsn: u64, duration_ms: u64) {
967 use std::sync::atomic::Ordering;
968
969 self.last_materialized_lsn.store(lsn, Ordering::Relaxed);
970 self.last_checkpoint_duration_ms
971 .store(duration_ms, Ordering::Relaxed);
972 self.checkpoints_completed.fetch_add(1, Ordering::Relaxed);
973 }
974
975 pub(crate) fn snapshot(&self, current_lsn: u64) -> CheckpointProjectionStatsSnapshot {
976 use std::sync::atomic::Ordering;
977
978 let last_materialized_lsn = self.last_materialized_lsn.load(Ordering::Relaxed);
979 CheckpointProjectionStatsSnapshot {
980 current_lsn,
981 last_materialized_lsn,
982 projection_lag: current_lsn.saturating_sub(last_materialized_lsn),
983 checkpoints_completed: self.checkpoints_completed.load(Ordering::Relaxed),
984 last_checkpoint_duration_ms: self.last_checkpoint_duration_ms.load(Ordering::Relaxed),
985 }
986 }
987}
988
989#[derive(Debug, Clone, Copy)]
990pub(crate) struct CheckpointProjectionStatsSnapshot {
991 pub(crate) current_lsn: u64,
992 pub(crate) last_materialized_lsn: u64,
993 pub(crate) projection_lag: u64,
994 pub(crate) checkpoints_completed: u64,
995 pub(crate) last_checkpoint_duration_ms: u64,
996}
997
998struct RuntimeInner {
999 db: Arc<RedDB>,
1000 layout: PhysicalLayout,
1001 embedded_single_file: bool,
1002 pub(crate) memory_budget: crate::storage::memory_budget::MemoryBudget,
1005 indices: IndexCatalog,
1006 pool_config: ConnectionPoolConfig,
1007 pool: Mutex<PoolState>,
1008 started_at_unix_ms: u128,
1009 probabilistic: probabilistic_store::ProbabilisticStore,
1010 index_store: index_store::IndexStore,
1011 cdc: crate::replication::cdc::CdcBuffer,
1012 pub(crate) checkpoint_projection_stats: CheckpointProjectionStats,
1013 pub(crate) checkpoint_columnar_emission_budget_chunks: usize,
1014 pub(crate) columnar_projection_size_floor_rows: usize,
1015 backup_scheduler: crate::replication::scheduler::BackupScheduler,
1016 query_cache: parking_lot::RwLock<crate::storage::query::planner::cache::PlanCache>,
1017 result_cache: parking_lot::RwLock<(
1018 HashMap<String, RuntimeResultCacheEntry>,
1019 std::collections::VecDeque<String>,
1020 )>,
1021 result_blob_cache: crate::storage::cache::BlobCache,
1022 result_blob_entries: parking_lot::RwLock<(
1023 HashMap<String, RuntimeResultCacheEntry>,
1024 std::collections::VecDeque<String>,
1025 )>,
1026 ask_answer_cache_entries:
1027 parking_lot::RwLock<(HashSet<String>, std::collections::VecDeque<String>)>,
1028 result_cache_shadow_divergences: std::sync::atomic::AtomicU64,
1029 result_cache_hits: std::sync::atomic::AtomicU64,
1032 result_cache_misses: std::sync::atomic::AtomicU64,
1033 result_cache_evictions: std::sync::atomic::AtomicU64,
1034 ask_daily_spend:
1035 parking_lot::RwLock<HashMap<String, crate::runtime::ai::cost_guard::DailyState>>,
1036 queue_message_locks: parking_lot::RwLock<HashMap<String, Arc<parking_lot::Mutex<()>>>>,
1039 rmw_locks: RmwLockTable,
1043 planner_dirty_tables: parking_lot::RwLock<HashSet<String>>,
1044 ec_registry: Arc<crate::ec::config::EcRegistry>,
1045 config_registry: Arc<crate::auth::registry::ConfigRegistry>,
1046 ec_worker: crate::ec::worker::EcWorker,
1047 auth_store: parking_lot::RwLock<Option<Arc<crate::auth::store::AuthStore>>>,
1052 oauth_validator: parking_lot::RwLock<Option<Arc<crate::auth::oauth::OAuthValidator>>>,
1059 browser_token_authority:
1066 parking_lot::RwLock<Option<Arc<crate::auth::browser_token::BrowserTokenAuthority>>>,
1067 views: parking_lot::RwLock<HashMap<String, Arc<crate::storage::query::ast::CreateViewQuery>>>,
1078 materialized_views: parking_lot::RwLock<crate::storage::cache::result::MaterializedViewCache>,
1079 pub(crate) retention_sweeper:
1084 parking_lot::RwLock<crate::runtime::retention_sweeper::RetentionSweeperState>,
1085 snapshot_manager: Arc<crate::storage::transaction::snapshot::SnapshotManager>,
1093 tx_contexts:
1100 parking_lot::RwLock<HashMap<u64, crate::storage::transaction::snapshot::TxnContext>>,
1101 lock_manager: Arc<crate::runtime::lock_manager::LockManager>,
1108 env_config_overrides: HashMap<String, String>,
1114 tx_local_tenants: parking_lot::RwLock<HashMap<u64, Option<String>>>,
1122 rls_policies: parking_lot::RwLock<
1129 HashMap<(String, String), Arc<crate::storage::query::ast::CreatePolicyQuery>>,
1130 >,
1131 rls_enabled_tables: parking_lot::RwLock<HashSet<String>>,
1132 foreign_tables: Arc<crate::storage::fdw::ForeignTableRegistry>,
1140 pending_tombstones: parking_lot::RwLock<
1152 HashMap<
1153 u64,
1154 Vec<(
1155 String,
1156 crate::storage::unified::entity::EntityId,
1157 crate::storage::transaction::snapshot::Xid,
1158 crate::storage::transaction::snapshot::Xid,
1159 )>,
1160 >,
1161 >,
1162 pending_versioned_updates: parking_lot::RwLock<
1168 HashMap<
1169 u64,
1170 Vec<(
1171 String,
1172 crate::storage::unified::entity::EntityId,
1173 crate::storage::unified::entity::EntityId,
1174 crate::storage::transaction::snapshot::Xid,
1175 crate::storage::transaction::snapshot::Xid,
1176 )>,
1177 >,
1178 >,
1179 pending_queue_dedup: parking_lot::RwLock<HashMap<u64, Vec<(String, String, EntityId)>>>,
1185 pending_kv_watch_events:
1186 parking_lot::RwLock<HashMap<u64, Vec<crate::replication::cdc::KvWatchEvent>>>,
1187 pending_store_wal_actions:
1188 parking_lot::RwLock<HashMap<u64, crate::storage::unified::DeferredStoreWalActions>>,
1189 pending_claim_locks: parking_lot::RwLock<HashMap<(String, EntityId), u64>>,
1195 tenant_tables: parking_lot::RwLock<HashMap<String, String>>,
1204 ddl_epoch: std::sync::atomic::AtomicU64,
1210 write_gate: Arc<crate::runtime::write_gate::WriteGate>,
1221 lifecycle: crate::runtime::lifecycle::Lifecycle,
1227 resource_limits: crate::runtime::resource_limits::ResourceLimits,
1232 audit_log: Arc<crate::runtime::audit_log::AuditLogger>,
1236 control_event_ledger:
1240 parking_lot::RwLock<Arc<dyn crate::runtime::control_events::ControlEventLedger>>,
1241 control_event_config: crate::runtime::control_events::ControlEventConfig,
1242 query_audit: Arc<crate::runtime::query_audit::QueryAuditStream>,
1246 lease_lifecycle: std::sync::OnceLock<Arc<crate::runtime::lease_lifecycle::LeaseLifecycle>>,
1253 replica_apply_metrics: Arc<crate::replication::logical::ReplicaApplyMetrics>,
1257 replica_link_metrics: Arc<crate::replication::reconnect::ReplicaLinkMetrics>,
1262 quota_bucket: crate::runtime::quota_bucket::QuotaBucket,
1265 schema_vocabulary: parking_lot::RwLock<crate::runtime::schema_vocabulary::SchemaVocabulary>,
1270 slow_query_logger: Arc<crate::telemetry::slow_query_logger::SlowQueryLogger>,
1276 slow_query_store: Arc<crate::telemetry::slow_query_store::SlowQueryStore>,
1281 kv_stats: KvStatsCounters,
1284 metrics_ingest_stats: MetricsIngestCounters,
1285 metrics_tenant_activity_stats: MetricsTenantActivityCounters,
1286 claim_telemetry: std::sync::Arc<claim_telemetry::ClaimTelemetryCounters>,
1289 queue_telemetry: std::sync::Arc<queue_telemetry::QueueTelemetryCounters>,
1293 query_latency_telemetry: std::sync::Arc<query_latency_telemetry::QueryLatencyTelemetry>,
1298 occupancy_sampler: std::sync::Arc<occupancy_sampler::OccupancySampler>,
1303 node_load_telemetry: std::sync::Arc<node_load_telemetry::NodeLoadTelemetry>,
1308 queue_presence: std::sync::Arc<crate::storage::queue::presence::ConsumerPresenceRegistry>,
1315 vector_introspection:
1324 std::sync::Arc<crate::storage::vector::introspection::VectorIntrospectionRegistry>,
1325 queue_wait_registry: std::sync::Arc<queue_wait_registry::QueueWaitRegistry>,
1329 pending_queue_wakes: parking_lot::RwLock<HashMap<u64, Vec<(String, String)>>>,
1335 kv_tag_index: KvTagIndex,
1337 chain_tip_cache:
1343 parking_lot::Mutex<HashMap<String, crate::runtime::blockchain_kind::ChainTipFull>>,
1344 chain_integrity_broken: parking_lot::Mutex<HashMap<String, bool>>,
1349 integrity_tombstones:
1356 parking_lot::Mutex<Vec<crate::runtime::integrity_tombstone::TombstoneRange>>,
1357 integrity_tombstones_state: std::sync::atomic::AtomicU8,
1358}
1359
1360#[derive(Clone)]
1361pub struct RedDBRuntime {
1362 inner: Arc<RuntimeInner>,
1363}
1364
1365pub struct RuntimeConnection {
1366 id: u64,
1367 inner: Arc<RuntimeInner>,
1368}
1369
1370pub struct CausalSession {
1371 runtime: RedDBRuntime,
1372 bookmark: Option<crate::replication::CausalBookmark>,
1373 wait_timeout: std::time::Duration,
1374}
1375
1376impl CausalSession {
1377 pub fn bookmark_token(&self) -> Option<String> {
1378 self.bookmark.map(|bookmark| bookmark.encode())
1379 }
1380
1381 pub fn inject_bookmark(&mut self, token: &str) -> RedDBResult<()> {
1382 let bookmark = crate::replication::CausalBookmark::decode(token)
1383 .map_err(|err| RedDBError::InvalidOperation(err.to_string()))?;
1384 self.bookmark = Some(bookmark);
1385 Ok(())
1386 }
1387
1388 pub fn execute_query(&mut self, query: &str) -> RedDBResult<RuntimeQueryResult> {
1389 if is_select_query_text(query) {
1390 if let Some(bookmark) = self.bookmark {
1391 self.runtime
1392 .wait_for_bookmark(&bookmark, self.wait_timeout)?;
1393 }
1394 }
1395 let result = self.runtime.execute_query(query)?;
1396 if let Some(token) = result.bookmark.as_deref() {
1397 self.inject_bookmark(token)?;
1398 }
1399 Ok(result)
1400 }
1401}
1402
1403fn is_select_query_text(query: &str) -> bool {
1404 query
1405 .trim_start()
1406 .get(..6)
1407 .is_some_and(|prefix| prefix.eq_ignore_ascii_case("select"))
1408}
1409
1410pub mod ai;
1411pub mod analytics_schema_registry;
1412pub(crate) mod analytics_source_catalog;
1413pub mod ask_pipeline;
1414pub mod audit_log;
1415pub mod audit_query;
1416pub mod authorized_search;
1417pub(crate) mod authz;
1418pub mod batch_insert;
1419pub mod blockchain_kind;
1420mod collection_contract;
1421pub mod config_matrix;
1422pub mod config_overlay;
1423pub mod config_watcher;
1424pub mod continuous_materialized_view;
1425pub mod control_events;
1426pub(crate) mod ddl;
1427pub mod disk_space_monitor;
1428mod dml_target_scan;
1429pub mod evidence_export;
1430pub(crate) mod execution_context;
1431mod expr_eval;
1432mod graph_dsl;
1433mod graph_tvf;
1434mod health_connection;
1435mod impl_audit_control;
1436mod impl_backup;
1437mod impl_catalog_accessors;
1438mod impl_cdc;
1439mod impl_config;
1440mod impl_config_secret;
1441pub(crate) mod impl_core;
1442mod impl_core_outer_scope;
1443mod impl_core_result_cache_scopes;
1444mod impl_ddl;
1445mod impl_dml;
1446mod impl_dml_batch;
1447mod impl_dml_chain;
1448mod impl_dml_claim;
1449mod impl_dml_crypto;
1450mod impl_dml_returning;
1451mod impl_dml_support;
1452mod impl_dml_tenant;
1453mod impl_dml_ttl;
1454mod impl_dml_update_analysis;
1455mod impl_dml_update_target;
1456mod impl_ec;
1457pub mod impl_ephemeral;
1458mod impl_events;
1459mod impl_fast_path;
1460mod impl_graph;
1461mod impl_graph_commands;
1462pub mod impl_kv;
1463mod impl_lifecycle;
1464mod impl_materialized_view;
1465mod impl_migrations;
1466mod impl_native;
1467mod impl_physical;
1468mod impl_primary_replica_file;
1469mod impl_probabilistic;
1470pub mod impl_queue;
1471mod impl_ranking;
1472mod impl_replica_loop;
1473mod impl_replication_commit;
1474mod impl_runtime_index_registry;
1475mod impl_telemetry_accessors;
1476mod impl_tenant_registry;
1477pub(crate) use impl_queue::RedwireWaitOutcome;
1478pub(crate) mod claim_telemetry;
1479mod impl_search;
1480mod impl_serverless;
1481mod impl_timeseries;
1482mod impl_tree;
1483mod impl_vcs;
1484mod index_store;
1485pub mod integrity_tombstone;
1486mod join_filter;
1487mod keyed_spine;
1488pub mod kv_watch;
1489pub mod lease_lifecycle;
1490pub mod lease_loop;
1491pub mod lease_timer_wheel;
1492pub mod lifecycle;
1493pub mod lock_manager;
1494pub mod locking;
1495pub(crate) mod materialization_limit;
1496pub(crate) mod metric_descriptor_catalog;
1497pub(crate) mod mutation;
1498pub(crate) mod mvcc_lifecycle;
1499pub(crate) mod node_load_telemetry;
1500pub(crate) mod occupancy_sampler;
1501pub(crate) mod primary_queue_store;
1502mod probabilistic_store;
1503pub mod query_audit;
1504pub(crate) mod query_exec;
1505pub(crate) mod query_latency_telemetry;
1506pub(crate) mod queue_lifecycle;
1507pub(crate) mod queue_telemetry;
1508pub(crate) mod queue_wait_registry;
1509pub mod quota_bucket;
1510pub(crate) mod ranking_descriptor_catalog;
1511mod record_search;
1512mod red_schema;
1513pub(crate) mod replica_queue_store;
1514pub mod resource_limits;
1515mod result_cache_runtime;
1516pub(crate) mod retention_filter;
1517pub(crate) mod retention_sweeper;
1518mod rls_injection;
1519pub(crate) mod scalar_evaluator;
1520pub mod schema_diff;
1521pub mod schema_vocabulary;
1522pub(crate) mod score_sketch;
1523pub(crate) mod sessionize;
1524pub mod signed_chain;
1525pub mod signed_writes_kind;
1526pub(crate) mod slo_descriptor_catalog;
1527pub mod snapshot_reuse;
1528mod statement_frame;
1529mod table_row_mvcc_resolver;
1530pub mod turbo_crash_inject;
1531mod vcs_command;
1532mod vector_index;
1533pub mod vector_turbo_kind;
1534pub(crate) mod window_phase;
1535pub mod within_clause;
1536pub mod write_gate;
1537
1538pub use self::claim_telemetry::ClaimTelemetrySnapshot;
1539pub use self::graph_dsl::*;
1540use self::join_filter::*;
1541use self::query_exec::*;
1542use self::record_search::*;
1543pub use self::statement_frame::EffectiveScope;
1544
1545pub mod mvcc {
1551 pub use super::impl_core::{
1552 capture_current_snapshot, clear_current_auth_identity, clear_current_connection_id,
1553 clear_current_snapshot, clear_current_tenant, current_connection_id, current_tenant,
1554 entity_visible_under_current_snapshot, entity_visible_with_context,
1555 set_current_auth_identity, set_current_connection_id, set_current_snapshot,
1556 set_current_tenant, snapshot_bundle, with_snapshot_bundle, SnapshotBundle, SnapshotContext,
1557 };
1558}
1559
1560pub mod record_search_helpers {
1562 use crate::storage::query::UnifiedRecord;
1563 use crate::storage::UnifiedEntity;
1564 use std::collections::BTreeSet;
1565
1566 pub fn entity_type_and_capabilities(
1567 entity: &UnifiedEntity,
1568 ) -> (&'static str, BTreeSet<String>) {
1569 super::record_search::runtime_entity_type_and_capabilities(entity)
1570 }
1571
1572 pub fn any_record_from_entity(entity: UnifiedEntity) -> Option<UnifiedRecord> {
1577 super::record_search::runtime_any_record_from_entity(entity)
1578 }
1579}