1use std::collections::BTreeMap;
14use std::rc::Rc;
15
16use differential_dataflow::consolidation::ConsolidatingContainerBuilder;
17use differential_dataflow::operators::arrange::Arranged;
18use differential_dataflow::trace::cursor::{BatchCursor, BatchKey, BatchVal};
19use differential_dataflow::trace::implementations::BatchContainer;
20use differential_dataflow::trace::{Cursor, Navigable, TraceReader};
21use differential_dataflow::{AsCollection, Data, VecCollection};
22use mz_compute_types::dataflows::DataflowDescription;
23use mz_compute_types::dyncfgs::{
24 ENABLE_COLUMN_PAGED_BATCHER, ENABLE_COMPUTE_RENDER_FUELED_AS_SPECIFIC_COLLECTION,
25 ENABLE_COMPUTE_TEMPORAL_BUCKETING, TEMPORAL_BUCKETING_SUMMARY,
26};
27use mz_compute_types::plan::scalar::{LirScalarExpr, mfp_mir_to_lir_plan, mfp_plan_lir_to_mir};
28use mz_compute_types::plan::{ArrangementStrategy, AvailableCollections};
29use mz_dyncfg::ConfigSet;
30use mz_expr::{Eval, Id, MfpPlan};
31use mz_ore::soft_assert_or_log;
32use mz_repr::fixed_length::ExtendDatums;
33use mz_repr::{DatumVec, DatumVecBorrow, Diff, GlobalId, Row, RowArena, SharedRow, StableRow};
34use mz_storage_types::controller::CollectionMetadata;
35use mz_timely_util::columnar::batcher;
36use mz_timely_util::columnar::builder::ColumnBuilder;
37use mz_timely_util::columnar::{Col2ValBatcher, Col2ValPagedBatcher, columnar_exchange};
38use mz_timely_util::columnation::ColumnationChunker;
39use timely::ContainerBuilder;
40use timely::container::{CapacityContainerBuilder, PushInto};
41use timely::dataflow::channels::pact::{ExchangeCore, Pipeline};
42use timely::dataflow::operators::Capability;
43use timely::dataflow::operators::generic::builder_rc::OperatorBuilder;
44use timely::dataflow::operators::generic::{OutputBuilder, OutputBuilderSession};
45use timely::dataflow::{Scope, Stream};
46use timely::progress::operate::FrontierInterest;
47use timely::progress::{Antichain, Timestamp};
48
49use crate::compute_state::ComputeState;
50use crate::extensions::arrange::{KeyCollection, MzArrange, MzArrangeCore};
51use crate::extensions::reduce::MzReduce;
52use crate::render::columnar::CollectionEdge;
53use crate::render::errors::{DataflowErrorSer, ErrorLogger};
54use crate::render::{LinearJoinSpec, MaybeBucketByTime, RenderTimestamp};
55use crate::typedefs::{
56 ErrAgent, ErrBatcher, ErrBuilder, ErrEnter, ErrSpine, RowRowAgent, RowRowEnter, RowRowSpine,
57};
58use mz_row_spine::{DatumSeq, RowRowBuilder, RowRowColPagedBuilder};
59
60pub struct Context<'scope, T: RenderTimestamp> {
68 pub(crate) scope: Scope<'scope, T>,
72 pub debug_name: String,
74 pub dataflow_id: usize,
76 pub export_ids: Vec<GlobalId>,
78 pub as_of_frontier: Antichain<mz_repr::Timestamp>,
83 pub until: Antichain<mz_repr::Timestamp>,
86 pub bindings: BTreeMap<Id, CollectionBundle<'scope, T>>,
88 pub(super) compute_logger: Option<crate::logging::compute::Logger>,
90 pub(super) linear_join_spec: LinearJoinSpec,
92 pub dataflow_expiration: Antichain<mz_repr::Timestamp>,
95 pub config_set: Rc<ConfigSet>,
97}
98
99impl<'scope, T: RenderTimestamp> Context<'scope, T> {
100 pub fn for_dataflow_in<Plan>(
102 dataflow: &DataflowDescription<Plan, CollectionMetadata>,
103 scope: Scope<'scope, T>,
104 compute_state: &ComputeState,
105 until: Antichain<mz_repr::Timestamp>,
106 dataflow_expiration: Antichain<mz_repr::Timestamp>,
107 ) -> Self {
108 use mz_ore::collections::CollectionExt as IteratorExt;
109 let dataflow_id = *scope.addr().into_first();
110 let as_of_frontier = dataflow
111 .as_of
112 .clone()
113 .unwrap_or_else(|| Antichain::from_elem(Timestamp::minimum()));
114
115 let export_ids = dataflow.export_ids().collect();
116
117 let compute_logger = if dataflow.is_transient() {
121 None
122 } else {
123 compute_state.compute_logger.clone()
124 };
125
126 Self {
127 scope,
128 debug_name: dataflow.debug_name.clone(),
129 dataflow_id,
130 export_ids,
131 as_of_frontier,
132 until,
133 bindings: BTreeMap::new(),
134 compute_logger,
135 linear_join_spec: compute_state.linear_join_spec,
136 dataflow_expiration,
137 config_set: Rc::clone(&compute_state.worker_config),
138 }
139 }
140}
141
142impl<'scope, T: RenderTimestamp> Context<'scope, T> {
143 pub fn insert_id(
148 &mut self,
149 id: Id,
150 collection: CollectionBundle<'scope, T>,
151 ) -> Option<CollectionBundle<'scope, T>> {
152 self.bindings.insert(id, collection)
153 }
154 pub fn remove_id(&mut self, id: Id) -> Option<CollectionBundle<'scope, T>> {
158 self.bindings.remove(&id)
159 }
160 pub fn update_id(&mut self, id: Id, collection: CollectionBundle<'scope, T>) {
162 if !self.bindings.contains_key(&id) {
163 self.bindings.insert(id, collection);
164 } else {
165 let binding = self
166 .bindings
167 .get_mut(&id)
168 .expect("Binding verified to exist");
169 if collection.collection.is_some() {
170 binding.collection = collection.collection;
171 }
172 for (key, flavor) in collection.arranged.into_iter() {
173 binding.arranged.insert(key, flavor);
174 }
175 }
176 }
177 pub fn lookup_id(&self, id: Id) -> Option<CollectionBundle<'scope, T>> {
179 self.bindings.get(&id).cloned()
180 }
181
182 pub(super) fn error_logger(&self) -> ErrorLogger {
183 ErrorLogger::new(self.debug_name.clone())
184 }
185}
186
187impl<'scope, T: RenderTimestamp> Context<'scope, T> {
188 pub fn enter_region<'a>(
190 &self,
191 region: Scope<'a, T>,
192 bindings: Option<&std::collections::BTreeSet<Id>>,
193 ) -> Context<'a, T> {
194 let bindings = self
195 .bindings
196 .iter()
197 .filter(|(key, _)| bindings.as_ref().map(|b| b.contains(key)).unwrap_or(true))
198 .map(|(key, bundle)| (*key, bundle.enter_region(region)))
199 .collect();
200
201 Context {
202 scope: region,
203 debug_name: self.debug_name.clone(),
204 dataflow_id: self.dataflow_id.clone(),
205 export_ids: self.export_ids.clone(),
206 as_of_frontier: self.as_of_frontier.clone(),
207 until: self.until.clone(),
208 compute_logger: self.compute_logger.clone(),
209 linear_join_spec: self.linear_join_spec.clone(),
210 bindings,
211 dataflow_expiration: self.dataflow_expiration.clone(),
212 config_set: Rc::clone(&self.config_set),
213 }
214 }
215}
216
217#[derive(Clone)]
219pub enum ArrangementFlavor<'scope, T: RenderTimestamp> {
220 Local(
222 Arranged<'scope, RowRowAgent<T, Diff>>,
223 Arranged<'scope, ErrAgent<T, Diff>>,
224 ),
225 Trace(
230 GlobalId,
231 Arranged<'scope, RowRowEnter<mz_repr::Timestamp, Diff, T>>,
232 Arranged<'scope, ErrEnter<mz_repr::Timestamp, T>>,
233 ),
234}
235
236impl<'scope, T: RenderTimestamp> ArrangementFlavor<'scope, T> {
237 #[deprecated(note = "Use `flat_map` instead.")]
245 pub fn as_collection(
246 &self,
247 ) -> (
248 VecCollection<'scope, T, Row, Diff>,
249 VecCollection<'scope, T, DataflowErrorSer, Diff>,
250 ) {
251 let mut datums = DatumVec::new();
252 let logic = move |k: DatumSeq, v: DatumSeq| {
253 let temp_storage = RowArena::new();
254 let mut datums_borrow = datums.borrow();
255 k.extend_datums(&temp_storage, &mut datums_borrow, None);
256 v.extend_datums(&temp_storage, &mut datums_borrow, None);
257 SharedRow::pack(&**datums_borrow)
258 };
259 match &self {
260 ArrangementFlavor::Local(oks, errs) => (
261 oks.clone().as_collection(logic),
262 errs.clone().as_collection(|k, &()| k.clone()),
263 ),
264 ArrangementFlavor::Trace(_, oks, errs) => (
265 oks.clone().as_collection(logic),
266 errs.clone().as_collection(|k, &()| k.clone()),
267 ),
268 }
269 }
270
271 pub fn flat_map<D, DCB, L>(
304 &self,
305 key: Option<&Row>,
306 max_demand: usize,
307 logic: L,
308 ) -> (
309 Stream<'scope, T, DCB::Container>,
310 VecCollection<'scope, T, DataflowErrorSer, Diff>,
311 )
312 where
313 D: Data,
314 DCB: ContainerBuilder + PushInto<(D, T, Diff)>,
315 L: for<'a, 'b> FnMut(
316 &'a mut DatumVecBorrow<'b>,
317 T,
318 Diff,
319 &mut Session<T, DCB>,
320 &mut Session<T, ECB<T>>,
321 ) -> usize
322 + 'static,
323 {
324 match &self {
327 ArrangementFlavor::Local(oks, errs) => {
328 let (oks, mfp_errs) = CollectionBundle::<T>::flat_map_core_fallible::<_, _, DCB, _>(
329 oks.clone(),
330 key,
331 max_demand,
332 logic,
333 REFUEL,
334 );
335 let errs = errs.clone().as_collection(|k, &()| k.clone());
336 let errs = errs.concat(mfp_errs.as_collection());
337 (oks, errs)
338 }
339 ArrangementFlavor::Trace(_, oks, errs) => {
340 let (oks, mfp_errs) = CollectionBundle::<T>::flat_map_core_fallible::<_, _, DCB, _>(
341 oks.clone(),
342 key,
343 max_demand,
344 logic,
345 REFUEL,
346 );
347 let errs = errs.clone().as_collection(|k, &()| k.clone());
348 let errs = errs.concat(mfp_errs.as_collection());
349 (oks, errs)
350 }
351 }
352 }
353
354 pub fn flat_map_ok<D, DCB, L>(
359 &self,
360 key: Option<&Row>,
361 max_demand: usize,
362 logic: L,
363 ) -> (
364 Stream<'scope, T, DCB::Container>,
365 VecCollection<'scope, T, DataflowErrorSer, Diff>,
366 )
367 where
368 D: Data,
369 DCB: ContainerBuilder + PushInto<(D, T, Diff)>,
370 L: for<'a, 'b> FnMut(&'a mut DatumVecBorrow<'b>, T, Diff, &mut Session<T, DCB>) -> usize
371 + 'static,
372 {
373 match &self {
374 ArrangementFlavor::Local(oks, errs) => {
375 let oks = CollectionBundle::<T>::flat_map_core_ok::<_, _, DCB, _>(
376 oks.clone(),
377 key,
378 max_demand,
379 logic,
380 REFUEL,
381 );
382 let errs = errs.clone().as_collection(|k, &()| k.clone());
383 (oks, errs)
384 }
385 ArrangementFlavor::Trace(_, oks, errs) => {
386 let oks = CollectionBundle::<T>::flat_map_core_ok::<_, _, DCB, _>(
387 oks.clone(),
388 key,
389 max_demand,
390 logic,
391 REFUEL,
392 );
393 let errs = errs.clone().as_collection(|k, &()| k.clone());
394 (oks, errs)
395 }
396 }
397 }
398}
399impl<'scope, T: RenderTimestamp> ArrangementFlavor<'scope, T> {
400 pub fn scope(&self) -> Scope<'scope, T> {
402 match self {
403 ArrangementFlavor::Local(oks, _errs) => oks.stream.scope(),
404 ArrangementFlavor::Trace(_gid, oks, _errs) => oks.stream.scope(),
405 }
406 }
407
408 pub fn enter_region<'a>(&self, region: Scope<'a, T>) -> ArrangementFlavor<'a, T> {
410 match self {
411 ArrangementFlavor::Local(oks, errs) => ArrangementFlavor::Local(
412 oks.clone().enter_region(region),
413 errs.clone().enter_region(region),
414 ),
415 ArrangementFlavor::Trace(gid, oks, errs) => ArrangementFlavor::Trace(
416 *gid,
417 oks.clone().enter_region(region),
418 errs.clone().enter_region(region),
419 ),
420 }
421 }
422}
423impl<'scope, T: RenderTimestamp> ArrangementFlavor<'scope, T> {
424 pub fn leave_region<'outer>(&self, outer: Scope<'outer, T>) -> ArrangementFlavor<'outer, T> {
426 match self {
427 ArrangementFlavor::Local(oks, errs) => ArrangementFlavor::Local(
428 oks.clone().leave_region(outer),
429 errs.clone().leave_region(outer),
430 ),
431 ArrangementFlavor::Trace(gid, oks, errs) => ArrangementFlavor::Trace(
432 *gid,
433 oks.clone().leave_region(outer),
434 errs.clone().leave_region(outer),
435 ),
436 }
437 }
438}
439pub(crate) fn distinct_arranged_errs<'a, T: RenderTimestamp>(
451 errs: Arranged<'a, ErrAgent<T, Diff>>,
452 name: &str,
453) -> Arranged<'a, ErrAgent<T, Diff>> {
454 errs.mz_reduce_abelian::<_, ErrBuilder<_, _>, ErrSpine<_, _>, _>(
455 name,
456 |_err, _input, output| output.push(((), Diff::ONE)),
457 )
458}
459
460pub(crate) fn distinct_errs_collection<'a, T: RenderTimestamp>(
465 errs: VecCollection<'a, T, DataflowErrorSer, Diff>,
466) -> VecCollection<'a, T, DataflowErrorSer, Diff> {
467 let errs: KeyCollection<_, _, _> = errs.into();
468 let errs = errs
469 .mz_arrange::<ColumnationChunker<_>, ErrBatcher<_, _>, ErrBuilder<_, _>, ErrSpine<_, _>>(
470 "Arrange errors",
471 );
472 distinct_arranged_errs(errs, "Distinct errors").as_collection(|err, _| err.clone())
473}
474
475#[derive(Clone)]
480pub struct CollectionBundle<'scope, T: RenderTimestamp> {
481 pub collection: Option<(
482 CollectionEdge<'scope, T>,
483 VecCollection<'scope, T, DataflowErrorSer, Diff>,
484 )>,
485 pub arranged: BTreeMap<Vec<LirScalarExpr>, ArrangementFlavor<'scope, T>>,
486}
487
488impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
489 pub fn from_collections(
491 oks: VecCollection<'scope, T, Row, Diff>,
492 errs: VecCollection<'scope, T, DataflowErrorSer, Diff>,
493 ) -> Self {
494 Self::from_edge(CollectionEdge::Vec(oks), errs)
495 }
496
497 pub fn from_edge(
499 oks: CollectionEdge<'scope, T>,
500 errs: VecCollection<'scope, T, DataflowErrorSer, Diff>,
501 ) -> Self {
502 Self {
503 collection: Some((oks, errs)),
504 arranged: BTreeMap::default(),
505 }
506 }
507
508 pub fn from_expressions(
510 exprs: Vec<LirScalarExpr>,
511 arrangements: ArrangementFlavor<'scope, T>,
512 ) -> Self {
513 let mut arranged = BTreeMap::new();
514 arranged.insert(exprs, arrangements);
515 Self {
516 collection: None,
517 arranged,
518 }
519 }
520
521 pub fn from_columns<I: IntoIterator<Item = usize>>(
523 columns: I,
524 arrangements: ArrangementFlavor<'scope, T>,
525 ) -> Self {
526 let mut keys = Vec::new();
527 for column in columns {
528 keys.push(LirScalarExpr::column(column));
529 }
530 Self::from_expressions(keys, arrangements)
531 }
532
533 pub fn scope(&self) -> Scope<'scope, T> {
535 if let Some((oks, _errs)) = &self.collection {
536 oks.scope()
537 } else {
538 self.arranged
539 .values()
540 .next()
541 .expect("Must contain a valid collection")
542 .scope()
543 }
544 }
545
546 pub fn distinct_errs(mut self) -> Self {
572 if let Some((oks, errs)) = self.collection.take() {
573 self.collection = Some((oks, distinct_errs_collection(errs)));
574 }
575 for (key, flavor) in std::mem::take(&mut self.arranged) {
576 let flavor = match flavor {
577 ArrangementFlavor::Local(oks, errs) => {
578 let name = format!("Distinct errors[{key:?}]");
581 ArrangementFlavor::Local(oks, distinct_arranged_errs(errs, &name))
582 }
583 flavor @ ArrangementFlavor::Trace(..) => flavor,
584 };
585 self.arranged.insert(key, flavor);
586 }
587 self
588 }
589
590 pub fn enter_region<'inner>(&self, region: Scope<'inner, T>) -> CollectionBundle<'inner, T> {
592 CollectionBundle {
593 collection: self.collection.as_ref().map(|(oks, errs)| {
594 (
595 oks.clone().enter_region(region),
596 errs.clone().enter_region(region),
597 )
598 }),
599 arranged: self
600 .arranged
601 .iter()
602 .map(|(key, bundle)| (key.clone(), bundle.enter_region(region)))
603 .collect(),
604 }
605 }
606}
607
608impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
609 pub fn leave_region<'outer>(&self, outer: Scope<'outer, T>) -> CollectionBundle<'outer, T> {
611 CollectionBundle {
612 collection: self.collection.as_ref().map(|(oks, errs)| {
613 (
614 oks.clone().leave_region(outer),
615 errs.clone().leave_region(outer),
616 )
617 }),
618 arranged: self
619 .arranged
620 .iter()
621 .map(|(key, bundle)| (key.clone(), bundle.leave_region(outer)))
622 .collect(),
623 }
624 }
625}
626
627impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
628 pub fn as_specific_collection(
641 &self,
642 key: Option<&[LirScalarExpr]>,
643 config_set: &ConfigSet,
644 ) -> (
645 VecCollection<'scope, T, Row, Diff>,
646 VecCollection<'scope, T, DataflowErrorSer, Diff>,
647 ) {
648 match key {
654 None => {
655 let (oks, errs) = self
656 .collection
657 .clone()
658 .expect("The unarranged collection doesn't exist.");
659 (oks.into_vec(), errs)
660 }
661 Some(key) => {
662 let arranged = self.arranged.get(key).unwrap_or_else(|| {
663 panic!("The collection arranged by {:?} doesn't exist.", key)
664 });
665 if ENABLE_COMPUTE_RENDER_FUELED_AS_SPECIFIC_COLLECTION.get(config_set) {
666 let (ok, err) = arranged
670 .flat_map_ok::<_, CapacityContainerBuilder<Vec<(Row, T, Diff)>>, _>(
671 None,
672 usize::MAX,
673 |borrow, t, r, ok_session| {
674 ok_session.give((SharedRow::pack(borrow.iter()), t, r));
675 1
676 },
677 );
678 (ok.as_collection(), err)
679 } else {
680 #[allow(deprecated)]
681 arranged.as_collection()
682 }
683 }
684 }
685 }
686
687 pub fn flat_map<D, DCB, L>(
703 &self,
704 key_val: Option<(Vec<LirScalarExpr>, Option<Row>)>,
705 max_demand: usize,
706 logic: L,
707 ) -> (
708 Stream<'scope, T, DCB::Container>,
709 VecCollection<'scope, T, DataflowErrorSer, Diff>,
710 )
711 where
712 D: Data,
713 DCB: ContainerBuilder + PushInto<(D, T, Diff)>,
714 L: for<'a> FnMut(
715 &'a mut DatumVecBorrow<'_>,
716 T,
717 Diff,
718 &mut Session<T, DCB>,
719 &mut Session<T, ECB<T>>,
720 ) -> usize
721 + 'static,
722 {
723 if let Some((key, val)) = key_val {
727 self.arrangement(&key)
728 .expect("Should have ensured during planning that this arrangement exists.")
729 .flat_map::<_, DCB, _>(val.as_ref(), max_demand, logic)
730 } else {
731 let (oks, errs) = self
732 .collection
733 .clone()
734 .expect("Invariant violated: CollectionBundle contains no collection.");
735 let (ok_stream, err_stream) = oks.flat_map_datums::<DCB, _>(max_demand, logic);
736 let errs = errs.concat(err_stream.as_collection());
737 (ok_stream, errs)
738 }
739 }
740
741 fn flat_map_core_fallible<Tr, D, DCB, L>(
752 trace: Arranged<'scope, Tr>,
753 key: Option<&<<BatchCursor<Tr> as Cursor>::KeyContainer as BatchContainer>::Owned>,
754 max_demand: usize,
755 mut logic: L,
756 refuel: usize,
757 ) -> (
758 Stream<'scope, T, DCB::Container>,
759 Stream<'scope, T, Vec<(DataflowErrorSer, T, Diff)>>,
760 )
761 where
762 Tr: TraceReader<Batch: Navigable, Time = T> + Clone + 'static,
763 for<'a> BatchCursor<Tr>:
764 Cursor<Key<'a>: ExtendDatums, Val<'a>: ExtendDatums, Time = T, Diff = mz_repr::Diff>,
765 <<BatchCursor<Tr> as Cursor>::KeyContainer as BatchContainer>::Owned: PartialEq,
766 D: Data,
767 DCB: ContainerBuilder + PushInto<(D, T, Diff)>,
768 L: for<'a, 'b> FnMut(
772 &'a mut DatumVecBorrow<'b>,
773 T,
774 mz_repr::Diff,
775 &mut Session<T, DCB>,
776 &mut Session<T, ECB<T>>,
777 ) -> usize
778 + 'static,
779 {
780 let scope = trace.stream.scope();
781
782 let mut key_con = <BatchCursor<Tr> as Cursor>::KeyContainer::with_capacity(1);
783 if let Some(key) = &key {
784 key_con.push_own(key);
785 }
786 let mode = if key.is_some() { "index" } else { "scan" };
787 let name = format!("ArrangementFlatMap({})", mode);
788
789 let mut builder = OperatorBuilder::new(name, scope.clone());
790 let (ok_output, ok_stream) = builder.new_output();
791 let mut ok_output = OutputBuilder::<_, DCB>::from(ok_output);
792 let (err_output, err_stream) = builder.new_output();
793 let mut err_output = OutputBuilder::<_, ECB<T>>::from(err_output);
794 let mut input = builder.new_input(trace.stream.clone(), Pipeline);
795 let operator_info = builder.operator_info();
796
797 builder.build(move |_capabilities| {
798 let activator = scope.activator_for(operator_info.address);
800 let mut todo = std::collections::VecDeque::new();
802 move |_frontiers| {
803 let key = key_con.get(0);
804 let mut ok_output = ok_output.activate();
805 let mut err_output = err_output.activate();
806
807 input.for_each(|time, data| {
809 let ok_cap = time.retain(0);
812 let err_cap = time.retain(1);
813 for batch in data.iter() {
814 todo.push_back(PendingWork::new(
815 ok_cap.clone(),
816 err_cap.clone(),
817 batch.cursor(),
818 batch.clone(),
819 ));
820 }
821 });
822
823 let mut temp_storage = RowArena::new();
828 let mut datums = DatumVec::new();
829 let mut decode_logic =
830 |k: BatchKey<'_, Tr>,
831 v: BatchVal<'_, Tr>,
832 t: T,
833 d: mz_repr::Diff,
834 ok_session: &mut Session<T, DCB>,
835 err_session: &mut Session<T, ECB<T>>| {
836 temp_storage.clear();
837 let mut datums_borrow = datums.borrow();
838 k.extend_datums(&temp_storage, &mut datums_borrow, Some(max_demand));
839 let remaining = max_demand.saturating_sub(datums_borrow.len());
840 v.extend_datums(&temp_storage, &mut datums_borrow, Some(remaining));
841 logic(&mut datums_borrow, t, d, ok_session, err_session)
842 };
843
844 let mut fuel = refuel;
846 while !todo.is_empty() && fuel > 0 {
847 todo.front_mut().unwrap().do_work(
848 key.as_ref(),
849 &mut decode_logic,
850 &mut fuel,
851 &mut ok_output,
852 &mut err_output,
853 );
854 if fuel > 0 {
855 todo.pop_front();
856 }
857 }
858 if !todo.is_empty() {
860 activator.activate();
861 }
862 }
863 });
864
865 (ok_stream, err_stream)
866 }
867
868 fn flat_map_core_ok<Tr, D, DCB, L>(
874 trace: Arranged<'scope, Tr>,
875 key: Option<&<<BatchCursor<Tr> as Cursor>::KeyContainer as BatchContainer>::Owned>,
876 max_demand: usize,
877 mut logic: L,
878 refuel: usize,
879 ) -> Stream<'scope, T, DCB::Container>
880 where
881 Tr: TraceReader<Batch: Navigable, Time = T> + Clone + 'static,
882 for<'a> BatchCursor<Tr>:
883 Cursor<Key<'a>: ExtendDatums, Val<'a>: ExtendDatums, Time = T, Diff = mz_repr::Diff>,
884 <<BatchCursor<Tr> as Cursor>::KeyContainer as BatchContainer>::Owned: PartialEq,
885 D: Data,
886 DCB: ContainerBuilder + PushInto<(D, T, Diff)>,
887 L: for<'a, 'b> FnMut(
890 &'a mut DatumVecBorrow<'b>,
891 T,
892 mz_repr::Diff,
893 &mut Session<T, DCB>,
894 ) -> usize
895 + 'static,
896 {
897 let scope = trace.stream.scope();
898
899 let mut key_con = <BatchCursor<Tr> as Cursor>::KeyContainer::with_capacity(1);
900 if let Some(key) = &key {
901 key_con.push_own(key);
902 }
903 let mode = if key.is_some() { "index" } else { "scan" };
904 let name = format!("ArrangementFlatMapOk({})", mode);
905
906 let mut builder = OperatorBuilder::new(name, scope.clone());
907 let (ok_output, ok_stream) = builder.new_output();
908 let mut ok_output = OutputBuilder::<_, DCB>::from(ok_output);
909 let mut input = builder.new_input(trace.stream.clone(), Pipeline);
910 let operator_info = builder.operator_info();
911
912 builder.build(move |_capabilities| {
913 let activator = scope.activator_for(operator_info.address);
914 let mut todo = std::collections::VecDeque::new();
915 move |_frontiers| {
916 let key = key_con.get(0);
917 let mut ok_output = ok_output.activate();
918
919 input.for_each(|time, data| {
920 let cap = time.retain(0);
921 for batch in data.iter() {
922 todo.push_back(PendingWorkOk::new(
923 cap.clone(),
924 batch.cursor(),
925 batch.clone(),
926 ));
927 }
928 });
929
930 let mut temp_storage = RowArena::new();
932 let mut datums = DatumVec::new();
933 let mut decode_logic =
934 |k: BatchKey<'_, Tr>,
935 v: BatchVal<'_, Tr>,
936 t: T,
937 d: mz_repr::Diff,
938 ok_session: &mut Session<T, DCB>| {
939 temp_storage.clear();
940 let mut datums_borrow = datums.borrow();
941 k.extend_datums(&temp_storage, &mut datums_borrow, Some(max_demand));
942 let remaining = max_demand.saturating_sub(datums_borrow.len());
943 v.extend_datums(&temp_storage, &mut datums_borrow, Some(remaining));
944 logic(&mut datums_borrow, t, d, ok_session)
945 };
946
947 let mut fuel = refuel;
948 while !todo.is_empty() && fuel > 0 {
949 todo.front_mut().unwrap().do_work(
950 key.as_ref(),
951 &mut decode_logic,
952 &mut fuel,
953 &mut ok_output,
954 );
955 if fuel > 0 {
956 todo.pop_front();
957 }
958 }
959 if !todo.is_empty() {
960 activator.activate();
961 }
962 }
963 });
964
965 ok_stream
966 }
967
968 pub fn arrangement(&self, key: &[LirScalarExpr]) -> Option<ArrangementFlavor<'scope, T>> {
973 self.arranged.get(key).map(|x| x.clone())
974 }
975}
976
977impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
978 pub fn as_collection_core(
987 &self,
988 mfp_plan: MfpPlan<LirScalarExpr>,
989 key_val: Option<(Vec<LirScalarExpr>, Option<StableRow>)>,
990 until: Antichain<mz_repr::Timestamp>,
991 config_set: &ConfigSet,
992 ) -> (
993 VecCollection<'scope, T, mz_repr::Row, Diff>,
994 VecCollection<'scope, T, DataflowErrorSer, Diff>,
995 ) {
996 let key_val = key_val.map(|(key, val)| (key, val.map(|val| val.0)));
999 let has_key_val = if let Some((_key, Some(_val))) = &key_val {
1005 true
1006 } else {
1007 false
1008 };
1009
1010 if mfp_plan.is_identity() && !has_key_val {
1011 let key = key_val.map(|(k, _v)| k);
1012 return self.as_specific_collection(key.as_deref(), config_set);
1013 }
1014
1015 let (mfp_plan, max_demand) = {
1020 let mut mir_mfp = mfp_plan_lir_to_mir(mfp_plan).into_map_filter_project();
1021 let max_demand = mir_mfp.demand().last().map(|x| *x + 1).unwrap_or(0);
1022 mir_mfp.permute_fn(|c| c, max_demand);
1023 mir_mfp.optimize();
1024 let plan = mfp_mir_to_lir_plan(mir_mfp);
1025 (plan, max_demand)
1026 };
1027
1028 let mut datum_vec = DatumVec::new();
1029 let until = std::rc::Rc::new(until);
1031
1032 let (stream, errors) = self
1033 .flat_map::<_, ConsolidatingContainerBuilder<Vec<(Row, T, Diff)>>, _>(
1034 key_val,
1035 max_demand,
1036 move |row_datums, time, diff, ok_session, err_session| {
1037 let mut row_builder = SharedRow::get();
1038 let until = std::rc::Rc::clone(&until);
1039 let temp_storage = RowArena::new();
1040 let row_iter = row_datums.iter();
1041 let mut datums_local = datum_vec.borrow();
1042 datums_local.extend(row_iter);
1043 let event_time = time.event_time();
1044 let mut work: usize = 0;
1045 for result in mfp_plan.evaluate(
1046 &mut datums_local,
1047 &temp_storage,
1048 event_time,
1049 diff.clone(),
1050 move |time| !until.less_equal(time),
1051 &mut row_builder,
1052 ) {
1053 work += 1;
1054 match result {
1055 Ok((row, event_time, diff)) => {
1056 let mut time: T = time.clone();
1058 *time.event_time_mut() = event_time;
1059 ok_session.give((row, time, diff));
1060 }
1061 Err((e, event_time, diff)) => {
1062 let mut time: T = time.clone();
1064 *time.event_time_mut() = event_time;
1065 err_session.give((e, time, diff));
1066 }
1067 }
1068 }
1069 work
1070 },
1071 );
1072
1073 (stream.as_collection(), errors)
1074 }
1075 pub fn ensure_collections(
1076 mut self,
1077 collections: AvailableCollections,
1078 input_key: Option<Vec<LirScalarExpr>>,
1079 input_mfp: MfpPlan<LirScalarExpr>,
1080 as_of: Antichain<mz_repr::Timestamp>,
1081 until: Antichain<mz_repr::Timestamp>,
1082 config_set: &ConfigSet,
1083 strategy: ArrangementStrategy,
1084 ) -> Self
1085 where
1086 T: MaybeBucketByTime,
1087 {
1088 if collections == Default::default() {
1089 return self;
1090 }
1091 for (key, _, _) in collections.arranged.iter() {
1100 soft_assert_or_log!(
1101 !self.arranged.contains_key(key),
1102 "LIR ArrangeBy tried to create an existing arrangement"
1103 );
1104 }
1105
1106 let mut bucketed = false;
1109
1110 let will_create_arrangement = collections
1114 .arranged
1115 .iter()
1116 .any(|(key, _, _)| !self.arranged.contains_key(key));
1117
1118 let form_raw_collection = collections.raw || will_create_arrangement;
1120 if form_raw_collection && self.collection.is_none() {
1121 let (oks, errs) =
1122 self.as_collection_core(input_mfp, input_key.map(|k| (k, None)), until, config_set);
1123 let effective_strategy = if will_create_arrangement {
1127 strategy
1128 } else {
1129 ArrangementStrategy::Direct
1130 };
1131 let oks = if matches!(effective_strategy, ArrangementStrategy::TemporalBucketing)
1132 && ENABLE_COMPUTE_TEMPORAL_BUCKETING.get(config_set)
1133 {
1134 let summary: mz_repr::Timestamp = TEMPORAL_BUCKETING_SUMMARY
1135 .get(config_set)
1136 .try_into()
1137 .expect("must fit");
1138 bucketed = true;
1139 T::maybe_apply_temporal_bucketing(oks.inner, as_of.clone(), summary)
1140 } else {
1141 oks
1142 };
1143 self.collection = Some((CollectionEdge::Vec(oks), errs));
1144 }
1145 for (key, _, thinning) in collections.arranged {
1146 if !self.arranged.contains_key(&key) {
1147 let name = format!("ArrangeBy[{:?}]", key);
1149
1150 let (oks, errs) = self
1151 .collection
1152 .take()
1153 .expect("Collection constructed above");
1154 let oks = oks.into_vec();
1155 let effective_strategy = if bucketed {
1160 ArrangementStrategy::Direct
1161 } else {
1162 strategy
1163 };
1164 let oks = if matches!(effective_strategy, ArrangementStrategy::TemporalBucketing)
1165 && ENABLE_COMPUTE_TEMPORAL_BUCKETING.get(config_set)
1166 {
1167 let summary: mz_repr::Timestamp = TEMPORAL_BUCKETING_SUMMARY
1168 .get(config_set)
1169 .try_into()
1170 .expect("must fit");
1171 bucketed = true;
1172 T::maybe_apply_temporal_bucketing(oks.inner, as_of.clone(), summary)
1173 } else {
1174 oks
1175 };
1176 let use_paged_path = ENABLE_COLUMN_PAGED_BATCHER.get(config_set);
1177 let (oks, errs_keyed, passthrough) = Self::arrange_collection(
1178 &name,
1179 oks,
1180 key.clone(),
1181 thinning.clone(),
1182 use_paged_path,
1183 );
1184 let errs_concat: KeyCollection<_, _, _> = errs.clone().concat(errs_keyed).into();
1185 self.collection = Some((CollectionEdge::Vec(passthrough), errs));
1186 let errs =
1187 errs_concat.mz_arrange::<
1188 ColumnationChunker<_>,
1189 ErrBatcher<_, _>,
1190 ErrBuilder<_, _>,
1191 ErrSpine<_, _>,
1192 >(
1193 &format!("{}-errors", name),
1194 );
1195 self.arranged
1196 .insert(key, ArrangementFlavor::Local(oks, errs));
1197 }
1198 }
1199 self
1200 }
1201
1202 fn arrange_collection(
1213 name: &String,
1214 oks: VecCollection<'scope, T, Row, Diff>,
1215 key: Vec<LirScalarExpr>,
1216 thinning: Vec<usize>,
1217 use_paged_path: bool,
1218 ) -> (
1219 Arranged<'scope, RowRowAgent<T, Diff>>,
1220 VecCollection<'scope, T, DataflowErrorSer, Diff>,
1221 VecCollection<'scope, T, Row, Diff>,
1222 ) {
1223 let mut builder = OperatorBuilder::new("FormArrangementKey".to_string(), oks.inner.scope());
1228 let (ok_output, ok_stream) = builder.new_output();
1229 let mut ok_output =
1230 OutputBuilder::<_, ColumnBuilder<((Row, Row), T, Diff)>>::from(ok_output);
1231 let (err_output, err_stream) = builder.new_output();
1232 let mut err_output = OutputBuilder::from(err_output);
1233 let (passthrough_output, passthrough_stream) = builder.new_output();
1234 let mut passthrough_output = OutputBuilder::from(passthrough_output);
1235 let mut input = builder.new_input(oks.inner, Pipeline);
1236 builder.set_notify_for(0, FrontierInterest::Never);
1237 builder.build(move |_capabilities| {
1238 let mut key_buf = Row::default();
1239 let mut val_buf = Row::default();
1240 let mut datums = DatumVec::new();
1241 move |_frontiers| {
1242 let mut temp_storage = RowArena::new();
1245 let mut ok_output = ok_output.activate();
1246 let mut err_output = err_output.activate();
1247 let mut passthrough_output = passthrough_output.activate();
1248 input.for_each(|time, data| {
1249 let mut ok_session = ok_output.session_with_builder(&time);
1250 let mut err_session = err_output.session(&time);
1251 for (row, time, diff) in data.iter() {
1252 temp_storage.clear();
1253 let datums = datums.borrow_with(row);
1254 let key_iter = key.iter().map(|k| k.eval(&datums, &temp_storage));
1255 match key_buf.packer().try_extend(key_iter) {
1256 Ok(()) => {
1257 let val_datum_iter = thinning.iter().map(|c| datums[*c]);
1258 val_buf.packer().extend(val_datum_iter);
1259 ok_session.give(((&*key_buf, &*val_buf), time, diff));
1260 }
1261 Err(e) => {
1262 err_session.give((e.into(), time.clone(), *diff));
1263 }
1264 }
1265 }
1266 passthrough_output.session(&time).give_container(data);
1267 });
1268 }
1269 });
1270
1271 let exchange =
1272 ExchangeCore::<ColumnBuilder<_>, _>::new_core(columnar_exchange::<Row, Row, T, Diff>);
1273 let oks = if use_paged_path {
1274 ok_stream.mz_arrange_core::<
1275 _,
1276 batcher::ColumnChunker<_>,
1277 Col2ValPagedBatcher<_, _, _, _>,
1278 RowRowColPagedBuilder<_, _>,
1279 RowRowSpine<_, _>,
1280 >(exchange, name)
1281 } else {
1282 ok_stream.mz_arrange_core::<
1283 _,
1284 batcher::Chunker<_>,
1285 Col2ValBatcher<_, _, _, _>,
1286 RowRowBuilder<_, _>,
1287 RowRowSpine<_, _>,
1288 >(exchange, name)
1289 };
1290 (
1291 oks,
1292 err_stream.as_collection(),
1293 passthrough_stream.as_collection(),
1294 )
1295 }
1296}
1297
1298pub(crate) type Session<'a, 'b, T, CB> =
1302 timely::dataflow::operators::generic::Session<'a, 'b, T, CB, Capability<T>>;
1303
1304pub(crate) type ECB<T> = ConsolidatingContainerBuilder<Vec<(DataflowErrorSer, T, Diff)>>;
1309
1310const REFUEL: usize = 1_000_000;
1314
1315struct PendingWork<C>
1316where
1317 C: Cursor,
1318{
1319 ok_capability: Capability<C::Time>,
1321 err_capability: Capability<C::Time>,
1323 cursor: C,
1324 batch: C::Storage,
1325}
1326
1327impl<C> PendingWork<C>
1328where
1329 C: Cursor<KeyContainer: BatchContainer<Owned: PartialEq + Sized>>,
1330{
1331 fn new(
1334 ok_capability: Capability<C::Time>,
1335 err_capability: Capability<C::Time>,
1336 cursor: C,
1337 batch: C::Storage,
1338 ) -> Self {
1339 Self {
1340 ok_capability,
1341 err_capability,
1342 cursor,
1343 batch,
1344 }
1345 }
1346 fn do_work<D, DCB, L>(
1349 &mut self,
1350 key: Option<&C::Key<'_>>,
1351 logic: &mut L,
1352 fuel: &mut usize,
1353 ok_output: &mut OutputBuilderSession<'_, C::Time, DCB>,
1354 err_output: &mut OutputBuilderSession<'_, C::Time, ECB<C::Time>>,
1355 ) where
1356 D: Data,
1357 DCB: ContainerBuilder + PushInto<(D, C::Time, C::Diff)>,
1358 L: FnMut(
1359 C::Key<'_>,
1360 C::Val<'_>,
1361 C::Time,
1362 C::Diff,
1363 &mut Session<C::Time, DCB>,
1364 &mut Session<C::Time, ECB<C::Time>>,
1365 ) -> usize,
1366 {
1367 let mut ok_session = ok_output.session_with_builder(&self.ok_capability);
1368 let mut err_session = err_output.session_with_builder(&self.err_capability);
1369 walk_cursor(&mut self.cursor, &self.batch, key, fuel, |k, v, t, d| {
1370 logic(k, v, t, d, &mut ok_session, &mut err_session)
1371 });
1372 }
1373}
1374
1375struct PendingWorkOk<C>
1378where
1379 C: Cursor,
1380{
1381 capability: Capability<C::Time>,
1382 cursor: C,
1383 batch: C::Storage,
1384}
1385
1386impl<C> PendingWorkOk<C>
1387where
1388 C: Cursor<KeyContainer: BatchContainer<Owned: PartialEq + Sized>>,
1389{
1390 fn new(capability: Capability<C::Time>, cursor: C, batch: C::Storage) -> Self {
1391 Self {
1392 capability,
1393 cursor,
1394 batch,
1395 }
1396 }
1397
1398 fn do_work<D, DCB, L>(
1401 &mut self,
1402 key: Option<&C::Key<'_>>,
1403 logic: &mut L,
1404 fuel: &mut usize,
1405 ok_output: &mut OutputBuilderSession<'_, C::Time, DCB>,
1406 ) where
1407 D: Data,
1408 DCB: ContainerBuilder + PushInto<(D, C::Time, C::Diff)>,
1409 L: FnMut(C::Key<'_>, C::Val<'_>, C::Time, C::Diff, &mut Session<C::Time, DCB>) -> usize,
1410 {
1411 let mut ok_session = ok_output.session_with_builder(&self.capability);
1412 walk_cursor(&mut self.cursor, &self.batch, key, fuel, |k, v, t, d| {
1413 logic(k, v, t, d, &mut ok_session)
1414 });
1415 }
1416}
1417
1418fn walk_cursor<C, F>(
1428 cursor: &mut C,
1429 batch: &C::Storage,
1430 key: Option<&C::Key<'_>>,
1431 fuel: &mut usize,
1432 mut emit: F,
1433) where
1434 C: Cursor<KeyContainer: BatchContainer<Owned: PartialEq + Sized>>,
1435 F: FnMut(C::Key<'_>, C::Val<'_>, C::Time, C::Diff) -> usize,
1436{
1437 use differential_dataflow::consolidation::consolidate;
1438
1439 let mut work: usize = 0;
1440 let mut buffer = Vec::new();
1441 if let Some(key) = key {
1442 let key = C::KeyContainer::reborrow(*key);
1443 if cursor.get_key(batch).map(|k| k == key) != Some(true) {
1444 cursor.seek_key(batch, key);
1445 }
1446 if cursor.get_key(batch).map(|k| k == key) == Some(true) {
1447 let key = cursor.key(batch);
1448 while let Some(val) = cursor.get_val(batch) {
1449 cursor.map_times(batch, |time, diff| {
1450 buffer.push((C::owned_time(time), C::owned_diff(diff)));
1451 });
1452 consolidate(&mut buffer);
1453 for (time, diff) in buffer.drain(..) {
1454 work += emit(key, val, time, diff);
1455 }
1456 cursor.step_val(batch);
1457 if work >= *fuel {
1458 *fuel = 0;
1459 return;
1460 }
1461 }
1462 }
1463 } else {
1464 while let Some(key) = cursor.get_key(batch) {
1465 while let Some(val) = cursor.get_val(batch) {
1466 cursor.map_times(batch, |time, diff| {
1467 buffer.push((C::owned_time(time), C::owned_diff(diff)));
1468 });
1469 consolidate(&mut buffer);
1470 for (time, diff) in buffer.drain(..) {
1471 work += emit(key, val, time, diff);
1472 }
1473 cursor.step_val(batch);
1474 if work >= *fuel {
1475 *fuel = 0;
1476 return;
1477 }
1478 }
1479 cursor.step_key(batch);
1480 }
1481 }
1482 *fuel -= work;
1483}