Skip to content

Commit 7d4bca7

Browse files
committed
update
1 parent 723c4b2 commit 7d4bca7

12 files changed

Lines changed: 124 additions & 67 deletions

File tree

src/runtime/streaming/api/context.rs

Lines changed: 108 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,18 @@
1010
// See the License for the specific language governing permissions and
1111
// limitations under the License.
1212

13-
use crate::runtime::streaming::memory::MemoryPool;
14-
use crate::runtime::streaming::network::endpoint::PhysicalSender;
15-
use crate::runtime::streaming::protocol::event::StreamEvent;
16-
use crate::runtime::streaming::protocol::event::TrackedEvent;
13+
use std::sync::Arc;
14+
use std::time::{Duration, SystemTime};
1715

16+
use anyhow::{Context, Result};
1817
use arrow_array::RecordBatch;
19-
use std::sync::Arc;
20-
use std::time::Duration;
2118

22-
/// 与单个子任务绑定的运行时参数(可由 `TaskContext::new` 默认填充,后续可扩展为从 Job 配置注入)。
19+
use crate::runtime::streaming::memory::MemoryPool;
20+
use crate::runtime::streaming::network::endpoint::PhysicalSender;
21+
use crate::runtime::streaming::protocol::event::{StreamEvent, TrackedEvent};
22+
2323
#[derive(Debug, Clone)]
2424
pub struct TaskContextConfig {
25-
/// Source 在无数据(`SourceEvent::Idle`)时的退避休眠时长。
2625
pub source_idle_timeout: Duration,
2726
}
2827

@@ -34,110 +33,168 @@ impl Default for TaskContextConfig {
3433
}
3534
}
3635

36+
/// Task execution context.
37+
///
38+
/// Acts as the sole bridge between operators and engine infrastructure (network, memory,
39+
/// configuration) for a single subtask.
3740
pub struct TaskContext {
41+
/// Job identifier.
3842
pub job_id: String,
39-
pub vertex_id: u32,
40-
pub subtask_idx: u32,
43+
/// Logical pipeline (vertex) index within the job graph.
44+
pub pipeline_id: u32,
45+
/// This subtask's index within the pipeline's parallelism.
46+
pub subtask_index: u32,
47+
/// Number of parallel subtasks for this pipeline.
4148
pub parallelism: u32,
4249

43-
pub outboxes: Vec<PhysicalSender>,
50+
/// Precomputed display string for high-frequency logging without per-call allocation.
51+
task_name: String,
4452

53+
/// Downstream physical senders (outbound edges).
54+
downstream_senders: Vec<PhysicalSender>,
55+
56+
/// Global memory pool for back-pressure and accounting.
4557
memory_pool: Arc<MemoryPool>,
4658

47-
current_watermark: Option<std::time::SystemTime>,
59+
/// Latest aligned event-time watermark for this subtask.
60+
current_watermark: Option<SystemTime>,
4861

62+
/// Subtask-level tunables.
4963
config: TaskContextConfig,
5064
}
5165

