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::{Cursor, Navigable, Span};
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_row_spine::RowRowBuilder;
28use mz_timely_util::columnar::batcher;
29use mz_timely_util::columnar::builder::ColumnBuilder;
30use mz_timely_util::columnar::{Col2ValBatcher, Column, columnar_exchange};
31use mz_timely_util::replay::MzReplay;
32use timely::dataflow::channels::pact::{ExchangeCore, Pipeline};
33use timely::dataflow::operators::Operator;
34use timely::dataflow::operators::generic::OutputBuilder;
35use timely::dataflow::operators::generic::builder_rc::OperatorBuilder;
36use timely::dataflow::operators::generic::operator::empty;
37use timely::dataflow::{Scope, StreamVec};
38use timely::scheduling::activate::{Activations, Activator};
39use tracing::error;
40use uuid::Uuid;
41
42use crate::extensions::arrange::MzArrangeCore;
43use crate::logging::{
44 ComputeLog, EventQueue, LogCollection, LogVariant, OutputSessionColumnar, PermutedRowPacker,
45 SharedLoggingState, Update,
46};
47use crate::typedefs::RowRowSpine;
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 Col2ValBatcher<_, _, _, _, batcher::Chunker<_>, RowRowBuilder<_, _>>,
451 RowRowSpine<_, _>,
452 >(exchange, &format!("Arrange {variant:?}"))
453 .trace;
454 let collection = LogCollection {
455 trace,
456 token: Rc::clone(&token),
457 };
458 collections.insert(variant, collection);
459 }
460 }
461
462 Return { collections }
463 })
464}
465
466fn make_string_datum<V>(value: V, scratch: &mut String) -> Datum<'_>
472where
473 V: Display,
474{
475 scratch.clear();
476 write!(scratch, "{}", value).expect("writing to a `String` can't fail");
477 Datum::String(scratch)
478}
479
480fn epoch_offset_datum(offset: Duration) -> Datum<'static> {
485 let secs = i64::try_from(offset.as_secs()).expect("must fit");
486 let datetime = DateTime::from_timestamp(secs, offset.subsec_nanos())
487 .expect("epoch offset is in range for `DateTime`");
488 let timestamp = CheckedTimestamp::try_from(datetime)
489 .expect("epoch offset is a valid timestamp")
490 .round_to_precision(None)
491 .expect("epoch offset is far from the maximum timestamp");
492 Datum::TimestampTz(timestamp)
493}
494
495struct DemuxState {
497 activations: Rc<RefCell<Activations>>,
499 worker_id: usize,
501 scratch_string_a: String,
503 scratch_string_b: String,
505 exports: BTreeMap<GlobalId, ExportState>,
507 peek_stash: BTreeMap<Uuid, Duration>,
509 arrangement_size: BTreeMap<usize, ArrangementSizeState>,
511 lir_mapping: BTreeMap<GlobalId, BTreeMap<LirId, LirMetadata>>,
513 dataflow_global_ids: BTreeMap<usize, BTreeSet<GlobalId>>,
515 arrangement_heap_allocations_packer: PermutedRowPacker,
517 arrangement_heap_capacity_packer: PermutedRowPacker,
519 arrangement_heap_size_packer: PermutedRowPacker,
521 dataflow_global_packer: PermutedRowPacker,
523 error_count_packer: PermutedRowPacker,
525 export_packer: PermutedRowPacker,
527 frontier_packer: PermutedRowPacker,
529 import_frontier_packer: PermutedRowPacker,
531 lir_mapping_packer: PermutedRowPacker,
533 operator_hydration_status_packer: PermutedRowPacker,
535 peek_duration_packer: PermutedRowPacker,
537 peek_packer: PermutedRowPacker,
539 hydration_time_packer: PermutedRowPacker,
541}
542
543impl DemuxState {
544 fn new(activations: Rc<RefCell<Activations>>, worker_id: usize) -> Self {
545 Self {
546 activations,
547 worker_id,
548 scratch_string_a: String::new(),
549 scratch_string_b: String::new(),
550 exports: Default::default(),
551 peek_stash: Default::default(),
552 arrangement_size: Default::default(),
553 lir_mapping: Default::default(),
554 dataflow_global_ids: Default::default(),
555 arrangement_heap_allocations_packer: PermutedRowPacker::new(
556 ComputeLog::ArrangementHeapAllocations,
557 ),
558 arrangement_heap_capacity_packer: PermutedRowPacker::new(
559 ComputeLog::ArrangementHeapCapacity,
560 ),
561 arrangement_heap_size_packer: PermutedRowPacker::new(ComputeLog::ArrangementHeapSize),
562 dataflow_global_packer: PermutedRowPacker::new(ComputeLog::DataflowGlobal),
563 error_count_packer: PermutedRowPacker::new(ComputeLog::ErrorCount),
564 export_packer: PermutedRowPacker::new(ComputeLog::DataflowCurrent),
565 frontier_packer: PermutedRowPacker::new(ComputeLog::FrontierCurrent),
566 hydration_time_packer: PermutedRowPacker::new(ComputeLog::HydrationTime),
567 import_frontier_packer: PermutedRowPacker::new(ComputeLog::ImportFrontierCurrent),
568 lir_mapping_packer: PermutedRowPacker::new(ComputeLog::LirMapping),
569 operator_hydration_status_packer: PermutedRowPacker::new(
570 ComputeLog::OperatorHydrationStatus,
571 ),
572 peek_duration_packer: PermutedRowPacker::new(ComputeLog::PeekDuration),
573 peek_packer: PermutedRowPacker::new(ComputeLog::PeekCurrent),
574 }
575 }
576
577 fn pack_arrangement_heap_allocations_update(
579 &mut self,
580 operator_id: usize,
581 ) -> (&RowRef, &RowRef) {
582 self.arrangement_heap_allocations_packer.pack_slice(&[
583 Datum::UInt64(operator_id.try_into().expect("operator_id too big")),
584 Datum::UInt64(u64::cast_from(self.worker_id)),
585 ])
586 }
587
588 fn pack_arrangement_heap_capacity_update(&mut self, operator_id: usize) -> (&RowRef, &RowRef) {
590 self.arrangement_heap_capacity_packer.pack_slice(&[
591 Datum::UInt64(operator_id.try_into().expect("operator_id too big")),
592 Datum::UInt64(u64::cast_from(self.worker_id)),
593 ])
594 }
595
596 fn pack_arrangement_heap_size_update(&mut self, operator_id: usize) -> (&RowRef, &RowRef) {
598 self.arrangement_heap_size_packer.pack_slice(&[
599 Datum::UInt64(operator_id.try_into().expect("operator_id too big")),
600 Datum::UInt64(u64::cast_from(self.worker_id)),
601 ])
602 }
603
604 fn pack_dataflow_global_update(
606 &mut self,
607 dataflow_index: usize,
608 global_id: GlobalId,
609 ) -> (&RowRef, &RowRef) {
610 self.dataflow_global_packer.pack_slice(&[
611 Datum::UInt64(u64::cast_from(dataflow_index)),
612 Datum::UInt64(u64::cast_from(self.worker_id)),
613 make_string_datum(global_id, &mut self.scratch_string_a),
614 ])
615 }
616
617 fn pack_error_count_update(&mut self, export_id: GlobalId, count: Diff) -> (&RowRef, &RowRef) {
619 self.error_count_packer.pack_slice(&[
623 make_string_datum(export_id, &mut self.scratch_string_a),
624 Datum::UInt64(u64::cast_from(self.worker_id)),
625 Datum::Int64(count.into_inner()),
626 ])
627 }
628
629 fn pack_export_update(
631 &mut self,
632 export_id: GlobalId,
633 dataflow_index: usize,
634 ) -> (&RowRef, &RowRef) {
635 self.export_packer.pack_slice(&[
636 make_string_datum(export_id, &mut self.scratch_string_a),
637 Datum::UInt64(u64::cast_from(self.worker_id)),
638 Datum::UInt64(u64::cast_from(dataflow_index)),
639 ])
640 }
641
642 fn pack_hydration_time_update(
645 &mut self,
646 export_id: GlobalId,
647 time_ns: Option<u64>,
648 timestamps: &HydrationTimestamps,
649 ) -> (&RowRef, &RowRef) {
650 self.hydration_time_packer.pack_slice(&[
651 make_string_datum(export_id, &mut self.scratch_string_a),
652 Datum::UInt64(u64::cast_from(self.worker_id)),
653 Datum::from(time_ns),
654 epoch_offset_datum(timestamps.installed_at),
655 timestamps
656 .started_at
657 .map_or(Datum::Null, epoch_offset_datum),
658 timestamps
659 .hydrated_at
660 .map_or(Datum::Null, epoch_offset_datum),
661 ])
662 }
663
664 fn pack_import_frontier_update(
666 &mut self,
667 export_id: GlobalId,
668 import_id: GlobalId,
669 time: Timestamp,
670 ) -> (&RowRef, &RowRef) {
671 self.import_frontier_packer.pack_slice(&[
672 make_string_datum(export_id, &mut self.scratch_string_a),
673 make_string_datum(import_id, &mut self.scratch_string_b),
674 Datum::UInt64(u64::cast_from(self.worker_id)),
675 Datum::MzTimestamp(time),
676 ])
677 }
678
679 fn pack_lir_mapping_update(
681 &mut self,
682 global_id: GlobalId,
683 lir_id: LirId,
684 operator: String,
685 parent_lir_id: Option<LirId>,
686 nesting: u8,
687 operator_span: (usize, usize),
688 ) -> (&RowRef, &RowRef) {
689 self.lir_mapping_packer.pack_slice(&[
690 make_string_datum(global_id, &mut self.scratch_string_a),
691 Datum::UInt64(lir_id.into()),
692 Datum::UInt64(u64::cast_from(self.worker_id)),
693 make_string_datum(operator, &mut self.scratch_string_b),
694 parent_lir_id.map_or(Datum::Null, |lir_id| Datum::UInt64(lir_id.into())),
695 Datum::UInt16(u16::cast_from(nesting)),
696 Datum::UInt64(u64::cast_from(operator_span.0)),
697 Datum::UInt64(u64::cast_from(operator_span.1)),
698 ])
699 }
700
701 fn pack_operator_hydration_status_update(
704 &mut self,
705 export_id: GlobalId,
706 lir_id: LirId,
707 hydrated: bool,
708 ) -> (&RowRef, &RowRef) {
709 self.operator_hydration_status_packer.pack_slice(&[
710 make_string_datum(export_id, &mut self.scratch_string_a),
711 Datum::UInt64(lir_id.into()),
712 Datum::UInt64(u64::cast_from(self.worker_id)),
713 Datum::from(hydrated),
714 ])
715 }
716
717 fn pack_peek_duration_update(
719 &mut self,
720 peek_type: PeekType,
721 bucket: u128,
722 ) -> (&RowRef, &RowRef) {
723 self.peek_duration_packer.pack_slice(&[
724 Datum::UInt64(u64::cast_from(self.worker_id)),
725 Datum::String(peek_type.name()),
726 Datum::UInt64(bucket.try_into().expect("bucket too big")),
727 ])
728 }
729
730 fn pack_peek_update(
732 &mut self,
733 id: GlobalId,
734 time: Timestamp,
735 uuid: Uuid,
736 peek_type: PeekType,
737 ) -> (&RowRef, &RowRef) {
738 self.peek_packer.pack_slice(&[
739 Datum::Uuid(uuid),
740 Datum::UInt64(u64::cast_from(self.worker_id)),
741 make_string_datum(id, &mut self.scratch_string_a),
742 Datum::String(peek_type.name()),
743 Datum::MzTimestamp(time),
744 ])
745 }
746
747 fn pack_frontier_update(&mut self, export_id: GlobalId, time: Timestamp) -> (&RowRef, &RowRef) {
749 self.frontier_packer.pack_slice(&[
750 make_string_datum(export_id, &mut self.scratch_string_a),
751 Datum::UInt64(u64::cast_from(self.worker_id)),
752 Datum::MzTimestamp(time),
753 ])
754 }
755}
756
757#[derive(Debug, Clone, Copy)]
767struct HydrationTimestamps {
768 installed_at: Duration,
770 started_at: Option<Duration>,
775 hydrated_at: Option<Duration>,
777}
778
779struct ExportState {
781 dataflow_index: usize,
783 error_count: Diff,
788 created_at: Instant,
790 hydration_time_ns: Option<u64>,
792 hydration_timestamps: HydrationTimestamps,
798 operator_hydration: BTreeMap<LirId, bool>,
800}
801
802impl ExportState {
803 fn new(dataflow_index: usize, installed_at: Duration) -> Self {
804 Self {
805 dataflow_index,
806 error_count: Diff::ZERO,
807 created_at: Instant::now(),
808 hydration_time_ns: None,
809 hydration_timestamps: HydrationTimestamps {
810 installed_at,
811 started_at: None,
812 hydrated_at: None,
813 },
814 operator_hydration: BTreeMap::new(),
815 }
816 }
817}
818
819#[derive(Default, Debug)]
821struct ArrangementSizeState {
822 size: isize,
823 capacity: isize,
824 count: isize,
825}
826
827struct DemuxOutput<'a, 'b> {
829 export: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
830 frontier: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
831 import_frontier: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
832 peek: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
833 peek_duration: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
834 arrangement_heap_allocations: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
835 arrangement_heap_capacity: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
836 arrangement_heap_size: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
837 hydration_time: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
838 operator_hydration_status: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
839 error_count: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
840 lir_mapping: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
841 dataflow_global_ids: OutputSessionColumnar<'a, 'b, Update<(Row, Row)>>,
842}
843
844struct DemuxHandler<'a, 'b, 'c> {
846 state: &'a mut DemuxState,
848 shared_state: &'a mut SharedLoggingState,
850 output: &'a mut DemuxOutput<'b, 'c>,
852 logging_interval_ms: u128,
854 time: Duration,
856}
857
858impl DemuxHandler<'_, '_, '_> {
859 fn ts(&self) -> Timestamp {
862 let time_ms = self.time.as_millis();
863 let interval = self.logging_interval_ms;
864 let rounded = (time_ms / interval + 1) * interval;
865 rounded.try_into().expect("must fit")
866 }
867
868 fn handle(&mut self, event: Ref<'_, ComputeEvent>) {
870 use ComputeEventReference::*;
871 match event {
872 Export(export) => self.handle_export(export),
873 ExportDropped(export_dropped) => self.handle_export_dropped(export_dropped),
874 Peek(peek) if peek.installed => self.handle_peek_install(peek),
875 Peek(peek) => self.handle_peek_retire(peek),
876 Frontier(frontier) => self.handle_frontier(frontier),
877 ImportFrontier(import_frontier) => self.handle_import_frontier(import_frontier),
878 ArrangementHeapSize(inner) => self.handle_arrangement_heap_size(inner),
879 ArrangementHeapCapacity(inner) => self.handle_arrangement_heap_capacity(inner),
880 ArrangementHeapAllocations(inner) => self.handle_arrangement_heap_allocations(inner),
881 ArrangementHeapSizeOperator(inner) => self.handle_arrangement_heap_size_operator(inner),
882 ArrangementHeapSizeOperatorDrop(inner) => {
883 self.handle_arrangement_heap_size_operator_dropped(inner)
884 }
885 DataflowShutdown(shutdown) => self.handle_dataflow_shutdown(shutdown),
886 ErrorCount(error_count) => self.handle_error_count(error_count),
887 HydrationStart(hydration) => self.handle_hydration_start(hydration),
888 Hydration(hydration) => self.handle_hydration(hydration),
889 OperatorHydration(hydration) => self.handle_operator_hydration(hydration),
890 LirMapping(mapping) => self.handle_lir_mapping(mapping),
891 DataflowGlobal(global) => self.handle_dataflow_global(global),
892 }
893 }
894
895 fn handle_export(
896 &mut self,
897 ExportReference {
898 export_id,
899 dataflow_index,
900 }: Ref<'_, Export>,
901 ) {
902 let export_id = Columnar::into_owned(export_id);
903 let ts = self.ts();
904 let datum = self.state.pack_export_update(export_id, dataflow_index);
905 self.output.export.give((datum, ts, Diff::ONE));
906
907 let installed_at = self.time;
910
911 let existing = self
912 .state
913 .exports
914 .insert(export_id, ExportState::new(dataflow_index, installed_at));
915 if existing.is_some() {
916 error!(%export_id, "export already registered");
917 }
918
919 let timestamps = HydrationTimestamps {
921 installed_at,
922 started_at: None,
923 hydrated_at: None,
924 };
925 let datum = self
926 .state
927 .pack_hydration_time_update(export_id, None, ×tamps);
928 self.output.hydration_time.give((datum, ts, Diff::ONE));
929 }
930
931 fn handle_export_dropped(
932 &mut self,
933 ExportDroppedReference { export_id }: Ref<'_, ExportDropped>,
934 ) {
935 let export_id = Columnar::into_owned(export_id);
936 let Some(export) = self.state.exports.remove(&export_id) else {
937 error!(%export_id, "missing exports entry at time of export drop");
938 return;
939 };
940
941 let ts = self.ts();
942 let dataflow_index = export.dataflow_index;
943
944 let datum = self.state.pack_export_update(export_id, dataflow_index);
945 self.output.export.give((datum, ts, Diff::MINUS_ONE));
946
947 if export.error_count != Diff::ZERO {
949 let datum = self
950 .state
951 .pack_error_count_update(export_id, export.error_count);
952 self.output.error_count.give((datum, ts, Diff::MINUS_ONE));
953 }
954
955 let datum = self.state.pack_hydration_time_update(
957 export_id,
958 export.hydration_time_ns,
959 &export.hydration_timestamps,
960 );
961 self.output
962 .hydration_time
963 .give((datum, ts, Diff::MINUS_ONE));
964
965 for (lir_id, hydrated) in export.operator_hydration {
967 let datum = self
968 .state
969 .pack_operator_hydration_status_update(export_id, lir_id, hydrated);
970 self.output
971 .operator_hydration_status
972 .give((datum, ts, Diff::MINUS_ONE));
973 }
974 }
975
976 fn handle_dataflow_shutdown(
977 &mut self,
978 DataflowShutdownReference { dataflow_index }: Ref<'_, DataflowShutdown>,
979 ) {
980 let ts = self.ts();
981
982 if let Some(global_ids) = self.state.dataflow_global_ids.remove(&dataflow_index) {
984 for global_id in global_ids {
985 let datum = self
987 .state
988 .pack_dataflow_global_update(dataflow_index, global_id);
989 self.output
990 .dataflow_global_ids
991 .give((datum, ts, Diff::MINUS_ONE));
992
993 if let Some(mappings) = self.state.lir_mapping.remove(&global_id) {
995 for (
996 lir_id,
997 LirMetadata {
998 operator,
999 parent_lir_id,
1000 nesting,
1001 operator_span,
1002 },
1003 ) in mappings
1004 {
1005 let datum = self.state.pack_lir_mapping_update(
1006 global_id,
1007 lir_id,
1008 operator,
1009 parent_lir_id,
1010 nesting,
1011 operator_span,
1012 );
1013 self.output.lir_mapping.give((datum, ts, Diff::MINUS_ONE));
1014 }
1015 }
1016 }
1017 }
1018 }
1019
1020 fn handle_error_count(&mut self, ErrorCountReference { export_id, diff }: Ref<'_, ErrorCount>) {
1021 let ts = self.ts();
1022 let export_id = Columnar::into_owned(export_id);
1023
1024 let Some(export) = self.state.exports.get_mut(&export_id) else {
1025 return;
1028 };
1029
1030 let old_count = export.error_count;
1031 let new_count = old_count + diff;
1032 export.error_count = new_count;
1033
1034 if old_count != Diff::ZERO {
1035 let datum = self.state.pack_error_count_update(export_id, old_count);
1036 self.output.error_count.give((datum, ts, Diff::MINUS_ONE));
1037 }
1038 if new_count != Diff::ZERO {
1039 let datum = self.state.pack_error_count_update(export_id, new_count);
1040 self.output.error_count.give((datum, ts, Diff::ONE));
1041 }
1042 }
1043
1044 fn handle_hydration_start(
1045 &mut self,
1046 HydrationStartReference { export_id }: Ref<'_, HydrationStart>,
1047 ) {
1048 let ts = self.ts();
1049 let started_at = self.time;
1051 let export_id = Columnar::into_owned(export_id);
1052
1053 let Some(export) = self.state.exports.get_mut(&export_id) else {
1054 error!(%export_id, "hydration start event for unknown export");
1055 return;
1056 };
1057 if export.hydration_timestamps.started_at.is_some() {
1058 return;
1062 }
1063
1064 let old_timestamps = export.hydration_timestamps;
1065 export.hydration_timestamps.started_at = Some(started_at);
1066 let new_timestamps = export.hydration_timestamps;
1067 let time_ns = export.hydration_time_ns;
1068
1069 let retraction = self
1070 .state
1071 .pack_hydration_time_update(export_id, time_ns, &old_timestamps);
1072 self.output
1073 .hydration_time
1074 .give((retraction, ts, Diff::MINUS_ONE));
1075 let insertion = self
1076 .state
1077 .pack_hydration_time_update(export_id, time_ns, &new_timestamps);
1078 self.output.hydration_time.give((insertion, ts, Diff::ONE));
1079 }
1080
1081 fn handle_hydration(&mut self, HydrationReference { export_id }: Ref<'_, Hydration>) {
1082 let ts = self.ts();
1083 let hydrated_at = self.time;
1085 let export_id = Columnar::into_owned(export_id);
1086
1087 let Some(export) = self.state.exports.get_mut(&export_id) else {
1088 error!(%export_id, "hydration event for unknown export");
1089 return;
1090 };
1091 if export.hydration_time_ns.is_some() {
1092 return;
1095 }
1096
1097 let duration = export.created_at.elapsed();
1098 let nanos = u64::try_from(duration.as_nanos()).expect("must fit");
1099 export.hydration_time_ns = Some(nanos);
1100
1101 let old_timestamps = export.hydration_timestamps;
1102 export.hydration_timestamps.hydrated_at = Some(hydrated_at);
1103 if export.hydration_timestamps.started_at.is_none() {
1113 export.hydration_timestamps.started_at = Some(export.hydration_timestamps.installed_at);
1114 }
1115 let new_timestamps = export.hydration_timestamps;
1116
1117 let retraction = self
1118 .state
1119 .pack_hydration_time_update(export_id, None, &old_timestamps);
1120 self.output
1121 .hydration_time
1122 .give((retraction, ts, Diff::MINUS_ONE));
1123 let insertion =
1124 self.state
1125 .pack_hydration_time_update(export_id, Some(nanos), &new_timestamps);
1126 self.output.hydration_time.give((insertion, ts, Diff::ONE));
1127 }
1128
1129 fn handle_operator_hydration(
1130 &mut self,
1131 OperatorHydrationReference {
1132 export_id,
1133 lir_id,
1134 hydrated,
1135 }: Ref<'_, OperatorHydration>,
1136 ) {
1137 let ts = self.ts();
1138 let export_id = Columnar::into_owned(export_id);
1139 let lir_id = Columnar::into_owned(lir_id);
1140 let hydrated = Columnar::into_owned(hydrated);
1141
1142 let Some(export) = self.state.exports.get_mut(&export_id) else {
1143 return;
1146 };
1147
1148 let old_status = export.operator_hydration.get(&lir_id).copied();
1149 export.operator_hydration.insert(lir_id, hydrated);
1150
1151 if let Some(hydrated) = old_status {
1152 let retraction = self
1153 .state
1154 .pack_operator_hydration_status_update(export_id, lir_id, hydrated);
1155 self.output
1156 .operator_hydration_status
1157 .give((retraction, ts, Diff::MINUS_ONE));
1158 }
1159
1160 let insertion = self
1161 .state
1162 .pack_operator_hydration_status_update(export_id, lir_id, hydrated);
1163 self.output
1164 .operator_hydration_status
1165 .give((insertion, ts, Diff::ONE));
1166 }
1167
1168 fn handle_peek_install(
1169 &mut self,
1170 PeekEventReference {
1171 id,
1172 time,
1173 uuid,
1174 peek_type,
1175 installed: _,
1176 }: Ref<'_, PeekEvent>,
1177 ) {
1178 let id = Columnar::into_owned(id);
1179 let uuid = Uuid::from_bytes(uuid::Bytes::into_owned(uuid));
1180 let ts = self.ts();
1181 let datum = self.state.pack_peek_update(id, time, uuid, peek_type);
1182 self.output.peek.give((datum, ts, Diff::ONE));
1183
1184 let existing = self.state.peek_stash.insert(uuid, self.time);
1185 if existing.is_some() {
1186 error!(%uuid, "peek already registered");
1187 }
1188 }
1189
1190 fn handle_peek_retire(
1191 &mut self,
1192 PeekEventReference {
1193 id,
1194 time,
1195 uuid,
1196 peek_type,
1197 installed: _,
1198 }: Ref<'_, PeekEvent>,
1199 ) {
1200 let id = Columnar::into_owned(id);
1201 let uuid = Uuid::from_bytes(uuid::Bytes::into_owned(uuid));
1202 let ts = self.ts();
1203 let datum = self.state.pack_peek_update(id, time, uuid, peek_type);
1204 self.output.peek.give((datum, ts, Diff::MINUS_ONE));
1205
1206 if let Some(start) = self.state.peek_stash.remove(&uuid) {
1207 let elapsed_ns = self.time.saturating_sub(start).as_nanos();
1208 let bucket = elapsed_ns.next_power_of_two();
1209 let datum = self.state.pack_peek_duration_update(peek_type, bucket);
1210 self.output.peek_duration.give((datum, ts, Diff::ONE));
1211 } else {
1212 error!(%uuid, "peek not yet registered");
1213 }
1214 }
1215
1216 fn handle_frontier(
1217 &mut self,
1218 FrontierReference {
1219 export_id,
1220 time,
1221 diff,
1222 }: Ref<'_, Frontier>,
1223 ) {
1224 let export_id = Columnar::into_owned(export_id);
1225 let diff = Diff::from(*diff);
1226 let ts = self.ts();
1227 let time = Columnar::into_owned(time);
1228 let datum = self.state.pack_frontier_update(export_id, time);
1229 self.output.frontier.give((datum, ts, diff));
1230 }
1231
1232 fn handle_import_frontier(
1233 &mut self,
1234 ImportFrontierReference {
1235 import_id,
1236 export_id,
1237 time,
1238 diff,
1239 }: Ref<'_, ImportFrontier>,
1240 ) {
1241 let import_id = Columnar::into_owned(import_id);
1242 let export_id = Columnar::into_owned(export_id);
1243 let diff = Diff::from(*diff);
1244 let ts = self.ts();
1245 let time = Columnar::into_owned(time);
1246 let datum = self
1247 .state
1248 .pack_import_frontier_update(export_id, import_id, time);
1249 self.output.import_frontier.give((datum, ts, diff));
1250 }
1251
1252 fn handle_arrangement_heap_size(
1254 &mut self,
1255 ArrangementHeapSizeReference {
1256 operator_id,
1257 delta_size,
1258 }: Ref<'_, ArrangementHeapSize>,
1259 ) {
1260 let ts = self.ts();
1261 let Some(state) = self.state.arrangement_size.get_mut(&operator_id) else {
1262 return;
1263 };
1264
1265 state.size += delta_size;
1266
1267 let datum = self.state.pack_arrangement_heap_size_update(operator_id);
1268 let diff = Diff::cast_from(delta_size);
1269 self.output.arrangement_heap_size.give((datum, ts, diff));
1270 }
1271
1272 fn handle_arrangement_heap_capacity(
1274 &mut self,
1275 ArrangementHeapCapacityReference {
1276 operator_id,
1277 delta_capacity,
1278 }: Ref<'_, ArrangementHeapCapacity>,
1279 ) {
1280 let ts = self.ts();
1281 let Some(state) = self.state.arrangement_size.get_mut(&operator_id) else {
1282 return;
1283 };
1284
1285 state.capacity += delta_capacity;
1286
1287 let datum = self
1288 .state
1289 .pack_arrangement_heap_capacity_update(operator_id);
1290 let diff = Diff::cast_from(delta_capacity);
1291 self.output
1292 .arrangement_heap_capacity
1293 .give((datum, ts, diff));
1294 }
1295
1296 fn handle_arrangement_heap_allocations(
1298 &mut self,
1299 ArrangementHeapAllocationsReference {
1300 operator_id,
1301 delta_allocations,
1302 }: Ref<'_, ArrangementHeapAllocations>,
1303 ) {
1304 let ts = self.ts();
1305 let Some(state) = self.state.arrangement_size.get_mut(&operator_id) else {
1306 return;
1307 };
1308
1309 state.count += delta_allocations;
1310
1311 let datum = self
1312 .state
1313 .pack_arrangement_heap_allocations_update(operator_id);
1314 let diff = Diff::cast_from(delta_allocations);
1315 self.output
1316 .arrangement_heap_allocations
1317 .give((datum, ts, diff));
1318 }
1319
1320 fn handle_arrangement_heap_size_operator(
1322 &mut self,
1323 ArrangementHeapSizeOperatorReference {
1324 operator_id,
1325 address,
1326 }: Ref<'_, ArrangementHeapSizeOperator>,
1327 ) {
1328 let activator = Activator::new(
1329 address.into_iter().collect(),
1330 Rc::clone(&self.state.activations),
1331 );
1332 let existing = self
1333 .state
1334 .arrangement_size
1335 .insert(operator_id, Default::default());
1336 if existing.is_some() {
1337 error!(%operator_id, "arrangement size operator already registered");
1338 }
1339 let existing = self
1340 .shared_state
1341 .arrangement_size_activators
1342 .insert(operator_id, activator);
1343 if existing.is_some() {
1344 error!(%operator_id, "arrangement size activator already registered");
1345 }
1346 }
1347
1348 fn handle_arrangement_heap_size_operator_dropped(
1350 &mut self,
1351 event: Ref<'_, ArrangementHeapSizeOperatorDrop>,
1352 ) {
1353 let operator_id = event.operator_id;
1354 if let Some(state) = self.state.arrangement_size.remove(&operator_id) {
1355 let ts = self.ts();
1356 let allocations = self
1357 .state
1358 .pack_arrangement_heap_allocations_update(operator_id);
1359 let diff = -Diff::cast_from(state.count);
1360 self.output
1361 .arrangement_heap_allocations
1362 .give((allocations, ts, diff));
1363
1364 let capacity = self
1365 .state
1366 .pack_arrangement_heap_capacity_update(operator_id);
1367 let diff = -Diff::cast_from(state.capacity);
1368 self.output
1369 .arrangement_heap_capacity
1370 .give((capacity, ts, diff));
1371
1372 let size = self.state.pack_arrangement_heap_size_update(operator_id);
1373 let diff = -Diff::cast_from(state.size);
1374 self.output.arrangement_heap_size.give((size, ts, diff));
1375 }
1376 self.shared_state
1377 .arrangement_size_activators
1378 .remove(&operator_id);
1379 }
1380
1381 fn handle_lir_mapping(
1383 &mut self,
1384 LirMappingReference { global_id, mapping }: Ref<'_, LirMapping>,
1385 ) {
1386 let global_id = Columnar::into_owned(global_id);
1387 let mappings = || mapping.into_iter().map(Columnar::into_owned);
1389 self.state
1390 .lir_mapping
1391 .entry(global_id)
1392 .and_modify(|existing_mapping| existing_mapping.extend(mappings()))
1393 .or_insert_with(|| mappings().collect());
1394
1395 let ts = self.ts();
1397 for (lir_id, meta) in mapping.into_iter() {
1398 let datum = self.state.pack_lir_mapping_update(
1399 global_id,
1400 Columnar::into_owned(lir_id),
1401 Columnar::into_owned(meta.operator),
1402 Columnar::into_owned(meta.parent_lir_id),
1403 Columnar::into_owned(meta.nesting),
1404 Columnar::into_owned(meta.operator_span),
1405 );
1406 self.output.lir_mapping.give((datum, ts, Diff::ONE));
1407 }
1408 }
1409
1410 fn handle_dataflow_global(
1411 &mut self,
1412 DataflowGlobalReference {
1413 dataflow_index,
1414 global_id,
1415 }: Ref<'_, DataflowGlobal>,
1416 ) {
1417 let global_id = Columnar::into_owned(global_id);
1418 self.state
1419 .dataflow_global_ids
1420 .entry(dataflow_index)
1421 .and_modify(|globals| {
1422 if !globals.insert(global_id) {
1424 error!(%dataflow_index, %global_id, "dataflow mapping already knew about this GlobalId");
1425 }
1426 })
1427 .or_insert_with(|| BTreeSet::from([global_id]));
1428
1429 let ts = self.ts();
1430 let datum = self
1431 .state
1432 .pack_dataflow_global_update(dataflow_index, global_id);
1433 self.output.dataflow_global_ids.give((datum, ts, Diff::ONE));
1434 }
1435}
1436
1437pub struct CollectionLogging {
1442 export_id: GlobalId,
1443 logger: Logger,
1444
1445 logged_frontier: Option<Timestamp>,
1446 logged_import_frontiers: BTreeMap<GlobalId, Timestamp>,
1447}
1448
1449impl CollectionLogging {
1450 pub fn new(
1452 export_id: GlobalId,
1453 logger: Logger,
1454 dataflow_index: usize,
1455 import_ids: impl Iterator<Item = GlobalId>,
1456 ) -> Self {
1457 logger.log(&ComputeEvent::Export(Export {
1458 export_id,
1459 dataflow_index,
1460 }));
1461
1462 let mut self_ = Self {
1463 export_id,
1464 logger,
1465 logged_frontier: None,
1466 logged_import_frontiers: Default::default(),
1467 };
1468
1469 let initial_frontier = Some(Timestamp::MIN);
1471 self_.set_frontier(initial_frontier);
1472 import_ids.for_each(|id| self_.set_import_frontier(id, initial_frontier));
1473
1474 self_
1475 }
1476
1477 pub fn set_frontier(&mut self, new_time: Option<Timestamp>) {
1479 let old_time = self.logged_frontier;
1480 self.logged_frontier = new_time;
1481
1482 if old_time != new_time {
1483 let export_id = self.export_id;
1484 let retraction = old_time.map(|time| {
1485 ComputeEvent::Frontier(Frontier {
1486 export_id,
1487 time,
1488 diff: -1,
1489 })
1490 });
1491 let insertion = new_time.map(|time| {
1492 ComputeEvent::Frontier(Frontier {
1493 export_id,
1494 time,
1495 diff: 1,
1496 })
1497 });
1498 let events = retraction.as_ref().into_iter().chain(insertion.as_ref());
1499 self.logger.log_many(events);
1500 }
1501 }
1502
1503 pub fn set_import_frontier(&mut self, import_id: GlobalId, new_time: Option<Timestamp>) {
1506 let old_time = self.logged_import_frontiers.remove(&import_id);
1507 if let Some(time) = new_time {
1508 self.logged_import_frontiers.insert(import_id, time);
1509 }
1510
1511 if old_time != new_time {
1512 let export_id = self.export_id;
1513 let retraction = old_time.map(|time| {
1514 ComputeEvent::ImportFrontier(ImportFrontier {
1515 import_id,
1516 export_id,
1517 time,
1518 diff: -1,
1519 })
1520 });
1521 let insertion = new_time.map(|time| {
1522 ComputeEvent::ImportFrontier(ImportFrontier {
1523 import_id,
1524 export_id,
1525 time,
1526 diff: 1,
1527 })
1528 });
1529 let events = retraction.as_ref().into_iter().chain(insertion.as_ref());
1530 self.logger.log_many(events);
1531 }
1532 }
1533
1534 pub fn set_hydration_start(&self) {
1539 self.logger
1540 .log(&ComputeEvent::HydrationStart(HydrationStart {
1541 export_id: self.export_id,
1542 }));
1543 }
1544
1545 pub fn set_hydrated(&self) {
1547 self.logger.log(&ComputeEvent::Hydration(Hydration {
1548 export_id: self.export_id,
1549 }));
1550 }
1551
1552 pub fn export_id(&self) -> GlobalId {
1554 self.export_id
1555 }
1556}
1557
1558impl Drop for CollectionLogging {
1559 fn drop(&mut self) {
1560 self.set_frontier(None);
1562
1563 let import_ids: Vec<_> = self.logged_import_frontiers.keys().copied().collect();
1564 for import_id in import_ids {
1565 self.set_import_frontier(import_id, None);
1566 }
1567
1568 self.logger.log(&ComputeEvent::ExportDropped(ExportDropped {
1569 export_id: self.export_id,
1570 }));
1571 }
1572}
1573
1574pub(crate) trait LogDataflowErrors {
1577 fn log_dataflow_errors(self, logger: Logger, export_id: GlobalId) -> Self;
1578}
1579
1580impl<'scope, T, D> LogDataflowErrors for VecCollection<'scope, T, D, Diff>
1581where
1582 T: timely::progress::Timestamp,
1583 D: Clone + 'static,
1584{
1585 fn log_dataflow_errors(self, logger: Logger, export_id: GlobalId) -> Self {
1586 self.inner
1587 .unary(Pipeline, "LogDataflowErrorsCollection", |_cap, _info| {
1588 move |input, output| {
1589 input.for_each(|cap, data| {
1590 let diff = data.iter().map(|(_d, _t, r)| *r).sum::<Diff>();
1591 logger.log(&ComputeEvent::ErrorCount(ErrorCount { export_id, diff }));
1592
1593 output.session(&cap).give_container(data);
1594 });
1595 }
1596 })
1597 .as_collection()
1598 }
1599}
1600
1601impl<'scope, T, B> LogDataflowErrors for StreamVec<'scope, T, Span<T, B>>
1602where
1603 T: timely::progress::Timestamp,
1604 B: Navigable + Clone + 'static,
1605 for<'a> B::Cursor: Cursor<DiffGat<'a> = &'a Diff>,
1606{
1607 fn log_dataflow_errors(self, logger: Logger, export_id: GlobalId) -> Self {
1608 self.unary(Pipeline, "LogDataflowErrorsStream", |_cap, _info| {
1609 move |input, output| {
1610 input.for_each(|cap, data| {
1611 let diff = data
1612 .iter()
1613 .filter_map(|span| span.inner.as_ref())
1614 .map(sum_batch_diffs)
1615 .sum::<Diff>();
1616 logger.log(&ComputeEvent::ErrorCount(ErrorCount { export_id, diff }));
1617
1618 output.session(&cap).give_container(data);
1619 });
1620 }
1621 })
1622 }
1623}
1624
1625fn sum_batch_diffs<B>(batch: &B) -> Diff
1632where
1633 B: Navigable,
1634 for<'a> B::Cursor: Cursor<DiffGat<'a> = &'a Diff>,
1635{
1636 let mut sum = Diff::ZERO;
1637 let mut cursor = batch.cursor();
1638
1639 while cursor.key_valid(batch) {
1640 while cursor.val_valid(batch) {
1641 cursor.map_times(batch, |_t, r| sum += r);
1642 cursor.step_val(batch);
1643 }
1644 cursor.step_key(batch);
1645 }
1646
1647 sum
1648}
1649
1650#[cfg(test)]
1651mod tests {
1652 use super::*;
1653
1654 #[mz_ore::test]
1655 fn test_compute_event_size() {
1656 assert_eq!(56, std::mem::size_of::<ComputeEvent>())
1658 }
1659}