1use std::cell::RefCell;
13use std::collections::BTreeMap;
14use std::rc::Rc;
15use std::time::Duration;
16
17use columnar::{Columnar, Index};
18use columnation::{Columnation, CopyRegion};
19use mz_compute_client::logging::LoggingConfig;
20use mz_ore::cast::CastFrom;
21use mz_repr::{Datum, Diff, Timestamp};
22use mz_row_spine::RowRowBuilder;
23use mz_timely_util::columnar::batcher;
24use mz_timely_util::columnar::builder::ColumnBuilder;
25use mz_timely_util::columnar::{Col2ValBatcher, columnar_exchange};
26use mz_timely_util::replay::MzReplay;
27use timely::dataflow::Scope;
28use timely::dataflow::channels::pact::{ExchangeCore, Pipeline};
29use timely::dataflow::operators::Operator;
30use timely::dataflow::operators::generic::OutputBuilder;
31use timely::dataflow::operators::generic::builder_rc::OperatorBuilder;
32use timely::dataflow::operators::generic::operator::empty;
33use timely::logging::{
34 ChannelsEvent, MessagesEvent, OperatesEvent, ParkEvent, ScheduleEvent, ShutdownEvent,
35 TimelyEvent,
36};
37use tracing::error;
38
39use crate::extensions::arrange::MzArrangeCore;
40use crate::logging::compute::{ComputeEvent, DataflowShutdown};
41use crate::logging::{
42 EventQueue, LogVariant, OutputSessionColumnar, OutputSessionVec, PermutedRowPacker, TimelyLog,
43 Update,
44};
45use crate::logging::{LogCollection, SharedLoggingState, consolidate_and_pack};
46use crate::typedefs::RowRowSpine;
47use crate::typedefs::{ConsolidateBatcher, ConsolidateKeyValBatcher};
48
49pub(super) struct Return {
51 pub collections: BTreeMap<LogVariant, LogCollection>,
53}
54
55pub(super) fn construct(
62 scope: Scope<'_, Timestamp>,
63 config: &LoggingConfig,
64 event_queue: EventQueue<Vec<(Duration, TimelyEvent)>>,
65 shared_state: Rc<RefCell<SharedLoggingState>>,
66) -> Return {
67 scope.scoped("timely logging", move |scope| {
68 let enable_logging = config.enable_logging;
69 let (logs, token) = if enable_logging {
70 event_queue.links.mz_replay(
71 scope,
72 "timely logs",
73 config.interval,
74 event_queue.activator,
75 )
76 } else {
77 let token: Rc<dyn std::any::Any> = Rc::new(Box::new(()));
78 (empty(scope), token)
79 };
80
81 let mut demux = OperatorBuilder::new("Timely Logging Demux".to_string(), scope.clone());
84 let mut input = demux.new_input(logs, Pipeline);
85 let (operates_out, operates) = demux.new_output();
86 let mut operates_out = OutputBuilder::from(operates_out);
87 let (channels_out, channels) = demux.new_output();
88 let mut channels_out = OutputBuilder::from(channels_out);
89 let (addresses_out, addresses) = demux.new_output();
90 let mut addresses_out = OutputBuilder::from(addresses_out);
91 let (parks_out, parks) = demux.new_output();
92 let mut parks_out = OutputBuilder::from(parks_out);
93 let (messages_sent_out, messages_sent) = demux.new_output();
94 let mut messages_sent_out = OutputBuilder::from(messages_sent_out);
95 let (messages_received_out, messages_received) = demux.new_output();
96 let mut messages_received_out = OutputBuilder::from(messages_received_out);
97 let (schedules_duration_out, schedules_duration) = demux.new_output();
98 let mut schedules_duration_out = OutputBuilder::from(schedules_duration_out);
99 let (schedules_histogram_out, schedules_histogram) = demux.new_output();
100 let mut schedules_histogram_out = OutputBuilder::from(schedules_histogram_out);
101 let (batches_sent_out, batches_sent) = demux.new_output();
102 let mut batches_sent_out = OutputBuilder::from(batches_sent_out);
103 let (batches_received_out, batches_received) = demux.new_output();
104 let mut batches_received_out = OutputBuilder::from(batches_received_out);
105
106 let worker_id = scope.index();
107 let mut demux_state = DemuxState::default();
108 demux.build(|_capability| {
109 let peers = scope.peers();
110 let logging_interval_ms = std::cmp::max(1, config.interval.as_millis());
111 move |_frontiers| {
112 let mut operates = operates_out.activate();
113 let mut channels = channels_out.activate();
114 let mut addresses = addresses_out.activate();
115 let mut parks = parks_out.activate();
116 let mut messages_sent = messages_sent_out.activate();
117 let mut messages_received = messages_received_out.activate();
118 let mut batches_sent = batches_sent_out.activate();
119 let mut batches_received = batches_received_out.activate();
120 let mut schedules_duration = schedules_duration_out.activate();
121 let mut schedules_histogram = schedules_histogram_out.activate();
122
123 input.for_each(|cap, data| {
124 let mut output_buffers = DemuxOutput {
125 operates: operates.session_with_builder(&cap),
126 channels: channels.session_with_builder(&cap),
127 addresses: addresses.session_with_builder(&cap),
128 parks: parks.session_with_builder(&cap),
129 messages_sent: messages_sent.session_with_builder(&cap),
130 messages_received: messages_received.session_with_builder(&cap),
131 schedules_duration: schedules_duration.session_with_builder(&cap),
132 schedules_histogram: schedules_histogram.session_with_builder(&cap),
133 batches_sent: batches_sent.session_with_builder(&cap),
134 batches_received: batches_received.session_with_builder(&cap),
135 };
136
137 for (time, event) in data.drain(..) {
138 if let TimelyEvent::Messages(msg) = &event {
139 match msg.is_send {
140 true => assert_eq!(msg.source, worker_id),
141 false => assert_eq!(msg.target, worker_id),
142 }
143 }
144
145 DemuxHandler {
146 state: &mut demux_state,
147 shared_state: &mut shared_state.borrow_mut(),
148 output: &mut output_buffers,
149 logging_interval_ms,
150 peers,
151 time,
152 }
153 .handle(event);
154 }
155 });
156 }
157 });
158
159 let operates = consolidate_and_pack::<
164 ConsolidateKeyValBatcher<_, _, _, _>,
165 ColumnBuilder<_>,
166 _,
167 _,
168 _,
169 _,
170 >(
171 operates,
172 TimelyLog::Operates,
173 move |data, packer, session| {
174 for ((id, name), time, diff) in data.iter() {
175 let data = packer.pack_slice(&[
176 Datum::UInt64(u64::cast_from(*id)),
177 Datum::UInt64(u64::cast_from(worker_id)),
178 Datum::String(name),
179 ]);
180 session.give((data, time, diff));
181 }
182 },
183 );
184
185 let channels = channels.unary::<ColumnBuilder<_>, _, _, _>(
188 Pipeline,
189 "ToRow Channels",
190 |_cap, _info| {
191 let mut packer = PermutedRowPacker::new(TimelyLog::Channels);
192 move |input, output| {
193 input.for_each_stamp(|time, data| {
194 let mut session = output.session_with_builder(&time);
195 for d in data.flat_map(|c| c.borrow().into_index_iter()) {
196 let ((datum, ()), time, diff) = d;
197 let (source_node, source_port) = datum.source;
198 let (target_node, target_port) = datum.target;
199 let data = packer.pack_slice(&[
200 Datum::UInt64(u64::cast_from(datum.id)),
201 Datum::UInt64(u64::cast_from(worker_id)),
202 Datum::UInt64(u64::cast_from(source_node)),
203 Datum::UInt64(u64::cast_from(source_port)),
204 Datum::UInt64(u64::cast_from(target_node)),
205 Datum::UInt64(u64::cast_from(target_port)),
206 Datum::String(
207 std::str::from_utf8(datum.typ).expect("valid string"),
208 ),
209 ]);
210 session.give((data, time, diff));
211 }
212 });
213 }
214 },
215 );
216
217 type KVB<K, V, T, D> = ConsolidateKeyValBatcher<K, V, T, D>;
219 type KB<K, T, D> = ConsolidateBatcher<K, T, D>;
220
221 let addresses = consolidate_and_pack::<KVB<_, _, _, _>, ColumnBuilder<_>, _, _, _, _>(
222 addresses,
223 TimelyLog::Addresses,
224 move |data, packer, session| {
225 for ((id, address), time, diff) in data.iter() {
226 let data = packer.pack_by_index(|packer, index| match index {
227 0 => packer.push(Datum::UInt64(u64::cast_from(*id))),
228 1 => packer.push(Datum::UInt64(u64::cast_from(worker_id))),
229 2 => {
230 let list = address.iter().map(|i| Datum::UInt64(u64::cast_from(*i)));
231 packer.push_list(list)
232 }
233 _ => unreachable!("Addresses relation has three columns"),
234 });
235 session.give((data, time, diff));
236 }
237 },
238 );
239
240 let parks = consolidate_and_pack::<KB<_, _, _>, ColumnBuilder<_>, _, _, _, _>(
241 parks,
242 TimelyLog::Parks,
243 move |data, packer, session| {
244 for ((datum, ()), time, diff) in data.iter() {
245 let data = packer.pack_slice(&[
246 Datum::UInt64(u64::cast_from(worker_id)),
247 Datum::UInt64(datum.duration_pow),
248 datum
249 .requested_pow
250 .map(Datum::UInt64)
251 .unwrap_or(Datum::Null),
252 ]);
253 session.give((data, time, diff));
254 }
255 },
256 );
257
258 let batches_sent = consolidate_and_pack::<KB<_, _, _>, ColumnBuilder<_>, _, _, _, _>(
259 batches_sent,
260 TimelyLog::BatchesSent,
261 move |data, packer, session| {
262 for ((datum, ()), time, diff) in data.iter() {
263 let data = packer.pack_slice(&[
264 Datum::UInt64(u64::cast_from(datum.channel)),
265 Datum::UInt64(u64::cast_from(worker_id)),
266 Datum::UInt64(u64::cast_from(datum.worker)),
267 ]);
268 session.give((data, time, diff));
269 }
270 },
271 );
272
273 let batches_received = consolidate_and_pack::<KB<_, _, _>, ColumnBuilder<_>, _, _, _, _>(
274 batches_received,
275 TimelyLog::BatchesReceived,
276 move |data, packer, session| {
277 for ((datum, ()), time, diff) in data.iter() {
278 let data = packer.pack_slice(&[
279 Datum::UInt64(u64::cast_from(datum.channel)),
280 Datum::UInt64(u64::cast_from(datum.worker)),
281 Datum::UInt64(u64::cast_from(worker_id)),
282 ]);
283 session.give((data, time, diff));
284 }
285 },
286 );
287
288 let messages_sent = consolidate_and_pack::<KB<_, _, _>, ColumnBuilder<_>, _, _, _, _>(
289 messages_sent,
290 TimelyLog::MessagesSent,
291 move |data, packer, session| {
292 for ((datum, ()), time, diff) in data.iter() {
293 let data = packer.pack_slice(&[
294 Datum::UInt64(u64::cast_from(datum.channel)),
295 Datum::UInt64(u64::cast_from(worker_id)),
296 Datum::UInt64(u64::cast_from(datum.worker)),
297 ]);
298 session.give((data, time, diff));
299 }
300 },
301 );
302
303 let messages_received = consolidate_and_pack::<KB<_, _, _>, ColumnBuilder<_>, _, _, _, _>(
304 messages_received,
305 TimelyLog::MessagesReceived,
306 move |data, packer, session| {
307 for ((datum, ()), time, diff) in data.iter() {
308 let data = packer.pack_slice(&[
309 Datum::UInt64(u64::cast_from(datum.channel)),
310 Datum::UInt64(u64::cast_from(datum.worker)),
311 Datum::UInt64(u64::cast_from(worker_id)),
312 ]);
313 session.give((data, time, diff));
314 }
315 },
316 );
317
318 let elapsed = consolidate_and_pack::<KB<_, _, _>, ColumnBuilder<_>, _, _, _, _>(
319 schedules_duration,
320 TimelyLog::Elapsed,
321 move |data, packer, session| {
322 for ((operator, ()), time, diff) in data.iter() {
323 let data = packer.pack_slice(&[
324 Datum::UInt64(u64::cast_from(*operator)),
325 Datum::UInt64(u64::cast_from(worker_id)),
326 ]);
327 session.give((data, time, diff));
328 }
329 },
330 );
331
332 let histogram = consolidate_and_pack::<KB<_, _, _>, ColumnBuilder<_>, _, _, _, _>(
333 schedules_histogram,
334 TimelyLog::Histogram,
335 move |data, packer, session| {
336 for ((datum, ()), time, diff) in data.iter() {
337 let data = packer.pack_slice(&[
338 Datum::UInt64(u64::cast_from(datum.operator)),
339 Datum::UInt64(u64::cast_from(worker_id)),
340 Datum::UInt64(datum.duration_pow),
341 ]);
342 session.give((data, time, diff));
343 }
344 },
345 );
346
347 let logs = {
348 use TimelyLog::*;
349 [
350 (Operates, operates),
351 (Channels, channels),
352 (Elapsed, elapsed),
353 (Histogram, histogram),
354 (Addresses, addresses),
355 (Parks, parks),
356 (MessagesSent, messages_sent),
357 (MessagesReceived, messages_received),
358 (BatchesSent, batches_sent),
359 (BatchesReceived, batches_received),
360 ]
361 };
362
363 let mut collections = BTreeMap::new();
365 for (variant, collection) in logs {
366 let variant = LogVariant::Timely(variant);
367 if config.index_logs.contains_key(&variant) {
368 type Batcher<K, V, T, R, Chu, Se> = Col2ValBatcher<K, V, T, R, Chu, Se>;
370 type Builder<T, R> = RowRowBuilder<T, R>;
371 type Chu<K, V, T, R> =
372 batcher::Chunker<mz_timely_util::columnation::ColumnationStack<((K, V), T, R)>>;
373 type Arranger<K, V, T, R> = Batcher<K, V, T, R, Chu<K, V, T, R>, Builder<T, R>>;
374 let trace = collection
375 .mz_arrange_core::<_, Arranger<_, _, _, _>, RowRowSpine<_, _>>(
376 ExchangeCore::<ColumnBuilder<_>, _>::new_core(
377 columnar_exchange::<mz_repr::Row, mz_repr::Row, Timestamp, Diff>,
378 ),
379 &format!("Arrange {variant:?}"),
380 )
381 .trace;
382 let collection = LogCollection {
383 trace,
384 token: Rc::clone(&token),
385 };
386 collections.insert(variant, collection);
387 }
388 }
389
390 Return { collections }
391 })
392}
393
394#[derive(Default)]
396struct DemuxState {
397 operators: BTreeMap<usize, OperatesEvent>,
399 dataflow_channels: BTreeMap<usize, Vec<ChannelsEvent>>,
401 last_park: Option<Park>,
403 messages_sent: BTreeMap<usize, Box<[MessageCount]>>,
405 messages_received: BTreeMap<usize, Box<[MessageCount]>>,
407 schedule_starts: Vec<(usize, Duration)>,
409 schedules_data: BTreeMap<usize, Vec<(isize, Diff)>>,
412}
413
414struct Park {
415 time: Duration,
417 requested: Option<Duration>,
419}
420
421#[derive(Default, Copy, Clone, Debug)]
423struct MessageCount {
424 batches: i64,
426 records: Diff,
428}
429
430struct DemuxOutput<'a, 'b> {
436 operates: OutputSessionVec<'a, 'b, Update<(usize, String)>>,
437 channels: OutputSessionColumnar<'a, 'b, Update<(ChannelDatum, ())>>,
438 addresses: OutputSessionVec<'a, 'b, Update<(usize, Vec<usize>)>>,
439 parks: OutputSessionVec<'a, 'b, Update<(ParkDatum, ())>>,
440 batches_sent: OutputSessionVec<'a, 'b, Update<(MessageDatum, ())>>,
441 batches_received: OutputSessionVec<'a, 'b, Update<(MessageDatum, ())>>,
442 messages_sent: OutputSessionVec<'a, 'b, Update<(MessageDatum, ())>>,
443 messages_received: OutputSessionVec<'a, 'b, Update<(MessageDatum, ())>>,
444 schedules_duration: OutputSessionVec<'a, 'b, Update<(usize, ())>>,
445 schedules_histogram: OutputSessionVec<'a, 'b, Update<(ScheduleHistogramDatum, ())>>,
446}
447
448#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Columnar)]
449struct ChannelDatum {
450 id: usize,
451 source: (usize, usize),
452 target: (usize, usize),
453 typ: String,
454}
455
456#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
457struct ParkDatum {
458 duration_pow: u64,
459 requested_pow: Option<u64>,
460}
461
462impl Columnation for ParkDatum {
463 type InnerRegion = CopyRegion<Self>;
464}
465
466#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
467struct MessageDatum {
468 channel: usize,
469 worker: usize,
470}
471
472impl Columnation for MessageDatum {
473 type InnerRegion = CopyRegion<Self>;
474}
475
476#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
477struct ScheduleHistogramDatum {
478 operator: usize,
479 duration_pow: u64,
480}
481
482impl Columnation for ScheduleHistogramDatum {
483 type InnerRegion = CopyRegion<Self>;
484}
485
486struct DemuxHandler<'a, 'b, 'c> {
488 state: &'a mut DemuxState,
490 shared_state: &'a mut SharedLoggingState,
492 output: &'a mut DemuxOutput<'b, 'c>,
494 logging_interval_ms: u128,
496 peers: usize,
498 time: Duration,
500}
501
502impl DemuxHandler<'_, '_, '_> {
503 fn ts(&self) -> Timestamp {
506 let time_ms = self.time.as_millis();
507 let interval = self.logging_interval_ms;
508 let rounded = (time_ms / interval + 1) * interval;
509 rounded.try_into().expect("must fit")
510 }
511
512 fn handle(&mut self, event: TimelyEvent) {
514 use TimelyEvent::*;
515
516 match event {
517 Operates(e) => self.handle_operates(e),
518 Channels(e) => self.handle_channels(e),
519 Shutdown(e) => self.handle_shutdown(e),
520 Park(e) => self.handle_park(e),
521 Messages(e) => self.handle_messages(e),
522 Schedule(e) => self.handle_schedule(e),
523 _ => (),
524 }
525 }
526
527 fn handle_operates(&mut self, event: OperatesEvent) {
528 let ts = self.ts();
529 let datum = (event.id, event.name.clone());
530 self.output.operates.give((datum, ts, Diff::ONE));
531
532 let datum = (event.id, event.addr.clone());
533 self.output.addresses.give((datum, ts, Diff::ONE));
534
535 self.state.operators.insert(event.id, event);
536 }
537
538 fn handle_channels(&mut self, event: ChannelsEvent) {
539 let ts = self.ts();
540 let datum = ChannelDatumReference {
541 id: event.id,
542 source: event.source,
543 target: event.target,
544 typ: &event.typ,
545 };
546 self.output.channels.give(((datum, ()), ts, Diff::ONE));
547
548 let datum = (event.id, event.scope_addr.clone());
549 self.output.addresses.give((datum, ts, Diff::ONE));
550
551 let dataflow_index = event.scope_addr[0];
552 self.state
553 .dataflow_channels
554 .entry(dataflow_index)
555 .or_default()
556 .push(event);
557 }
558
559 fn handle_shutdown(&mut self, event: ShutdownEvent) {
560 let Some(operator) = self.state.operators.remove(&event.id) else {
566 error!(operator_id = ?event.id, "missing operator entry at time of shutdown");
567 return;
568 };
569
570 let ts = self.ts();
572 let datum = (operator.id, operator.name);
573 self.output.operates.give((datum, ts, Diff::MINUS_ONE));
574
575 if let Some(schedules) = self.state.schedules_data.remove(&event.id) {
577 for (bucket, (count, elapsed_ns)) in IntoIterator::into_iter(schedules)
578 .enumerate()
579 .filter(|(_, (count, _))| *count != 0)
580 {
581 self.output
582 .schedules_duration
583 .give(((event.id, ()), ts, Diff::from(-elapsed_ns)));
584
585 let datum = ScheduleHistogramDatum {
586 operator: event.id,
587 duration_pow: 1 << bucket,
588 };
589 let diff = Diff::cast_from(-count);
590 self.output
591 .schedules_histogram
592 .give(((datum, ()), ts, diff));
593 }
594 }
595
596 if operator.addr.len() == 1 {
597 let dataflow_index = operator.addr[0];
598 self.handle_dataflow_shutdown(dataflow_index);
599 }
600
601 let datum = (operator.id, operator.addr);
602 self.output.addresses.give((datum, ts, Diff::MINUS_ONE));
603 }
604
605 fn handle_dataflow_shutdown(&mut self, dataflow_index: usize) {
606 self.shared_state.compute_logger.as_ref().map(|logger| {
608 logger.log(&(ComputeEvent::DataflowShutdown(DataflowShutdown { dataflow_index })))
609 });
610
611 let Some(channels) = self.state.dataflow_channels.remove(&dataflow_index) else {
613 return;
614 };
615
616 let ts = self.ts();
617 for channel in channels {
618 let datum = ChannelDatumReference {
620 id: channel.id,
621 source: channel.source,
622 target: channel.target,
623 typ: &channel.typ,
624 };
625 self.output
626 .channels
627 .give(((datum, ()), ts, Diff::MINUS_ONE));
628
629 let datum = (channel.id, channel.scope_addr);
630 self.output.addresses.give((datum, ts, Diff::MINUS_ONE));
631
632 if let Some(sent) = self.state.messages_sent.remove(&channel.id) {
634 for (target_worker, count) in sent.iter().enumerate() {
635 let datum = MessageDatum {
636 channel: channel.id,
637 worker: target_worker,
638 };
639 self.output
640 .messages_sent
641 .give(((datum, ()), ts, Diff::from(-count.records)));
642 self.output
643 .batches_sent
644 .give(((datum, ()), ts, Diff::from(-count.batches)));
645 }
646 }
647 if let Some(received) = self.state.messages_received.remove(&channel.id) {
648 for (source_worker, count) in received.iter().enumerate() {
649 let datum = MessageDatum {
650 channel: channel.id,
651 worker: source_worker,
652 };
653 self.output.messages_received.give((
654 (datum, ()),
655 ts,
656 Diff::from(-count.records),
657 ));
658 self.output.batches_received.give((
659 (datum, ()),
660 ts,
661 Diff::from(-count.batches),
662 ));
663 }
664 }
665 }
666 }
667
668 fn handle_park(&mut self, event: ParkEvent) {
669 match event {
670 ParkEvent::Park(requested) => {
671 let park = Park {
672 time: self.time,
673 requested,
674 };
675 let existing = self.state.last_park.replace(park);
676 if existing.is_some() {
677 error!("park without a succeeding unpark");
678 }
679 }
680 ParkEvent::Unpark => {
681 let Some(park) = self.state.last_park.take() else {
682 error!("unpark without a preceding park");
683 return;
684 };
685
686 let duration_ns = self.time.saturating_sub(park.time).as_nanos();
687 let duration_pow =
688 u64::try_from(duration_ns.next_power_of_two()).expect("must fit");
689 let requested_pow = park
690 .requested
691 .map(|r| u64::try_from(r.as_nanos().next_power_of_two()).expect("must fit"));
692
693 let ts = self.ts();
694 let datum = ParkDatum {
695 duration_pow,
696 requested_pow,
697 };
698 self.output.parks.give(((datum, ()), ts, Diff::ONE));
699 }
700 }
701 }
702
703 fn handle_messages(&mut self, event: MessagesEvent) {
704 let ts = self.ts();
705 let count = Diff::from(event.record_count);
706
707 if event.is_send {
708 let datum = MessageDatum {
709 channel: event.channel,
710 worker: event.target,
711 };
712 self.output.messages_sent.give(((datum, ()), ts, count));
713 self.output.batches_sent.give(((datum, ()), ts, Diff::ONE));
714
715 let sent_counts = self
716 .state
717 .messages_sent
718 .entry(event.channel)
719 .or_insert_with(|| vec![Default::default(); self.peers].into_boxed_slice());
720 sent_counts[event.target].records += count;
721 sent_counts[event.target].batches += 1;
722 } else {
723 let datum = MessageDatum {
724 channel: event.channel,
725 worker: event.source,
726 };
727 self.output.messages_received.give(((datum, ()), ts, count));
728 self.output
729 .batches_received
730 .give(((datum, ()), ts, Diff::ONE));
731
732 let received_counts = self
733 .state
734 .messages_received
735 .entry(event.channel)
736 .or_insert_with(|| vec![Default::default(); self.peers].into_boxed_slice());
737 received_counts[event.source].records += count;
738 received_counts[event.source].batches += 1;
739 }
740 }
741
742 fn handle_schedule(&mut self, event: ScheduleEvent) {
743 match event.start_stop {
744 timely::logging::StartStop::Start => {
745 self.state.schedule_starts.push((event.id, self.time));
746 }
747 timely::logging::StartStop::Stop => {
748 let Some((old_id, start_time)) = self.state.schedule_starts.pop() else {
749 error!(operator_id = ?event.id, "schedule stop without preceding start");
750 return;
751 };
752
753 if old_id != event.id {
754 error!(start_id = ?old_id, stop_id = ?event.id, "schedule stop without preceding start");
755 return;
756 }
757
758 let elapsed_ns = self.time.saturating_sub(start_time).as_nanos();
759 let elapsed_i64 = i64::try_from(elapsed_ns).expect("must fit");
760 let elapsed_diff = Diff::from(elapsed_i64);
761 let elapsed_pow = u64::try_from(elapsed_ns.next_power_of_two()).expect("must fit");
762
763 let ts = self.ts();
764 let datum = event.id;
765 self.output
766 .schedules_duration
767 .give(((datum, ()), ts, elapsed_diff));
768
769 let datum = ScheduleHistogramDatum {
770 operator: event.id,
771 duration_pow: elapsed_pow,
772 };
773 self.output
774 .schedules_histogram
775 .give(((datum, ()), ts, Diff::ONE));
776
777 let index = usize::cast_from(elapsed_pow.trailing_zeros());
779 let data = self.state.schedules_data.entry(event.id).or_default();
780 grow_vec(data, index);
781 let (count, duration) = &mut data[index];
782 *count += 1;
783 *duration += elapsed_diff;
784 }
785 }
786 }
787}
788
789fn grow_vec<T>(vec: &mut Vec<T>, index: usize)
793where
794 T: Clone + Default,
795{
796 if vec.len() <= index {
797 vec.resize(index + 1, Default::default());
798 }
799}