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