Skip to main content

risingwave_stream/task/
actor_manager.rs

1// Copyright 2025 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 core::time::Duration;
16use std::fmt::Debug;
17use std::sync::Arc;
18
19use async_recursion::async_recursion;
20use futures::{FutureExt, TryFutureExt};
21use itertools::Itertools;
22use risingwave_common::bail;
23use risingwave_common::bitmap::Bitmap;
24use risingwave_common::catalog::{ColumnId, Field, Schema};
25use risingwave_common::config::{MetricLevel, StreamingConfig, merge_streaming_config_section};
26use risingwave_common::hash::VnodeBitmapExt;
27use risingwave_common::operator::{unique_executor_id, unique_operator_id};
28use risingwave_common::util::runtime::BackgroundShutdownRuntime;
29use risingwave_pb::id::{ExecutorId, GlobalOperatorId};
30use risingwave_pb::stream_plan::stream_node::NodeBody;
31use risingwave_pb::stream_plan::{
32    self, StreamNode, StreamScanNode, StreamScanType, SyncLogStoreNode,
33};
34use risingwave_pb::stream_service::inject_barrier_request::BuildActorInfo;
35use risingwave_storage::monitor::HummockTraceFutureExt;
36use risingwave_storage::table::batch_table::BatchTable;
37use risingwave_storage::{StateStore, dispatch_state_store};
38use thiserror_ext::AsReport;
39use tokio::sync::mpsc::UnboundedReceiver;
40use tokio::task::JoinHandle;
41
42use crate::common::table::state_table::StateTableBuilder;
43use crate::error::StreamResult;
44use crate::executor::monitor::StreamingMetrics;
45use crate::executor::subtask::SubtaskHandle;
46use crate::executor::{
47    Actor, ActorContext, ActorContextRef, DispatchExecutor, Execute, Executor, ExecutorInfo,
48    MergeExecutorInput, SnapshotBackfillExecutor, SyncLogStoreDispatchExecutor,
49    TroublemakerExecutor, WrapperExecutor,
50};
51use crate::from_proto::{MergeExecutorBuilder, create_executor};
52use crate::task::{
53    ActorEvalErrorReport, ActorId, AtomicU64Ref, FragmentId, LocalBarrierManager, NewOutputRequest,
54    StreamEnvironment, await_tree_key,
55};
56
57/// An actor without a vnode bitmap is a singleton, which owns the only `SINGLETON_VNODE`.
58fn actor_vnode_bitmap(actor: &BuildActorInfo) -> Bitmap {
59    actor
60        .vnode_bitmap
61        .as_ref()
62        .map_or_else(|| Bitmap::singleton().clone(), Bitmap::from)
63}
64
65/// Default capacity for `ConfigOverrideCache`.
66/// Since we only support per-job config override right now, 256 jobs should be sufficient on a single node.
67pub const CONFIG_OVERRIDE_CACHE_DEFAULT_CAPACITY: u64 = 256;
68pub type ConfigOverrideCache = moka::sync::Cache<String, Arc<StreamingConfig>>;
69
70/// [Spawning actors](`Self::spawn_actor`), called by [`crate::task::barrier_worker::managed_state::PartialGraphState`].
71///
72/// See [`crate::task`] for architecture overview.
73pub(crate) struct StreamActorManager {
74    pub(super) env: StreamEnvironment,
75    pub(super) streaming_metrics: Arc<StreamingMetrics>,
76
77    /// Watermark epoch number.
78    pub(super) watermark_epoch: AtomicU64Ref,
79
80    /// Manages the await-trees of all actors.
81    pub(super) await_tree_reg: Option<await_tree::Registry>,
82
83    /// Runtime for the streaming actors.
84    pub(super) runtime: BackgroundShutdownRuntime,
85
86    /// Cache for overridden configuration: `config_override` -> `StreamingConfig`.
87    ///
88    /// Since the override is based on `env.global_config`, which won't change after creation,
89    /// we can use the `config_override` as the only key.
90    pub(super) config_override_cache: ConfigOverrideCache,
91}
92
93impl StreamActorManager {
94    fn get_executor_id(actor_context: &ActorContext, node: &StreamNode) -> ExecutorId {
95        // We assume that the operator_id of different instances from the same RelNode will be the
96        // same.
97        unique_executor_id(actor_context.id, node.operator_id)
98    }
99
100    fn get_executor_info(node: &StreamNode, executor_id: ExecutorId) -> ExecutorInfo {
101        let schema: Schema = node.fields.iter().map(Field::from).collect();
102
103        let stream_key = node
104            .get_stream_key()
105            .iter()
106            .map(|idx| *idx as usize)
107            .collect::<Vec<_>>();
108
109        let stream_kind = node.stream_kind();
110
111        let identity = format!("{} {:X}", node.get_node_body().unwrap(), executor_id);
112
113        ExecutorInfo {
114            schema,
115            stream_key,
116            stream_kind,
117            identity,
118            id: executor_id,
119        }
120    }
121
122    async fn create_merge_executor_allow_empty_upstream(
123        &self,
124        stream_node: &StreamNode,
125        actor_context: &ActorContextRef,
126        local_barrier_manager: &LocalBarrierManager,
127    ) -> StreamResult<MergeExecutorInput> {
128        let info = Self::get_executor_info(
129            stream_node,
130            Self::get_executor_id(actor_context, stream_node),
131        );
132        let NodeBody::Merge(merge_node) = stream_node.get_node_body()? else {
133            bail!("expected Merge input, got {}", stream_node.get_identity());
134        };
135        MergeExecutorBuilder::new_input_allow_empty_upstream(
136            local_barrier_manager.clone(),
137            self.streaming_metrics.clone(),
138            actor_context.clone(),
139            info,
140            merge_node,
141            actor_context.config.developer.chunk_size,
142        )
143        .await
144    }
145
146    async fn create_snapshot_backfill_node(
147        &self,
148        stream_node: &StreamNode,
149        node: &StreamScanNode,
150        actor_context: &ActorContextRef,
151        vnode_bitmap: Bitmap,
152        local_barrier_manager: &LocalBarrierManager,
153        state_store: impl StateStore,
154    ) -> StreamResult<Executor> {
155        let [upstream_node, _]: &[_; 2] = stream_node.input.as_slice().try_into().unwrap();
156        let chunk_size = actor_context.config.developer.chunk_size;
157        let upstream = self
158            .create_merge_executor_allow_empty_upstream(
159                upstream_node,
160                actor_context,
161                local_barrier_manager,
162            )
163            .await?;
164
165        let table_desc = node.get_table_desc()?;
166
167        let output_indices = node
168            .output_indices
169            .iter()
170            .map(|&i| i as usize)
171            .collect_vec();
172        let info = Self::get_executor_info(
173            stream_node,
174            Self::get_executor_id(actor_context, stream_node),
175        );
176        let stream_key = info.stream_key.clone();
177
178        let column_ids = node
179            .upstream_column_ids
180            .iter()
181            .map(ColumnId::from)
182            .collect_vec();
183
184        let progress = local_barrier_manager.register_create_mview_progress(actor_context);
185
186        let vnodes = Arc::new(vnode_bitmap);
187        let upstream_table = BatchTable::new_partial(
188            state_store.clone(),
189            column_ids,
190            Some(vnodes.clone()),
191            table_desc,
192        );
193
194        let state_table = node.get_state_table()?;
195        let state_table = StateTableBuilder::new(state_table, state_store.clone(), Some(vnodes))
196            .enable_preload_all_rows_by_config(&actor_context.config)
197            .build()
198            .await;
199
200        let executor = SnapshotBackfillExecutor::new(
201            upstream_table,
202            state_table,
203            upstream,
204            node.pk_scan_range.as_ref(),
205            output_indices,
206            stream_key,
207            actor_context.clone(),
208            progress,
209            chunk_size,
210            node.rate_limit.into(),
211            self.streaming_metrics.clone(),
212            node.snapshot_backfill_epoch,
213        )?
214        .boxed();
215
216        if crate::consistency::insane() {
217            let mut troubled_info = info.clone();
218            troubled_info.identity = format!("{} (troubled)", info.identity);
219            Ok((
220                info,
221                TroublemakerExecutor::new((troubled_info, executor).into(), chunk_size),
222            )
223                .into())
224        } else {
225            Ok((info, executor).into())
226        }
227    }
228
229    /// Create a chain(tree) of nodes, with given `store`.
230    #[expect(clippy::too_many_arguments)]
231    #[async_recursion]
232    async fn create_nodes_inner(
233        &self,
234        fragment_id: FragmentId,
235        node: &stream_plan::StreamNode,
236        env: StreamEnvironment,
237        store: impl StateStore,
238        actor_context: &ActorContextRef,
239        vnode_bitmap: Bitmap,
240        has_stateful: bool,
241        subtasks: &mut Vec<SubtaskHandle>,
242        local_barrier_manager: &LocalBarrierManager,
243    ) -> StreamResult<Executor> {
244        // The "stateful" here means that the executor may issue read operations to the state store
245        // massively and continuously. Used to decide whether to apply the optimization of subtasks.
246        fn is_stateful_executor(stream_node: &StreamNode) -> bool {
247            matches!(
248                stream_node.get_node_body().unwrap(),
249                NodeBody::HashAgg(_)
250                    | NodeBody::HashJoin(_)
251                    | NodeBody::DeltaIndexJoin(_)
252                    | NodeBody::Lookup(_)
253                    | NodeBody::StreamScan(_)
254                    | NodeBody::StreamCdcScan(_)
255                    | NodeBody::DynamicFilter(_)
256                    | NodeBody::GroupTopN(_)
257                    | NodeBody::Now(_)
258            )
259        }
260        let is_stateful = is_stateful_executor(node);
261
262        let executor = if let NodeBody::StreamScan(stream_scan) = node.get_node_body().unwrap()
263            && let Ok(StreamScanType::SnapshotBackfill) = stream_scan.get_stream_scan_type()
264        {
265            dispatch_state_store!(env.state_store(), store, {
266                self.create_snapshot_backfill_node(
267                    node,
268                    stream_scan,
269                    actor_context,
270                    vnode_bitmap,
271                    local_barrier_manager,
272                    store,
273                )
274                .await
275            })?
276        } else if matches!(
277            node.get_node_body().unwrap(),
278            NodeBody::IcebergWithPkIndexWriter(_)
279        ) {
280            // Compaction alternates between the normal and resolver inputs, so both merge
281            // executors must remain alive while temporarily disconnected.
282            let [first_merge_node, second_merge_node] = node.input.as_slice() else {
283                bail!("IcebergWithPkIndexWriter requires exactly two Merge inputs");
284            };
285            let merge_nodes = [first_merge_node, second_merge_node];
286            let mut input = Vec::with_capacity(merge_nodes.len());
287            for merge_node in merge_nodes {
288                let merge_input = self
289                    .create_merge_executor_allow_empty_upstream(
290                        merge_node,
291                        actor_context,
292                        local_barrier_manager,
293                    )
294                    .await?;
295                let merge_executor = merge_input.into_executor();
296                input.push(Self::wrap_executor(
297                    merge_executor,
298                    actor_context,
299                    has_stateful || is_stateful,
300                    subtasks,
301                ));
302            }
303            self.generate_executor_from_inputs(
304                fragment_id,
305                node,
306                env,
307                store,
308                actor_context,
309                vnode_bitmap,
310                local_barrier_manager,
311                input,
312            )
313            .await?
314        } else {
315            // Create the input executor before creating itself
316            let mut input = Vec::with_capacity(node.input.iter().len());
317            for input_stream_node in &node.input {
318                input.push(
319                    self.create_nodes_inner(
320                        fragment_id,
321                        input_stream_node,
322                        env.clone(),
323                        store.clone(),
324                        actor_context,
325                        vnode_bitmap.clone(),
326                        has_stateful || is_stateful,
327                        subtasks,
328                        local_barrier_manager,
329                    )
330                    .await?,
331                );
332            }
333
334            self.generate_executor_from_inputs(
335                fragment_id,
336                node,
337                env,
338                store,
339                actor_context,
340                vnode_bitmap,
341                local_barrier_manager,
342                input,
343            )
344            .await?
345        };
346        Ok(Self::wrap_executor(
347            executor,
348            actor_context,
349            has_stateful || is_stateful,
350            subtasks,
351        ))
352    }
353
354    #[expect(clippy::too_many_arguments)]
355    async fn generate_executor_from_inputs(
356        &self,
357        fragment_id: FragmentId,
358        node: &stream_plan::StreamNode,
359        env: StreamEnvironment,
360        store: impl StateStore,
361        actor_context: &ActorContextRef,
362        vnode_bitmap: Bitmap,
363        local_barrier_manager: &LocalBarrierManager,
364        input: Vec<Executor>,
365    ) -> StreamResult<Executor> {
366        let op_info = node.get_identity().clone();
367
368        // We assume that the operator_id of different instances from the same RelNode will be the
369        // same.
370        let executor_id = Self::get_executor_id(actor_context, node);
371        let operator_id = unique_operator_id(fragment_id, node.operator_id);
372
373        let info = Self::get_executor_info(node, executor_id);
374
375        let eval_error_report = ActorEvalErrorReport {
376            actor_context: actor_context.clone(),
377            identity: info.identity.clone().into(),
378        };
379
380        // Build the executor with params.
381        let executor_params = ExecutorParams {
382            env: env.clone(),
383
384            info: info.clone(),
385            executor_id,
386            operator_id,
387            op_info,
388            input,
389            fragment_id,
390            executor_stats: self.streaming_metrics.clone(),
391            actor_context: actor_context.clone(),
392            vnode_bitmap,
393            eval_error_report,
394            watermark_epoch: self.watermark_epoch.clone(),
395            local_barrier_manager: local_barrier_manager.clone(),
396            config: actor_context.config.clone(),
397        };
398
399        create_executor(executor_params, node, store).await
400    }
401
402    fn wrap_executor(
403        executor: Executor,
404        actor_context: &ActorContextRef,
405        has_stateful: bool,
406        subtasks: &mut Vec<SubtaskHandle>,
407    ) -> Executor {
408        let info = executor.info().clone();
409        // Wrap the executor for debug purpose.
410        let wrapped = WrapperExecutor::new(executor, actor_context.clone());
411        let executor = (info, wrapped).into();
412
413        // If there're multiple stateful executors in this actor, we will wrap it into a subtask.
414        if has_stateful {
415            // TODO(bugen): subtask does not work with tracing spans.
416            // let (subtask, executor) = subtask::wrap(executor, actor_context.id);
417            // subtasks.push(subtask);
418            // executor.boxed()
419
420            let _ = subtasks;
421        }
422        executor
423    }
424
425    /// Create a chain(tree) of nodes and return the head executor.
426    async fn create_nodes(
427        &self,
428        fragment_id: FragmentId,
429        node: &stream_plan::StreamNode,
430        env: StreamEnvironment,
431        actor_context: &ActorContextRef,
432        vnode_bitmap: Bitmap,
433        local_barrier_manager: &LocalBarrierManager,
434    ) -> StreamResult<(Executor, Vec<SubtaskHandle>)> {
435        let mut subtasks = vec![];
436
437        let executor = dispatch_state_store!(env.state_store(), store, {
438            self.create_nodes_inner(
439                fragment_id,
440                node,
441                env,
442                store,
443                actor_context,
444                vnode_bitmap,
445                false,
446                &mut subtasks,
447                local_barrier_manager,
448            )
449            .await
450        })?;
451
452        Ok((executor, subtasks))
453    }
454
455    /// Get the overridden configuration for the given `config_override`.
456    fn get_overridden_config(
457        &self,
458        config_override: &str,
459        actor_id: ActorId,
460    ) -> Arc<StreamingConfig> {
461        self.config_override_cache
462            .get_with_by_ref(config_override, || {
463                let global = self.env.global_config();
464                match merge_streaming_config_section(global.as_ref(), config_override) {
465                    Ok(Some(config)) => {
466                        tracing::info!(%actor_id, "applied configuration override");
467                        Arc::new(config)
468                    }
469                    Ok(None) => global.clone(), // nothing to override
470                    Err(e) => {
471                        // We should have validated the config override when user specified it for the job.
472                        // However, we still tolerate invalid config override here in case there's
473                        // any compatibility issue.
474                        tracing::error!(
475                            error = %e.as_report(),
476                            %actor_id,
477                            "failed to apply configuration override, use global config instead",
478                        );
479                        global.clone()
480                    }
481                }
482            })
483    }
484
485    async fn create_actor(
486        self: Arc<Self>,
487        actor: BuildActorInfo,
488        fragment_id: FragmentId,
489        node: Arc<StreamNode>,
490        local_barrier_manager: LocalBarrierManager,
491        new_output_request_rx: UnboundedReceiver<(ActorId, NewOutputRequest)>,
492        actor_config: Arc<StreamingConfig>,
493    ) -> StreamResult<Actor<DispatchExecutor>> {
494        let expr_context = actor.expr_context.clone().unwrap();
495        let actor_context = ActorContext::create(
496            &actor,
497            fragment_id,
498            self.env.total_mem_usage(),
499            self.streaming_metrics.clone(),
500            self.env.meta_client(),
501            actor_config,
502            self.env.clone(),
503        );
504        let vnode_bitmap = actor_vnode_bitmap(&actor);
505
506        let (executor, subtasks) = self
507            .create_nodes(
508                fragment_id,
509                &node,
510                self.env.clone(),
511                &actor_context,
512                vnode_bitmap,
513                &local_barrier_manager,
514            )
515            .await?;
516
517        let dispatcher = DispatchExecutor::new(
518            executor,
519            new_output_request_rx,
520            actor.dispatchers,
521            &actor_context,
522        )
523        .await?;
524
525        let actor = Actor::new(
526            dispatcher,
527            subtasks,
528            self.streaming_metrics.clone(),
529            actor_context.clone(),
530            expr_context,
531            local_barrier_manager,
532        );
533        Ok(actor)
534    }
535
536    #[expect(clippy::too_many_arguments)]
537    async fn create_actor_with_log_store_dispatcher<S: StateStore>(
538        self: Arc<Self>,
539        actor: BuildActorInfo,
540        fragment_id: FragmentId,
541        node: Arc<StreamNode>,
542        local_barrier_manager: LocalBarrierManager,
543        new_output_request_rx: UnboundedReceiver<(ActorId, NewOutputRequest)>,
544        actor_config: Arc<StreamingConfig>,
545        sync: Box<SyncLogStoreNode>,
546        state_store: S,
547    ) -> StreamResult<Actor<SyncLogStoreDispatchExecutor<S>>> {
548        let expr_context = actor.expr_context.clone().unwrap();
549        let actor_context = ActorContext::create(
550            &actor,
551            fragment_id,
552            self.env.total_mem_usage(),
553            self.streaming_metrics.clone(),
554            self.env.meta_client(),
555            actor_config,
556            self.env.clone(),
557        );
558        let vnode_bitmap = actor_vnode_bitmap(&actor);
559
560        let [input] = node.input.as_slice() else {
561            bail!("SyncLogStoreNode should have exactly one input");
562        };
563
564        let (executor, subtasks) = self
565            .create_nodes(
566                fragment_id,
567                input,
568                self.env.clone(),
569                &actor_context,
570                vnode_bitmap.clone(),
571                &local_barrier_manager,
572            )
573            .await?;
574
575        let dispatcher = SyncLogStoreDispatchExecutor::new(
576            executor,
577            new_output_request_rx,
578            actor.dispatchers,
579            &actor_context,
580            sync.as_ref(),
581            vnode_bitmap,
582            state_store,
583        )
584        .await?;
585
586        let actor = Actor::new(
587            dispatcher,
588            subtasks,
589            self.streaming_metrics.clone(),
590            actor_context.clone(),
591            expr_context,
592            local_barrier_manager,
593        );
594        Ok(actor)
595    }
596
597    pub(super) fn spawn_actor(
598        self: &Arc<Self>,
599        actor: BuildActorInfo,
600        fragment_id: FragmentId,
601        node: Arc<StreamNode>,
602        local_barrier_manager: LocalBarrierManager,
603        new_output_request_rx: UnboundedReceiver<(ActorId, NewOutputRequest)>,
604    ) -> (JoinHandle<()>, Option<JoinHandle<()>>) {
605        let stream_actor_ref = &actor;
606        let actor_id = stream_actor_ref.actor_id;
607        let actor_config = self.get_overridden_config(&actor.config_override, actor_id);
608
609        let monitor = actor_config
610            .developer
611            .enable_actor_tokio_metrics
612            .then(tokio_metrics::TaskMonitor::new);
613
614        let handle = {
615            let trace_span = format!("Actor {actor_id}: `{}`", stream_actor_ref.mview_definition);
616            let barrier_manager = local_barrier_manager;
617            let node_body = node.get_node_body().unwrap().clone();
618            let use_sync_log_store_dispatcher =
619                !actor_config.developer.disable_sync_log_store_dispatcher;
620            // wrap the future of `create_actor` with `boxed` to avoid stack overflow
621            let actor = match node_body {
622                NodeBody::SyncLogStore(sync) if use_sync_log_store_dispatcher => {
623                    tracing::info!(
624                        "SyncLogStoreDispatchExecutor is created for fragment {}",
625                        fragment_id
626                    );
627                    dispatch_state_store!(self.env.state_store(), store, {
628                        self
629                        .clone()
630                        .create_actor_with_log_store_dispatcher(
631                            actor,
632                            fragment_id,
633                            node,
634                            barrier_manager.clone(),
635                            new_output_request_rx,
636                            actor_config,
637                            sync,
638                            store,
639                        )
640                        .and_then(|actor| actor.run())
641                        .map(move |result| {
642                            if let Err(err) = result {
643                                tracing::error!(%actor_id, error = ?err.as_report(), "actor exit with error");
644                                barrier_manager.notify_failure(actor_id, err);
645                            }
646                        })
647                        .boxed()
648                    })
649                }
650                _ => {
651                    self
652                    .clone()
653                    .create_actor(
654                        actor,
655                        fragment_id,
656                        node,
657                        barrier_manager.clone(),
658                        new_output_request_rx,
659                        actor_config,
660                    )
661                    .and_then(|actor| actor.run())
662                    .map(move |result| {
663                        if let Err(err) = result {
664                        // TODO: check error type and panic if it's unexpected.
665                        // Intentionally use `?` on the report to also include the backtrace.
666                        tracing::error!(%actor_id, error = ?err.as_report(), "actor exit with error");
667                        barrier_manager.notify_failure(actor_id, err);
668                        }
669                    })
670                    .boxed()
671                }
672            };
673            let traced = match &self.await_tree_reg {
674                Some(m) => m
675                    .register(await_tree_key::Actor(actor_id), trace_span)
676                    .instrument(actor)
677                    .left_future(),
678                None => actor.right_future(),
679            };
680            let instrumented = match &monitor {
681                Some(m) => m.instrument(traced).left_future(),
682                None => traced.right_future(),
683            };
684            // If hummock tracing is not enabled, it directly returns wrapped future.
685            let may_track_hummock = instrumented.may_trace_hummock();
686
687            self.runtime.spawn(may_track_hummock)
688        };
689
690        let enable_count_metrics = self.streaming_metrics.level >= MetricLevel::Debug;
691        let monitor_handle = if let Some(monitor) = monitor {
692            let streaming_metrics = self.streaming_metrics.clone();
693            let actor_monitor_task = self.runtime.spawn(async move {
694                let metrics = streaming_metrics.new_actor_metrics(actor_id, fragment_id);
695                let mut interval = tokio::time::interval(Duration::from_secs(15));
696                for task_metrics in monitor.intervals() {
697                    interval.tick().await; // tick at the start since the first interval tick is at 0s.
698                    metrics
699                        .actor_poll_duration
700                        .inc_by(task_metrics.total_poll_duration.as_nanos() as u64);
701                    metrics
702                        .actor_idle_duration
703                        .inc_by(task_metrics.total_idle_duration.as_nanos() as u64);
704                    metrics
705                        .actor_scheduled_duration
706                        .inc_by(task_metrics.total_scheduled_duration.as_nanos() as u64);
707
708                    if enable_count_metrics {
709                        metrics.actor_poll_cnt.inc_by(task_metrics.total_poll_count);
710                        metrics
711                            .actor_idle_cnt
712                            .inc_by(task_metrics.total_idled_count);
713                        metrics
714                            .actor_scheduled_cnt
715                            .inc_by(task_metrics.total_scheduled_count);
716                    }
717                }
718            });
719            Some(actor_monitor_task)
720        } else {
721            None
722        };
723        (handle, monitor_handle)
724    }
725}
726
727/// Parameters to construct executors.
728/// - [`crate::from_proto::create_executor`]
729/// - [`StreamActorManager::create_nodes`]
730pub struct ExecutorParams {
731    pub env: StreamEnvironment,
732
733    /// Basic information about the executor.
734    pub info: ExecutorInfo,
735
736    /// Executor id, unique across all actors.
737    pub executor_id: ExecutorId,
738
739    /// Operator id, unique for each operator in fragment.
740    pub operator_id: GlobalOperatorId,
741
742    /// Information of the operator from plan node, like `StreamHashJoin { .. }`.
743    // TODO: use it for `identity`
744    pub op_info: String,
745
746    /// The input executor.
747    pub input: Vec<Executor>,
748
749    /// `FragmentId` of the actor
750    pub fragment_id: FragmentId,
751
752    /// Metrics
753    pub executor_stats: Arc<StreamingMetrics>,
754
755    /// Actor context
756    pub actor_context: ActorContextRef,
757
758    /// Vnodes owned by this executor. Represented in bitmap.
759    pub vnode_bitmap: Bitmap,
760
761    /// Used for reporting expression evaluation errors.
762    pub eval_error_report: ActorEvalErrorReport,
763
764    /// `watermark_epoch` field in `MemoryManager`
765    pub watermark_epoch: AtomicU64Ref,
766
767    pub local_barrier_manager: LocalBarrierManager,
768
769    /// The local streaming configuration for this specific actor. Same as `actor_context.config`.
770    ///
771    /// Compared to `stream_env.global_config`, this config can have some entries overridden by the user.
772    pub config: Arc<StreamingConfig>,
773}
774
775impl Debug for ExecutorParams {
776    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
777        f.debug_struct("ExecutorParams")
778            .field("info", &self.info)
779            .field("executor_id", &self.executor_id)
780            .field("operator_id", &self.operator_id)
781            .field("op_info", &self.op_info)
782            .field("input", &self.input.len())
783            .field("actor_id", &self.actor_context.id)
784            .finish_non_exhaustive()
785    }
786}