Skip to main content

linkrs_query/cache/plan_cache/
entry.rs

1use crate::cache::config::CachePriority;
2use crate::cache::plan_cache::key::ParamPosition;
3use crate::executor::streaming::plan::PhysicalPlan;
4use std::sync::Arc;
5use std::time::{Duration, Instant};
6
7/// Cached query plan entries
8///
9/// stores [`Arc<PhysicalPlan>`] instead of [`ExecutionPlan`] so that the
10/// cached plan is an immutable, verifiable arena plan that can be shared
11/// across concurrent executions without re-building.
12#[derive(Debug, Clone)]
13pub struct CachedPlan {
14    /// Query template (parameterized form)
15    pub query_template: String,
16    /// Immutable arena-based physical plan.
17    pub plan: Arc<PhysicalPlan>,
18    /// Parameter location information (for parameter binding)
19    pub param_positions: Vec<ParamPosition>,
20    /// Creation time
21    pub created_at: Instant,
22    /// Last access time
23    pub last_accessed: Instant,
24    /// Number of visits
25    pub access_count: u64,
26    /// Average execution time (milliseconds)
27    pub avg_execution_time_ms: f64,
28    /// Number of executions
29    pub execution_count: u64,
30    /// Cache priority
31    pub priority: CachePriority,
32    /// Plan complexity score (for eviction decisions)
33    pub complexity_score: u32,
34    /// Estimated compute cost (milliseconds)
35    pub estimated_compute_cost: u64,
36    /// Current TTL
37    pub current_ttl: Duration,
38    /// Dependent tables (for invalidation)
39    pub dependent_tables: Vec<String>,
40    /// Whether the plan performs DML (requires a write transaction scope).
41    pub is_dml: bool,
42    /// Whether the plan is a transaction control statement (BEGIN/COMMIT/ROLLBACK).
43    pub is_transaction: bool,
44}
45
46impl CachedPlan {
47    /// Estimate memory usage (bytes)
48    pub fn estimate_memory(&self) -> usize {
49        let mut total = 0;
50
51        total += std::mem::size_of::<String>();
52        total += self.query_template.capacity();
53
54        total += std::mem::size_of::<Vec<ParamPosition>>();
55        for pos in &self.param_positions {
56            total += std::mem::size_of::<ParamPosition>();
57            if let Some(ref name) = pos.name {
58                total += std::mem::size_of::<String>();
59                total += name.capacity();
60            }
61        }
62
63        total += self.estimate_plan_memory(&self.plan);
64
65        total += std::mem::size_of::<Instant>() * 2;
66        total += std::mem::size_of::<u64>() * 3;
67        total += std::mem::size_of::<f64>() * 2;
68        total += std::mem::size_of::<CachePriority>();
69        total += std::mem::size_of::<u32>();
70        total += std::mem::size_of::<Duration>();
71
72        total += std::mem::size_of::<Vec<String>>();
73        for table in &self.dependent_tables {
74            total += std::mem::size_of::<String>();
75            total += table.capacity();
76        }
77
78        total
79    }
80
81    /// Estimate memory usage for the physical plan.
82    ///
83    /// uses [`PhysicalPlan::operator_count`] and spec sizes for estimation.
84    fn estimate_plan_memory(&self, plan: &PhysicalPlan) -> usize {
85        let base_size = std::mem::size_of::<PhysicalPlan>();
86        let op_count = plan.operator_count();
87        let fragment_count = plan.fragment_count();
88        let per_op_estimate = 256; // rough estimate per operator spec
89
90        base_size + (op_count * per_op_estimate) + (fragment_count * 64)
91    }
92
93    /// Calculate cache value score (for eviction decisions)
94    pub fn value_score(&self) -> f64 {
95        let frequency_score = self.access_count as f64 * 0.4;
96        let cost_score = (self.estimated_compute_cost as f64 / 1000.0) * 0.3;
97        let priority_score = (self.priority as i32 as f64) * 0.2;
98        let size_penalty = (self.query_template.len() as f64 / 1024.0) * 0.1;
99
100        frequency_score + cost_score + priority_score - size_penalty
101    }
102}