1use std::cell::RefCell;
13use std::collections::{BTreeMap, BTreeSet};
14use std::fmt::{Display, Write};
15use std::rc::Rc;
16use std::time::{Duration, Instant};
17
18use chrono::DateTime;
19use columnar::{Columnar, Index, Ref};
20use differential_dataflow::VecCollection;
21use differential_dataflow::collection::AsCollection;
22use differential_dataflow::trace::{BatchReader, Cursor, Navigable};
23use mz_compute_types::plan::LirId;
24use mz_ore::cast::CastFrom;
25use mz_repr::adt::timestamp::CheckedTimestamp;
26use mz_repr::{Datum, Diff, GlobalId, Row, RowRef, Timestamp};
27use mz_timely_util::columnar::batcher;
28use mz_timely_util::columnar::builder::ColumnBuilder;
29use mz_timely_util::columnar::{Col2ValBatcher, Column, columnar_exchange};
30use mz_timely_util::replay::MzReplay;
31use timely::dataflow::channels::pact::{ExchangeCore, Pipeline};
32use timely::dataflow::operators::Operator;
33use timely::dataflow::operators::generic::OutputBuilder;
34use timely::dataflow::operators::generic::builder_rc::OperatorBuilder;
35use timely::dataflow::operators::generic::operator::empty;
36use timely::dataflow::{Scope, StreamVec};
37use timely::scheduling::activate::{Activations, Activator};
38use tracing::error;
39use uuid::Uuid;
40
41use crate::extensions::arrange::MzArrangeCore;
42use crate::logging::{
43 ComputeLog, EventQueue, LogCollection, LogVariant, OutputSessionColumnar, PermutedRowPacker,
44 SharedLoggingState, Update,
45};
46use crate::typedefs::RowRowSpine;
47use mz_row_spine::RowRowBuilder;
48
49pub type Logger = timely::logging_core::Logger<ComputeEventBuilder>;
51pub type ComputeEventBuilder = ColumnBuilder<(Duration, ComputeEvent)>;
52
53#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
55pub struct Export {
56 pub export_id: GlobalId,
58 pub dataflow_index: usize,
60}
61
62#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
64pub struct ExportDropped {
65 pub export_id: GlobalId,
67}
68
69#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
71pub struct PeekEvent {
72 pub id: GlobalId,
74 pub time: Timestamp,
76 pub uuid: uuid::Bytes,
78 pub peek_type: PeekType,
81 pub installed: bool,
83}
84
85#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
87pub struct Frontier {
88 pub export_id: GlobalId,
89 pub time: Timestamp,
90 pub diff: i8,
91}
92
93#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
95pub struct ImportFrontier {
96 pub import_id: GlobalId,
97 pub export_id: GlobalId,
98 pub time: Timestamp,
99 pub diff: i8,
100}
101
102#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
104pub struct ArrangementHeapSize {
105 pub operator_id: usize,
107 pub delta_size: isize,
109}
110
111#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
113pub struct ArrangementHeapCapacity {
114 pub operator_id: usize,
116 pub delta_capacity: isize,
118}
119
120#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
122pub struct ArrangementHeapAllocations {
123 pub operator_id: usize,
125 pub delta_allocations: isize,
127}
128
129#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
131pub struct ArrangementHeapSizeOperator {
132 pub operator_id: usize,
134 pub address: Vec<usize>,
136}
137
138#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
140pub struct ArrangementHeapSizeOperatorDrop {
141 pub operator_id: usize,
143}
144
145#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
147pub struct DataflowShutdown {
148 pub dataflow_index: usize,
150}
151
152#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
154pub struct ErrorCount {
155 pub export_id: GlobalId,
157 pub diff: Diff,
159}
160
161#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
163pub struct HydrationStart {
164 pub export_id: GlobalId,
166}
167
168#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
170pub struct Hydration {
171 pub export_id: GlobalId,
173}
174
175#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
177pub struct OperatorHydration {
178 pub export_id: GlobalId,
180 pub lir_id: LirId,
182 pub hydrated: bool,
184}
185
186#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
188pub struct LirMapping {
189 pub global_id: GlobalId,
196 pub mapping: Vec<(LirId, LirMetadata)>,
199}
200
201#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
203pub struct DataflowGlobal {
204 pub dataflow_index: usize,
206 pub global_id: GlobalId,
208}
209
210#[derive(Debug, Clone, PartialOrd, PartialEq, Columnar)]
212pub enum ComputeEvent {
213 Export(Export),
215 ExportDropped(ExportDropped),
217 Peek(PeekEvent),
219 Frontier(Frontier),
221 ImportFrontier(ImportFrontier),
223 ArrangementHeapSize(ArrangementHeapSize),
225 ArrangementHeapCapacity(ArrangementHeapCapacity),
227 ArrangementHeapAllocations(ArrangementHeapAllocations),
229 ArrangementHeapSizeOperator(ArrangementHeapSizeOperator),
231 ArrangementHeapSizeOperatorDrop(ArrangementHeapSizeOperatorDrop),
233 DataflowShutdown(DataflowShutdown),
235 ErrorCount(ErrorCount),
237 HydrationStart(HydrationStart),
239 Hydration(Hydration),
241 OperatorHydration(OperatorHydration),
243 LirMapping(LirMapping),
247 DataflowGlobal(DataflowGlobal),
248}
249
250#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Columnar)]
252pub enum PeekType {
253 Index,
255 Persist,
257}
258
259impl PeekType {
260 fn name(self) -> &'static str {
262 match self {
263 PeekType::Index => "index",
264 PeekType::Persist => "persist",
265 }
266 }
267}
268
269#[derive(Clone, Debug, PartialEq, PartialOrd, Columnar)]
271pub struct LirMetadata {
272 operator: String,
274 parent_lir_id: Option<LirId>,
276 nesting: u8,
278 operator_span: (usize, usize),
281}
282
283impl LirMetadata {
284 pub fn new(
286 operator: String,
287 parent_lir_id: Option<LirId>,
288 nesting: u8,
289 operator_span: (usize, usize),
290 ) -> Self {
291 Self {
292 operator,
293 parent_lir_id,
294 nesting,
295 operator_span,
296 }
297 }
298}
299
300pub(super) struct Return {
302 pub collections: BTreeMap<LogVariant, LogCollection>,
304}
305
306pub(super) fn construct<'scope>(
316 scope: Scope<'scope, Timestamp>,
317 activations: Rc<RefCell<Activations>>,
318 config: &mz_compute_client::logging::LoggingConfig,
319 event_queue: EventQueue<Column<(Duration, ComputeEvent)>>,
320 shared_state: Rc<RefCell<SharedLoggingState>>,
321) -> Return {
322 let logging_interval_ms = std::cmp::max(1, config.interval.as_millis());
323
324 scope.scoped("compute logging", move |scope| {
325 let enable_logging = config.enable_logging;
326 let (logs, token) = if enable_logging {
327 event_queue.links.mz_replay(
328 scope,
329 "compute logs",
330 config.interval,
331 event_queue.activator,
332 )
333 } else {
334 let token: Rc<dyn std::any::Any> = Rc::new(Box::new(()));
335 (empty(scope), token)
336 };
337
338 let mut demux = OperatorBuilder::new("Compute Logging Demux".to_string(), scope.clone());
341 let mut input = demux.new_input(logs, Pipeline);
342 let (export_out, export) = demux.new_output();
343 let mut export_out = OutputBuilder::from(export_out);
344 let (frontier_out, frontier) = demux.new_output();
345 let mut frontier_out = OutputBuilder::from(frontier_out);
346 let (import_frontier_out, import_frontier) = demux.new_output();
347 let mut import_frontier_out = OutputBuilder::from(import_frontier_out);
348 let (peek_out, peek) = demux.new_output();
349 let mut peek_out = OutputBuilder::from(peek_out);
350 let (peek_duration_out, peek_duration) = demux.new_output();
351 let mut peek_duration_out = OutputBuilder::from(peek_duration_out);
352 let (arrangement_heap_size_out, arrangement_heap_size) = demux.new_output();
353 let mut arrangement_heap_size_out = OutputBuilder::from(arrangement_heap_size_out);
354 let (arrangement_heap_capacity_out, arrangement_heap_capacity) = demux.new_output();
355 let mut arrangement_heap_capacity_out = OutputBuilder::from(arrangement_heap_capacity_out);
356 let (arrangement_heap_allocations_out, arrangement_heap_allocations) = demux.new_output();
357 let mut arrangement_heap_allocations_out =
358 OutputBuilder::from(arrangement_heap_allocations_out);
359 let (error_count_out, error_count) = demux.new_output();
360 let mut error_count_out = OutputBuilder::from(error_count_out);
361 let (hydration_time_out, hydration_time) = demux.new_output();
362 let mut hydration_time_out = OutputBuilder::from(hydration_time_out);
363 let (operator_hydration_status_out, operator_hydration_status) = demux.new_output();
364 let mut operator_hydration_status_out = OutputBuilder::from(operator_hydration_status_out);
365 let (lir_mapping_out, lir_mapping) = demux.new_output();
366 let mut lir_mapping_out = OutputBuilder::from(lir_mapping_out);
367 let (dataflow_global_ids_out, dataflow_global_ids) = demux.new_output();
368 let mut dataflow_global_ids_out = OutputBuilder::from(dataflow_global_ids_out);
369
370 let mut demux_state = DemuxState::new(activations, scope.index());
371 demux.build(move |_capability| {
372 move |_frontiers| {
373 let mut export = export_out.activate();
374 let mut frontier = frontier_out.activate();
375 let mut import_frontier = import_frontier_out.activate();
376 let mut peek = peek_out.activate();
377 let mut peek_duration = peek_duration_out.activate();
378 let mut arrangement_heap_size = arrangement_heap_size_out.activate();
379 let mut arrangement_heap_capacity = arrangement_heap_capacity_out.activate();
380 let mut arrangement_heap_allocations = arrangement_heap_allocations_out.activate();
381 let mut error_count = error_count_out.activate();
382 let mut hydration_time = hydration_time_out.activate();
383 let mut operator_hydration_status = operator_hydration_status_out.activate();
384 let mut lir_mapping = lir_mapping_out.activate();
385 let mut dataflow_global_ids = dataflow_global_ids_out.activate();
386
387 input.for_each(|cap, data| {
388 let mut output_sessions = DemuxOutput {
389 export: export.session_with_builder(&cap),
390 frontier: frontier.session_with_builder(&cap),
391 import_frontier: import_frontier.session_with_builder(&cap),
392 peek: peek.session_with_builder(&cap),
393 peek_duration: peek_duration.session_with_builder(&cap),
394 arrangement_heap_allocations: arrangement_heap_allocations
395 .session_with_builder(&cap),
396 arrangement_heap_capacity: arrangement_heap_capacity
397 .session_with_builder(&cap),
398 arrangement_heap_size: arrangement_heap_size.session_with_builder(&cap),
399 error_count: error_count.session_with_builder(&cap),
400 hydration_time: hydration_time.session_with_builder(&cap),
401 operator_hydration_status: operator_hydration_status
402 .session_with_builder(&cap),
403 lir_mapping: lir_mapping.session_with_builder(&cap),
404 dataflow_global_ids: dataflow_global_ids.session_with_builder(&cap),
405 };
406
407 let shared_state = &mut shared_state.borrow_mut();
408 for (time, event) in data.borrow().into_index_iter() {
409 DemuxHandler {
410 state: &mut demux_state,
411 shared_state,
412 output: &mut output_sessions,
413 logging_interval_ms,
414 time,
415 }
416 .handle(event);
417 }
418 });
419 }
420 });
421
422 use ComputeLog::*;
423 let logs = [
424 (ArrangementHeapAllocations, arrangement_heap_allocations),
425 (ArrangementHeapCapacity, arrangement_heap_capacity),
426 (ArrangementHeapSize, arrangement_heap_size),
427 (DataflowCurrent, export),
428 (DataflowGlobal, dataflow_global_ids),
429 (ErrorCount, error_count),
430 (FrontierCurrent, frontier),
431 (HydrationTime, hydration_time),
432 (ImportFrontierCurrent, import_frontier),
433 (LirMapping, lir_mapping),
434 (OperatorHydrationStatus, operator_hydration_status),
435 (PeekCurrent, peek),
436 (PeekDuration, peek_duration),
437 ];
438
439 let mut collections = BTreeMap::new();
441 for (variant, stream) in logs {
442 let variant = LogVariant::Compute(variant);
443 if config.index_logs.contains_key(&variant) {
444 let exchange = ExchangeCore::<ColumnBuilder<_>, _>::new_core(
445 columnar_exchange::<Row, Row, Timestamp, Diff>,
446 );
447 let trace = stream
448 .mz_arrange_core::<
449 _,
450 batcher::Chunker<_>,
451 Col2ValBatcher<_, _, _, _>,
452 RowRowBuilder<_, _>,
453 RowRowSpine<_, _>,
454 >(exchange, &format!("Arrange {variant:?}"))
455 .trace;
456 let collection = LogCollection {
457 trace,
458 token: Rc::clone(&token),
459 };
460 collections.insert(variant, collection);
461 }
462 }
463
464 Return { collections }
465 })
466}
467
468fn make_string_datum<V>(value: V, scratch: &mut String) -> Datum<'_>
474where
475 V: Display,
476{
477 scratch.clear();
478 write!(scratch, "{}", value).expect("writing to a `String` can't fail");
479 Datum::String(scratch)
480}
481
482fn epoch_offset_datum(offset: Duration) -> Datum<'static> {
487 let secs = i64::try_from(offset.as_secs()).expect("must fit");
488 let datetime = DateTime::from_timestamp(secs, offset.subsec_nanos())
489 .expect("epoch offset is in range for `DateTime`");
490 let timestamp = CheckedTimestamp::try_from(datetime)
491 .expect("epoch offset is a valid timestamp")
492 .round_to_precision(None)
493 .expect("epoch offset is far from the maximum timestamp");
494 Datum::TimestampTz(timestamp)
495}
496
497struct DemuxState {
499 activations: Rc<RefCell<Activations>>,
501 worker_id: usize,
503 scratch_string_a: String,
505 scratch_string_b: String,
507 exports: BTreeMap<GlobalId, ExportState>,
509 peek_stash: BTreeMap<Uuid, Duration>,
511 arrangement_size: BTreeMap<usize, ArrangementSizeState>,
513 lir_mapping: BTreeMap<GlobalId, BTreeMap<LirId, LirMetadata>>,
515 dataflow_global_ids: BTreeMap<usize, BTreeSet<GlobalId>>,
517 arrangement_heap_allocations_packer: PermutedRowPacker,
519 arrangement_heap_capacity_packer: PermutedRowPacker,
521 arrangement_heap_size_packer: PermutedRowPacker,
523 dataflow_global_packer: PermutedRowPacker,
525 error_count_packer: PermutedRowPacker,
527 export_packer: PermutedRowPacker,
529 frontier_packer: PermutedRowPacker,
531 import_frontier_packer: PermutedRowPacker,
533 lir_mapping_packer: PermutedRowPacker,
535 operator_hydration_status_packer: PermutedRowPacker,
537 peek_duration_packer: PermutedRowPacker,
539 peek_packer: PermutedRowPacker,
541 hydration_time_packer: PermutedRowPacker,
543}
544
545impl DemuxState {
546 fn new(activations: Rc<RefCell<Activations>>, worker_id: usize) -> Self {
547 Self {
548 activations,
549 worker_id,
550 scratch_string_a: String::new(),
551 scratch_string_b: String::new(),
552 exports: Default::default(),
553 peek_stash: Default::default(),
554 arrangement_size: Default::default(),
555 lir_mapping: Default::default(),
556 dataflow_global_ids: Default::default(),
557 arrangement_heap_allocations_packer: PermutedRowPacker::new(
558 ComputeLog::ArrangementHeapAllocations,
559 ),
560 arrangement_heap_capacity_packer: PermutedRowPacker::new(
561 ComputeLog::ArrangementHeapCapacity,
562 ),
563 arrangement_heap_size_packer: PermutedRowPacker::new(ComputeLog::ArrangementHeapSize),
564 dataflow_global_packer: PermutedRowPacker::new(ComputeLog::DataflowGlobal),
565 error_count_packer: PermutedRowPacker::new(ComputeLog::ErrorCount),
566 export_packer: PermutedRowPacker::new(ComputeLog::DataflowCurrent),
567 frontier_packer: PermutedRowPacker::new(ComputeLog::FrontierCurrent),
568 hydration_time_packer: PermutedRowPacker::new(ComputeLog::HydrationTime),
569 import_frontier_packer: PermutedRowPacker::new(ComputeLog::ImportFrontierCurrent),
570 lir_mapping_packer: PermutedRowPacker::new(ComputeLog::LirMapping),
571 operator_hydration_status_packer: PermutedRowPacker::new(
572 ComputeLog::OperatorHydrationStatus,
573 ),
574 peek_duration_packer: PermutedRowPacker::new(ComputeLog::PeekDuration),
575 peek_packer: PermutedRowPacker::new(ComputeLog::PeekCurrent),
576 }
577 }
578
579 fn pack_arrangement_heap_allocations_update(
581 &mut self,
582 operator_id: usize,
583 ) -> (&RowRef, &RowRef) {
584 self.arrangement_heap_allocations_packer.pack_slice(&[
585 Datum::UInt64(operator_id.try_into().expect("operator_id too big")),
586 Datum::UInt64(u64::cast_from(self.worker_id)),
587 ])
588 }
589
590 fn pack_arrangement_heap_capacity_update(&mut self, operator_id: usize) -> (&RowRef, &RowRef) {
592 self.arrangement_heap_capacity_packer.pack_slice(&[
593 Datum::UInt64(operator_id.try_into().expect("operator_id too big")),
594 Datum::UInt64(u64::cast_from(self.worker_id)),
595 ])
596 }
597
598 fn pack_arrangement_heap_size_update(&mut self, operator_id: usize) -> (&RowRef, &RowRef) {
600 self.arrangement_heap_size_packer.pack_slice(&[
601 Datum::UInt64(operator_id.try_into().expect("operator_id too big")),
602 Datum::UInt64(u64::cast_from(self.worker_id)),
603 ])
604 }
605
606 fn pack_dataflow_global_update(
608 &mut self,
609 dataflow_index: usize,
610 global_id: GlobalId,
611 ) -> (&RowRef, &RowRef) {
612 self.dataflow_global_packer.pack_slice(&[
613 Datum::UInt64(u64::cast_from(dataflow_index)),
614 Datum::UInt64(u64::cast_from(self.worker_id)),
615 make_string_datum(global_id, &mut self.scratch_string_a),
616 ])
617 }
618
619 fn pack_error_count_update(&mut self, export_id: GlobalId, count: Diff) -> (&RowRef, &RowRef) {
621 self.error_count_packer.pack_slice(&[
625 make_string_datum(export_id, &mut self.scratch_string_a),
626 Datum::UInt64(u64::cast_from(self.worker_id)),
627 Datum::Int64(count.into_inner()),
628 ])
629 }
630
631 fn pack_export_update(
633 &mut self,
634 export_id: GlobalId,
635 dataflow_index: usize,
636 ) -> (&RowRef, &RowRef) {
637 self.export_packer.pack_slice(&[
638 make_string_datum(export_id, &mut self.scratch_string_a),
639 Datum::UInt64(u64::cast_from(self.worker_id)),
640 Datum::UInt64(u64::cast_from(dataflow_index)),
641 ])
642 }
643
644 fn pack_hydration_time_update(
647 &mut self,
648 export_id: GlobalId,
649 time_ns: Option<u64>,
650 timestamps: &HydrationTimestamps,
651 ) -> (&RowRef, &RowRef) {
652 self.hydration_time_packer.pack_slice(&[
653 make_string_datum(export_id, &mut self.scratch_string_a),
654 Datum::UInt64(u64::cast_from(self.worker_id)),
655 Datum::from(time_ns),
656 epoch_offset_datum(timestamps.installed_at),
657 timestamps
658 .started_at
659 .map_or(Datum::Null, epoch_offset_datum),
660 timestamps
661 .hydrated_at
662 .map_or(Datum::Null, epoch_offset_datum),
663 ])
664 }
665
666 fn pack_import_frontier_update(
668 &mut self,
669 export_id: GlobalId,
670 import_id: GlobalId,
671 time: Timestamp,
672 ) -> (&RowRef, &RowRef) {
673 self.import_frontier_packer.pack_slice(&[
674 make_string_datum(export_id, &mut self.scratch_string_a),
675 make_string_datum(import_id, &mut self.scratch_string_b),
676 Datum::UInt64(u64::cast_from(self.worker_id)),
677 Datum::MzTimestamp(time),
678 ])
679 }
680
681 fn pack_lir_mapping_update(
683 &mut self,
684 global_id: GlobalId,
685 lir_id: LirId,
686 operator: String,
687 parent_lir_id: Option<LirId>,
688 nesting: u8,
689 operator_span: (usize, usize),
690 ) -> (&RowRef, &RowRef) {
691 self.lir_mapping_packer.pack_slice(&[
692 make_string_datum(global_id, &mut self.scratch_string_a),
693 Datum::UInt64(lir_id.into()),
694 Datum::UInt64(u64::cast_from(self.worker_id)),
695 make_string_datum(operator, &mut self.scratch_string_b),
696 parent_lir_id.map_or(Datum::Null, |lir_id| Datum::UInt64(lir_id.into())),
697 Datum::UInt16(u16::cast_from(nesting)),
698 Datum::UInt64(u64::cast_from(operator_span.0)),
699 Datum::UInt64(u64::cast_from(operator_span.1)),
700 ])
701 }
702
703 fn pack_operator_hydration_status_update(
706 &mut self,
707 export_id: GlobalId,
708 lir_id: LirId,
709 hydrated: bool,
710 ) -> (&RowRef, &RowRef) {
711 self.operator_hydration_status_packer.pack_slice(&[
712 make_string_datum(export_id, &mut self.scratch_string_a),
713 Datum::UInt64(lir_id.into()),
714 Datum::UInt64(u64::cast_from(self.worker_id)),
715 Datum::from(hydrated),
716 ])
717 }
718
719 fn pack_peek_duration_update(
721 &mut self,
722 peek_type: PeekType,
723 bucket: u128,
724 ) -> (&RowRef, &RowRef) {
725 self.peek_duration_packer.pack_slice(&[
726 Datum::UInt64(u64::cast_from(self.worker_id)),
727 Datum::String(peek_type.name()),
728 Datum::UInt64(bucket.try_into().expect("bucket too big")),
729 ])
730 }
731
732 fn pack_peek_update(
734 &mut self,
735 id: GlobalId,
736 time: Timestamp,
737 uuid: Uuid,
738 peek_type: PeekType,
739 ) -> (&RowRef, &RowRef) {
740 self.peek_packer.pack_slice(&[
741 Datum::Uuid(uuid),
742 Datum::UInt64(u64::cast_from(self.worker_id)),
743 make_string_datum(id, &mut self.scratch_string_a),
744 Datum::String(peek_type.name()),
745 Datum::MzTimestamp(time),
746 ])
747 }
748
749 fn pack_frontier_update(&mut self, export_id: GlobalId, time: Timestamp) -> (&RowRef, &RowRef) {
751 self.frontier_packer.pack_slice(&[
752 make_string_datum(export_id, &mut self.scratch_string_a),
753 Datum::UInt64(u64::cast_from(self.worker_id)),
754 Datum::MzTimestamp(time),
755 ])
756 }
757}
758
759#[derive(Debug, Clone, Copy)]
769struct HydrationTimestamps {
770 installed_at: Duration,
772 started_at: Option<Duration>,
777 hydrated_at: Option<Duration>,
779}
780
781struct ExportState {
783 dataflow_index: usize,
785 error_count: Diff,
790 created_at: Instant,
792 hydration_time_ns: Option<u64>,
794 hydration_timestamps: HydrationTimestamps,
800 operator_hydration: BTreeMap<LirId, bool>,
802}
803
804impl ExportState {
805 fn new(dataflow_index: usize, installed_at: Duration) -> Self {
806 Self {
807 dataflow_index,
808 error_count: Diff::ZERO,
809 created_at: Instant::now(),
810 hydration_time_ns: None,
811 hydration_timestamps: HydrationTimestamps {
812 installed_at,
813 started_at: None,
814 hydrated_at: None,
815 },
816 operator_hydration: BTreeMap::new(),
817 }
818 }
819}
820
821#[derive(Default, Debug)]
823struct ArrangementSizeState {
824 size: isize,
825 capacity: isize,
826 count: isize,
827}
828
829struct DemuxOutput<'a, 'b> {
831 export: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
832 frontier: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
833 import_frontier: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
834 peek: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
835 peek_duration: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
836 arrangement_heap_allocations: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
837 arrangement_heap_capacity: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
838 arrangement_heap_size: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
839 hydration_time: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
840 operator_hydration_status: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
841 error_count: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
842 lir_mapping: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
843 dataflow_global_ids: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
844}
845
846struct DemuxHandler<'a, 'b, 'c> {
848 state: &'a mut DemuxState,
850 shared_state: &'a mut SharedLoggingState,
852 output: &'a mut DemuxOutput<'b, 'c>,
854 logging_interval_ms: u128,
856 time: Duration,
858}
859
860impl DemuxHandler<'_, '_, '_> {
861 fn ts(&self) -> Timestamp {
864 let time_ms = self.time.as_millis();
865 let interval = self.logging_interval_ms;
866 let rounded = (time_ms / interval + 1) * interval;
867 rounded.try_into().expect("must fit")
868 }
869
870 fn handle(&mut self, event: Ref<'_, ComputeEvent>) {
872 use ComputeEventReference::*;
873 match event {
874 Export(export) => self.handle_export(export),
875 ExportDropped(export_dropped) => self.handle_export_dropped(export_dropped),
876 Peek(peek) if peek.installed => self.handle_peek_install(peek),
877 Peek(peek) => self.handle_peek_retire(peek),
878 Frontier(frontier) => self.handle_frontier(frontier),
879 ImportFrontier(import_frontier) => self.handle_import_frontier(import_frontier),
880 ArrangementHeapSize(inner) => self.handle_arrangement_heap_size(inner),
881 ArrangementHeapCapacity(inner) => self.handle_arrangement_heap_capacity(inner),
882 ArrangementHeapAllocations(inner) => self.handle_arrangement_heap_allocations(inner),
883 ArrangementHeapSizeOperator(inner) => self.handle_arrangement_heap_size_operator(inner),
884 ArrangementHeapSizeOperatorDrop(inner) => {
885 self.handle_arrangement_heap_size_operator_dropped(inner)
886 }
887 DataflowShutdown(shutdown) => self.handle_dataflow_shutdown(shutdown),
888 ErrorCount(error_count) => self.handle_error_count(error_count),
889 HydrationStart(hydration) => self.handle_hydration_start(hydration),
890 Hydration(hydration) => self.handle_hydration(hydration),
891 OperatorHydration(hydration) => self.handle_operator_hydration(hydration),
892 LirMapping(mapping) => self.handle_lir_mapping(mapping),
893 DataflowGlobal(global) => self.handle_dataflow_global(global),
894 }
895 }
896
897 fn handle_export(
898 &mut self,
899 ExportReference {
900 export_id,
901 dataflow_index,
902 }: Ref<'_, Export>,
903 ) {
904 let export_id = Columnar::into_owned(export_id);
905 let ts = self.ts();
906 let datum = self.state.pack_export_update(export_id, dataflow_index);
907 self.output.export.give((datum, ts, Diff::ONE));
908
909 let installed_at = self.time;
912
913 let existing = self
914 .state
915 .exports
916 .insert(export_id, ExportState::new(dataflow_index, installed_at));
917 if existing.is_some() {
918 error!(%export_id, "export already registered");
919 }
920
921 let timestamps = HydrationTimestamps {
923 installed_at,
924 started_at: None,
925 hydrated_at: None,
926 };
927 let datum = self
928 .state
929 .pack_hydration_time_update(export_id, None, ×tamps);
930 self.output.hydration_time.give((datum, ts, Diff::ONE));
931 }
932
933 fn handle_export_dropped(
934 &mut self,
935 ExportDroppedReference { export_id }: Ref<'_, ExportDropped>,
936 ) {
937 let export_id = Columnar::into_owned(export_id);
938 let Some(export) = self.state.exports.remove(&export_id) else {
939 error!(%export_id, "missing exports entry at time of export drop");
940 return;
941 };
942
943 let ts = self.ts();
944 let dataflow_index = export.dataflow_index;
945
946 let datum = self.state.pack_export_update(export_id, dataflow_index);
947 self.output.export.give((datum, ts, Diff::MINUS_ONE));
948
949 if export.error_count != Diff::ZERO {
951 let datum = self
952 .state
953 .pack_error_count_update(export_id, export.error_count);
954 self.output.error_count.give((datum, ts, Diff::MINUS_ONE));
955 }
956
957 let datum = self.state.pack_hydration_time_update(
959 export_id,
960 export.hydration_time_ns,
961 &export.hydration_timestamps,
962 );
963 self.output
964 .hydration_time
965 .give((datum, ts, Diff::MINUS_ONE));
966
967 for (lir_id, hydrated) in export.operator_hydration {
969 let datum = self
970 .state
971 .pack_operator_hydration_status_update(export_id, lir_id, hydrated);
972 self.output
973 .operator_hydration_status
974 .give((datum, ts, Diff::MINUS_ONE));
975 }
976 }
977
978 fn handle_dataflow_shutdown(
979 &mut self,
980 DataflowShutdownReference { dataflow_index }: Ref<'_, DataflowShutdown>,
981 ) {
982 let ts = self.ts();
983
984 if let Some(global_ids) = self.state.dataflow_global_ids.remove(&dataflow_index) {
986 for global_id in global_ids {
987 let datum = self
989 .state
990 .pack_dataflow_global_update(dataflow_index, global_id);
991 self.output
992 .dataflow_global_ids
993 .give((datum, ts, Diff::MINUS_ONE));
994
995 if let Some(mappings) = self.state.lir_mapping.remove(&global_id) {
997 for (
998 lir_id,
999 LirMetadata {
1000 operator,
1001 parent_lir_id,
1002 nesting,
1003 operator_span,
1004 },
1005 ) in mappings
1006 {
1007 let datum = self.state.pack_lir_mapping_update(
1008 global_id,
1009 lir_id,
1010 operator,
1011 parent_lir_id,
1012 nesting,
1013 operator_span,
1014 );
1015 self.output.lir_mapping.give((datum, ts, Diff::MINUS_ONE));
1016 }
1017 }
1018 }
1019 }
1020 }
1021
1022 fn handle_error_count(&mut self, ErrorCountReference { export_id, diff }: Ref<'_, ErrorCount>) {
1023 let ts = self.ts();
1024 let export_id = Columnar::into_owned(export_id);
1025
1026 let Some(export) = self.state.exports.get_mut(&export_id) else {
1027 return;
1030 };
1031
1032 let old_count = export.error_count;
1033 let new_count = old_count + diff;
1034 export.error_count = new_count;
1035
1036 if old_count != Diff::ZERO {
1037 let datum = self.state.pack_error_count_update(export_id, old_count);
1038 self.output.error_count.give((datum, ts, Diff::MINUS_ONE));
1039 }
1040 if new_count != Diff::ZERO {
1041 let datum = self.state.pack_error_count_update(export_id, new_count);
1042 self.output.error_count.give((datum, ts, Diff::ONE));
1043 }
1044 }
1045
1046 fn handle_hydration_start(
1047 &mut self,
1048 HydrationStartReference { export_id }: Ref<'_, HydrationStart>,
1049 ) {
1050 let ts = self.ts();
1051 let started_at = self.time;
1053 let export_id = Columnar::into_owned(export_id);
1054
1055 let Some(export) = self.state.exports.get_mut(&export_id) else {
1056 error!(%export_id, "hydration start event for unknown export");
1057 return;
1058 };
1059 if export.hydration_timestamps.started_at.is_some() {
1060 return;
1064 }
1065
1066 let old_timestamps = export.hydration_timestamps;
1067 export.hydration_timestamps.started_at = Some(started_at);
1068 let new_timestamps = export.hydration_timestamps;
1069 let time_ns = export.hydration_time_ns;
1070
1071 let retraction = self
1072 .state
1073 .pack_hydration_time_update(export_id, time_ns, &old_timestamps);
1074 self.output
1075 .hydration_time
1076 .give((retraction, ts, Diff::MINUS_ONE));
1077 let insertion = self
1078 .state
1079 .pack_hydration_time_update(export_id, time_ns, &new_timestamps);
1080 self.output.hydration_time.give((insertion, ts, Diff::ONE));
1081 }
1082
1083 fn handle_hydration(&mut self, HydrationReference { export_id }: Ref<'_, Hydration>) {
1084 let ts = self.ts();
1085 let hydrated_at = self.time;
1087 let export_id = Columnar::into_owned(export_id);
1088
1089 let Some(export) = self.state.exports.get_mut(&export_id) else {
1090 error!(%export_id, "hydration event for unknown export");
1091 return;
1092 };
1093 if export.hydration_time_ns.is_some() {
1094 return;
1097 }
1098
1099 let duration = export.created_at.elapsed();
1100 let nanos = u64::try_from(duration.as_nanos()).expect("must fit");
1101 export.hydration_time_ns = Some(nanos);
1102
1103 let old_timestamps = export.hydration_timestamps;
1104 export.hydration_timestamps.hydrated_at = Some(hydrated_at);
1105 if export.hydration_timestamps.started_at.is_none() {
1115 export.hydration_timestamps.started_at = Some(export.hydration_timestamps.installed_at);
1116 }
1117 let new_timestamps = export.hydration_timestamps;
1118
1119 let retraction = self
1120 .state
1121 .pack_hydration_time_update(export_id, None, &old_timestamps);
1122 self.output
1123 .hydration_time
1124 .give((retraction, ts, Diff::MINUS_ONE));
1125 let insertion =
1126 self.state
1127 .pack_hydration_time_update(export_id, Some(nanos), &new_timestamps);
1128 self.output.hydration_time.give((insertion, ts, Diff::ONE));
1129 }
1130
1131 fn handle_operator_hydration(
1132 &mut self,
1133 OperatorHydrationReference {
1134 export_id,
1135 lir_id,
1136 hydrated,
1137 }: Ref<'_, OperatorHydration>,
1138 ) {
1139 let ts = self.ts();
1140 let export_id = Columnar::into_owned(export_id);
1141 let lir_id = Columnar::into_owned(lir_id);
1142 let hydrated = Columnar::into_owned(hydrated);
1143
1144 let Some(export) = self.state.exports.get_mut(&export_id) else {
1145 return;
1148 };
1149
1150 let old_status = export.operator_hydration.get(&lir_id).copied();
1151 export.operator_hydration.insert(lir_id, hydrated);
1152
1153 if let Some(hydrated) = old_status {
1154 let retraction = self
1155 .state
1156 .pack_operator_hydration_status_update(export_id, lir_id, hydrated);
1157 self.output
1158 .operator_hydration_status
1159 .give((retraction, ts, Diff::MINUS_ONE));
1160 }
1161
1162 let insertion = self
1163 .state
1164 .pack_operator_hydration_status_update(export_id, lir_id, hydrated);
1165 self.output
1166 .operator_hydration_status
1167 .give((insertion, ts, Diff::ONE));
1168 }
1169
1170 fn handle_peek_install(
1171 &mut self,
1172 PeekEventReference {
1173 id,
1174 time,
1175 uuid,
1176 peek_type,
1177 installed: _,
1178 }: Ref<'_, PeekEvent>,
1179 ) {
1180 let id = Columnar::into_owned(id);
1181 let uuid = Uuid::from_bytes(uuid::Bytes::into_owned(uuid));
1182 let ts = self.ts();
1183 let datum = self.state.pack_peek_update(id, time, uuid, peek_type);
1184 self.output.peek.give((datum, ts, Diff::ONE));
1185
1186 let existing = self.state.peek_stash.insert(uuid, self.time);
1187 if existing.is_some() {
1188 error!(%uuid, "peek already registered");
1189 }
1190 }
1191
1192 fn handle_peek_retire(
1193 &mut self,
1194 PeekEventReference {
1195 id,
1196 time,
1197 uuid,
1198 peek_type,
1199 installed: _,
1200 }: Ref<'_, PeekEvent>,
1201 ) {
1202 let id = Columnar::into_owned(id);
1203 let uuid = Uuid::from_bytes(uuid::Bytes::into_owned(uuid));
1204 let ts = self.ts();
1205 let datum = self.state.pack_peek_update(id, time, uuid, peek_type);
1206 self.output.peek.give((datum, ts, Diff::MINUS_ONE));
1207
1208 if let Some(start) = self.state.peek_stash.remove(&uuid) {
1209 let elapsed_ns = self.time.saturating_sub(start).as_nanos();
1210 let bucket = elapsed_ns.next_power_of_two();
1211 let datum = self.state.pack_peek_duration_update(peek_type, bucket);
1212 self.output.peek_duration.give((datum, ts, Diff::ONE));
1213 } else {
1214 error!(%uuid, "peek not yet registered");
1215 }
1216 }
1217
1218 fn handle_frontier(
1219 &mut self,
1220 FrontierReference {
1221 export_id,
1222 time,
1223 diff,
1224 }: Ref<'_, Frontier>,
1225 ) {
1226 let export_id = Columnar::into_owned(export_id);
1227 let diff = Diff::from(*diff);
1228 let ts = self.ts();
1229 let time = Columnar::into_owned(time);
1230 let datum = self.state.pack_frontier_update(export_id, time);
1231 self.output.frontier.give((datum, ts, diff));
1232 }
1233
1234 fn handle_import_frontier(
1235 &mut self,
1236 ImportFrontierReference {
1237 import_id,
1238 export_id,
1239 time,
1240 diff,
1241 }: Ref<'_, ImportFrontier>,
1242 ) {
1243 let import_id = Columnar::into_owned(import_id);
1244 let export_id = Columnar::into_owned(export_id);
1245 let diff = Diff::from(*diff);
1246 let ts = self.ts();
1247 let time = Columnar::into_owned(time);
1248 let datum = self
1249 .state
1250 .pack_import_frontier_update(export_id, import_id, time);
1251 self.output.import_frontier.give((datum, ts, diff));
1252 }
1253
1254 fn handle_arrangement_heap_size(
1256 &mut self,
1257 ArrangementHeapSizeReference {
1258 operator_id,
1259 delta_size,
1260 }: Ref<'_, ArrangementHeapSize>,
1261 ) {
1262 let ts = self.ts();
1263 let Some(state) = self.state.arrangement_size.get_mut(&operator_id) else {
1264 return;
1265 };
1266
1267 state.size += delta_size;
1268
1269 let datum = self.state.pack_arrangement_heap_size_update(operator_id);
1270 let diff = Diff::cast_from(delta_size);
1271 self.output.arrangement_heap_size.give((datum, ts, diff));
1272 }
1273
1274 fn handle_arrangement_heap_capacity(
1276 &mut self,
1277 ArrangementHeapCapacityReference {
1278 operator_id,
1279 delta_capacity,
1280 }: Ref<'_, ArrangementHeapCapacity>,
1281 ) {
1282 let ts = self.ts();
1283 let Some(state) = self.state.arrangement_size.get_mut(&operator_id) else {
1284 return;
1285 };
1286
1287 state.capacity += delta_capacity;
1288
1289 let datum = self
1290 .state
1291 .pack_arrangement_heap_capacity_update(operator_id);
1292 let diff = Diff::cast_from(delta_capacity);
1293 self.output
1294 .arrangement_heap_capacity
1295 .give((datum, ts, diff));
1296 }
1297
1298 fn handle_arrangement_heap_allocations(
1300 &mut self,
1301 ArrangementHeapAllocationsReference {
1302 operator_id,
1303 delta_allocations,
1304 }: Ref<'_, ArrangementHeapAllocations>,
1305 ) {
1306 let ts = self.ts();
1307 let Some(state) = self.state.arrangement_size.get_mut(&operator_id) else {
1308 return;
1309 };
1310
1311 state.count += delta_allocations;
1312
1313 let datum = self
1314 .state
1315 .pack_arrangement_heap_allocations_update(operator_id);
1316 let diff = Diff::cast_from(delta_allocations);
1317 self.output
1318 .arrangement_heap_allocations
1319 .give((datum, ts, diff));
1320 }
1321
1322 fn handle_arrangement_heap_size_operator(
1324 &mut self,
1325 ArrangementHeapSizeOperatorReference {
1326 operator_id,
1327 address,
1328 }: Ref<'_, ArrangementHeapSizeOperator>,
1329 ) {
1330 let activator = Activator::new(
1331 address.into_iter().collect(),
1332 Rc::clone(&self.state.activations),
1333 );
1334 let existing = self
1335 .state
1336 .arrangement_size
1337 .insert(operator_id, Default::default());
1338 if existing.is_some() {
1339 error!(%operator_id, "arrangement size operator already registered");
1340 }
1341 let existing = self
1342 .shared_state
1343 .arrangement_size_activators
1344 .insert(operator_id, activator);
1345 if existing.is_some() {
1346 error!(%operator_id, "arrangement size activator already registered");
1347 }
1348 }
1349
1350 fn handle_arrangement_heap_size_operator_dropped(
1352 &mut self,
1353 event: Ref<'_, ArrangementHeapSizeOperatorDrop>,
1354 ) {
1355 let operator_id = event.operator_id;
1356 if let Some(state) = self.state.arrangement_size.remove(&operator_id) {
1357 let ts = self.ts();
1358 let allocations = self
1359 .state
1360 .pack_arrangement_heap_allocations_update(operator_id);
1361 let diff = -Diff::cast_from(state.count);
1362 self.output
1363 .arrangement_heap_allocations
1364 .give((allocations, ts, diff));
1365
1366 let capacity = self
1367 .state
1368 .pack_arrangement_heap_capacity_update(operator_id);
1369 let diff = -Diff::cast_from(state.capacity);
1370 self.output
1371 .arrangement_heap_capacity
1372 .give((capacity, ts, diff));
1373
1374 let size = self.state.pack_arrangement_heap_size_update(operator_id);
1375 let diff = -Diff::cast_from(state.size);
1376 self.output.arrangement_heap_size.give((size, ts, diff));
1377 }
1378 self.shared_state
1379 .arrangement_size_activators
1380 .remove(&operator_id);
1381 }
1382
1383 fn handle_lir_mapping(
1385 &mut self,
1386 LirMappingReference { global_id, mapping }: Ref<'_, LirMapping>,
1387 ) {
1388 let global_id = Columnar::into_owned(global_id);
1389 let mappings = || mapping.into_iter().map(Columnar::into_owned);
1391 self.state
1392 .lir_mapping
1393 .entry(global_id)
1394 .and_modify(|existing_mapping| existing_mapping.extend(mappings()))
1395 .or_insert_with(|| mappings().collect());
1396
1397 let ts = self.ts();
1399 for (lir_id, meta) in mapping.into_iter() {
1400 let datum = self.state.pack_lir_mapping_update(
1401 global_id,
1402 Columnar::into_owned(lir_id),
1403 Columnar::into_owned(meta.operator),
1404 Columnar::into_owned(meta.parent_lir_id),
1405 Columnar::into_owned(meta.nesting),
1406 Columnar::into_owned(meta.operator_span),
1407 );
1408 self.output.lir_mapping.give((datum, ts, Diff::ONE));
1409 }
1410 }
1411
1412 fn handle_dataflow_global(
1413 &mut self,
1414 DataflowGlobalReference {
1415 dataflow_index,
1416 global_id,
1417 }: Ref<'_, DataflowGlobal>,
1418 ) {
1419 let global_id = Columnar::into_owned(global_id);
1420 self.state
1421 .dataflow_global_ids
1422 .entry(dataflow_index)
1423 .and_modify(|globals| {
1424 if !globals.insert(global_id) {
1426 error!(%dataflow_index, %global_id, "dataflow mapping already knew about this GlobalId");
1427 }
1428 })
1429 .or_insert_with(|| BTreeSet::from([global_id]));
1430
1431 let ts = self.ts();
1432 let datum = self
1433 .state
1434 .pack_dataflow_global_update(dataflow_index, global_id);
1435 self.output.dataflow_global_ids.give((datum, ts, Diff::ONE));
1436 }
1437}
1438
1439pub struct CollectionLogging {
1444 export_id: GlobalId,
1445 logger: Logger,
1446
1447 logged_frontier: Option<Timestamp>,
1448 logged_import_frontiers: BTreeMap<GlobalId, Timestamp>,
1449}
1450
1451impl CollectionLogging {
1452 pub fn new(
1454 export_id: GlobalId,
1455 logger: Logger,
1456 dataflow_index: usize,
1457 import_ids: impl Iterator<Item = GlobalId>,
1458 ) -> Self {
1459 logger.log(&ComputeEvent::Export(Export {
1460 export_id,
1461 dataflow_index,
1462 }));
1463
1464 let mut self_ = Self {
1465 export_id,
1466 logger,
1467 logged_frontier: None,
1468 logged_import_frontiers: Default::default(),
1469 };
1470
1471 let initial_frontier = Some(Timestamp::MIN);
1473 self_.set_frontier(initial_frontier);
1474 import_ids.for_each(|id| self_.set_import_frontier(id, initial_frontier));
1475
1476 self_
1477 }
1478
1479 pub fn set_frontier(&mut self, new_time: Option<Timestamp>) {
1481 let old_time = self.logged_frontier;
1482 self.logged_frontier = new_time;
1483
1484 if old_time != new_time {
1485 let export_id = self.export_id;
1486 let retraction = old_time.map(|time| {
1487 ComputeEvent::Frontier(Frontier {
1488 export_id,
1489 time,
1490 diff: -1,
1491 })
1492 });
1493 let insertion = new_time.map(|time| {
1494 ComputeEvent::Frontier(Frontier {
1495 export_id,
1496 time,
1497 diff: 1,
1498 })
1499 });
1500 let events = retraction.as_ref().into_iter().chain(insertion.as_ref());
1501 self.logger.log_many(events);
1502 }
1503 }
1504
1505 pub fn set_import_frontier(&mut self, import_id: GlobalId, new_time: Option<Timestamp>) {
1508 let old_time = self.logged_import_frontiers.remove(&import_id);
1509 if let Some(time) = new_time {
1510 self.logged_import_frontiers.insert(import_id, time);
1511 }
1512
1513 if old_time != new_time {
1514 let export_id = self.export_id;
1515 let retraction = old_time.map(|time| {
1516 ComputeEvent::ImportFrontier(ImportFrontier {
1517 import_id,
1518 export_id,
1519 time,
1520 diff: -1,
1521 })
1522 });
1523 let insertion = new_time.map(|time| {
1524 ComputeEvent::ImportFrontier(ImportFrontier {
1525 import_id,
1526 export_id,
1527 time,
1528 diff: 1,
1529 })
1530 });
1531 let events = retraction.as_ref().into_iter().chain(insertion.as_ref());
1532 self.logger.log_many(events);
1533 }
1534 }
1535
1536 pub fn set_hydration_start(&self) {
1541 self.logger
1542 .log(&ComputeEvent::HydrationStart(HydrationStart {
1543 export_id: self.export_id,
1544 }));
1545 }
1546
1547 pub fn set_hydrated(&self) {
1549 self.logger.log(&ComputeEvent::Hydration(Hydration {
1550 export_id: self.export_id,
1551 }));
1552 }
1553
1554 pub fn export_id(&self) -> GlobalId {
1556 self.export_id
1557 }
1558}
1559
1560impl Drop for CollectionLogging {
1561 fn drop(&mut self) {
1562 self.set_frontier(None);
1564
1565 let import_ids: Vec<_> = self.logged_import_frontiers.keys().copied().collect();
1566 for import_id in import_ids {
1567 self.set_import_frontier(import_id, None);
1568 }
1569
1570 self.logger.log(&ComputeEvent::ExportDropped(ExportDropped {
1571 export_id: self.export_id,
1572 }));
1573 }
1574}
1575
1576pub(crate) trait LogDataflowErrors {
1579 fn log_dataflow_errors(self, logger: Logger, export_id: GlobalId) -> Self;
1580}
1581
1582impl<'scope, T, D> LogDataflowErrors for VecCollection<'scope, T, D, Diff>
1583where
1584 T: timely::progress::Timestamp,
1585 D: Clone + 'static,
1586{
1587 fn log_dataflow_errors(self, logger: Logger, export_id: GlobalId) -> Self {
1588 self.inner
1589 .unary(Pipeline, "LogDataflowErrorsCollection", |_cap, _info| {
1590 move |input, output| {
1591 input.for_each(|cap, data| {
1592 let diff = data.iter().map(|(_d, _t, r)| *r).sum::<Diff>();
1593 logger.log(&ComputeEvent::ErrorCount(ErrorCount { export_id, diff }));
1594
1595 output.session(&cap).give_container(data);
1596 });
1597 }
1598 })
1599 .as_collection()
1600 }
1601}
1602
1603impl<'scope, T, B> LogDataflowErrors for StreamVec<'scope, T, B>
1604where
1605 T: timely::progress::Timestamp,
1606 B: BatchReader + Navigable + Clone + 'static,
1607 for<'a> B::Cursor: Cursor<DiffGat<'a> = &'a Diff>,
1608{
1609 fn log_dataflow_errors(self, logger: Logger, export_id: GlobalId) -> Self {
1610 self.unary(Pipeline, "LogDataflowErrorsStream", |_cap, _info| {
1611 move |input, output| {
1612 input.for_each(|cap, data| {
1613 let diff = data.iter().map(sum_batch_diffs).sum::<Diff>();
1614 logger.log(&ComputeEvent::ErrorCount(ErrorCount { export_id, diff }));
1615
1616 output.session(&cap).give_container(data);
1617 });
1618 }
1619 })
1620 }
1621}
1622
1623fn sum_batch_diffs<B>(batch: &B) -> Diff
1630where
1631 B: BatchReader + Navigable,
1632 for<'a> B::Cursor: Cursor<DiffGat<'a> = &'a Diff>,
1633{
1634 let mut sum = Diff::ZERO;
1635 let mut cursor = batch.cursor();
1636
1637 while cursor.key_valid(batch) {
1638 while cursor.val_valid(batch) {
1639 cursor.map_times(batch, |_t, r| sum += r);
1640 cursor.step_val(batch);
1641 }
1642 cursor.step_key(batch);
1643 }
1644
1645 sum
1646}
1647
1648#[cfg(test)]
1649mod tests {
1650 use super::*;
1651
1652 #[mz_ore::test]
1653 fn test_compute_event_size() {
1654 assert_eq!(56, std::mem::size_of::<ComputeEvent>())
1656 }
1657}