1use std::collections::BTreeMap;
14use std::fmt;
15use std::num::NonZeroIsize;
16use std::ops::{AddAssign, Bound, RangeBounds, SubAssign};
17
18use differential_dataflow::consolidation::{consolidate, consolidate_updates};
19use differential_dataflow::logging::{BatchEvent, DropEvent};
20use itertools::{Either, Itertools};
21use mz_compute_types::dyncfgs::{
22 CONSOLIDATING_VEC_GROWTH_DAMPENER, CORRECTION_V2_CHAIN_PROPORTIONALITY,
23 CORRECTION_V2_CHUNK_SIZE, ENABLE_CORRECTION_V2,
24};
25use mz_dyncfg::ConfigSet;
26use mz_persist_client::metrics::{SinkMetrics, SinkWorkerMetrics, UpdateDelta};
27use mz_repr::{Diff, Timestamp};
28use timely::PartialOrder;
29use timely::progress::Antichain;
30use tokio::sync::mpsc;
31
32use crate::logging::compute::{
33 ArrangementHeapAllocations, ArrangementHeapCapacity, ArrangementHeapSize,
34 ArrangementHeapSizeOperator, ArrangementHeapSizeOperatorDrop, ComputeEvent,
35 Logger as ComputeLogger,
36};
37use crate::sink::correction_v2::{CorrectionV2, Data};
38
39pub enum Correction<D: Data> {
46 V1(CorrectionV1<D>),
48 V2(CorrectionV2<D>),
50}
51
52impl<D: Data> Correction<D> {
53 pub fn new(
55 metrics: SinkMetrics,
56 worker_metrics: SinkWorkerMetrics,
57 logging: Option<ChannelLogging>,
58 config: &ConfigSet,
59 ) -> Self {
60 if ENABLE_CORRECTION_V2.get(config) {
61 let prop = CORRECTION_V2_CHAIN_PROPORTIONALITY.get(config);
62 let chunk_size = CORRECTION_V2_CHUNK_SIZE.get(config);
63 Self::V2(CorrectionV2::new(
64 metrics,
65 worker_metrics,
66 logging,
67 prop,
68 chunk_size,
69 ))
70 } else {
71 let growth_dampener = CONSOLIDATING_VEC_GROWTH_DAMPENER.get(config);
72 Self::V1(CorrectionV1::new(metrics, worker_metrics, growth_dampener))
73 }
74 }
75
76 pub fn insert(&mut self, updates: &mut Vec<(D, Timestamp, Diff)>) {
78 match self {
79 Self::V1(c) => c.insert(updates),
80 Self::V2(c) => c.insert(updates),
81 }
82 }
83
84 pub fn insert_negated(&mut self, updates: &mut Vec<(D, Timestamp, Diff)>) {
86 match self {
87 Self::V1(c) => c.insert_negated(updates),
88 Self::V2(c) => c.insert_negated(updates),
89 }
90 }
91
92 pub fn updates_before(
94 &mut self,
95 upper: &Antichain<Timestamp>,
96 ) -> Box<dyn Iterator<Item = (D, Timestamp, Diff)> + Send + '_> {
97 match self {
98 Self::V1(c) => Box::new(c.updates_before(upper)),
99 Self::V2(c) => Box::new(c.updates_before(upper)),
100 }
101 }
102
103 pub fn consolidate_before(&mut self, upper: &Antichain<Timestamp>) {
108 match self {
109 Self::V1(c) => c.consolidate_before(upper),
110 Self::V2(c) => c.consolidate_before(upper),
111 }
112 }
113
114 pub fn consolidated_updates_before<'a>(
121 &'a self,
122 upper: &Antichain<Timestamp>,
123 ) -> impl Iterator<Item = (D, Timestamp, Diff)> + Send + use<'a, D> {
124 match self {
125 Self::V1(c) => Either::Left(c.consolidated_updates_before(upper)),
126 Self::V2(c) => Either::Right(c.consolidated_updates_before(upper)),
127 }
128 }
129
130 pub fn advance_since(&mut self, since: Antichain<Timestamp>) {
136 match self {
137 Self::V1(c) => c.advance_since(since),
138 Self::V2(c) => c.advance_since(since),
139 }
140 }
141
142 pub fn consolidate_at_since(&mut self) {
144 match self {
145 Self::V1(c) => c.consolidate_at_since(),
146 Self::V2(c) => c.consolidate_at_since(),
147 }
148 }
149}
150
151pub struct CorrectionV1<D> {
163 updates: BTreeMap<Timestamp, ConsolidatingVec<D>>,
165 since: Antichain<Timestamp>,
167
168 total_size: LengthAndCapacity,
172 metrics: SinkMetrics,
174 worker_metrics: SinkWorkerMetrics,
176 growth_dampener: usize,
178}
179
180impl<D> CorrectionV1<D> {
181 pub fn new(
183 metrics: SinkMetrics,
184 worker_metrics: SinkWorkerMetrics,
185 growth_dampener: usize,
186 ) -> Self {
187 Self {
188 updates: Default::default(),
189 since: Antichain::from_elem(Timestamp::MIN),
190 total_size: Default::default(),
191 metrics,
192 worker_metrics,
193 growth_dampener,
194 }
195 }
196
197 fn update_metrics(&mut self, new_size: LengthAndCapacity) {
199 let old_size = self.total_size;
200 let len_delta = UpdateDelta::new(new_size.length, old_size.length);
201 let cap_delta = UpdateDelta::new(new_size.capacity, old_size.capacity);
202 self.metrics
203 .report_correction_update_deltas(len_delta, cap_delta);
204 self.worker_metrics
205 .report_correction_update_totals(new_size.length, new_size.capacity);
206
207 self.total_size = new_size;
208 }
209}
210
211impl<D: Data> CorrectionV1<D> {
212 pub fn insert(&mut self, updates: &mut Vec<(D, Timestamp, Diff)>) {
214 let Some(since_ts) = self.since.as_option() else {
215 updates.clear();
217 return;
218 };
219
220 for (_, time, _) in &mut *updates {
221 *time = std::cmp::max(*time, *since_ts);
222 }
223 self.insert_inner(updates);
224 }
225
226 pub fn insert_negated(&mut self, updates: &mut Vec<(D, Timestamp, Diff)>) {
228 let Some(since_ts) = self.since.as_option() else {
229 updates.clear();
231 return;
232 };
233
234 for (_, time, diff) in &mut *updates {
235 *time = std::cmp::max(*time, *since_ts);
236 *diff = -*diff;
237 }
238 self.insert_inner(updates);
239 }
240
241 fn insert_inner(&mut self, updates: &mut Vec<(D, Timestamp, Diff)>) {
245 consolidate_updates(updates);
246 updates.sort_unstable_by_key(|(_, time, _)| *time);
247
248 let mut new_size = self.total_size;
249 let mut updates = updates.drain(..).peekable();
250 while let Some(&(_, time, _)) = updates.peek() {
251 mz_ore::soft_assert_no_log!(
252 self.since.less_equal(&time),
253 "update not advanced by `since`"
254 );
255
256 let data = updates
257 .peeking_take_while(|(_, t, _)| *t == time)
258 .map(|(d, _, r)| (d, r));
259
260 use std::collections::btree_map::Entry;
261 match self.updates.entry(time) {
262 Entry::Vacant(entry) => {
263 let mut vec: ConsolidatingVec<_> = data.collect();
264 vec.growth_dampener = self.growth_dampener;
265 new_size += (vec.len(), vec.capacity());
266 entry.insert(vec);
267 }
268 Entry::Occupied(mut entry) => {
269 let vec = entry.get_mut();
270 new_size -= (vec.len(), vec.capacity());
271 vec.extend(data);
272 new_size += (vec.len(), vec.capacity());
273 }
274 }
275 }
276
277 self.update_metrics(new_size);
278 }
279
280 fn range_before(upper: &Antichain<Timestamp>) -> (Bound<Timestamp>, Bound<Timestamp>) {
282 let start = Bound::Included(Timestamp::MIN);
283 let end = match upper.as_option() {
284 Some(ts) => Bound::Excluded(*ts),
285 None => Bound::Unbounded,
286 };
287 (start, end)
288 }
289
290 pub fn consolidate_before(&mut self, upper: &Antichain<Timestamp>) {
292 let _ = self.consolidate(Self::range_before(upper));
293 }
294
295 pub fn consolidated_updates_before<'a>(
301 &'a self,
302 upper: &Antichain<Timestamp>,
303 ) -> impl Iterator<Item = (D, Timestamp, Diff)> + Send + use<'a, D> {
304 self.updates
305 .range(Self::range_before(upper))
306 .flat_map(|(t, data)| data.iter().map(|(d, r)| (d.clone(), *t, *r)))
307 }
308
309 pub fn updates_before<'a>(
311 &'a mut self,
312 upper: &Antichain<Timestamp>,
313 ) -> impl Iterator<Item = (D, Timestamp, Diff)> + Send + use<'a, D> {
314 self.consolidate_before(upper);
315 self.consolidated_updates_before(upper)
316 }
317
318 fn consolidate<R>(&mut self, range: R) -> usize
322 where
323 R: RangeBounds<Timestamp>,
324 {
325 let mut new_size = self.total_size;
326
327 let updates = self.updates.range_mut(range);
328 let count = updates.fold(0, |acc, (_, data)| {
329 new_size -= (data.len(), data.capacity());
330 data.consolidate();
331 new_size += (data.len(), data.capacity());
332 acc + data.len()
333 });
334
335 self.update_metrics(new_size);
336 count
337 }
338
339 pub fn advance_since(&mut self, since: Antichain<Timestamp>) {
345 assert!(PartialOrder::less_equal(&self.since, &since));
346
347 if since != self.since {
348 self.advance_by(&since);
349 self.since = since;
350 }
351 }
352
353 pub fn advance_by(&mut self, frontier: &Antichain<Timestamp>) {
357 let Some(target_ts) = frontier.as_option() else {
358 self.updates.clear();
359 self.update_metrics(Default::default());
360 return;
361 };
362
363 let mut new_size = self.total_size;
364 while let Some((ts, data)) = self.updates.pop_first() {
365 if frontier.less_equal(&ts) {
366 self.updates.insert(ts, data);
368 break;
369 }
370
371 use std::collections::btree_map::Entry;
372 match self.updates.entry(*target_ts) {
373 Entry::Vacant(entry) => {
374 entry.insert(data);
375 }
376 Entry::Occupied(mut entry) => {
377 let vec = entry.get_mut();
378 new_size -= (data.len(), data.capacity());
379 new_size -= (vec.len(), vec.capacity());
380 vec.extend(data);
381 new_size += (vec.len(), vec.capacity());
382 }
383 }
384 }
385
386 self.update_metrics(new_size);
387 }
388
389 pub fn consolidate_at_since(&mut self) {
391 let Some(since_ts) = self.since.as_option() else {
392 return;
393 };
394
395 let start = Bound::Included(*since_ts);
396 let end = match since_ts.try_step_forward() {
397 Some(ts) => Bound::Excluded(ts),
398 None => Bound::Unbounded,
399 };
400
401 self.consolidate((start, end));
402 }
403}
404
405impl<D> Drop for CorrectionV1<D> {
406 fn drop(&mut self) {
407 self.update_metrics(Default::default());
408 }
409}
410
411#[derive(Clone, Copy, Debug, Default)]
413pub(super) struct LengthAndCapacity {
414 pub length: usize,
415 pub capacity: usize,
416}
417
418impl AddAssign<Self> for LengthAndCapacity {
419 fn add_assign(&mut self, size: Self) {
420 self.length += size.length;
421 self.capacity += size.capacity;
422 }
423}
424
425impl AddAssign<(usize, usize)> for LengthAndCapacity {
426 fn add_assign(&mut self, (len, cap): (usize, usize)) {
427 self.length += len;
428 self.capacity += cap;
429 }
430}
431
432impl SubAssign<(usize, usize)> for LengthAndCapacity {
433 fn sub_assign(&mut self, (len, cap): (usize, usize)) {
434 self.length -= len;
435 self.capacity -= cap;
436 }
437}
438
439#[derive(Debug)]
445pub(crate) struct ConsolidatingVec<D> {
446 data: Vec<(D, Diff)>,
447 min_capacity: usize,
450 growth_dampener: usize,
456}
457
458impl<D: Ord> ConsolidatingVec<D> {
459 pub fn len(&self) -> usize {
461 self.data.len()
462 }
463
464 pub fn capacity(&self) -> usize {
466 self.data.capacity()
467 }
468
469 pub fn push(&mut self, item: (D, Diff)) {
477 let capacity = self.data.capacity();
478 if self.data.len() == capacity {
479 self.consolidate();
481
482 let length = self.data.len();
485 let dampener = self.growth_dampener;
486 if capacity < length + length / (dampener + 1) {
487 let new_cap = capacity + capacity / (dampener + 1);
491 self.data.reserve_exact(new_cap - length);
492 }
493 }
494
495 self.data.push(item);
496 }
497
498 pub fn consolidate(&mut self) {
500 consolidate(&mut self.data);
501
502 if self.data.len() < self.data.capacity() / 4 {
507 self.data.shrink_to(self.min_capacity);
508 }
509 }
510
511 pub fn iter(&self) -> impl Iterator<Item = &(D, Diff)> {
513 self.data.iter()
514 }
515}
516
517impl<D> IntoIterator for ConsolidatingVec<D> {
518 type Item = (D, Diff);
519 type IntoIter = std::vec::IntoIter<(D, Diff)>;
520
521 fn into_iter(self) -> Self::IntoIter {
522 self.data.into_iter()
523 }
524}
525
526impl<D> FromIterator<(D, Diff)> for ConsolidatingVec<D> {
527 fn from_iter<I>(iter: I) -> Self
528 where
529 I: IntoIterator<Item = (D, Diff)>,
530 {
531 Self {
532 data: Vec::from_iter(iter),
533 min_capacity: 0,
534 growth_dampener: 0,
535 }
536 }
537}
538
539impl<D: Ord> Extend<(D, Diff)> for ConsolidatingVec<D> {
540 fn extend<I>(&mut self, iter: I)
541 where
542 I: IntoIterator<Item = (D, Diff)>,
543 {
544 for item in iter {
545 self.push(item);
546 }
547 }
548}
549
550#[derive(Clone, Copy, Debug, Default)]
552pub(super) struct SizeMetrics {
553 pub size: usize,
554 pub capacity: usize,
555 pub allocations: usize,
556}
557
558impl AddAssign<Self> for SizeMetrics {
559 fn add_assign(&mut self, other: Self) {
560 self.size += other.size;
561 self.capacity += other.capacity;
562 self.allocations += other.allocations;
563 }
564}
565
566#[derive(Debug)]
568pub enum LoggingEvent {
569 ChainCreated(usize),
571 ChainDropped(usize),
573 SizeDiff(NonZeroIsize),
575 CapacityDiff(NonZeroIsize),
577 AllocationsDiff(NonZeroIsize),
579}
580
581#[derive(Clone, Debug)]
587pub struct ChannelLogging(mpsc::UnboundedSender<LoggingEvent>);
588
589impl ChannelLogging {
590 pub fn new(tx: mpsc::UnboundedSender<LoggingEvent>) -> Self {
592 Self(tx)
593 }
594
595 pub fn chain_created(&self, updates: usize) {
597 let _ = self.0.send(LoggingEvent::ChainCreated(updates));
598 }
599
600 pub fn chain_dropped(&self, updates: usize) {
602 let _ = self.0.send(LoggingEvent::ChainDropped(updates));
603 }
604
605 pub fn report_size_diff(&self, diff: isize) {
607 if let Some(diff) = NonZeroIsize::new(diff) {
608 let _ = self.0.send(LoggingEvent::SizeDiff(diff));
609 }
610 }
611
612 pub fn report_capacity_diff(&self, diff: isize) {
614 if let Some(diff) = NonZeroIsize::new(diff) {
615 let _ = self.0.send(LoggingEvent::CapacityDiff(diff));
616 }
617 }
618
619 pub fn report_allocations_diff(&self, diff: isize) {
621 if let Some(diff) = NonZeroIsize::new(diff) {
622 let _ = self.0.send(LoggingEvent::AllocationsDiff(diff));
623 }
624 }
625}
626
627pub(super) struct CorrectionLogger {
636 compute_logger: ComputeLogger,
637 differential_logger: differential_dataflow::logging::Logger,
638 operator_id: usize,
639 rx: mpsc::UnboundedReceiver<LoggingEvent>,
640 net_batches: isize,
642 net_records: isize,
644 net_size: isize,
646 net_capacity: isize,
648 net_allocations: isize,
650}
651
652impl fmt::Debug for CorrectionLogger {
653 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
654 f.debug_struct("CorrectionLogger")
655 .field("operator_id", &self.operator_id)
656 .finish_non_exhaustive()
657 }
658}
659
660impl CorrectionLogger {
661 pub fn new(
662 compute_logger: ComputeLogger,
663 differential_logger: differential_dataflow::logging::Logger,
664 operator_id: usize,
665 address: Vec<usize>,
666 rx: mpsc::UnboundedReceiver<LoggingEvent>,
667 ) -> Self {
668 compute_logger.log(&ComputeEvent::ArrangementHeapSizeOperator(
669 ArrangementHeapSizeOperator {
670 operator_id,
671 address,
672 },
673 ));
674
675 Self {
676 compute_logger,
677 differential_logger,
678 operator_id,
679 rx,
680 net_batches: 0,
681 net_records: 0,
682 net_size: 0,
683 net_capacity: 0,
684 net_allocations: 0,
685 }
686 }
687
688 pub fn apply_events(&mut self) {
690 use LoggingEvent::*;
691
692 while let Ok(event) = self.rx.try_recv() {
693 match event {
694 ChainCreated(length) => {
695 self.net_batches += 1;
696 self.net_records += isize::try_from(length).expect("must fit");
697 self.differential_logger.log(BatchEvent {
698 operator: self.operator_id,
699 length,
700 });
701 }
702 ChainDropped(length) => {
703 self.net_batches -= 1;
704 self.net_records -= isize::try_from(length).expect("must fit");
705 self.differential_logger.log(DropEvent {
706 operator: self.operator_id,
707 length,
708 });
709 }
710 SizeDiff(delta_size) => {
711 self.net_size += delta_size.get();
712 self.compute_logger.log(&ComputeEvent::ArrangementHeapSize(
713 ArrangementHeapSize {
714 operator_id: self.operator_id,
715 delta_size: delta_size.get(),
716 },
717 ));
718 }
719 CapacityDiff(delta_capacity) => {
720 self.net_capacity += delta_capacity.get();
721 self.compute_logger
722 .log(&ComputeEvent::ArrangementHeapCapacity(
723 ArrangementHeapCapacity {
724 operator_id: self.operator_id,
725 delta_capacity: delta_capacity.get(),
726 },
727 ));
728 }
729 AllocationsDiff(delta_allocations) => {
730 self.net_allocations += delta_allocations.get();
731 self.compute_logger
732 .log(&ComputeEvent::ArrangementHeapAllocations(
733 ArrangementHeapAllocations {
734 operator_id: self.operator_id,
735 delta_allocations: delta_allocations.get(),
736 },
737 ));
738 }
739 }
740 }
741 }
742}
743
744impl Drop for CorrectionLogger {
745 fn drop(&mut self) {
746 self.apply_events();
750
751 for i in 0..self.net_batches {
758 let length = if i == 0 {
759 usize::try_from(self.net_records).unwrap_or(0)
760 } else {
761 0
762 };
763 self.differential_logger.log(DropEvent {
764 operator: self.operator_id,
765 length,
766 });
767 }
768
769 if self.net_size != 0 {
771 self.compute_logger
772 .log(&ComputeEvent::ArrangementHeapSize(ArrangementHeapSize {
773 operator_id: self.operator_id,
774 delta_size: -self.net_size,
775 }));
776 }
777 if self.net_capacity != 0 {
778 self.compute_logger
779 .log(&ComputeEvent::ArrangementHeapCapacity(
780 ArrangementHeapCapacity {
781 operator_id: self.operator_id,
782 delta_capacity: -self.net_capacity,
783 },
784 ));
785 }
786 if self.net_allocations != 0 {
787 self.compute_logger
788 .log(&ComputeEvent::ArrangementHeapAllocations(
789 ArrangementHeapAllocations {
790 operator_id: self.operator_id,
791 delta_allocations: -self.net_allocations,
792 },
793 ));
794 }
795
796 self.compute_logger
797 .log(&ComputeEvent::ArrangementHeapSizeOperatorDrop(
798 ArrangementHeapSizeOperatorDrop {
799 operator_id: self.operator_id,
800 },
801 ));
802 }
803}