1use std::collections::{HashMap, HashSet};
16
17use anyhow::anyhow;
18use risingwave_common::catalog::{DatabaseId, TableId};
19use risingwave_pb::catalog::Database;
20use risingwave_pb::hummock::HummockVersionStats;
21use risingwave_pb::meta::PbRecoveryStatus;
22use tokio::sync::oneshot::Sender;
23
24use crate::barrier::info::BarrierInfo;
25use crate::manager::ActiveStreamingWorkerNodes;
26use crate::model::{ActorId, BackfillUpstreamType, FragmentId, StreamActor, SubscriptionId};
27use crate::notification::Notifier;
28use crate::{MetaError, MetaResult};
29
30mod backfill_order_control;
31pub mod cdc_progress;
32mod checkpoint;
33mod command;
34pub use command::RescheduleContext;
35mod complete_task;
36pub(super) mod context;
37mod edge_builder;
38mod info;
39mod manager;
40mod partial_graph;
41mod progress;
42mod rpc;
43mod schedule;
44#[cfg(test)]
45mod tests;
46mod trace;
47mod utils;
48mod worker;
49
50pub use backfill_order_control::{BackfillNode, BackfillOrderState};
51use risingwave_common::id::JobId;
52use risingwave_pb::ddl_service::PbBackfillType;
53
54pub use self::command::{
55 BarrierKind, Command, CreateStreamingJobCommandInfo, CreateStreamingJobType,
56 IndependentStreamingJobType, ReplaceStreamJobPlan, Reschedule, ReschedulePlan,
57 ResumeBackfillTarget, SinceEpochInfo, SnapshotBackfillInfo,
58};
59pub(crate) use self::info::{SharedActorInfos, SharedFragmentInfo};
60pub use self::manager::{BarrierManagerRef, GlobalBarrierManager};
61pub(crate) use self::rpc::to_partial_graph_id;
62pub use self::schedule::BarrierScheduler;
63pub use self::trace::TracedEpoch;
64use crate::barrier::cdc_progress::CdcProgress;
65use crate::barrier::context::recovery::LoadedRecoveryContext;
66use crate::controller::fragment::InflightFragmentInfo;
67use crate::stream::cdc::CdcTableSnapshotSplits;
68
69enum RecoveryReason {
71 Bootstrap,
73 Failover(MetaError),
75 Adhoc,
77}
78
79enum BarrierManagerStatus {
81 Starting,
83 Recovering(RecoveryReason),
85 Running,
87}
88
89struct Scheduled {
91 database_id: DatabaseId,
92 command: Command,
93 notifier: Notifier,
94 span: tracing::Span,
95}
96
97impl From<&BarrierManagerStatus> for PbRecoveryStatus {
98 fn from(status: &BarrierManagerStatus) -> Self {
99 match status {
100 BarrierManagerStatus::Starting => Self::StatusStarting,
101 BarrierManagerStatus::Recovering(reason) => match reason {
102 RecoveryReason::Bootstrap => Self::StatusStarting,
103 RecoveryReason::Failover(_) | RecoveryReason::Adhoc => Self::StatusRecovering,
104 },
105 BarrierManagerStatus::Running => Self::StatusRunning,
106 }
107 }
108}
109
110pub(crate) struct BackfillProgress {
111 pub(crate) progress: String,
112 pub(crate) backfill_type: PbBackfillType,
113}
114
115#[derive(Debug, Clone, Copy)]
116pub(crate) struct FragmentBackfillProgress {
117 pub(crate) job_id: JobId,
118 pub(crate) fragment_id: FragmentId,
119 pub(crate) consumed_rows: u64,
120 pub(crate) done: bool,
121 pub(crate) upstream_type: BackfillUpstreamType,
122}
123
124pub(crate) struct UpdateDatabaseBarrierRequest {
125 pub database_id: DatabaseId,
126 pub barrier_interval_ms: Option<u32>,
127 pub checkpoint_frequency: Option<u64>,
128 pub sender: Sender<()>,
129}
130
131pub(crate) enum BarrierManagerRequest {
132 GetBackfillProgress(Sender<MetaResult<HashMap<JobId, BackfillProgress>>>),
133 GetFragmentBackfillProgress(Sender<MetaResult<Vec<FragmentBackfillProgress>>>),
134 GetCdcProgress(Sender<MetaResult<HashMap<JobId, CdcProgress>>>),
135 AdhocRecovery(Sender<()>),
136 UpdateDatabaseBarrier(UpdateDatabaseBarrierRequest),
137}
138
139#[derive(Debug)]
140struct BarrierWorkerRuntimeInfoSnapshot {
141 active_streaming_nodes: ActiveStreamingWorkerNodes,
142 recovery_context: LoadedRecoveryContext,
143 state_table_committed_epochs: HashMap<TableId, u64>,
144 state_table_log_epochs: HashMap<TableId, Vec<(Vec<u64>, u64)>>,
146 mv_depended_subscriptions: HashMap<TableId, HashMap<SubscriptionId, u64>>,
147 creating_jobs: HashSet<JobId>,
148 hummock_version_stats: HummockVersionStats,
149 database_infos: Vec<Database>,
150 cdc_table_snapshot_splits: HashMap<JobId, CdcTableSnapshotSplits>,
151}
152
153impl BarrierWorkerRuntimeInfoSnapshot {
154 fn validate_database_info(
155 database_id: DatabaseId,
156 database_jobs: &HashMap<JobId, HashMap<FragmentId, InflightFragmentInfo>>,
157 active_streaming_nodes: &ActiveStreamingWorkerNodes,
158 stream_actors: &HashMap<ActorId, StreamActor>,
159 state_table_committed_epochs: &HashMap<TableId, u64>,
160 ) -> MetaResult<()> {
161 {
162 for fragment in database_jobs.values().flat_map(|job| job.values()) {
163 for (actor_id, actor) in &fragment.actors {
164 if !active_streaming_nodes
165 .current()
166 .contains_key(&actor.worker_id)
167 {
168 return Err(anyhow!(
169 "worker_id {} for actor {} does not exist",
170 actor.worker_id,
171 actor_id
172 )
173 .into());
174 }
175 if !stream_actors.contains_key(actor_id) {
176 return Err(anyhow!("cannot find StreamActor of actor {}", actor_id).into());
177 }
178 }
179 for state_table_id in &fragment.state_table_ids {
180 if !state_table_committed_epochs.contains_key(state_table_id) {
181 return Err(anyhow!(
182 "state table {} is not registered to hummock",
183 state_table_id
184 )
185 .into());
186 }
187 }
188 }
189 for (job_id, fragments) in database_jobs {
190 let mut committed_epochs =
191 InflightFragmentInfo::existing_table_ids(fragments.values()).map(|table_id| {
192 (
193 table_id,
194 *state_table_committed_epochs
195 .get(&table_id)
196 .expect("checked exist"),
197 )
198 });
199 let (first_table, first_epoch) = committed_epochs.next().ok_or_else(|| {
200 anyhow!(
201 "job {} in database {} has no state table after recovery",
202 job_id,
203 database_id
204 )
205 })?;
206 for (table_id, epoch) in committed_epochs {
207 if epoch != first_epoch {
208 return Err(anyhow!(
209 "job {} in database {} has tables with different table ids. {}:{}, {}:{}",
210 job_id,
211 database_id,
212 first_table,
213 first_epoch,
214 table_id,
215 epoch
216 )
217 .into());
218 }
219 }
220 }
221 }
222 Ok(())
223 }
224}
225
226#[derive(Debug)]
227struct DatabaseRuntimeInfoSnapshot {
228 recovery_context: LoadedRecoveryContext,
229 state_table_committed_epochs: HashMap<TableId, u64>,
230 state_table_log_epochs: HashMap<TableId, Vec<(Vec<u64>, u64)>>,
232 mv_depended_subscriptions: HashMap<TableId, HashMap<SubscriptionId, u64>>,
233 creating_jobs: HashSet<JobId>,
234 cdc_table_snapshot_splits: HashMap<JobId, CdcTableSnapshotSplits>,
235}