reifydb_engine/
subscription.rs1use std::{result::Result as StdResult, sync::Arc};
5
6use reifydb_core::{
7 common::CommitVersion, interface::catalog::id::SubscriptionId, metrics::execution::ExecutionMetrics,
8 value::column::columns::Columns,
9};
10use reifydb_evaluate::stack::SymbolTable;
11use reifydb_rql::flow::flow::FlowDag;
12use reifydb_transaction::{multi::lease::VersionLeaseGuard, transaction::Transaction};
13use reifydb_value::{Result, error::Error as TypeError, params::Params, value::identity::IdentityId};
14
15use crate::engine::StandardEngine;
16
17#[derive(Debug, Clone)]
18pub struct SubscriptionContext {
19 pub id: SubscriptionId,
20 pub identity: IdentityId,
21 pub symbols: SymbolTable,
22 pub params: Params,
23}
24
25#[derive(Debug)]
26pub enum HydrateError {
27 SubscriptionNotFound,
28 UnsupportedSourceType,
29 RowCapExceeded {
30 cap: u64,
31 },
32 Engine(TypeError),
33 Internal(String),
34}
35
36impl From<TypeError> for HydrateError {
37 fn from(e: TypeError) -> Self {
38 HydrateError::Engine(e)
39 }
40}
41
42impl HydrateError {
43 pub fn is_version_evicted(&self) -> bool {
44 matches!(self, HydrateError::Engine(e) if e.0.code == "TXN_012")
45 }
46
47 pub fn wire_code(&self) -> &'static str {
48 match self {
49 Self::SubscriptionNotFound => "HYDRATION_FAILED",
50 Self::UnsupportedSourceType => "HYDRATION_UNSUPPORTED_SOURCE",
51 Self::RowCapExceeded {
52 ..
53 } => "HYDRATION_TOO_LARGE",
54 Self::Engine(_) => {
55 if self.is_version_evicted() {
56 "HYDRATION_VERSION_EVICTED"
57 } else {
58 "HYDRATION_FAILED"
59 }
60 }
61 Self::Internal(_) => "HYDRATION_FAILED",
62 }
63 }
64
65 pub fn wire_message(&self, rql: &str, cap: u64) -> String {
66 match self {
67 Self::SubscriptionNotFound => "Subscription not found at hydration time".to_string(),
68 Self::UnsupportedSourceType => "hydration is not supported for SourceSeries / SourceInlineData; use WITH { hydration: { enabled: false } } to subscribe without it".to_string(),
69 Self::RowCapExceeded { .. } => format!(
70 "Hydration exceeds subscribe.max_hydration_rows={}; add `TAKE N` upstream, lower with WITH {{ hydration: {{ max_rows: ... }} }}, or disable with WITH {{ hydration: {{ enabled: false }} }}. Query: {}",
71 cap, rql
72 ),
73 Self::Engine(e) => {
74 if self.is_version_evicted() {
75 e.0.message.clone()
76 } else {
77 e.to_string()
78 }
79 }
80 Self::Internal(s) => s.clone(),
81 }
82 }
83}
84
85#[derive(Debug)]
86pub struct HydrateOutcome {
87 pub version: CommitVersion,
88 pub batches: Vec<Columns>,
89 pub metrics: ExecutionMetrics,
90}
91
92pub trait SubscriptionService: Send + Sync {
93 fn next_id(&self) -> SubscriptionId;
94
95 fn register_subscription(
96 &self,
97 flow_dag: FlowDag,
98 column_names: Vec<String>,
99 hydration_enabled: bool,
100 ctx: SubscriptionContext,
101 txn: &mut Transaction<'_>,
102 ) -> Result<()>;
103
104 fn unregister_subscription(&self, id: &SubscriptionId) -> Result<()>;
105
106 fn hydrate(
107 &self,
108 sub_id: SubscriptionId,
109 engine: &StandardEngine,
110 identity: IdentityId,
111 lease: VersionLeaseGuard,
112 max_rows: u64,
113 ) -> StdResult<HydrateOutcome, HydrateError>;
114}
115
116pub type SubscriptionServiceRef = Arc<dyn SubscriptionService>;