5266
impl TaskContext {
5367
pub fn new(
5468
job_id: String,
55-
vertex_id: u32,
56-
subtask_idx: u32,
69+
pipeline_id: u32,
70+
subtask_index: u32,
5771
parallelism: u32,
58-
outboxes: Vec<PhysicalSender>,
72+
downstream_senders: Vec<PhysicalSender>,
5973
memory_pool: Arc<MemoryPool>,
6074
) -> Self {
75+
let task_name = format!(
76+
"Task-[{}]-Pipe[{}]-Sub[{}/{}]",
77+
job_id, pipeline_id, subtask_index, parallelism
78+
);
79+
6180
Self {
6281
job_id,
63-
vertex_id,
64-
subtask_idx,
82+
pipeline_id,
83+
subtask_index,
6584
parallelism,
66-
outboxes,
85+
task_name,
86+
downstream_senders,
6787
memory_pool,
6888
current_watermark: None,
6989
config: TaskContextConfig::default(),
7090
}
7191
}
7292

93+
#[inline]
7394
pub fn config(&self) -> &TaskContextConfig {
7495
&self.config
7596
}
7697

77-
// ========================================================================
78-
// ========================================================================
98+
#[inline]
99+
pub fn task_name(&self) -> &str {
100+
&self.task_name
101+
}
102+
103+
// -------------------------------------------------------------------------
104+
// Watermark
105+
// -------------------------------------------------------------------------
79106

80-
pub fn last_present_watermark(&self) -> Option<std::time::SystemTime> {
107+
#[inline]
108+
pub fn current_watermark(&self) -> Option<SystemTime> {
81109
self.current_watermark
82110
}
83111

84-
pub fn advance_watermark(&mut self, watermark: std::time::SystemTime) {
112+
pub fn advance_watermark(&mut self, watermark: SystemTime) {
85113
if let Some(current) = self.current_watermark {
86-
if watermark > current {
87-
self.current_watermark = Some(watermark);
88-
}
114+
self.current_watermark = Some(current.max(watermark));
89115
} else {
90116
self.current_watermark = Some(watermark);
91117
}
92118
}
93119

94-
// ========================================================================
95-
// ========================================================================
96-
97-
pub fn task_identity(&self) -> String {
98-
format!(
99-
"Job[{}], Vertex[{}], Subtask[{}/{}]",
100-
self.job_id, self.vertex_id, self.subtask_idx, self.parallelism
101-
)
102-
}
103-
104-
// ========================================================================
105-
// ========================================================================
120+
// -------------------------------------------------------------------------
121+
// Data emission
122+
// -------------------------------------------------------------------------
106123

107-
pub async fn collect(&self, batch: RecordBatch) -> anyhow::Result<()> {
108-
if self.outboxes.is_empty() {
124+
/// Fan-out a data batch to all downstreams (forward / broadcast).
125+
pub async fn collect(&self, batch: RecordBatch) -> Result<()> {
126+
if self.downstream_senders.is_empty() {
109127
return Ok(());
110128
}
111129

112130
let bytes_required = batch.get_array_memory_size();
113131
let ticket = self.memory_pool.request_memory(bytes_required).await;
114132
let tracked_event = TrackedEvent::new(StreamEvent::Data(batch), Some(ticket));
115133

116-
for outbox in &self.outboxes {
117-
outbox.send(tracked_event.clone()).await?;
118-
}
119-
Ok(())
134+
self.broadcast_event(tracked_event).await
120135
}
121136

122-
pub async fn collect_keyed(&self, key_hash: u64, batch: RecordBatch) -> anyhow::Result<()> {
123-
if self.outboxes.is_empty() {
137+
/// Route a batch to one downstream by hash partitioning (shuffle).
138+
pub async fn collect_keyed(&self, key_hash: u64, batch: RecordBatch) -> Result<()> {
139+
let num_downstreams = self.downstream_senders.len();
140+
if num_downstreams == 0 {
124141
return Ok(());
125142
}
126143

127144
let bytes_required = batch.get_array_memory_size();
128145
let ticket = self.memory_pool.request_memory(bytes_required).await;
129-
let tracked_event = TrackedEvent::new(StreamEvent::Data(batch), Some(ticket));
146+
let event = TrackedEvent::new(StreamEvent::Data(batch), Some(ticket));
147+
148+
let target_idx = (key_hash as usize) % num_downstreams;
149+
150+
self.downstream_senders[target_idx]
151+
.send(event)
152+
.await
153+
.with_context(|| {
154+
format!(
155+
"{} failed to route keyed data to downstream index {}",
156+
self.task_name, target_idx
157+
)
158+
})?;
130159

131-
let target_idx = (key_hash as usize) % self.outboxes.len();
132-
self.outboxes[target_idx].send(tracked_event).await?;
133160
Ok(())
134161
}
135162

136-
pub async fn broadcast(&self, event: StreamEvent) -> anyhow::Result<()> {
163+
/// Broadcast a control event (watermark, barrier, end-of-stream).
164+
pub async fn broadcast(&self, event: StreamEvent) -> Result<()> {
165+
if self.downstream_senders.is_empty() {
166+
return Ok(());
167+
}
137168
let tracked_event = TrackedEvent::control(event);
138-
for outbox in &self.outboxes {
139-
outbox.send(tracked_event.clone()).await?;
169+
self.broadcast_event(tracked_event).await
170+
}
171+
172+
// -------------------------------------------------------------------------
173+
// Internal dispatch
174+
// -------------------------------------------------------------------------
175+
176+
async fn broadcast_event(&self, event: TrackedEvent) -> Result<()> {
177+
let mut iter = self.downstream_senders.iter().enumerate().peekable();
178+
179+
while let Some((idx, sender)) = iter.next() {
180+
if iter.peek().is_some() {
181+
sender.send(event.clone()).await.with_context(|| {
182+
format!(
183+
"{} failed to broadcast event to downstream index {}",
184+
self.task_name, idx
185+
)
186+
})?;
187+
} else {
188+
sender.send(event).await.with_context(|| {
189+
format!(
190+
"{} failed to send final event to downstream index {}",
191+
self.task_name, idx
192+
)
193+
})?;
194+
break;
195+
}
140196
}
197+
141198
Ok(())
142199
}
143200
}

src/runtime/streaming/execution/pipeline.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ impl Pipeline {
6868
let span = info_span!(
6969
"pipeline_run",
7070
job_id = %self.ctx.job_id,
71-
vertex = self.ctx.vertex_id
71+
pipeline_id = self.ctx.pipeline_id
7272
);
7373

7474
async move {

src/runtime/streaming/execution/source_driver.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl SourceDriver {
5050
let span = info_span!(
5151
"source_run",
5252
job_id = %self.ctx.job_id,
53-
vertex = self.ctx.vertex_id,
53+
pipeline_id = self.ctx.pipeline_id,
5454
op = self.operator.name()
5555
);
5656

src/runtime/streaming/operators/joins/join_instance.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ impl InstantJoinOperator {
152152
let min_timestamp = min(time_column).ok_or_else(|| anyhow!("empty timestamp column"))?;
153153
let max_timestamp = max(time_column).ok_or_else(|| anyhow!("empty timestamp column"))?;
154154

155-
if let Some(watermark) = ctx.last_present_watermark()
155+
if let Some(watermark) = ctx.current_watermark()
156156
&& watermark > from_nanos(min_timestamp as u128)
157157
{
158158
warn!("Dropped late batch from {:?} before watermark", side);

src/runtime/streaming/operators/joins/join_with_expiration.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ impl JoinWithExpirationOperator {
132132
batch: RecordBatch,
133133
ctx: &mut TaskContext,
134134
) -> Result<Vec<StreamOutput>> {
135-
let current_time = ctx.last_present_watermark().unwrap_or_else(SystemTime::now);
135+
let current_time = ctx.current_watermark().unwrap_or_else(SystemTime::now);
136136

137137
self.left_state.expire(current_time);
138138
self.right_state.expire(current_time);

src/runtime/streaming/operators/sink/kafka/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ impl KafkaSinkOperator {
117117
config.set("enable.idempotence", "true");
118118
let transactional_id = format!(
119119
"fs-tx-{}-{}-{}-{}",
120-
ctx.job_id, self.topic, ctx.subtask_idx, idx
120+
ctx.job_id, self.topic, ctx.subtask_index, idx
121121
);
122122
config.set("transactional.id", &transactional_id);
123123

src/runtime/streaming/operators/source/kafka/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ impl KafkaSourceOperator {
189189
let group_id = match (&self.group_id, &self.group_id_prefix) {
190190
(Some(gid), _) => gid.clone(),
191191
(None, Some(prefix)) => {
192-
format!("{}-fs-{}-{}", prefix, ctx.job_id, ctx.subtask_idx)
192+
format!("{}-fs-{}-{}", prefix, ctx.job_id, ctx.subtask_index)
193193
}
194-
(None, None) => format!("fs-{}-{}-consumer", ctx.job_id, ctx.subtask_idx),
194+
(None, None) => format!("fs-{}-{}-consumer", ctx.job_id, ctx.subtask_index),
195195
};
196196

197197
for (key, value) in &self.client_configs {
@@ -223,7 +223,7 @@ impl KafkaSourceOperator {
223223
let pmax = ctx.parallelism.max(1) as i32;
224224

225225
for p in partitions {
226-
if p.id().rem_euclid(pmax) == ctx.subtask_idx as i32 {
226+
if p.id().rem_euclid(pmax) == ctx.subtask_index as i32 {
227227
let offset = state_map
228228
.get(&p.id())
229229
.map(|s| Offset::Offset(s.offset))
@@ -241,7 +241,7 @@ impl KafkaSourceOperator {
241241
if our_partitions.is_empty() {
242242
warn!(
243243
"[Task {}] Subscribed to no partitions. Entering idle mode.",
244-
ctx.subtask_idx
244+
ctx.subtask_index
245245
);
246246
self.is_empty_assignment = true;
247247
} else {
@@ -366,7 +366,7 @@ impl SourceOperator for KafkaSourceOperator {
366366
_barrier: CheckpointBarrier,
367367
ctx: &mut TaskContext,
368368
) -> Result<()> {
369-
debug!("Source [{}] executing checkpoint", ctx.subtask_idx);
369+
debug!("Source [{}] executing checkpoint", ctx.subtask_index);
370370

371371
let mut topic_partitions = TopicPartitionList::new();
372372
for (&partition, &offset) in &self.current_offsets {

src/runtime/streaming/operators/watermark/watermark_generator.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ impl Operator for WatermarkGeneratorOperator {
141141
if self.is_idle || time_since_last_emit > self.interval {
142142
debug!(
143143
"[{}] emitting expression watermark {}",
144-
ctx.subtask_idx,
144+
ctx.subtask_index,
145145
to_millis(self.state.max_watermark)
146146
);
147147

@@ -174,7 +174,7 @@ impl Operator for WatermarkGeneratorOperator {
174174
if !self.is_idle && elapsed > idle_timeout {
175175
info!(
176176
"task [{}] entering Idle after {:?}",
177-
ctx.subtask_idx, idle_timeout
177+
ctx.subtask_index, idle_timeout
178178
);
179179
self.is_idle = true;
180180
return Ok(vec![StreamOutput::Watermark(Watermark::Idle)]);

src/runtime/streaming/operators/windows/session_aggregating_window.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ impl Operator for SessionWindowOperator {
676676
batch: RecordBatch,
677677
ctx: &mut TaskContext,
678678
) -> Result<Vec<StreamOutput>> {
679-
let watermark_time = ctx.last_present_watermark();
679+
let watermark_time = ctx.current_watermark();
680680

681681
let filtered_batch = self.filter_batch_by_time(batch, watermark_time)?;
682682
if filtered_batch.num_rows() == 0 {

src/runtime/streaming/operators/windows/sliding_aggregating_window.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ impl Operator for SlidingWindowOperator {
339339
.ok_or_else(|| anyhow!("binning function must produce TimestampNanosecond"))?;
340340
let partition_ranges = partition(std::slice::from_ref(&sorted_bins))?.ranges();
341341

342-
let watermark = ctx.last_present_watermark();
342+
let watermark = ctx.current_watermark();
343343

344344
for range in partition_ranges {
345345
let bin_start = from_nanos(typed_bin.value(range.start) as u128);

0 commit comments

Comments
 (0)