1use std::collections::BTreeMap;
14use std::rc::Rc;
15
16use differential_dataflow::consolidation::ConsolidatingContainerBuilder;
17use differential_dataflow::operators::arrange::Arranged;
18use differential_dataflow::trace::implementations::BatchContainer;
19use differential_dataflow::trace::{BatchReader, Cursor, TraceReader};
20use differential_dataflow::{AsCollection, Data, VecCollection};
21use mz_compute_types::dataflows::DataflowDescription;
22use mz_compute_types::dyncfgs::{
23 ENABLE_COLUMN_PAGED_BATCHER, ENABLE_COMPUTE_RENDER_FUELED_AS_SPECIFIC_COLLECTION,
24 ENABLE_COMPUTE_TEMPORAL_BUCKETING, TEMPORAL_BUCKETING_SUMMARY,
25};
26use mz_compute_types::plan::scalar::{LirScalarExpr, mfp_mir_to_lir_plan, mfp_plan_lir_to_mir};
27use mz_compute_types::plan::{ArrangementStrategy, AvailableCollections};
28use mz_dyncfg::ConfigSet;
29use mz_expr::{Eval, Id, MfpPlan};
30use mz_ore::soft_assert_or_log;
31use mz_repr::fixed_length::ExtendDatums;
32use mz_repr::{DatumVec, DatumVecBorrow, Diff, GlobalId, Row, RowArena, SharedRow};
33use mz_storage_types::controller::CollectionMetadata;
34use mz_timely_util::columnar::batcher;
35use mz_timely_util::columnar::builder::ColumnBuilder;
36use mz_timely_util::columnar::{Col2ValBatcher, Col2ValPagedBatcher, columnar_exchange};
37use mz_timely_util::columnation::ColumnationChunker;
38use timely::ContainerBuilder;
39use timely::container::{CapacityContainerBuilder, PushInto};
40use timely::dataflow::channels::pact::{ExchangeCore, Pipeline};
41use timely::dataflow::operators::Capability;
42use timely::dataflow::operators::generic::builder_rc::OperatorBuilder;
43use timely::dataflow::operators::generic::{OutputBuilder, OutputBuilderSession};
44use timely::dataflow::{Scope, Stream};
45use timely::progress::operate::FrontierInterest;
46use timely::progress::{Antichain, Timestamp};
47
48use crate::compute_state::ComputeState;
49use crate::extensions::arrange::{KeyCollection, MzArrange, MzArrangeCore};
50use crate::render::errors::{DataflowErrorSer, ErrorLogger};
51use crate::render::{LinearJoinSpec, MaybeBucketByTime, RenderTimestamp};
52use crate::typedefs::{
53 ErrAgent, ErrBatcher, ErrBuilder, ErrEnter, ErrSpine, RowRowAgent, RowRowEnter, RowRowSpine,
54};
55use mz_row_spine::{DatumSeq, RowRowBuilder, RowRowColPagedBuilder};
56
57pub struct Context<'scope, T: RenderTimestamp> {
65 pub(crate) scope: Scope<'scope, T>,
69 pub debug_name: String,
71 pub dataflow_id: usize,
73 pub export_ids: Vec<GlobalId>,
75 pub as_of_frontier: Antichain<mz_repr::Timestamp>,
80 pub until: Antichain<mz_repr::Timestamp>,
83 pub bindings: BTreeMap<Id, CollectionBundle<'scope, T>>,
85 pub(super) compute_logger: Option<crate::logging::compute::Logger>,
87 pub(super) linear_join_spec: LinearJoinSpec,
89 pub dataflow_expiration: Antichain<mz_repr::Timestamp>,
92 pub config_set: Rc<ConfigSet>,
94}
95
96impl<'scope, T: RenderTimestamp> Context<'scope, T> {
97 pub fn for_dataflow_in<Plan>(
99 dataflow: &DataflowDescription<Plan, CollectionMetadata>,
100 scope: Scope<'scope, T>,
101 compute_state: &ComputeState,
102 until: Antichain<mz_repr::Timestamp>,
103 dataflow_expiration: Antichain<mz_repr::Timestamp>,
104 ) -> Self {
105 use mz_ore::collections::CollectionExt as IteratorExt;
106 let dataflow_id = *scope.addr().into_first();
107 let as_of_frontier = dataflow
108 .as_of
109 .clone()
110 .unwrap_or_else(|| Antichain::from_elem(Timestamp::minimum()));
111
112 let export_ids = dataflow.export_ids().collect();
113
114 let compute_logger = if dataflow.is_transient() {
118 None
119 } else {
120 compute_state.compute_logger.clone()
121 };
122
123 Self {
124 scope,
125 debug_name: dataflow.debug_name.clone(),
126 dataflow_id,
127 export_ids,
128 as_of_frontier,
129 until,
130 bindings: BTreeMap::new(),
131 compute_logger,
132 linear_join_spec: compute_state.linear_join_spec,
133 dataflow_expiration,
134 config_set: Rc::clone(&compute_state.worker_config),
135 }
136 }
137}
138
139impl<'scope, T: RenderTimestamp> Context<'scope, T> {
140 pub fn insert_id(
145 &mut self,
146 id: Id,
147 collection: CollectionBundle<'scope, T>,
148 ) -> Option<CollectionBundle<'scope, T>> {
149 self.bindings.insert(id, collection)
150 }
151 pub fn remove_id(&mut self, id: Id) -> Option<CollectionBundle<'scope, T>> {
155 self.bindings.remove(&id)
156 }
157 pub fn update_id(&mut self, id: Id, collection: CollectionBundle<'scope, T>) {
159 if !self.bindings.contains_key(&id) {
160 self.bindings.insert(id, collection);
161 } else {
162 let binding = self
163 .bindings
164 .get_mut(&id)
165 .expect("Binding verified to exist");
166 if collection.collection.is_some() {
167 binding.collection = collection.collection;
168 }
169 for (key, flavor) in collection.arranged.into_iter() {
170 binding.arranged.insert(key, flavor);
171 }
172 }
173 }
174 pub fn lookup_id(&self, id: Id) -> Option<CollectionBundle<'scope, T>> {
176 self.bindings.get(&id).cloned()
177 }
178
179 pub(super) fn error_logger(&self) -> ErrorLogger {
180 ErrorLogger::new(self.debug_name.clone())
181 }
182}
183
184impl<'scope, T: RenderTimestamp> Context<'scope, T> {
185 pub fn enter_region<'a>(
187 &self,
188 region: Scope<'a, T>,
189 bindings: Option<&std::collections::BTreeSet<Id>>,
190 ) -> Context<'a, T> {
191 let bindings = self
192 .bindings
193 .iter()
194 .filter(|(key, _)| bindings.as_ref().map(|b| b.contains(key)).unwrap_or(true))
195 .map(|(key, bundle)| (*key, bundle.enter_region(region)))
196 .collect();
197
198 Context {
199 scope: region,
200 debug_name: self.debug_name.clone(),
201 dataflow_id: self.dataflow_id.clone(),
202 export_ids: self.export_ids.clone(),
203 as_of_frontier: self.as_of_frontier.clone(),
204 until: self.until.clone(),
205 compute_logger: self.compute_logger.clone(),
206 linear_join_spec: self.linear_join_spec.clone(),
207 bindings,
208 dataflow_expiration: self.dataflow_expiration.clone(),
209 config_set: Rc::clone(&self.config_set),
210 }
211 }
212}
213
214#[derive(Clone)]
216pub enum ArrangementFlavor<'scope, T: RenderTimestamp> {
217 Local(
219 Arranged<'scope, RowRowAgent<T, Diff>>,
220 Arranged<'scope, ErrAgent<T, Diff>>,
221 ),
222 Trace(
227 GlobalId,
228 Arranged<'scope, RowRowEnter<mz_repr::Timestamp, Diff, T>>,
229 Arranged<'scope, ErrEnter<mz_repr::Timestamp, T>>,
230 ),
231}
232
233impl<'scope, T: RenderTimestamp> ArrangementFlavor<'scope, T> {
234 #[deprecated(note = "Use `flat_map` instead.")]
242 pub fn as_collection(
243 &self,
244 ) -> (
245 VecCollection<'scope, T, Row, Diff>,
246 VecCollection<'scope, T, DataflowErrorSer, Diff>,
247 ) {
248 let mut datums = DatumVec::new();
249 let logic = move |k: DatumSeq, v: DatumSeq| {
250 let temp_storage = RowArena::new();
251 let mut datums_borrow = datums.borrow();
252 k.extend_datums(&temp_storage, &mut datums_borrow, None);
253 v.extend_datums(&temp_storage, &mut datums_borrow, None);
254 SharedRow::pack(&**datums_borrow)
255 };
256 match &self {
257 ArrangementFlavor::Local(oks, errs) => (
258 oks.clone().as_collection(logic),
259 errs.clone().as_collection(|k, &()| k.clone()),
260 ),
261 ArrangementFlavor::Trace(_, oks, errs) => (
262 oks.clone().as_collection(logic),
263 errs.clone().as_collection(|k, &()| k.clone()),
264 ),
265 }
266 }
267
268 pub fn flat_map<D, DCB, L>(
301 &self,
302 key: Option<&Row>,
303 max_demand: usize,
304 logic: L,
305 ) -> (
306 Stream<'scope, T, DCB::Container>,
307 VecCollection<'scope, T, DataflowErrorSer, Diff>,
308 )
309 where
310 D: Data,
311 DCB: ContainerBuilder + PushInto<(D, T, Diff)>,
312 L: for<'a, 'b> FnMut(
313 &'a mut DatumVecBorrow<'b>,
314 T,
315 Diff,
316 &mut Session<T, DCB>,
317 &mut Session<T, ECB<T>>,
318 ) -> usize
319 + 'static,
320 {
321 match &self {
324 ArrangementFlavor::Local(oks, errs) => {
325 let (oks, mfp_errs) = CollectionBundle::<T>::flat_map_core_fallible::<_, _, DCB, _>(
326 oks.clone(),
327 key,
328 max_demand,
329 logic,
330 REFUEL,
331 );
332 let errs = errs.clone().as_collection(|k, &()| k.clone());
333 let errs = errs.concat(mfp_errs.as_collection());
334 (oks, errs)
335 }
336 ArrangementFlavor::Trace(_, oks, errs) => {
337 let (oks, mfp_errs) = CollectionBundle::<T>::flat_map_core_fallible::<_, _, DCB, _>(
338 oks.clone(),
339 key,
340 max_demand,
341 logic,
342 REFUEL,
343 );
344 let errs = errs.clone().as_collection(|k, &()| k.clone());
345 let errs = errs.concat(mfp_errs.as_collection());
346 (oks, errs)
347 }
348 }
349 }
350
351 pub fn flat_map_ok<D, DCB, L>(
356 &self,
357 key: Option<&Row>,
358 max_demand: usize,
359 logic: L,
360 ) -> (
361 Stream<'scope, T, DCB::Container>,
362 VecCollection<'scope, T, DataflowErrorSer, Diff>,
363 )
364 where
365 D: Data,
366 DCB: ContainerBuilder + PushInto<(D, T, Diff)>,
367 L: for<'a, 'b> FnMut(&'a mut DatumVecBorrow<'b>, T, Diff, &mut Session<T, DCB>) -> usize
368 + 'static,
369 {
370 match &self {
371 ArrangementFlavor::Local(oks, errs) => {
372 let oks = CollectionBundle::<T>::flat_map_core_ok::<_, _, DCB, _>(
373 oks.clone(),
374 key,
375 max_demand,
376 logic,
377 REFUEL,
378 );
379 let errs = errs.clone().as_collection(|k, &()| k.clone());
380 (oks, errs)
381 }
382 ArrangementFlavor::Trace(_, oks, errs) => {
383 let oks = CollectionBundle::<T>::flat_map_core_ok::<_, _, DCB, _>(
384 oks.clone(),
385 key,
386 max_demand,
387 logic,
388 REFUEL,
389 );
390 let errs = errs.clone().as_collection(|k, &()| k.clone());
391 (oks, errs)
392 }
393 }
394 }
395}
396impl<'scope, T: RenderTimestamp> ArrangementFlavor<'scope, T> {
397 pub fn scope(&self) -> Scope<'scope, T> {
399 match self {
400 ArrangementFlavor::Local(oks, _errs) => oks.stream.scope(),
401 ArrangementFlavor::Trace(_gid, oks, _errs) => oks.stream.scope(),
402 }
403 }
404
405 pub fn enter_region<'a>(&self, region: Scope<'a, T>) -> ArrangementFlavor<'a, T> {
407 match self {
408 ArrangementFlavor::Local(oks, errs) => ArrangementFlavor::Local(
409 oks.clone().enter_region(region),
410 errs.clone().enter_region(region),
411 ),
412 ArrangementFlavor::Trace(gid, oks, errs) => ArrangementFlavor::Trace(
413 *gid,
414 oks.clone().enter_region(region),
415 errs.clone().enter_region(region),
416 ),
417 }
418 }
419}
420impl<'scope, T: RenderTimestamp> ArrangementFlavor<'scope, T> {
421 pub fn leave_region<'outer>(&self, outer: Scope<'outer, T>) -> ArrangementFlavor<'outer, T> {
423 match self {
424 ArrangementFlavor::Local(oks, errs) => ArrangementFlavor::Local(
425 oks.clone().leave_region(outer),
426 errs.clone().leave_region(outer),
427 ),
428 ArrangementFlavor::Trace(gid, oks, errs) => ArrangementFlavor::Trace(
429 *gid,
430 oks.clone().leave_region(outer),
431 errs.clone().leave_region(outer),
432 ),
433 }
434 }
435}
436
437#[derive(Clone)]
442pub struct CollectionBundle<'scope, T: RenderTimestamp> {
443 pub collection: Option<(
444 VecCollection<'scope, T, Row, Diff>,
445 VecCollection<'scope, T, DataflowErrorSer, Diff>,
446 )>,
447 pub arranged: BTreeMap<Vec<LirScalarExpr>, ArrangementFlavor<'scope, T>>,
448}
449
450impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
451 pub fn from_collections(
453 oks: VecCollection<'scope, T, Row, Diff>,
454 errs: VecCollection<'scope, T, DataflowErrorSer, Diff>,
455 ) -> Self {
456 Self {
457 collection: Some((oks, errs)),
458 arranged: BTreeMap::default(),
459 }
460 }
461
462 pub fn from_expressions(
464 exprs: Vec<LirScalarExpr>,
465 arrangements: ArrangementFlavor<'scope, T>,
466 ) -> Self {
467 let mut arranged = BTreeMap::new();
468 arranged.insert(exprs, arrangements);
469 Self {
470 collection: None,
471 arranged,
472 }
473 }
474
475 pub fn from_columns<I: IntoIterator<Item = usize>>(
477 columns: I,
478 arrangements: ArrangementFlavor<'scope, T>,
479 ) -> Self {
480 let mut keys = Vec::new();
481 for column in columns {
482 keys.push(LirScalarExpr::column(column));
483 }
484 Self::from_expressions(keys, arrangements)
485 }
486
487 pub fn scope(&self) -> Scope<'scope, T> {
489 if let Some((oks, _errs)) = &self.collection {
490 oks.inner.scope()
491 } else {
492 self.arranged
493 .values()
494 .next()
495 .expect("Must contain a valid collection")
496 .scope()
497 }
498 }
499
500 pub fn enter_region<'inner>(&self, region: Scope<'inner, T>) -> CollectionBundle<'inner, T> {
502 CollectionBundle {
503 collection: self.collection.as_ref().map(|(oks, errs)| {
504 (
505 oks.clone().enter_region(region),
506 errs.clone().enter_region(region),
507 )
508 }),
509 arranged: self
510 .arranged
511 .iter()
512 .map(|(key, bundle)| (key.clone(), bundle.enter_region(region)))
513 .collect(),
514 }
515 }
516}
517
518impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
519 pub fn leave_region<'outer>(&self, outer: Scope<'outer, T>) -> CollectionBundle<'outer, T> {
521 CollectionBundle {
522 collection: self.collection.as_ref().map(|(oks, errs)| {
523 (
524 oks.clone().leave_region(outer),
525 errs.clone().leave_region(outer),
526 )
527 }),
528 arranged: self
529 .arranged
530 .iter()
531 .map(|(key, bundle)| (key.clone(), bundle.leave_region(outer)))
532 .collect(),
533 }
534 }
535}
536
537impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
538 pub fn as_specific_collection(
551 &self,
552 key: Option<&[LirScalarExpr]>,
553 config_set: &ConfigSet,
554 ) -> (
555 VecCollection<'scope, T, Row, Diff>,
556 VecCollection<'scope, T, DataflowErrorSer, Diff>,
557 ) {
558 match key {
564 None => self
565 .collection
566 .clone()
567 .expect("The unarranged collection doesn't exist."),
568 Some(key) => {
569 let arranged = self.arranged.get(key).unwrap_or_else(|| {
570 panic!("The collection arranged by {:?} doesn't exist.", key)
571 });
572 if ENABLE_COMPUTE_RENDER_FUELED_AS_SPECIFIC_COLLECTION.get(config_set) {
573 let (ok, err) = arranged
577 .flat_map_ok::<_, CapacityContainerBuilder<Vec<(Row, T, Diff)>>, _>(
578 None,
579 usize::MAX,
580 |borrow, t, r, ok_session| {
581 ok_session.give((SharedRow::pack(borrow.iter()), t, r));
582 1
583 },
584 );
585 (ok.as_collection(), err)
586 } else {
587 #[allow(deprecated)]
588 arranged.as_collection()
589 }
590 }
591 }
592 }
593
594 pub fn flat_map<D, DCB, L>(
610 &self,
611 key_val: Option<(Vec<LirScalarExpr>, Option<Row>)>,
612 max_demand: usize,
613 mut logic: L,
614 ) -> (
615 Stream<'scope, T, DCB::Container>,
616 VecCollection<'scope, T, DataflowErrorSer, Diff>,
617 )
618 where
619 D: Data,
620 DCB: ContainerBuilder + PushInto<(D, T, Diff)>,
621 L: for<'a> FnMut(
622 &'a mut DatumVecBorrow<'_>,
623 T,
624 Diff,
625 &mut Session<T, DCB>,
626 &mut Session<T, ECB<T>>,
627 ) -> usize
628 + 'static,
629 {
630 if let Some((key, val)) = key_val {
634 self.arrangement(&key)
635 .expect("Should have ensured during planning that this arrangement exists.")
636 .flat_map::<_, DCB, _>(val.as_ref(), max_demand, logic)
637 } else {
638 let (oks, errs) = self
639 .collection
640 .clone()
641 .expect("Invariant violated: CollectionBundle contains no collection.");
642 let scope = oks.inner.scope();
643 let mut builder = OperatorBuilder::new("CollectionFlatMap".to_string(), scope);
644 let (ok_output, ok_stream) = builder.new_output();
645 let mut ok_output = OutputBuilder::<_, DCB>::from(ok_output);
646 let (err_output, err_stream) = builder.new_output();
647 let mut err_output = OutputBuilder::<_, ECB<T>>::from(err_output);
648 let mut input = builder.new_input(oks.inner, Pipeline);
649 builder.build(move |_capabilities| {
650 let mut datums = DatumVec::new();
651 move |_frontiers| {
652 let mut ok_output = ok_output.activate();
653 let mut err_output = err_output.activate();
654 input.for_each(|time, data| {
655 let ok_cap = time.retain(0);
658 let err_cap = time.retain(1);
659 let mut ok_session = ok_output.session_with_builder(&ok_cap);
660 let mut err_session = err_output.session_with_builder(&err_cap);
661 for (v, t, d) in data.drain(..) {
662 logic(
663 &mut datums.borrow_with_limit(&v, max_demand),
664 t,
665 d,
666 &mut ok_session,
667 &mut err_session,
668 );
669 }
670 });
671 }
672 });
673 let errs = errs.concat(err_stream.as_collection());
674 (ok_stream, errs)
675 }
676 }
677
678 fn flat_map_core_fallible<Tr, D, DCB, L>(
689 trace: Arranged<'scope, Tr>,
690 key: Option<&<Tr::KeyContainer as BatchContainer>::Owned>,
691 max_demand: usize,
692 mut logic: L,
693 refuel: usize,
694 ) -> (
695 Stream<'scope, T, DCB::Container>,
696 Stream<'scope, T, Vec<(DataflowErrorSer, T, Diff)>>,
697 )
698 where
699 Tr: for<'a> TraceReader<
700 Key<'a>: ExtendDatums,
701 Val<'a>: ExtendDatums,
702 Time = T,
703 Diff = mz_repr::Diff,
704 > + Clone
705 + 'static,
706 <Tr::KeyContainer as BatchContainer>::Owned: PartialEq,
707 D: Data,
708 DCB: ContainerBuilder + PushInto<(D, T, Diff)>,
709 L: for<'a, 'b> FnMut(
713 &'a mut DatumVecBorrow<'b>,
714 T,
715 mz_repr::Diff,
716 &mut Session<T, DCB>,
717 &mut Session<T, ECB<T>>,
718 ) -> usize
719 + 'static,
720 {
721 let scope = trace.stream.scope();
722
723 let mut key_con = Tr::KeyContainer::with_capacity(1);
724 if let Some(key) = &key {
725 key_con.push_own(key);
726 }
727 let mode = if key.is_some() { "index" } else { "scan" };
728 let name = format!("ArrangementFlatMap({})", mode);
729
730 let mut builder = OperatorBuilder::new(name, scope.clone());
731 let (ok_output, ok_stream) = builder.new_output();
732 let mut ok_output = OutputBuilder::<_, DCB>::from(ok_output);
733 let (err_output, err_stream) = builder.new_output();
734 let mut err_output = OutputBuilder::<_, ECB<T>>::from(err_output);
735 let mut input = builder.new_input(trace.stream.clone(), Pipeline);
736 let operator_info = builder.operator_info();
737
738 builder.build(move |_capabilities| {
739 let activator = scope.activator_for(operator_info.address);
741 let mut todo = std::collections::VecDeque::new();
743 move |_frontiers| {
744 let key = key_con.get(0);
745 let mut ok_output = ok_output.activate();
746 let mut err_output = err_output.activate();
747
748 input.for_each(|time, data| {
750 let ok_cap = time.retain(0);
753 let err_cap = time.retain(1);
754 for batch in data.iter() {
755 todo.push_back(PendingWork::new(
756 ok_cap.clone(),
757 err_cap.clone(),
758 batch.cursor(),
759 batch.clone(),
760 ));
761 }
762 });
763
764 let mut temp_storage = RowArena::new();
769 let mut datums = DatumVec::new();
770 let mut decode_logic =
771 |k: Tr::Key<'_>,
772 v: Tr::Val<'_>,
773 t: T,
774 d: mz_repr::Diff,
775 ok_session: &mut Session<T, DCB>,
776 err_session: &mut Session<T, ECB<T>>| {
777 temp_storage.clear();
778 let mut datums_borrow = datums.borrow();
779 k.extend_datums(&temp_storage, &mut datums_borrow, Some(max_demand));
780 let remaining = max_demand.saturating_sub(datums_borrow.len());
781 v.extend_datums(&temp_storage, &mut datums_borrow, Some(remaining));
782 logic(&mut datums_borrow, t, d, ok_session, err_session)
783 };
784
785 let mut fuel = refuel;
787 while !todo.is_empty() && fuel > 0 {
788 todo.front_mut().unwrap().do_work(
789 key.as_ref(),
790 &mut decode_logic,
791 &mut fuel,
792 &mut ok_output,
793 &mut err_output,
794 );
795 if fuel > 0 {
796 todo.pop_front();
797 }
798 }
799 if !todo.is_empty() {
801 activator.activate();
802 }
803 }
804 });
805
806 (ok_stream, err_stream)
807 }
808
809 fn flat_map_core_ok<Tr, D, DCB, L>(
815 trace: Arranged<'scope, Tr>,
816 key: Option<&<Tr::KeyContainer as BatchContainer>::Owned>,
817 max_demand: usize,
818 mut logic: L,
819 refuel: usize,
820 ) -> Stream<'scope, T, DCB::Container>
821 where
822 Tr: for<'a> TraceReader<
823 Key<'a>: ExtendDatums,
824 Val<'a>: ExtendDatums,
825 Time = T,
826 Diff = mz_repr::Diff,
827 > + Clone
828 + 'static,
829 <Tr::KeyContainer as BatchContainer>::Owned: PartialEq,
830 D: Data,
831 DCB: ContainerBuilder + PushInto<(D, T, Diff)>,
832 L: for<'a, 'b> FnMut(
835 &'a mut DatumVecBorrow<'b>,
836 T,
837 mz_repr::Diff,
838 &mut Session<T, DCB>,
839 ) -> usize
840 + 'static,
841 {
842 let scope = trace.stream.scope();
843
844 let mut key_con = Tr::KeyContainer::with_capacity(1);
845 if let Some(key) = &key {
846 key_con.push_own(key);
847 }
848 let mode = if key.is_some() { "index" } else { "scan" };
849 let name = format!("ArrangementFlatMapOk({})", mode);
850
851 let mut builder = OperatorBuilder::new(name, scope.clone());
852 let (ok_output, ok_stream) = builder.new_output();
853 let mut ok_output = OutputBuilder::<_, DCB>::from(ok_output);
854 let mut input = builder.new_input(trace.stream.clone(), Pipeline);
855 let operator_info = builder.operator_info();
856
857 builder.build(move |_capabilities| {
858 let activator = scope.activator_for(operator_info.address);
859 let mut todo = std::collections::VecDeque::new();
860 move |_frontiers| {
861 let key = key_con.get(0);
862 let mut ok_output = ok_output.activate();
863
864 input.for_each(|time, data| {
865 let cap = time.retain(0);
866 for batch in data.iter() {
867 todo.push_back(PendingWorkOk::new(
868 cap.clone(),
869 batch.cursor(),
870 batch.clone(),
871 ));
872 }
873 });
874
875 let mut temp_storage = RowArena::new();
877 let mut datums = DatumVec::new();
878 let mut decode_logic =
879 |k: Tr::Key<'_>,
880 v: Tr::Val<'_>,
881 t: T,
882 d: mz_repr::Diff,
883 ok_session: &mut Session<T, DCB>| {
884 temp_storage.clear();
885 let mut datums_borrow = datums.borrow();
886 k.extend_datums(&temp_storage, &mut datums_borrow, Some(max_demand));
887 let remaining = max_demand.saturating_sub(datums_borrow.len());
888 v.extend_datums(&temp_storage, &mut datums_borrow, Some(remaining));
889 logic(&mut datums_borrow, t, d, ok_session)
890 };
891
892 let mut fuel = refuel;
893 while !todo.is_empty() && fuel > 0 {
894 todo.front_mut().unwrap().do_work(
895 key.as_ref(),
896 &mut decode_logic,
897 &mut fuel,
898 &mut ok_output,
899 );
900 if fuel > 0 {
901 todo.pop_front();
902 }
903 }
904 if !todo.is_empty() {
905 activator.activate();
906 }
907 }
908 });
909
910 ok_stream
911 }
912
913 pub fn arrangement(&self, key: &[LirScalarExpr]) -> Option<ArrangementFlavor<'scope, T>> {
918 self.arranged.get(key).map(|x| x.clone())
919 }
920}
921
922impl<'scope, T: RenderTimestamp> CollectionBundle<'scope, T> {
923 pub fn as_collection_core(
932 &self,
933 mfp_plan: MfpPlan<LirScalarExpr>,
934 key_val: Option<(Vec<LirScalarExpr>, Option<Row>)>,
935 until: Antichain<mz_repr::Timestamp>,
936 config_set: &ConfigSet,
937 ) -> (
938 VecCollection<'scope, T, mz_repr::Row, Diff>,
939 VecCollection<'scope, T, DataflowErrorSer, Diff>,
940 ) {
941 let has_key_val = if let Some((_key, Some(_val))) = &key_val {
947 true
948 } else {
949 false
950 };
951
952 if mfp_plan.is_identity() && !has_key_val {
953 let key = key_val.map(|(k, _v)| k);
954 return self.as_specific_collection(key.as_deref(), config_set);
955 }
956
957 let (mfp_plan, max_demand) = {
962 let mut mir_mfp = mfp_plan_lir_to_mir(mfp_plan).into_map_filter_project();
963 let max_demand = mir_mfp.demand().last().map(|x| *x + 1).unwrap_or(0);
964 mir_mfp.permute_fn(|c| c, max_demand);
965 mir_mfp.optimize();
966 let plan = mfp_mir_to_lir_plan(mir_mfp);
967 (plan, max_demand)
968 };
969
970 let mut datum_vec = DatumVec::new();
971 let until = std::rc::Rc::new(until);
973
974 let (stream, errors) = self
975 .flat_map::<_, ConsolidatingContainerBuilder<Vec<(Row, T, Diff)>>, _>(
976 key_val,
977 max_demand,
978 move |row_datums, time, diff, ok_session, err_session| {
979 let mut row_builder = SharedRow::get();
980 let until = std::rc::Rc::clone(&until);
981 let temp_storage = RowArena::new();
982 let row_iter = row_datums.iter();
983 let mut datums_local = datum_vec.borrow();
984 datums_local.extend(row_iter);
985 let event_time = time.event_time();
986 let mut work: usize = 0;
987 for result in mfp_plan.evaluate(
988 &mut datums_local,
989 &temp_storage,
990 event_time,
991 diff.clone(),
992 move |time| !until.less_equal(time),
993 &mut row_builder,
994 ) {
995 work += 1;
996 match result {
997 Ok((row, event_time, diff)) => {
998 let mut time: T = time.clone();
1000 *time.event_time_mut() = event_time;
1001 ok_session.give((row, time, diff));
1002 }
1003 Err((e, event_time, diff)) => {
1004 let mut time: T = time.clone();
1006 *time.event_time_mut() = event_time;
1007 err_session.give((e, time, diff));
1008 }
1009 }
1010 }
1011 work
1012 },
1013 );
1014
1015 (stream.as_collection(), errors)
1016 }
1017 pub fn ensure_collections(
1018 mut self,
1019 collections: AvailableCollections,
1020 input_key: Option<Vec<LirScalarExpr>>,
1021 input_mfp: MfpPlan<LirScalarExpr>,
1022 as_of: Antichain<mz_repr::Timestamp>,
1023 until: Antichain<mz_repr::Timestamp>,
1024 config_set: &ConfigSet,
1025 strategy: ArrangementStrategy,
1026 ) -> Self
1027 where
1028 T: MaybeBucketByTime,
1029 {
1030 if collections == Default::default() {
1031 return self;
1032 }
1033 for (key, _, _) in collections.arranged.iter() {
1042 soft_assert_or_log!(
1043 !self.arranged.contains_key(key),
1044 "LIR ArrangeBy tried to create an existing arrangement"
1045 );
1046 }
1047
1048 let mut bucketed = false;
1051
1052 let will_create_arrangement = collections
1056 .arranged
1057 .iter()
1058 .any(|(key, _, _)| !self.arranged.contains_key(key));
1059
1060 let form_raw_collection = collections.raw || will_create_arrangement;
1062 if form_raw_collection && self.collection.is_none() {
1063 let (oks, errs) =
1064 self.as_collection_core(input_mfp, input_key.map(|k| (k, None)), until, config_set);
1065 let effective_strategy = if will_create_arrangement {
1069 strategy
1070 } else {
1071 ArrangementStrategy::Direct
1072 };
1073 let oks = if matches!(effective_strategy, ArrangementStrategy::TemporalBucketing)
1074 && ENABLE_COMPUTE_TEMPORAL_BUCKETING.get(config_set)
1075 {
1076 let summary: mz_repr::Timestamp = TEMPORAL_BUCKETING_SUMMARY
1077 .get(config_set)
1078 .try_into()
1079 .expect("must fit");
1080 bucketed = true;
1081 T::maybe_apply_temporal_bucketing(oks.inner, as_of.clone(), summary)
1082 } else {
1083 oks
1084 };
1085 self.collection = Some((oks, errs));
1086 }
1087 for (key, _, thinning) in collections.arranged {
1088 if !self.arranged.contains_key(&key) {
1089 let name = format!("ArrangeBy[{:?}]", key);
1091
1092 let (oks, errs) = self
1093 .collection
1094 .take()
1095 .expect("Collection constructed above");
1096 let effective_strategy = if bucketed {
1101 ArrangementStrategy::Direct
1102 } else {
1103 strategy
1104 };
1105 let oks = if matches!(effective_strategy, ArrangementStrategy::TemporalBucketing)
1106 && ENABLE_COMPUTE_TEMPORAL_BUCKETING.get(config_set)
1107 {
1108 let summary: mz_repr::Timestamp = TEMPORAL_BUCKETING_SUMMARY
1109 .get(config_set)
1110 .try_into()
1111 .expect("must fit");
1112 bucketed = true;
1113 T::maybe_apply_temporal_bucketing(oks.inner, as_of.clone(), summary)
1114 } else {
1115 oks
1116 };
1117 let use_paged_path = ENABLE_COLUMN_PAGED_BATCHER.get(config_set);
1118 let (oks, errs_keyed, passthrough) = Self::arrange_collection(
1119 &name,
1120 oks,
1121 key.clone(),
1122 thinning.clone(),
1123 use_paged_path,
1124 );
1125 let errs_concat: KeyCollection<_, _, _> = errs.clone().concat(errs_keyed).into();
1126 self.collection = Some((passthrough, errs));
1127 let errs =
1128 errs_concat.mz_arrange::<
1129 ColumnationChunker<_>,
1130 ErrBatcher<_, _>,
1131 ErrBuilder<_, _>,
1132 ErrSpine<_, _>,
1133 >(
1134 &format!("{}-errors", name),
1135 );
1136 self.arranged
1137 .insert(key, ArrangementFlavor::Local(oks, errs));
1138 }
1139 }
1140 self
1141 }
1142
1143 fn arrange_collection(
1154 name: &String,
1155 oks: VecCollection<'scope, T, Row, Diff>,
1156 key: Vec<LirScalarExpr>,
1157 thinning: Vec<usize>,
1158 use_paged_path: bool,
1159 ) -> (
1160 Arranged<'scope, RowRowAgent<T, Diff>>,
1161 VecCollection<'scope, T, DataflowErrorSer, Diff>,
1162 VecCollection<'scope, T, Row, Diff>,
1163 ) {
1164 let mut builder = OperatorBuilder::new("FormArrangementKey".to_string(), oks.inner.scope());
1169 let (ok_output, ok_stream) = builder.new_output();
1170 let mut ok_output =
1171 OutputBuilder::<_, ColumnBuilder<((Row, Row), T, Diff)>>::from(ok_output);
1172 let (err_output, err_stream) = builder.new_output();
1173 let mut err_output = OutputBuilder::from(err_output);
1174 let (passthrough_output, passthrough_stream) = builder.new_output();
1175 let mut passthrough_output = OutputBuilder::from(passthrough_output);
1176 let mut input = builder.new_input(oks.inner, Pipeline);
1177 builder.set_notify_for(0, FrontierInterest::Never);
1178 builder.build(move |_capabilities| {
1179 let mut key_buf = Row::default();
1180 let mut val_buf = Row::default();
1181 let mut datums = DatumVec::new();
1182 move |_frontiers| {
1183 let mut temp_storage = RowArena::new();
1186 let mut ok_output = ok_output.activate();
1187 let mut err_output = err_output.activate();
1188 let mut passthrough_output = passthrough_output.activate();
1189 input.for_each(|time, data| {
1190 let mut ok_session = ok_output.session_with_builder(&time);
1191 let mut err_session = err_output.session(&time);
1192 for (row, time, diff) in data.iter() {
1193 temp_storage.clear();
1194 let datums = datums.borrow_with(row);
1195 let key_iter = key.iter().map(|k| k.eval(&datums, &temp_storage));
1196 match key_buf.packer().try_extend(key_iter) {
1197 Ok(()) => {
1198 let val_datum_iter = thinning.iter().map(|c| datums[*c]);
1199 val_buf.packer().extend(val_datum_iter);
1200 ok_session.give(((&*key_buf, &*val_buf), time, diff));
1201 }
1202 Err(e) => {
1203 err_session.give((e.into(), time.clone(), *diff));
1204 }
1205 }
1206 }
1207 passthrough_output.session(&time).give_container(data);
1208 });
1209 }
1210 });
1211
1212 let exchange =
1213 ExchangeCore::<ColumnBuilder<_>, _>::new_core(columnar_exchange::<Row, Row, T, Diff>);
1214 let oks = if use_paged_path {
1215 ok_stream.mz_arrange_core::<
1216 _,
1217 batcher::ColumnChunker<_>,
1218 Col2ValPagedBatcher<_, _, _, _>,
1219 RowRowColPagedBuilder<_, _>,
1220 RowRowSpine<_, _>,
1221 >(exchange, name)
1222 } else {
1223 ok_stream.mz_arrange_core::<
1224 _,
1225 batcher::Chunker<_>,
1226 Col2ValBatcher<_, _, _, _>,
1227 RowRowBuilder<_, _>,
1228 RowRowSpine<_, _>,
1229 >(exchange, name)
1230 };
1231 (
1232 oks,
1233 err_stream.as_collection(),
1234 passthrough_stream.as_collection(),
1235 )
1236 }
1237}
1238
1239type Session<'a, 'b, T, CB> =
1243 timely::dataflow::operators::generic::Session<'a, 'b, T, CB, Capability<T>>;
1244
1245type ECB<T> = ConsolidatingContainerBuilder<Vec<(DataflowErrorSer, T, Diff)>>;
1250
1251const REFUEL: usize = 1_000_000;
1255
1256struct PendingWork<C>
1257where
1258 C: Cursor,
1259{
1260 ok_capability: Capability<C::Time>,
1262 err_capability: Capability<C::Time>,
1264 cursor: C,
1265 batch: C::Storage,
1266}
1267
1268impl<C> PendingWork<C>
1269where
1270 C: Cursor<KeyContainer: BatchContainer<Owned: PartialEq + Sized>>,
1271{
1272 fn new(
1275 ok_capability: Capability<C::Time>,
1276 err_capability: Capability<C::Time>,
1277 cursor: C,
1278 batch: C::Storage,
1279 ) -> Self {
1280 Self {
1281 ok_capability,
1282 err_capability,
1283 cursor,
1284 batch,
1285 }
1286 }
1287 fn do_work<D, DCB, L>(
1290 &mut self,
1291 key: Option<&C::Key<'_>>,
1292 logic: &mut L,
1293 fuel: &mut usize,
1294 ok_output: &mut OutputBuilderSession<'_, C::Time, DCB>,
1295 err_output: &mut OutputBuilderSession<'_, C::Time, ECB<C::Time>>,
1296 ) where
1297 D: Data,
1298 DCB: ContainerBuilder + PushInto<(D, C::Time, C::Diff)>,
1299 L: FnMut(
1300 C::Key<'_>,
1301 C::Val<'_>,
1302 C::Time,
1303 C::Diff,
1304 &mut Session<C::Time, DCB>,
1305 &mut Session<C::Time, ECB<C::Time>>,
1306 ) -> usize,
1307 {
1308 let mut ok_session = ok_output.session_with_builder(&self.ok_capability);
1309 let mut err_session = err_output.session_with_builder(&self.err_capability);
1310 walk_cursor(&mut self.cursor, &self.batch, key, fuel, |k, v, t, d| {
1311 logic(k, v, t, d, &mut ok_session, &mut err_session)
1312 });
1313 }
1314}
1315
1316struct PendingWorkOk<C>
1319where
1320 C: Cursor,
1321{
1322 capability: Capability<C::Time>,
1323 cursor: C,
1324 batch: C::Storage,
1325}
1326
1327impl<C> PendingWorkOk<C>
1328where
1329 C: Cursor<KeyContainer: BatchContainer<Owned: PartialEq + Sized>>,
1330{
1331 fn new(capability: Capability<C::Time>, cursor: C, batch: C::Storage) -> Self {
1332 Self {
1333 capability,
1334 cursor,
1335 batch,
1336 }
1337 }
1338
1339 fn do_work<D, DCB, L>(
1342 &mut self,
1343 key: Option<&C::Key<'_>>,
1344 logic: &mut L,
1345 fuel: &mut usize,
1346 ok_output: &mut OutputBuilderSession<'_, C::Time, DCB>,
1347 ) where
1348 D: Data,
1349 DCB: ContainerBuilder + PushInto<(D, C::Time, C::Diff)>,
1350 L: FnMut(C::Key<'_>, C::Val<'_>, C::Time, C::Diff, &mut Session<C::Time, DCB>) -> usize,
1351 {
1352 let mut ok_session = ok_output.session_with_builder(&self.capability);
1353 walk_cursor(&mut self.cursor, &self.batch, key, fuel, |k, v, t, d| {
1354 logic(k, v, t, d, &mut ok_session)
1355 });
1356 }
1357}
1358
1359fn walk_cursor<C, F>(
1369 cursor: &mut C,
1370 batch: &C::Storage,
1371 key: Option<&C::Key<'_>>,
1372 fuel: &mut usize,
1373 mut emit: F,
1374) where
1375 C: Cursor<KeyContainer: BatchContainer<Owned: PartialEq + Sized>>,
1376 F: FnMut(C::Key<'_>, C::Val<'_>, C::Time, C::Diff) -> usize,
1377{
1378 use differential_dataflow::consolidation::consolidate;
1379
1380 let mut work: usize = 0;
1381 let mut buffer = Vec::new();
1382 if let Some(key) = key {
1383 let key = C::KeyContainer::reborrow(*key);
1384 if cursor.get_key(batch).map(|k| k == key) != Some(true) {
1385 cursor.seek_key(batch, key);
1386 }
1387 if cursor.get_key(batch).map(|k| k == key) == Some(true) {
1388 let key = cursor.key(batch);
1389 while let Some(val) = cursor.get_val(batch) {
1390 cursor.map_times(batch, |time, diff| {
1391 buffer.push((C::owned_time(time), C::owned_diff(diff)));
1392 });
1393 consolidate(&mut buffer);
1394 for (time, diff) in buffer.drain(..) {
1395 work += emit(key, val, time, diff);
1396 }
1397 cursor.step_val(batch);
1398 if work >= *fuel {
1399 *fuel = 0;
1400 return;
1401 }
1402 }
1403 }
1404 } else {
1405 while let Some(key) = cursor.get_key(batch) {
1406 while let Some(val) = cursor.get_val(batch) {
1407 cursor.map_times(batch, |time, diff| {
1408 buffer.push((C::owned_time(time), C::owned_diff(diff)));
1409 });
1410 consolidate(&mut buffer);
1411 for (time, diff) in buffer.drain(..) {
1412 work += emit(key, val, time, diff);
1413 }
1414 cursor.step_val(batch);
1415 if work >= *fuel {
1416 *fuel = 0;
1417 return;
1418 }
1419 }
1420 cursor.step_key(batch);
1421 }
1422 }
1423 *fuel -= work;
1424}