1use std::collections::hash_map::Entry;
16use std::collections::{HashMap, HashSet};
17use std::fmt::Display;
18use std::future::{pending, poll_fn};
19use std::sync::Arc;
20use std::task::Poll;
21
22use anyhow::anyhow;
23use await_tree::{InstrumentAwait, SpanExt};
24use futures::future::{BoxFuture, join, join_all};
25use futures::stream::{BoxStream, FuturesOrdered};
26use futures::{FutureExt, StreamExt, TryFutureExt};
27use itertools::Itertools;
28use risingwave_pb::stream_plan::barrier::BarrierKind;
29use risingwave_pb::stream_service::barrier_complete_response::{
30 PbCdcSourceOffsetUpdated, PbCdcTableBackfillProgress, PbCreateMviewProgress,
31 PbIcebergPkIndexSinkMetadata, PbListFinishedSource, PbLoadFinishedSource, PbLocalSstableInfo,
32};
33use risingwave_rpc_client::error::{ToTonicStatus, TonicStatusWrapper};
34use risingwave_storage::store_impl::AsHummock;
35use thiserror_ext::AsReport;
36use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
37use tokio::sync::oneshot;
38use tokio::task::JoinHandle;
39use tokio::{select, spawn};
40use tonic::{Code, Status};
41use tracing::warn;
42
43use self::managed_state::ManagedBarrierState;
44use crate::error::{ScoredStreamError, StreamError, StreamResult};
45#[cfg(test)]
46use crate::task::LocalBarrierManager;
47use crate::task::managed_state::{BarrierToComplete, ResetPartialGraphOutput};
48use crate::task::{
49 ActorId, AtomicU64Ref, CONFIG_OVERRIDE_CACHE_DEFAULT_CAPACITY, ConfigOverrideCache, FragmentId,
50 PartialGraphId, StreamActorManager, StreamEnvironment, UpDownActorIds,
51};
52pub mod managed_state;
53#[cfg(test)]
54mod tests;
55
56use risingwave_hummock_sdk::table_stats::to_prost_table_stats_map;
57use risingwave_hummock_sdk::{LocalSstableInfo, SyncResult};
58use risingwave_pb::stream_service::streaming_control_stream_request::{
59 InitRequest, Request, ResetPartialGraphsRequest,
60};
61use risingwave_pb::stream_service::streaming_control_stream_response::{
62 InitResponse, ReportPartialGraphFailureResponse, ResetPartialGraphResponse, Response,
63 ShutdownResponse,
64};
65use risingwave_pb::stream_service::{
66 BarrierCompleteResponse, InjectBarrierRequest, PbScoredError, StreamingControlStreamRequest,
67 StreamingControlStreamResponse, streaming_control_stream_response,
68};
69
70use crate::executor::Barrier;
71use crate::executor::exchange::permit::Receiver;
72use crate::executor::monitor::StreamingMetrics;
73use crate::task::barrier_worker::managed_state::{
74 ManagedBarrierStateDebugInfo, ManagedBarrierStateEvent, PartialGraphState, PartialGraphStatus,
75};
76
77pub const ENABLE_BARRIER_AGGREGATION: bool = false;
80
81#[derive(Debug)]
83pub struct BarrierCompleteResult {
84 pub sync_result: Option<SyncResult>,
86
87 pub create_mview_progress: Vec<PbCreateMviewProgress>,
89
90 pub list_finished_source_ids: Vec<PbListFinishedSource>,
92
93 pub load_finished_source_ids: Vec<PbLoadFinishedSource>,
95
96 pub cdc_table_backfill_progress: Vec<PbCdcTableBackfillProgress>,
97
98 pub cdc_source_offset_updated: Vec<PbCdcSourceOffsetUpdated>,
100
101 pub iceberg_pk_index_sink_metadata: Vec<PbIcebergPkIndexSinkMetadata>,
103
104 pub truncate_tables: Vec<TableId>,
106 pub refresh_finished_tables: Vec<TableId>,
108}
109
110pub(super) struct ControlStreamHandle {
114 #[expect(clippy::type_complexity)]
115 pair: Option<(
116 UnboundedSender<Result<StreamingControlStreamResponse, Status>>,
117 BoxStream<'static, Result<StreamingControlStreamRequest, Status>>,
118 )>,
119}
120
121impl ControlStreamHandle {
122 fn empty() -> Self {
123 Self { pair: None }
124 }
125
126 pub(super) fn new(
127 sender: UnboundedSender<Result<StreamingControlStreamResponse, Status>>,
128 request_stream: BoxStream<'static, Result<StreamingControlStreamRequest, Status>>,
129 ) -> Self {
130 Self {
131 pair: Some((sender, request_stream)),
132 }
133 }
134
135 pub(super) fn connected(&self) -> bool {
136 self.pair.is_some()
137 }
138
139 fn reset_stream_with_err(&mut self, err: Status) {
140 if let Some((sender, _)) = self.pair.take() {
141 let err = TonicStatusWrapper::new(err);
143 warn!(error = %err.as_report(), "control stream reset with error");
144
145 let err = err.into_inner();
146 if sender.send(Err(err)).is_err() {
147 warn!("failed to notify reset of control stream");
148 }
149 }
150 }
151
152 async fn shutdown_stream(&mut self) {
155 if let Some((sender, _)) = self.pair.take() {
156 if sender
157 .send(Ok(StreamingControlStreamResponse {
158 response: Some(streaming_control_stream_response::Response::Shutdown(
159 ShutdownResponse::default(),
160 )),
161 }))
162 .is_err()
163 {
164 warn!("failed to notify shutdown of control stream");
165 } else {
166 tracing::info!("waiting for meta service to close control stream...");
167
168 sender.closed().await;
175 }
176 } else {
177 debug!("control stream has been reset, ignore shutdown");
178 }
179 }
180
181 pub(super) fn ack_reset_partial_graph(
182 &mut self,
183 partial_graph_id: PartialGraphId,
184 root_err: Option<ScoredStreamError>,
185 ) {
186 self.send_response(Response::ResetPartialGraph(ResetPartialGraphResponse {
187 partial_graph_id,
188 root_err: root_err.map(|err| PbScoredError {
189 err_msg: err.error.to_report_string(),
190 score: err.score.0,
191 }),
192 }));
193 }
194
195 fn send_response(&mut self, response: streaming_control_stream_response::Response) {
196 if let Some((sender, _)) = self.pair.as_ref() {
197 if sender
198 .send(Ok(StreamingControlStreamResponse {
199 response: Some(response),
200 }))
201 .is_err()
202 {
203 self.pair = None;
204 warn!("fail to send response. control stream reset");
205 }
206 } else {
207 debug!(?response, "control stream has been reset. ignore response");
208 }
209 }
210
211 async fn next_request(&mut self) -> StreamingControlStreamRequest {
212 if let Some((_, stream)) = &mut self.pair {
213 match stream.next().await {
214 Some(Ok(request)) => {
215 return request;
216 }
217 Some(Err(e)) => self.reset_stream_with_err(
218 anyhow!(TonicStatusWrapper::new(e)) .context("failed to get request")
220 .to_status_unnamed(Code::Internal),
221 ),
222 None => self.reset_stream_with_err(Status::internal("end of stream")),
223 }
224 }
225 pending().await
226 }
227}
228
229pub(super) enum TakeReceiverRequest {
230 Remote {
231 result_sender: oneshot::Sender<StreamResult<Receiver>>,
232 upstream_fragment_id: FragmentId,
233 },
234 Local(permit::Sender),
235}
236
237#[derive(strum_macros::Display)]
241pub(super) enum LocalActorOperation {
242 NewControlStream {
243 handle: ControlStreamHandle,
244 init_request: InitRequest,
245 },
246 TakeReceiver {
247 partial_graph_id: PartialGraphId,
248 term_id: String,
249 ids: UpDownActorIds,
250 request: TakeReceiverRequest,
251 },
252 #[cfg(test)]
253 GetCurrentLocalBarrierManager(oneshot::Sender<LocalBarrierManager>),
254 #[cfg(test)]
255 TakePendingNewOutputRequest(ActorId, oneshot::Sender<Vec<(ActorId, NewOutputRequest)>>),
256 #[cfg(test)]
257 Flush(oneshot::Sender<()>),
258 InspectState {
259 result_sender: oneshot::Sender<String>,
260 },
261 Shutdown {
262 result_sender: oneshot::Sender<()>,
263 },
264}
265
266pub(super) struct LocalBarrierWorkerDebugInfo<'a> {
267 managed_barrier_state:
268 HashMap<PartialGraphId, (String, Option<ManagedBarrierStateDebugInfo<'a>>)>,
269 has_control_stream_connected: bool,
270}
271
272impl Display for LocalBarrierWorkerDebugInfo<'_> {
273 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
274 writeln!(
275 f,
276 "\nhas_control_stream_connected: {}",
277 self.has_control_stream_connected
278 )?;
279
280 for (partial_graph_id, (status, managed_barrier_state)) in &self.managed_barrier_state {
281 writeln!(
282 f,
283 "partial graph {} status: {} managed_barrier_state:\n{}",
284 partial_graph_id,
285 status,
286 managed_barrier_state
287 .as_ref()
288 .map(ToString::to_string)
289 .unwrap_or_default()
290 )?;
291 }
292 Ok(())
293 }
294}
295
296pub(super) struct LocalBarrierWorker {
304 pub(super) state: ManagedBarrierState,
306
307 await_epoch_completed_futures:
309 HashMap<PartialGraphId, FuturesOrdered<AwaitEpochCompletedFuture>>,
310
311 control_stream_handle: ControlStreamHandle,
312
313 pub(super) actor_manager: Arc<StreamActorManager>,
314
315 pub(super) term_id: String,
316}
317
318impl LocalBarrierWorker {
319 pub(super) fn new(actor_manager: Arc<StreamActorManager>, term_id: String) -> Self {
320 Self {
321 state: Default::default(),
322 await_epoch_completed_futures: Default::default(),
323 control_stream_handle: ControlStreamHandle::empty(),
324 actor_manager,
325 term_id,
326 }
327 }
328
329 fn to_debug_info(&self) -> LocalBarrierWorkerDebugInfo<'_> {
330 LocalBarrierWorkerDebugInfo {
331 managed_barrier_state: self
332 .state
333 .partial_graphs
334 .iter()
335 .map(|(partial_graph_id, status)| {
336 (*partial_graph_id, {
337 match status {
338 PartialGraphStatus::ReceivedExchangeRequest(_) => {
339 ("ReceivedExchangeRequest".to_owned(), None)
340 }
341 PartialGraphStatus::Running(state) => {
342 ("running".to_owned(), Some(state.to_debug_info()))
343 }
344 PartialGraphStatus::Suspended(state) => {
345 (format!("suspended: {:?}", state.suspend_time), None)
346 }
347 PartialGraphStatus::Resetting => ("resetting".to_owned(), None),
348 PartialGraphStatus::Unspecified => {
349 unreachable!()
350 }
351 }
352 })
353 })
354 .collect(),
355 has_control_stream_connected: self.control_stream_handle.connected(),
356 }
357 }
358
359 async fn next_completed_epoch(
360 futures: &mut HashMap<PartialGraphId, FuturesOrdered<AwaitEpochCompletedFuture>>,
361 ) -> (PartialGraphId, Barrier, StreamResult<BarrierCompleteResult>) {
362 poll_fn(|cx| {
363 for (partial_graph_id, futures) in &mut *futures {
364 if let Poll::Ready(Some((barrier, result))) = futures.poll_next_unpin(cx) {
365 return Poll::Ready((*partial_graph_id, barrier, result));
366 }
367 }
368 Poll::Pending
369 })
370 .await
371 }
372
373 async fn run(mut self, mut actor_op_rx: UnboundedReceiver<LocalActorOperation>) {
374 loop {
375 select! {
376 biased;
377 event = self.state.next_event() => {
378 match event {
379 ManagedBarrierStateEvent::BarrierCollected{
380 partial_graph_id,
381 barrier,
382 } => {
383 self.complete_barrier(partial_graph_id, barrier.epoch.prev);
386 }
387 ManagedBarrierStateEvent::ActorError{
388 partial_graph_id,
389 actor_id,
390 err,
391 } => {
392 self.on_partial_graph_failure(partial_graph_id, Some(actor_id), err, "recv actor failure");
393 }
394 ManagedBarrierStateEvent::PartialGraphsReset(output) => {
395 for (partial_graph_id, output) in output {
396 self.ack_partial_graph_reset(partial_graph_id, Some(output));
397 }
398 }
399 ManagedBarrierStateEvent::RegisterLocalUpstreamOutput{
400 actor_id,
401 upstream_actor_id,
402 upstream_partial_graph_id,
403 tx
404 } => {
405 self.handle_actor_op(LocalActorOperation::TakeReceiver {
406 partial_graph_id: upstream_partial_graph_id,
407 term_id: self.term_id.clone(),
408 ids: (upstream_actor_id, actor_id),
409 request: TakeReceiverRequest::Local(tx),
410 });
411 }
412 }
413 }
414 (partial_graph_id, barrier, result) = Self::next_completed_epoch(&mut self.await_epoch_completed_futures) => {
415 match result {
416 Ok(result) => {
417 self.on_epoch_completed(partial_graph_id, barrier.epoch.prev, result);
418 }
419 Err(err) => {
420 self.control_stream_handle.reset_stream_with_err(Status::internal(format!("failed to complete epoch: {} {:?} {:?}", partial_graph_id, barrier.epoch, err.as_report())));
424 }
425 }
426 },
427 actor_op = actor_op_rx.recv() => {
428 if let Some(actor_op) = actor_op {
429 match actor_op {
430 LocalActorOperation::NewControlStream { handle, init_request } => {
431 self.control_stream_handle.reset_stream_with_err(Status::internal("control stream has been reset to a new one"));
432 self.reset(init_request).await;
433 self.control_stream_handle = handle;
434 self.control_stream_handle.send_response(streaming_control_stream_response::Response::Init(InitResponse {}));
435 }
436 LocalActorOperation::Shutdown { result_sender } => {
437 if self.state.partial_graphs.values().any(|graph| {
438 match graph {
439 PartialGraphStatus::Running(graph) => {
440 !graph.actor_states.is_empty()
441 }
442 PartialGraphStatus::Suspended(_) | PartialGraphStatus::Resetting |
443 PartialGraphStatus::ReceivedExchangeRequest(_) => {
444 false
445 }
446 PartialGraphStatus::Unspecified => {
447 unreachable!()
448 }
449 }
450 }) {
451 tracing::warn!(
452 "shutdown with running actors, scaling or migration will be triggered"
453 );
454 }
455 self.control_stream_handle.shutdown_stream().await;
456 let _ = result_sender.send(());
457 }
458 actor_op => {
459 self.handle_actor_op(actor_op);
460 }
461 }
462 }
463 else {
464 break;
465 }
466 },
467 request = self.control_stream_handle.next_request() => {
468 let result = self.handle_streaming_control_request(request.request.expect("non empty"));
469 if let Err((partial_graph_id, err)) = result {
470 self.on_partial_graph_failure(partial_graph_id, None, err, "failed to inject barrier");
471 }
472 },
473 }
474 }
475 }
476
477 fn handle_streaming_control_request(
478 &mut self,
479 request: Request,
480 ) -> Result<(), (PartialGraphId, StreamError)> {
481 match request {
482 Request::InjectBarrier(req) => {
483 let partial_graph_id = req.partial_graph_id;
484 let result: StreamResult<()> = try {
485 let barrier = Barrier::from_protobuf(req.get_barrier().unwrap())
486 .map_err(StreamError::from)?;
487 self.send_barrier(&barrier, req)?;
488 };
489 result.map_err(|e| (partial_graph_id, e))?;
490 Ok(())
491 }
492 Request::RemovePartialGraph(req) => {
493 self.remove_partial_graphs(req.partial_graph_ids);
494 Ok(())
495 }
496 Request::CreatePartialGraph(req) => {
497 self.add_partial_graph(req.partial_graph_id);
498 Ok(())
499 }
500 Request::ResetPartialGraphs(req) => {
501 self.reset_partial_graphs(req);
502 Ok(())
503 }
504 Request::Init(_) => {
505 unreachable!()
506 }
507 }
508 }
509
510 fn handle_actor_op(&mut self, actor_op: LocalActorOperation) {
511 match actor_op {
512 LocalActorOperation::NewControlStream { .. } | LocalActorOperation::Shutdown { .. } => {
513 unreachable!("event {actor_op} should be handled separately in async context")
514 }
515 LocalActorOperation::TakeReceiver {
516 partial_graph_id,
517 term_id,
518 ids,
519 request,
520 } => {
521 let err = if self.term_id != term_id {
522 {
523 warn!(
524 ?ids,
525 term_id,
526 current_term_id = self.term_id,
527 "take receiver on unmatched term_id"
528 );
529 anyhow!(
530 "take receiver {:?} on unmatched term_id {} to current term_id {}",
531 ids,
532 term_id,
533 self.term_id
534 )
535 }
536 } else {
537 match self.state.partial_graphs.entry(partial_graph_id) {
538 Entry::Occupied(mut entry) => match entry.get_mut() {
539 PartialGraphStatus::ReceivedExchangeRequest(pending_requests) => {
540 pending_requests.push((ids, request));
541 return;
542 }
543 PartialGraphStatus::Running(graph) => {
544 let (upstream_actor_id, actor_id) = ids;
545 graph.new_actor_output_request(
546 actor_id,
547 upstream_actor_id,
548 request,
549 );
550 return;
551 }
552 PartialGraphStatus::Suspended(_) => {
553 anyhow!("partial graph suspended")
554 }
555 PartialGraphStatus::Resetting => {
556 anyhow!("partial graph resetting")
557 }
558 PartialGraphStatus::Unspecified => {
559 unreachable!()
560 }
561 },
562 Entry::Vacant(entry) => {
563 entry.insert(PartialGraphStatus::ReceivedExchangeRequest(vec![(
564 ids, request,
565 )]));
566 return;
567 }
568 }
569 };
570 if let TakeReceiverRequest::Remote { result_sender, .. } = request {
571 let _ = result_sender.send(Err(err.into()));
572 }
573 }
574 #[cfg(test)]
575 LocalActorOperation::GetCurrentLocalBarrierManager(sender) => {
576 let partial_graph_status = self
577 .state
578 .partial_graphs
579 .get(&crate::task::TEST_PARTIAL_GRAPH_ID)
580 .unwrap();
581 let partial_graph_state = risingwave_common::must_match!(partial_graph_status, PartialGraphStatus::Running(database_state) => database_state);
582 let _ = sender.send(partial_graph_state.local_barrier_manager.clone());
583 }
584 #[cfg(test)]
585 LocalActorOperation::TakePendingNewOutputRequest(actor_id, sender) => {
586 let partial_graph_status = self
587 .state
588 .partial_graphs
589 .get_mut(&crate::task::TEST_PARTIAL_GRAPH_ID)
590 .unwrap();
591
592 let partial_graph_state = risingwave_common::must_match!(partial_graph_status, PartialGraphStatus::Running(database_state) => database_state);
593 assert!(!partial_graph_state.actor_states.contains_key(&actor_id));
594 let requests = partial_graph_state
595 .actor_pending_new_output_requests
596 .remove(&actor_id)
597 .unwrap();
598 let _ = sender.send(requests);
599 }
600 #[cfg(test)]
601 LocalActorOperation::Flush(sender) => {
602 use futures::FutureExt;
603 while let Some(request) = self.control_stream_handle.next_request().now_or_never() {
604 self.handle_streaming_control_request(
605 request.request.expect("should not be empty"),
606 )
607 .unwrap();
608 }
609 while let Some(event) = self.state.next_event().now_or_never() {
610 match event {
611 ManagedBarrierStateEvent::BarrierCollected {
612 barrier,
613 partial_graph_id,
614 } => {
615 self.complete_barrier(partial_graph_id, barrier.epoch.prev);
616 }
617 ManagedBarrierStateEvent::ActorError { .. }
618 | ManagedBarrierStateEvent::PartialGraphsReset { .. }
619 | ManagedBarrierStateEvent::RegisterLocalUpstreamOutput { .. } => {
620 unreachable!()
621 }
622 }
623 }
624 sender.send(()).unwrap()
625 }
626 LocalActorOperation::InspectState { result_sender } => {
627 let debug_info = self.to_debug_info();
628 let _ = result_sender.send(debug_info.to_string());
629 }
630 }
631 }
632}
633
634mod await_epoch_completed_future {
635 use std::future::Future;
636
637 use futures::FutureExt;
638 use futures::future::BoxFuture;
639 use risingwave_common::id::TableId;
640 use risingwave_hummock_sdk::SyncResult;
641 use risingwave_pb::stream_service::barrier_complete_response::{
642 PbCdcSourceOffsetUpdated, PbCdcTableBackfillProgress, PbCreateMviewProgress,
643 PbIcebergPkIndexSinkMetadata, PbListFinishedSource, PbLoadFinishedSource,
644 };
645
646 use crate::error::StreamResult;
647 use crate::executor::Barrier;
648 use crate::task::{BarrierCompleteResult, await_tree_key};
649
650 pub(super) type AwaitEpochCompletedFuture =
651 impl Future<Output = (Barrier, StreamResult<BarrierCompleteResult>)> + 'static;
652
653 #[define_opaque(AwaitEpochCompletedFuture)]
654 #[expect(clippy::too_many_arguments)]
655 pub(super) fn instrument_complete_barrier_future(
656 complete_barrier_future: Option<BoxFuture<'static, StreamResult<SyncResult>>>,
657 barrier: Barrier,
658 barrier_await_tree_reg: Option<&await_tree::Registry>,
659 create_mview_progress: Vec<PbCreateMviewProgress>,
660 list_finished_source_ids: Vec<PbListFinishedSource>,
661 load_finished_source_ids: Vec<PbLoadFinishedSource>,
662 cdc_table_backfill_progress: Vec<PbCdcTableBackfillProgress>,
663 cdc_source_offset_updated: Vec<PbCdcSourceOffsetUpdated>,
664 iceberg_pk_index_sink_metadata: Vec<PbIcebergPkIndexSinkMetadata>,
665 truncate_tables: Vec<TableId>,
666 refresh_finished_tables: Vec<TableId>,
667 ) -> AwaitEpochCompletedFuture {
668 let prev_epoch = barrier.epoch.prev;
669 let future = async move {
670 if let Some(future) = complete_barrier_future {
671 let result = future.await;
672 result.map(Some)
673 } else {
674 Ok(None)
675 }
676 }
677 .map(move |result| {
678 (
679 barrier,
680 result.map(|sync_result| BarrierCompleteResult {
681 sync_result,
682 create_mview_progress,
683 list_finished_source_ids,
684 load_finished_source_ids,
685 cdc_table_backfill_progress,
686 cdc_source_offset_updated,
687 iceberg_pk_index_sink_metadata,
688 truncate_tables,
689 refresh_finished_tables,
690 }),
691 )
692 });
693 if let Some(reg) = barrier_await_tree_reg {
694 reg.register(
695 await_tree_key::BarrierAwait { prev_epoch },
696 format!("SyncEpoch({})", prev_epoch),
697 )
698 .instrument(future)
699 .left_future()
700 } else {
701 future.right_future()
702 }
703 }
704}
705
706use await_epoch_completed_future::*;
707use risingwave_common::catalog::TableId;
708use risingwave_pb::hummock::vector_index_delta::PbVectorIndexAdds;
709use risingwave_storage::{StateStoreImpl, dispatch_state_store};
710
711use crate::executor::exchange::permit;
712
713fn sync_epoch(
714 state_store: &StateStoreImpl,
715 streaming_metrics: &StreamingMetrics,
716 prev_epoch: u64,
717 table_ids: HashSet<TableId>,
718) -> BoxFuture<'static, StreamResult<SyncResult>> {
719 let timer = streaming_metrics.barrier_sync_latency.start_timer();
720
721 let state_store = state_store.clone();
722 let future = async move {
723 dispatch_state_store!(state_store, hummock, {
724 hummock.sync(vec![(prev_epoch, table_ids)]).await
725 })
726 };
727
728 future
729 .instrument_await(await_tree::span!("sync_epoch (epoch {})", prev_epoch))
730 .inspect_ok(move |_| {
731 timer.observe_duration();
732 })
733 .map_err(move |e| {
734 tracing::error!(
735 prev_epoch,
736 error = %e.as_report(),
737 "Failed to sync state store",
738 );
739 e.into()
740 })
741 .boxed()
742}
743
744impl LocalBarrierWorker {
745 fn complete_barrier(&mut self, partial_graph_id: PartialGraphId, prev_epoch: u64) {
746 {
747 let Some(graph_state) = self
748 .state
749 .partial_graphs
750 .get_mut(&partial_graph_id)
751 .expect("should exist")
752 .state_for_request()
753 else {
754 return;
755 };
756 let BarrierToComplete {
757 barrier,
758 table_ids,
759 create_mview_progress,
760 list_finished_source_ids,
761 load_finished_source_ids,
762 cdc_table_backfill_progress,
763 cdc_source_offset_updated,
764 iceberg_pk_index_sink_metadata,
765 truncate_tables,
766 refresh_finished_tables,
767 } = graph_state.pop_barrier_to_complete(prev_epoch);
768
769 let complete_barrier_future = match &barrier.kind {
770 BarrierKind::Unspecified => unreachable!(),
771 BarrierKind::Initial => {
772 tracing::info!(
773 epoch = prev_epoch,
774 "ignore sealing data for the first barrier"
775 );
776 tracing::info!(?prev_epoch, "ignored syncing data for the first barrier");
777 None
778 }
779 BarrierKind::Barrier => None,
780 BarrierKind::Checkpoint => Some(sync_epoch(
781 &self.actor_manager.env.state_store(),
782 &self.actor_manager.streaming_metrics,
783 prev_epoch,
784 table_ids.expect("should be Some on BarrierKind::Checkpoint"),
785 )),
786 };
787
788 self.await_epoch_completed_futures
789 .entry(partial_graph_id)
790 .or_default()
791 .push_back({
792 instrument_complete_barrier_future(
793 complete_barrier_future,
794 barrier,
795 self.actor_manager.await_tree_reg.as_ref(),
796 create_mview_progress,
797 list_finished_source_ids,
798 load_finished_source_ids,
799 cdc_table_backfill_progress,
800 cdc_source_offset_updated,
801 iceberg_pk_index_sink_metadata,
802 truncate_tables,
803 refresh_finished_tables,
804 )
805 });
806 }
807 }
808
809 fn on_epoch_completed(
810 &mut self,
811 partial_graph_id: PartialGraphId,
812 epoch: u64,
813 result: BarrierCompleteResult,
814 ) {
815 let BarrierCompleteResult {
816 create_mview_progress,
817 sync_result,
818 list_finished_source_ids,
819 load_finished_source_ids,
820 cdc_table_backfill_progress,
821 cdc_source_offset_updated,
822 iceberg_pk_index_sink_metadata,
823 truncate_tables,
824 refresh_finished_tables,
825 } = result;
826
827 let (synced_sstables, table_watermarks, old_value_ssts, vector_index_adds) = sync_result
828 .map(|sync_result| {
829 (
830 sync_result.uncommitted_ssts,
831 sync_result.table_watermarks,
832 sync_result.old_value_ssts,
833 sync_result.vector_index_adds,
834 )
835 })
836 .unwrap_or_default();
837
838 let result = {
839 {
840 streaming_control_stream_response::Response::CompleteBarrier(
841 BarrierCompleteResponse {
842 request_id: "todo".to_owned(),
843 partial_graph_id,
844 epoch,
845 status: None,
846 create_mview_progress,
847 synced_sstables: synced_sstables
848 .into_iter()
849 .map(
850 |LocalSstableInfo {
851 sst_info,
852 table_stats,
853 created_at,
854 }| PbLocalSstableInfo {
855 sst: Some(sst_info.into()),
856 table_stats_map: to_prost_table_stats_map(table_stats),
857 created_at,
858 },
859 )
860 .collect_vec(),
861 worker_id: self.actor_manager.env.worker_id(),
862 table_watermarks: table_watermarks
863 .into_iter()
864 .map(|(key, value)| (key, value.into()))
865 .collect(),
866 old_value_sstables: old_value_ssts
867 .into_iter()
868 .map(|sst| sst.sst_info.into())
869 .collect(),
870 list_finished_sources: list_finished_source_ids,
871 load_finished_sources: load_finished_source_ids,
872 cdc_source_offset_updated,
873 vector_index_adds: vector_index_adds
874 .into_iter()
875 .map(|(table_id, adds)| {
876 (
877 table_id,
878 PbVectorIndexAdds {
879 adds: adds.into_iter().map(|add| add.into()).collect(),
880 },
881 )
882 })
883 .collect(),
884 cdc_table_backfill_progress,
885 truncate_tables,
886 refresh_finished_tables,
887 iceberg_pk_index_sink_metadata,
888 },
889 )
890 }
891 };
892
893 self.control_stream_handle.send_response(result);
894 }
895
896 fn send_barrier(
903 &mut self,
904 barrier: &Barrier,
905 request: InjectBarrierRequest,
906 ) -> StreamResult<()> {
907 debug!(
908 target: "events::stream::barrier::manager::send",
909 "send barrier {:?}, actor_ids_to_collect = {:?}",
910 barrier,
911 request.actor_ids_to_collect
912 );
913
914 let status = self
915 .state
916 .partial_graphs
917 .get_mut(&request.partial_graph_id)
918 .expect("should exist");
919 if let Some(state) = status.state_for_request() {
920 state.transform_to_issued(barrier, request)?;
921 }
922 Ok(())
923 }
924
925 fn remove_partial_graphs(
926 &mut self,
927 partial_graph_ids: impl IntoIterator<Item = PartialGraphId>,
928 ) {
929 for partial_graph_id in partial_graph_ids {
930 if let Some(mut graph) = self.state.partial_graphs.remove(&partial_graph_id) {
931 if let Some(graph) = graph.state_for_request() {
932 assert!(
933 graph.graph_state.is_empty(),
934 "non empty graph to be removed: {}",
935 &graph.graph_state
936 );
937 }
938 } else {
939 warn!(
940 partial_graph_id = %partial_graph_id,
941 "no partial graph to remove"
942 );
943 }
944 }
945 }
946
947 fn add_partial_graph(&mut self, partial_graph_id: PartialGraphId) {
948 match self.state.partial_graphs.entry(partial_graph_id) {
949 Entry::Occupied(entry) => {
950 let status = entry.into_mut();
951 if let PartialGraphStatus::ReceivedExchangeRequest(pending_requests) = status {
952 let mut graph = PartialGraphState::new(
953 partial_graph_id,
954 self.term_id.clone(),
955 self.actor_manager.clone(),
956 );
957 for ((upstream_actor_id, actor_id), request) in pending_requests.drain(..) {
958 graph.new_actor_output_request(actor_id, upstream_actor_id, request);
959 }
960 *status = PartialGraphStatus::Running(graph);
961 } else {
962 panic!("duplicated partial graph: {}", partial_graph_id);
963 }
964
965 status
966 }
967 Entry::Vacant(entry) => {
968 entry.insert(PartialGraphStatus::Running(PartialGraphState::new(
969 partial_graph_id,
970 self.term_id.clone(),
971 self.actor_manager.clone(),
972 )))
973 }
974 };
975 }
976
977 fn reset_partial_graphs(&mut self, req: ResetPartialGraphsRequest) {
978 let mut table_ids_to_clear = HashSet::new();
979 let mut reset_futures = HashMap::new();
980 for partial_graph_id in req.partial_graph_ids {
981 if let Some(status) = self.state.partial_graphs.get_mut(&partial_graph_id) {
982 let reset_future = status.start_reset(
983 partial_graph_id,
984 self.await_epoch_completed_futures.remove(&partial_graph_id),
985 &mut table_ids_to_clear,
986 );
987 reset_futures.insert(partial_graph_id, reset_future);
988 } else {
989 self.ack_partial_graph_reset(partial_graph_id, None);
990 }
991 }
992 if reset_futures.is_empty() {
993 assert!(table_ids_to_clear.is_empty());
994 return;
995 }
996 let state_store = self.actor_manager.env.state_store();
997 self.state.resetting_graphs.push(spawn(async move {
998 let outputs =
999 join_all(
1000 reset_futures
1001 .into_iter()
1002 .map(|(partial_graph_id, future)| async move {
1003 (partial_graph_id, future.await)
1004 }),
1005 )
1006 .await;
1007 if !table_ids_to_clear.is_empty()
1008 && let Some(hummock) = state_store.as_hummock()
1009 {
1010 hummock.clear_tables(table_ids_to_clear).await;
1011 }
1012 outputs
1013 }));
1014 }
1015
1016 fn ack_partial_graph_reset(
1017 &mut self,
1018 partial_graph_id: PartialGraphId,
1019 reset_output: Option<ResetPartialGraphOutput>,
1020 ) {
1021 info!(
1022 %partial_graph_id,
1023 "partial graph reset successfully"
1024 );
1025 assert!(!self.state.partial_graphs.contains_key(&partial_graph_id));
1026 self.await_epoch_completed_futures.remove(&partial_graph_id);
1027 self.control_stream_handle.ack_reset_partial_graph(
1028 partial_graph_id,
1029 reset_output.and_then(|output| output.root_err),
1030 );
1031 }
1032
1033 fn on_partial_graph_failure(
1037 &mut self,
1038 partial_graph_id: PartialGraphId,
1039 failed_actor: Option<ActorId>,
1040 err: StreamError,
1041 message: impl Into<String>,
1042 ) {
1043 let message = message.into();
1044 error!(%partial_graph_id, ?failed_actor, message, err = ?err.as_report(), "suspend partial graph on error");
1045 let completing_futures = self.await_epoch_completed_futures.remove(&partial_graph_id);
1046 self.state
1047 .partial_graphs
1048 .get_mut(&partial_graph_id)
1049 .expect("should exist")
1050 .suspend(failed_actor, err, completing_futures);
1051 self.control_stream_handle
1052 .send_response(Response::ReportPartialGraphFailure(
1053 ReportPartialGraphFailureResponse { partial_graph_id },
1054 ));
1055 }
1056
1057 async fn reset(&mut self, init_request: InitRequest) {
1059 join(
1060 join_all(
1061 self.state
1062 .partial_graphs
1063 .values_mut()
1064 .map(|graph| graph.abort()),
1065 ),
1066 async {
1067 while let Some(join_result) = self.state.resetting_graphs.next().await {
1068 join_result.expect("failed to join reset partial graphs handle");
1069 }
1070 },
1071 )
1072 .await;
1073 if let Some(m) = self.actor_manager.await_tree_reg.as_ref() {
1074 m.clear();
1075 }
1076
1077 if let Some(hummock) = self.actor_manager.env.state_store().as_hummock() {
1078 hummock
1079 .clear_shared_buffer()
1080 .instrument_await("store_clear_shared_buffer".verbose())
1081 .await
1082 }
1083 self.actor_manager.env.dml_manager_ref().clear();
1084 *self = Self::new(self.actor_manager.clone(), init_request.term_id);
1085 self.actor_manager.env.client_pool().invalidate_all();
1086 }
1087
1088 pub fn spawn(
1090 env: StreamEnvironment,
1091 streaming_metrics: Arc<StreamingMetrics>,
1092 await_tree_reg: Option<await_tree::Registry>,
1093 watermark_epoch: AtomicU64Ref,
1094 actor_op_rx: UnboundedReceiver<LocalActorOperation>,
1095 ) -> JoinHandle<()> {
1096 let runtime = {
1097 let mut builder = tokio::runtime::Builder::new_multi_thread();
1098 if let Some(worker_threads_num) = env.global_config().actor_runtime_worker_threads_num {
1099 builder.worker_threads(worker_threads_num);
1100 }
1101 builder
1102 .thread_name("rw-streaming")
1103 .enable_all()
1104 .build()
1105 .unwrap()
1106 };
1107
1108 let actor_manager = Arc::new(StreamActorManager {
1109 env,
1110 streaming_metrics,
1111 watermark_epoch,
1112 await_tree_reg,
1113 runtime: runtime.into(),
1114 config_override_cache: ConfigOverrideCache::new(CONFIG_OVERRIDE_CACHE_DEFAULT_CAPACITY),
1115 });
1116 let worker = LocalBarrierWorker::new(actor_manager, "uninitialized".into());
1117 tokio::spawn(worker.run(actor_op_rx))
1118 }
1119}
1120
1121pub(super) struct EventSender<T>(pub(super) UnboundedSender<T>);
1122
1123impl<T> Clone for EventSender<T> {
1124 fn clone(&self) -> Self {
1125 Self(self.0.clone())
1126 }
1127}
1128
1129impl<T> EventSender<T> {
1130 pub(super) fn send_event(&self, event: T) {
1131 self.0.send(event).expect("should be able to send event")
1132 }
1133
1134 pub(super) async fn send_and_await<RSP>(
1135 &self,
1136 make_event: impl FnOnce(oneshot::Sender<RSP>) -> T,
1137 ) -> StreamResult<RSP> {
1138 let (tx, rx) = oneshot::channel();
1139 let event = make_event(tx);
1140 self.send_event(event);
1141 rx.await
1142 .map_err(|_| anyhow!("barrier manager maybe reset").into())
1143 }
1144}
1145
1146pub(crate) enum NewOutputRequest {
1147 Local(permit::Sender),
1148 Remote(permit::Sender),
1149}
1150
1151#[cfg(test)]
1152pub(crate) mod barrier_test_utils {
1153 use assert_matches::assert_matches;
1154 use futures::StreamExt;
1155 use risingwave_pb::stream_service::streaming_control_stream_request::{
1156 InitRequest, PbCreatePartialGraphRequest,
1157 };
1158 use risingwave_pb::stream_service::{
1159 InjectBarrierRequest, PbStreamingControlStreamRequest, StreamingControlStreamRequest,
1160 StreamingControlStreamResponse, streaming_control_stream_request,
1161 streaming_control_stream_response,
1162 };
1163 use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel};
1164 use tokio::sync::oneshot;
1165 use tokio_stream::wrappers::UnboundedReceiverStream;
1166 use tonic::Status;
1167
1168 use crate::executor::Barrier;
1169 use crate::task::barrier_worker::{ControlStreamHandle, EventSender, LocalActorOperation};
1170 use crate::task::{ActorId, LocalBarrierManager, NewOutputRequest, TEST_PARTIAL_GRAPH_ID};
1171
1172 pub(crate) struct LocalBarrierTestEnv {
1173 pub local_barrier_manager: LocalBarrierManager,
1174 pub(super) actor_op_tx: EventSender<LocalActorOperation>,
1175 pub request_tx: UnboundedSender<Result<StreamingControlStreamRequest, Status>>,
1176 pub response_rx: UnboundedReceiver<Result<StreamingControlStreamResponse, Status>>,
1177 }
1178
1179 impl LocalBarrierTestEnv {
1180 pub(crate) async fn for_test() -> Self {
1181 let actor_op_tx = LocalBarrierManager::spawn_for_test();
1182
1183 let (request_tx, request_rx) = unbounded_channel();
1184 let (response_tx, mut response_rx) = unbounded_channel();
1185
1186 request_tx
1187 .send(Ok(PbStreamingControlStreamRequest {
1188 request: Some(
1189 streaming_control_stream_request::Request::CreatePartialGraph(
1190 PbCreatePartialGraphRequest {
1191 partial_graph_id: TEST_PARTIAL_GRAPH_ID,
1192 },
1193 ),
1194 ),
1195 }))
1196 .unwrap();
1197
1198 actor_op_tx.send_event(LocalActorOperation::NewControlStream {
1199 handle: ControlStreamHandle::new(
1200 response_tx,
1201 UnboundedReceiverStream::new(request_rx).boxed(),
1202 ),
1203 init_request: InitRequest {
1204 term_id: "for_test".into(),
1205 },
1206 });
1207
1208 assert_matches!(
1209 response_rx.recv().await.unwrap().unwrap().response.unwrap(),
1210 streaming_control_stream_response::Response::Init(_)
1211 );
1212
1213 let local_barrier_manager = actor_op_tx
1214 .send_and_await(LocalActorOperation::GetCurrentLocalBarrierManager)
1215 .await
1216 .unwrap();
1217
1218 Self {
1219 local_barrier_manager,
1220 actor_op_tx,
1221 request_tx,
1222 response_rx,
1223 }
1224 }
1225
1226 pub(crate) fn inject_barrier(
1227 &self,
1228 barrier: &Barrier,
1229 actor_to_collect: impl IntoIterator<Item = ActorId>,
1230 ) {
1231 self.request_tx
1232 .send(Ok(StreamingControlStreamRequest {
1233 request: Some(streaming_control_stream_request::Request::InjectBarrier(
1234 InjectBarrierRequest {
1235 request_id: "".to_owned(),
1236 barrier: Some(barrier.to_protobuf()),
1237 actor_ids_to_collect: actor_to_collect.into_iter().collect(),
1238 table_ids_to_sync: vec![],
1239 partial_graph_id: TEST_PARTIAL_GRAPH_ID,
1240 actors_to_build: vec![],
1241 },
1242 )),
1243 }))
1244 .unwrap();
1245 }
1246
1247 pub(crate) async fn flush_all_events(&self) {
1248 Self::flush_all_events_impl(&self.actor_op_tx).await
1249 }
1250
1251 pub(super) async fn flush_all_events_impl(actor_op_tx: &EventSender<LocalActorOperation>) {
1252 let (tx, rx) = oneshot::channel();
1253 actor_op_tx.send_event(LocalActorOperation::Flush(tx));
1254 rx.await.unwrap()
1255 }
1256
1257 pub(crate) async fn take_pending_new_output_requests(
1258 &self,
1259 actor_id: ActorId,
1260 ) -> Vec<(ActorId, NewOutputRequest)> {
1261 self.actor_op_tx
1262 .send_and_await(|tx| LocalActorOperation::TakePendingNewOutputRequest(actor_id, tx))
1263 .await
1264 .unwrap()
1265 }
1266 }
1267}