1use std::collections::{BTreeMap, HashMap};
16use std::sync::Arc;
17
18use chrono::{DateTime, Utc};
19use futures::TryStreamExt;
20use futures::stream::BoxStream;
21use lazy_static::lazy_static;
22use regex::Regex;
23use serde::{Deserialize, Serialize};
24use snafu::OptionExt;
25use table::metadata::TableId;
26use table::table_name::TableName;
27
28use crate::FlownodeId;
29use crate::error::{self, Result};
30use crate::key::flow::FlowScoped;
31use crate::key::txn_helper::TxnOpGetResponseSet;
32use crate::key::{
33 BytesAdapter, DeserializedValueWithBytes, FlowId, FlowPartitionId, MetadataKey, MetadataValue,
34};
35use crate::kv_backend::KvBackendRef;
36use crate::kv_backend::txn::{Compare, CompareOp, Txn, TxnOp};
37use crate::range_stream::{DEFAULT_PAGE_SIZE, PaginationStream};
38use crate::rpc::KeyValue;
39use crate::rpc::store::RangeRequest;
40
41pub const FLOW_INFO_KEY_PREFIX: &str = "info";
42
43#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
45pub enum FlowStatus {
46 PendingSources,
48 #[default]
50 Active,
51}
52
53lazy_static! {
54 static ref FLOW_INFO_KEY_PATTERN: Regex =
55 Regex::new(&format!("^{FLOW_INFO_KEY_PREFIX}/([0-9]+)$")).unwrap();
56}
57
58pub struct FlowInfoKey(FlowScoped<FlowInfoKeyInner>);
62
63pub type FlowInfoDecodeResult = Result<Option<DeserializedValueWithBytes<FlowInfoValue>>>;
64
65impl<'a> MetadataKey<'a, FlowInfoKey> for FlowInfoKey {
66 fn to_bytes(&self) -> Vec<u8> {
67 self.0.to_bytes()
68 }
69
70 fn from_bytes(bytes: &'a [u8]) -> Result<FlowInfoKey> {
71 Ok(FlowInfoKey(FlowScoped::<FlowInfoKeyInner>::from_bytes(
72 bytes,
73 )?))
74 }
75}
76
77impl FlowInfoKey {
78 pub fn new(flow_id: FlowId) -> FlowInfoKey {
80 let inner = FlowInfoKeyInner::new(flow_id);
81 FlowInfoKey(FlowScoped::new(inner))
82 }
83
84 pub fn flow_id(&self) -> FlowId {
86 self.0.flow_id
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq)]
92struct FlowInfoKeyInner {
93 flow_id: FlowId,
94}
95
96impl FlowInfoKeyInner {
97 pub fn new(flow_id: FlowId) -> FlowInfoKeyInner {
99 FlowInfoKeyInner { flow_id }
100 }
101}
102
103impl<'a> MetadataKey<'a, FlowInfoKeyInner> for FlowInfoKeyInner {
104 fn to_bytes(&self) -> Vec<u8> {
105 format!("{FLOW_INFO_KEY_PREFIX}/{}", self.flow_id).into_bytes()
106 }
107
108 fn from_bytes(bytes: &'a [u8]) -> Result<FlowInfoKeyInner> {
109 let key = std::str::from_utf8(bytes).map_err(|e| {
110 error::InvalidMetadataSnafu {
111 err_msg: format!(
112 "FlowInfoKeyInner '{}' is not a valid UTF8 string: {e}",
113 String::from_utf8_lossy(bytes)
114 ),
115 }
116 .build()
117 })?;
118 let captures =
119 FLOW_INFO_KEY_PATTERN
120 .captures(key)
121 .context(error::InvalidMetadataSnafu {
122 err_msg: format!("Invalid FlowInfoKeyInner '{key}'"),
123 })?;
124 let flow_id = captures[1].parse::<FlowId>().unwrap();
126 Ok(FlowInfoKeyInner { flow_id })
127 }
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
135pub struct FlowScheduleConfig {
136 pub anchor_secs: i64,
138 pub start_secs: i64,
140 #[serde(default)]
142 pub missed_tick_policy: FlowMissedTickPolicy,
143 #[serde(default = "FlowScheduleConfig::default_catchup_max_runs")]
148 pub catchup_max_runs: u32,
149 #[serde(default = "FlowScheduleConfig::default_catchup_max_lag_secs")]
151 pub catchup_max_lag_secs: i64,
152}
153
154impl FlowScheduleConfig {
155 pub const DEFAULT_ANCHOR_SECS: i64 = 0;
156 pub const DEFAULT_CATCHUP_MAX_RUNS: u32 = 3;
157 pub const DEFAULT_CATCHUP_MAX_LAG_SECS: i64 = 300;
158
159 pub fn default_catchup_max_runs() -> u32 {
160 Self::DEFAULT_CATCHUP_MAX_RUNS
161 }
162
163 pub fn default_catchup_max_lag_secs() -> i64 {
164 Self::DEFAULT_CATCHUP_MAX_LAG_SECS
165 }
166
167 pub fn catchup_max_lag_secs_for_interval(eval_interval_secs: i64) -> i64 {
168 std::cmp::max(
169 Self::DEFAULT_CATCHUP_MAX_LAG_SECS,
170 3_i64.saturating_mul(eval_interval_secs),
171 )
172 }
173
174 pub fn default_with_start(start_secs: i64, eval_interval_secs: i64) -> Self {
175 Self::with_anchor(Self::DEFAULT_ANCHOR_SECS, start_secs, eval_interval_secs)
176 }
177
178 pub fn with_anchor(anchor_secs: i64, start_secs: i64, eval_interval_secs: i64) -> Self {
183 Self {
184 anchor_secs,
185 start_secs,
186 missed_tick_policy: FlowMissedTickPolicy::BoundedCatchUp,
187 catchup_max_runs: Self::DEFAULT_CATCHUP_MAX_RUNS,
188 catchup_max_lag_secs: Self::catchup_max_lag_secs_for_interval(eval_interval_secs),
189 }
190 }
191}
192
193impl Default for FlowScheduleConfig {
194 fn default() -> Self {
195 Self {
196 anchor_secs: Self::DEFAULT_ANCHOR_SECS,
197 start_secs: 0,
198 missed_tick_policy: FlowMissedTickPolicy::default(),
199 catchup_max_runs: Self::default_catchup_max_runs(),
200 catchup_max_lag_secs: Self::default_catchup_max_lag_secs(),
201 }
202 }
203}
204
205#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
207pub enum FlowMissedTickPolicy {
208 #[default]
211 #[serde(rename = "bounded_catch_up")]
212 BoundedCatchUp,
213 #[serde(rename = "skip")]
215 Skip,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
220pub struct FlowInfoValue {
221 #[serde(default)]
223 pub source_table_ids: Vec<TableId>,
224 #[serde(default)]
225 pub all_source_table_names: Vec<TableName>,
226 #[serde(default)]
227 pub unresolved_source_table_names: Vec<TableName>,
228 pub sink_table_name: TableName,
230 pub flownode_ids: BTreeMap<FlowPartitionId, FlownodeId>,
232 pub catalog_name: String,
234 #[serde(default)]
240 pub query_context: Option<crate::rpc::ddl::QueryContext>,
241 pub flow_name: String,
243 pub raw_sql: String,
245 pub expire_after: Option<i64>,
248 #[serde(default)]
253 pub eval_interval_secs: Option<i64>,
254 pub comment: String,
256 pub options: HashMap<String, String>,
258 #[serde(default)]
259 pub status: FlowStatus,
260 #[serde(default)]
262 pub created_time: DateTime<Utc>,
263 #[serde(default)]
265 pub updated_time: DateTime<Utc>,
266 #[serde(default)]
271 pub eval_schedule: Option<FlowScheduleConfig>,
272}
273
274impl FlowInfoValue {
275 pub fn is_pending(&self) -> bool {
276 self.status == FlowStatus::PendingSources
277 }
278
279 pub fn is_active(&self) -> bool {
280 self.status == FlowStatus::Active
281 }
282
283 pub fn flownode_ids(&self) -> &BTreeMap<FlowPartitionId, FlownodeId> {
285 &self.flownode_ids
286 }
287
288 pub fn insert_flownode_id(
290 &mut self,
291 partition: FlowPartitionId,
292 node: FlownodeId,
293 ) -> Option<FlownodeId> {
294 self.flownode_ids.insert(partition, node)
295 }
296
297 pub fn source_table_ids(&self) -> &[TableId] {
299 &self.source_table_ids
300 }
301
302 pub fn all_source_table_names(&self) -> &[TableName] {
303 &self.all_source_table_names
304 }
305
306 pub fn unresolved_source_table_names(&self) -> &[TableName] {
307 &self.unresolved_source_table_names
308 }
309
310 pub fn catalog_name(&self) -> &String {
311 &self.catalog_name
312 }
313
314 pub fn query_context(&self) -> &Option<crate::rpc::ddl::QueryContext> {
315 &self.query_context
316 }
317
318 pub fn flow_name(&self) -> &String {
319 &self.flow_name
320 }
321
322 pub fn sink_table_name(&self) -> &TableName {
323 &self.sink_table_name
324 }
325
326 pub fn raw_sql(&self) -> &String {
327 &self.raw_sql
328 }
329
330 pub fn expire_after(&self) -> Option<i64> {
331 self.expire_after
332 }
333
334 pub fn eval_interval(&self) -> Option<i64> {
335 self.eval_interval_secs
336 }
337
338 pub fn comment(&self) -> &String {
339 &self.comment
340 }
341
342 pub fn options(&self) -> &HashMap<String, String> {
343 &self.options
344 }
345
346 pub fn status(&self) -> &FlowStatus {
347 &self.status
348 }
349
350 pub fn created_time(&self) -> &DateTime<Utc> {
351 &self.created_time
352 }
353
354 pub fn updated_time(&self) -> &DateTime<Utc> {
355 &self.updated_time
356 }
357}
358
359pub type FlowInfoManagerRef = Arc<FlowInfoManager>;
360
361pub struct FlowInfoManager {
363 kv_backend: KvBackendRef,
364}
365
366pub fn flow_info_decoder(kv: KeyValue) -> Result<(FlowInfoKey, FlowInfoValue)> {
367 let key = FlowInfoKey::from_bytes(&kv.key)?;
368 let value = FlowInfoValue::try_from_raw_value(&kv.value)?;
369 Ok((key, value))
370}
371
372impl FlowInfoManager {
373 pub fn new(kv_backend: KvBackendRef) -> Self {
375 Self { kv_backend }
376 }
377
378 pub async fn get(&self, flow_id: FlowId) -> Result<Option<FlowInfoValue>> {
380 let key = FlowInfoKey::new(flow_id).to_bytes();
381 self.kv_backend
382 .get(&key)
383 .await?
384 .map(|x| FlowInfoValue::try_from_raw_value(&x.value))
385 .transpose()
386 }
387
388 pub async fn get_raw(
390 &self,
391 flow_id: FlowId,
392 ) -> Result<Option<DeserializedValueWithBytes<FlowInfoValue>>> {
393 let key = FlowInfoKey::new(flow_id).to_bytes();
394 self.kv_backend
395 .get(&key)
396 .await?
397 .map(|x| DeserializedValueWithBytes::from_inner_slice(&x.value))
398 .transpose()
399 }
400
401 pub fn flow_infos(&self) -> BoxStream<'static, Result<(FlowId, FlowInfoValue)>> {
402 let start_key = FlowScoped::new(BytesAdapter::from(
403 format!("{FLOW_INFO_KEY_PREFIX}/").into_bytes(),
404 ))
405 .to_bytes();
406 let req = RangeRequest::new().with_prefix(start_key);
407 let stream = PaginationStream::new(
408 self.kv_backend.clone(),
409 req,
410 DEFAULT_PAGE_SIZE,
411 flow_info_decoder,
412 )
413 .into_stream();
414
415 Box::pin(stream.map_ok(|(key, value)| (key.flow_id(), value)))
416 }
417
418 pub(crate) fn build_create_txn(
422 &self,
423 flow_id: FlowId,
424 flow_value: &FlowInfoValue,
425 ) -> Result<(
426 Txn,
427 impl FnOnce(&mut TxnOpGetResponseSet) -> FlowInfoDecodeResult,
428 )> {
429 let key = FlowInfoKey::new(flow_id).to_bytes();
430 let txn = Txn::put_if_not_exists(key.clone(), flow_value.try_as_raw_value()?);
431
432 Ok((
433 txn,
434 TxnOpGetResponseSet::decode_with(TxnOpGetResponseSet::filter(key)),
435 ))
436 }
437
438 pub(crate) fn build_update_txn(
443 &self,
444 flow_id: FlowId,
445 current_flow_value: &DeserializedValueWithBytes<FlowInfoValue>,
446 new_flow_value: &FlowInfoValue,
447 ) -> Result<(
448 Txn,
449 impl FnOnce(&mut TxnOpGetResponseSet) -> FlowInfoDecodeResult,
450 )> {
451 let key = FlowInfoKey::new(flow_id).to_bytes();
452 let raw_value = new_flow_value.try_as_raw_value()?;
453 let prev_value = current_flow_value.get_raw_bytes();
454 let txn = Txn::new()
455 .when(vec![Compare::new(
456 key.clone(),
457 CompareOp::Equal,
458 Some(prev_value),
459 )])
460 .and_then(vec![TxnOp::Put(key.clone(), raw_value)])
461 .or_else(vec![TxnOp::Get(key.clone())]);
462
463 Ok((
464 txn,
465 TxnOpGetResponseSet::decode_with(TxnOpGetResponseSet::filter(key)),
466 ))
467 }
468}
469
470#[cfg(test)]
471mod tests {
472 use super::*;
473
474 #[test]
475 fn test_key_serialization() {
476 let flow_info = FlowInfoKey::new(2);
477 assert_eq!(b"__flow/info/2".to_vec(), flow_info.to_bytes());
478 }
479
480 #[test]
481 fn test_key_deserialization() {
482 let bytes = b"__flow/info/2".to_vec();
483 let key = FlowInfoKey::from_bytes(&bytes).unwrap();
484 assert_eq!(key.flow_id(), 2);
485 }
486}