Skip to main content

selene_gql/runtime/
context_tiers.rs

1//! Procedure execution context tiers.
2
3use std::{rc::Rc, sync::Arc};
4
5use selene_core::{BindingTableId, CancellationChecker, DbString};
6use selene_graph::{
7    CANDIDATE_STATE_PROVIDER_TAG, CompactionReport, CompactionStats, GraphResult, IndexProvider,
8    Mutator, ProviderError, ProviderTag, SeleneGraph, SharedGraph, VectorCandidateSet,
9    VectorCandidateStateInfo, VectorIndexMaintenancePolicy, VectorIndexRebuildReport,
10};
11
12use crate::{BindingTable, BindingTableRegistry, ImplDefinedCaps, ProcedureTier};
13
14/// Read-tier procedure context.
15pub struct GraphContext<'a> {
16    snapshot: &'a SeleneGraph,
17    caps: &'a ImplDefinedCaps,
18    providers: &'a [Arc<dyn IndexProvider>],
19    cancellation: CancellationChecker<'a>,
20    binding_tables: Rc<BindingTableRegistry>,
21}
22
23impl<'a> GraphContext<'a> {
24    pub(crate) fn new(
25        snapshot: &'a SeleneGraph,
26        caps: &'a ImplDefinedCaps,
27        providers: &'a [Arc<dyn IndexProvider>],
28        cancellation: CancellationChecker<'a>,
29        binding_tables: Rc<BindingTableRegistry>,
30    ) -> Self {
31        Self {
32            snapshot,
33            caps,
34            providers,
35            cancellation,
36            binding_tables,
37        }
38    }
39
40    /// Borrow the graph snapshot visible to this procedure call.
41    #[must_use]
42    pub const fn snapshot(&self) -> &'a SeleneGraph {
43        self.snapshot
44    }
45
46    /// Borrow implementation-defined executor caps.
47    #[must_use]
48    pub const fn impl_defined_caps(&self) -> &'a ImplDefinedCaps {
49        self.caps
50    }
51
52    /// Look up a registered index provider by its fixed provider tag.
53    #[must_use]
54    pub fn index_provider_by_tag(&self, tag: ProviderTag) -> Option<Arc<dyn IndexProvider>> {
55        self.providers
56            .iter()
57            .find(|provider| provider.provider_tag() == tag)
58            .map(Arc::clone)
59    }
60
61    /// Look up a named maintained vector candidate set for this snapshot generation.
62    ///
63    /// # Errors
64    ///
65    /// Returns [`ProviderError`] when the candidate-state provider is present
66    /// but cannot prove it has applied through the graph snapshot generation.
67    pub fn vector_candidate_set(
68        &self,
69        name: &DbString,
70    ) -> Result<Option<VectorCandidateSet>, ProviderError> {
71        let Some(provider) = self.index_provider_by_tag(ProviderTag(CANDIDATE_STATE_PROVIDER_TAG))
72        else {
73            return Ok(None);
74        };
75        provider.vector_candidate_set(name, self.snapshot.meta.generation)
76    }
77
78    /// List maintained vector candidate-state descriptors for this snapshot generation.
79    ///
80    /// # Errors
81    ///
82    /// Returns [`ProviderError`] when the candidate-state provider is present
83    /// but cannot prove it has applied through the graph snapshot generation.
84    pub fn vector_candidate_state_infos(
85        &self,
86    ) -> Result<Vec<VectorCandidateStateInfo>, ProviderError> {
87        let Some(provider) = self.index_provider_by_tag(ProviderTag(CANDIDATE_STATE_PROVIDER_TAG))
88        else {
89            return Ok(Vec::new());
90        };
91        provider.vector_candidate_state_infos(self.snapshot.meta.generation)
92    }
93
94    /// Build the cancellation checker visible to this procedure call.
95    #[must_use]
96    pub const fn cancellation_checker(&self) -> CancellationChecker<'a> {
97        self.cancellation
98    }
99
100    /// Register a binding table for this procedure call's statement.
101    pub fn register_binding_table(&self, table: Arc<BindingTable>) -> BindingTableId {
102        self.binding_tables.register(table)
103    }
104}
105
106/// Mutation-tier procedure context.
107pub struct MutationContext<'a, 'g> {
108    mutator: Mutator<'a, 'g>,
109    caps: &'a ImplDefinedCaps,
110    cancellation: CancellationChecker<'a>,
111    binding_tables: Rc<BindingTableRegistry>,
112}
113
114impl<'a, 'g> MutationContext<'a, 'g> {
115    pub(crate) fn new(
116        mutator: Mutator<'a, 'g>,
117        caps: &'a ImplDefinedCaps,
118        cancellation: CancellationChecker<'a>,
119        binding_tables: Rc<BindingTableRegistry>,
120    ) -> Self {
121        Self {
122            mutator,
123            caps,
124            cancellation,
125            binding_tables,
126        }
127    }
128
129    /// Construct a mutation context for external procedure test harnesses.
130    #[cfg(any(test, feature = "test-harness"))]
131    #[must_use]
132    pub fn for_test(mutator: Mutator<'a, 'g>, caps: &'a ImplDefinedCaps) -> Self {
133        Self::new(
134            mutator,
135            caps,
136            CancellationChecker::disabled(),
137            Rc::new(BindingTableRegistry::new()),
138        )
139    }
140
141    /// Borrow the transaction-local working graph.
142    #[must_use]
143    pub fn snapshot(&self) -> &SeleneGraph {
144        self.mutator.read()
145    }
146
147    /// Borrow the mutation funnel.
148    pub fn mutator(&mut self) -> &mut Mutator<'a, 'g> {
149        &mut self.mutator
150    }
151
152    /// Look up a registered index provider through the held write transaction.
153    #[must_use]
154    pub fn index_provider_by_tag(&self, tag: ProviderTag) -> Option<Arc<dyn IndexProvider>> {
155        self.mutator.index_provider_by_tag(tag)
156    }
157
158    /// Borrow implementation-defined executor caps.
159    #[must_use]
160    pub const fn impl_defined_caps(&self) -> &'a ImplDefinedCaps {
161        self.caps
162    }
163
164    /// Build the cancellation checker visible to this procedure call.
165    #[must_use]
166    pub const fn cancellation_checker(&self) -> CancellationChecker<'a> {
167        self.cancellation
168    }
169
170    /// Register a binding table for this procedure call's statement.
171    pub fn register_binding_table(&self, table: Arc<BindingTable>) -> BindingTableId {
172        self.binding_tables.register(table)
173    }
174}
175
176/// Engine-maintenance procedure context.
177pub struct MaintenanceContext<'a, 'g> {
178    graph: &'g SharedGraph,
179    caps: &'a ImplDefinedCaps,
180    cancellation: CancellationChecker<'a>,
181    binding_tables: Rc<BindingTableRegistry>,
182}
183
184impl<'a, 'g> MaintenanceContext<'a, 'g> {
185    pub(crate) fn new(
186        graph: &'g SharedGraph,
187        caps: &'a ImplDefinedCaps,
188        cancellation: CancellationChecker<'a>,
189        binding_tables: Rc<BindingTableRegistry>,
190    ) -> Self {
191        Self {
192            graph,
193            caps,
194            cancellation,
195            binding_tables,
196        }
197    }
198
199    /// Borrow implementation-defined executor caps.
200    #[must_use]
201    pub const fn impl_defined_caps(&self) -> &'a ImplDefinedCaps {
202        self.caps
203    }
204
205    /// Build the cancellation checker visible to this procedure call.
206    #[must_use]
207    pub const fn cancellation_checker(&self) -> CancellationChecker<'a> {
208        self.cancellation
209    }
210
211    /// Rebuild all registered vector indexes from primary graph values.
212    ///
213    /// # Errors
214    ///
215    /// Returns graph errors raised by strict rebuild validation.
216    pub fn rebuild_vector_indexes(&self) -> GraphResult<VectorIndexRebuildReport> {
217        self.graph.rebuild_vector_indexes()
218    }
219
220    /// Rebuild vector indexes whose diagnostics recommend maintenance.
221    ///
222    /// # Errors
223    ///
224    /// Returns graph errors raised by strict rebuild validation.
225    pub fn rebuild_recommended_vector_indexes(&self) -> GraphResult<VectorIndexRebuildReport> {
226        self.graph.rebuild_recommended_vector_indexes()
227    }
228
229    /// Maintain recommended vector indexes under a caller-supplied policy.
230    ///
231    /// # Errors
232    ///
233    /// Returns graph errors raised by strict rebuild validation.
234    pub fn maintain_vector_indexes(
235        &self,
236        policy: VectorIndexMaintenancePolicy,
237    ) -> GraphResult<VectorIndexRebuildReport> {
238        self.graph.maintain_vector_indexes(policy)
239    }
240
241    /// Return current graph compaction pressure.
242    #[must_use]
243    pub fn compaction_stats(&self) -> CompactionStats {
244        self.graph.compaction_stats()
245    }
246
247    /// Compact dead rows out of the live graph.
248    ///
249    /// # Errors
250    ///
251    /// Returns graph errors raised by compaction consistency validation.
252    pub fn compact(&self) -> GraphResult<CompactionReport> {
253        self.graph.compact()
254    }
255
256    /// Register a binding table for this procedure call's statement.
257    pub fn register_binding_table(&self, table: Arc<BindingTable>) -> BindingTableId {
258        self.binding_tables.register(table)
259    }
260}
261
262/// Tier-tagged procedure context passed through [`crate::ProcedureRegistry`].
263#[non_exhaustive]
264pub enum ProcedureContext<'a, 'g> {
265    /// Read-only graph-tier context.
266    Graph(GraphContext<'a>),
267    /// Mutation-tier context.
268    Mutation(MutationContext<'a, 'g>),
269    /// Engine-maintenance context.
270    Maintenance(MaintenanceContext<'a, 'g>),
271}
272
273impl ProcedureContext<'_, '_> {
274    /// Return the context tier represented by this enum variant.
275    #[must_use]
276    pub const fn tier(&self) -> ProcedureTier {
277        match self {
278            Self::Graph(_) => ProcedureTier::Graph,
279            Self::Mutation(_) => ProcedureTier::Mutation,
280            Self::Maintenance(_) => ProcedureTier::Maintenance,
281        }
282    }
283
284    /// Register a binding table for the currently executing procedure call.
285    pub fn register_binding_table(&self, table: Arc<BindingTable>) -> BindingTableId {
286        match self {
287            Self::Graph(ctx) => ctx.register_binding_table(table),
288            Self::Mutation(ctx) => ctx.register_binding_table(table),
289            Self::Maintenance(ctx) => ctx.register_binding_table(table),
290        }
291    }
292}