Skip to main content

risingwave_stream/task/barrier_manager/
progress.rs

1// Copyright 2022 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt::{Display, Formatter};
16use std::sync::Arc;
17
18use risingwave_common::bitmap::Bitmap;
19use risingwave_common::catalog::TableId;
20use risingwave_common::util::epoch::EpochPair;
21use risingwave_pb::id::FragmentId;
22use risingwave_pb::stream_service::barrier_complete_response::PbCreateMviewProgress;
23
24use crate::executor::ActorContext;
25use crate::task::barrier_manager::LocalBarrierEvent::{
26    ReportCreateProgress, ReportTableChangeLogProgress,
27};
28use crate::task::barrier_worker::managed_state::PartialGraphState;
29use crate::task::cdc_progress::CdcTableBackfillState;
30use crate::task::{ActorId, LocalBarrierManager};
31
32type ConsumedEpoch = u64;
33type ConsumedRows = u64;
34type BufferedRows = u64;
35
36#[derive(Debug, Clone, Copy)]
37pub(crate) enum BackfillState {
38    ConsumingUpstreamTableOrSource(ConsumedEpoch, ConsumedRows, BufferedRows),
39    DoneConsumingUpstreamTableOrSource(ConsumedRows, BufferedRows),
40}
41
42#[derive(Debug)]
43pub(crate) struct TableChangeLogProgress {
44    pub(crate) actor_id: ActorId,
45    pub(crate) fragment_id: FragmentId,
46    pub(crate) upstream_table_id: TableId,
47    pub(crate) progress_table_id: TableId,
48    pub(crate) vnode_bitmap: Arc<Bitmap>,
49    pub(crate) truncate_epoch: u64,
50}
51
52impl BackfillState {
53    pub fn to_pb(self, fragment_id: FragmentId, actor_id: ActorId) -> PbCreateMviewProgress {
54        let (done, consumed_epoch, consumed_rows, buffered_rows) = match self {
55            BackfillState::ConsumingUpstreamTableOrSource(
56                consumed_epoch,
57                consumed_rows,
58                buffered_rows,
59            ) => (false, consumed_epoch, consumed_rows, buffered_rows),
60            BackfillState::DoneConsumingUpstreamTableOrSource(consumed_rows, buffered_rows) => {
61                (true, 0, consumed_rows, buffered_rows)
62            }
63        };
64        PbCreateMviewProgress {
65            backfill_actor_id: actor_id,
66            done,
67            consumed_epoch,
68            consumed_rows,
69            buffered_rows,
70            fragment_id,
71        }
72    }
73}
74
75impl Display for BackfillState {
76    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
77        match self {
78            BackfillState::ConsumingUpstreamTableOrSource(epoch, rows, buffered) => {
79                write!(
80                    f,
81                    "ConsumingUpstreamTable(epoch: {}, rows: {}, buffered: {})",
82                    epoch, rows, buffered
83                )
84            }
85            BackfillState::DoneConsumingUpstreamTableOrSource(rows, buffered) => {
86                write!(
87                    f,
88                    "DoneConsumingUpstreamTable(rows: {}, buffered: {})",
89                    rows, buffered
90                )
91            }
92        }
93    }
94}
95
96impl PartialGraphState {
97    pub(crate) fn update_create_mview_progress(
98        &mut self,
99        epoch: EpochPair,
100        fragment_id: FragmentId,
101        actor: ActorId,
102        state: BackfillState,
103    ) {
104        if let Some((prev_fragment_id, _)) = self
105            .graph_state
106            .event_mut(epoch)
107            .create_mview_progress
108            .insert(actor, (fragment_id, state))
109        {
110            assert_eq!(prev_fragment_id, fragment_id)
111        }
112    }
113
114    pub(crate) fn update_cdc_table_backfill_progress(
115        &mut self,
116        epoch: EpochPair,
117        actor: ActorId,
118        state: CdcTableBackfillState,
119    ) {
120        if let Some(actor_state) = self.actor_states.get(&actor)
121            && actor_state.inflight_barriers.contains(&epoch.prev)
122        {
123            self.graph_state
124                .event_mut(epoch)
125                .cdc_table_backfill_progress
126                .insert(actor, state);
127        } else {
128            warn!(?epoch, %actor, ?state, "ignore CDC table backfill progress");
129        }
130    }
131
132    pub(crate) fn update_table_change_log_progress(
133        &mut self,
134        epoch: EpochPair,
135        progress: TableChangeLogProgress,
136    ) {
137        let actor_id = progress.actor_id;
138        if let Some(actor_state) = self.actor_states.get(&actor_id)
139            && actor_state.inflight_barriers.contains(&epoch.prev)
140        {
141            self.graph_state
142                .event_mut(epoch)
143                .table_change_log_progress
144                .push(progress);
145        } else {
146            warn!(?epoch, %actor_id, ?progress, "ignore table change log progress");
147        }
148    }
149}
150
151impl LocalBarrierManager {
152    fn update_create_mview_progress(
153        &self,
154        epoch: EpochPair,
155        fragment_id: FragmentId,
156        actor: ActorId,
157        state: BackfillState,
158    ) {
159        self.send_event(ReportCreateProgress {
160            epoch,
161            fragment_id,
162            actor,
163            state,
164        })
165    }
166
167    fn report_table_change_log_progress(&self, epoch: EpochPair, progress: TableChangeLogProgress) {
168        self.send_event(ReportTableChangeLogProgress { epoch, progress });
169    }
170}
171
172/// The progress held by the backfill executors to report to the local barrier manager.
173///
174/// Progress can be computed by
175/// `total_rows_consumed` / `total_rows_upstream`.
176/// This yields the (approximate) percentage of rows we are done backfilling.
177///
178/// For `total_rows_consumed`, the progress is tracked in the following way:
179/// 1. Fetching the row count from our state table.
180///    This number is the total number, NOT incremental.
181///    This is done per actor.
182/// 2. Refreshing this number on the meta side, on every barrier.
183///    This is done by just summing up all the row counts from the actors.
184///
185/// For `total_rows_upstream`,
186/// this is fetched from `HummockVersion`'s statistics (`TableStats::total_key_count`).
187///
188/// This is computed per `HummockVersion`, which is updated whenever a checkpoint is committed.
189/// The `total_key_count` figure just counts the number of storage keys.
190/// For example, if a key is inserted and then deleted,
191/// it results two storage entries in `LSMt`, so count=2.
192/// Only after compaction, the count will drop back to 0.
193///
194/// So the total count could be more pessimistic, than actual progress.
195///
196/// It is fine for this number not to be precise,
197/// since we don't rely on it to update the status of a stream job internally.
198///
199/// TODO(kwannoel): Perhaps it is possible to get total key count of the replicated state table
200/// for arrangement backfill. We can use that to estimate the progress as well, and avoid recording
201/// `row_count` state for it.
202pub struct CreateMviewProgressReporter {
203    barrier_manager: LocalBarrierManager,
204
205    fragment_id: FragmentId,
206
207    /// The id of the actor containing the backfill executors.
208    backfill_actor_id: ActorId,
209
210    state: Option<BackfillState>,
211}
212
213impl CreateMviewProgressReporter {
214    pub fn new(
215        barrier_manager: LocalBarrierManager,
216        fragment_id: FragmentId,
217        backfill_actor_id: ActorId,
218    ) -> Self {
219        Self {
220            barrier_manager,
221            fragment_id,
222            backfill_actor_id,
223            state: None,
224        }
225    }
226
227    #[cfg(test)]
228    pub fn for_test(barrier_manager: LocalBarrierManager) -> Self {
229        Self::new(barrier_manager, 0.into(), 0.into())
230    }
231
232    pub fn actor_id(&self) -> ActorId {
233        self.backfill_actor_id
234    }
235
236    /// Report the change-log frontier persisted by the same barrier. This remains active after
237    /// creation progress has reached `Done`.
238    pub fn report_table_change_log_progress(
239        &self,
240        epoch: EpochPair,
241        upstream_table_id: TableId,
242        progress_table_id: TableId,
243        vnode_bitmap: Arc<Bitmap>,
244        truncate_epoch: u64,
245    ) {
246        self.barrier_manager.report_table_change_log_progress(
247            epoch,
248            TableChangeLogProgress {
249                actor_id: self.backfill_actor_id,
250                fragment_id: self.fragment_id,
251                upstream_table_id,
252                progress_table_id,
253                vnode_bitmap,
254                truncate_epoch,
255            },
256        );
257    }
258
259    fn update_inner(&mut self, epoch: EpochPair, state: BackfillState) {
260        self.state = Some(state);
261        self.barrier_manager.update_create_mview_progress(
262            epoch,
263            self.fragment_id,
264            self.backfill_actor_id,
265            state,
266        );
267    }
268
269    /// Update the progress to `ConsumingUpstream(consumed_epoch, consumed_rows)`. The epoch must be
270    /// monotonically increasing.
271    /// `current_epoch` should be provided to locate the barrier under concurrent checkpoint.
272    /// `current_consumed_rows` is an accumulated value.
273    pub fn update(
274        &mut self,
275        epoch: EpochPair,
276        consumed_epoch: ConsumedEpoch,
277        current_consumed_rows: ConsumedRows,
278    ) {
279        self.update_with_buffered_rows(epoch, consumed_epoch, current_consumed_rows, 0);
280    }
281
282    /// Update the progress with buffered rows information.
283    /// This is used by locality backfill to report precise progress including buffered data.
284    pub fn update_with_buffered_rows(
285        &mut self,
286        epoch: EpochPair,
287        consumed_epoch: ConsumedEpoch,
288        current_consumed_rows: ConsumedRows,
289        buffered_rows: BufferedRows,
290    ) {
291        match self.state {
292            Some(BackfillState::ConsumingUpstreamTableOrSource(last, last_consumed_rows, _)) => {
293                assert!(
294                    last <= consumed_epoch,
295                    "last_epoch: {:#?} must be greater than consumed epoch: {:#?}",
296                    last,
297                    consumed_epoch
298                );
299                assert!(last_consumed_rows <= current_consumed_rows);
300            }
301            Some(state) => {
302                panic!(
303                    "should not update consuming progress at invalid state: {:?}",
304                    state
305                )
306            }
307            None => {}
308        };
309        tracing::debug!(
310            actor_id = %self.backfill_actor_id,
311            ?epoch,
312            consumed_epoch,
313            current_consumed_rows,
314            buffered_rows,
315            "progress update"
316        );
317        self.update_inner(
318            epoch,
319            BackfillState::ConsumingUpstreamTableOrSource(
320                consumed_epoch,
321                current_consumed_rows,
322                buffered_rows,
323            ),
324        );
325    }
326
327    /// The difference from [`Self::update`] (MV backfill) is that we
328    /// don't care `ConsumedEpoch` here.
329    pub fn update_for_source_backfill(
330        &mut self,
331        epoch: EpochPair,
332        current_consumed_rows: ConsumedRows,
333    ) {
334        match self.state {
335            Some(BackfillState::ConsumingUpstreamTableOrSource(
336                dummy_last_epoch,
337                _last_consumed_rows,
338                _,
339            )) => {
340                debug_assert_eq!(dummy_last_epoch, 0);
341            }
342            Some(state) => {
343                panic!(
344                    "should not update consuming progress at invalid state: {:?}",
345                    state
346                )
347            }
348            None => {}
349        };
350        self.update_inner(
351            epoch,
352            // fill a dummy ConsumedEpoch and no buffered rows
353            BackfillState::ConsumingUpstreamTableOrSource(0, current_consumed_rows, 0),
354        );
355    }
356
357    /// Finish the progress. If the progress is already finished, then perform no-op.
358    /// `current_epoch` should be provided to locate the barrier under concurrent checkpoint.
359    pub fn finish(&mut self, epoch: EpochPair, current_consumed_rows: ConsumedRows) {
360        self.finish_with_buffered_rows(epoch, current_consumed_rows, 0);
361    }
362
363    /// Finish the progress with buffered rows information.
364    /// This is used by locality backfill to report any remaining buffered rows at completion.
365    pub fn finish_with_buffered_rows(
366        &mut self,
367        epoch: EpochPair,
368        current_consumed_rows: ConsumedRows,
369        buffered_rows: BufferedRows,
370    ) {
371        if let Some(BackfillState::DoneConsumingUpstreamTableOrSource(_, _)) = self.state {
372            return;
373        }
374        tracing::debug!(
375            actor_id = %self.backfill_actor_id,
376            ?epoch,
377            current_consumed_rows,
378            buffered_rows,
379            "progress finish"
380        );
381        // When finishing, report the consumed rows and buffered rows separately
382        self.update_inner(
383            epoch,
384            BackfillState::DoneConsumingUpstreamTableOrSource(current_consumed_rows, buffered_rows),
385        );
386    }
387}
388
389impl LocalBarrierManager {
390    /// Create a struct for reporting the progress of creating mview. The backfill executors should
391    /// report the progress of barrier rearranging continuously using this. The updated progress
392    /// will be collected by the local barrier manager and reported to the meta service in this
393    /// epoch.
394    ///
395    /// When all backfill executors of the creating mview finish, the creation progress will be done at
396    /// frontend and the mview will be exposed to the user.
397    pub(crate) fn register_create_mview_progress(
398        &self,
399        actor_ctx: &ActorContext,
400    ) -> CreateMviewProgressReporter {
401        let fragment_id = actor_ctx.fragment_id;
402        let backfill_actor_id = actor_ctx.id;
403        trace!(%fragment_id, %backfill_actor_id, "register create mview progress");
404        CreateMviewProgressReporter::new(self.clone(), fragment_id, backfill_actor_id)
405    }
406}