1use std::collections::BTreeMap;
11use std::rc::Rc;
12use std::sync::{Arc, Weak};
13
14use differential_dataflow::batcher::Batcher;
15use differential_dataflow::difference::Semigroup;
16use differential_dataflow::lattice::Lattice;
17use differential_dataflow::operators::arrange::arrangement::arrange_core;
18use differential_dataflow::operators::arrange::{Arranged, TraceAgent};
19use differential_dataflow::trace::implementations::BatchContainer;
20use differential_dataflow::trace::implementations::spine_fueled::{Spine, SpineBatch};
21use differential_dataflow::trace::{Trace, TraceReader};
22use differential_dataflow::{Collection, Data, ExchangeData, Hashable, VecCollection};
23use mz_compute_types::dyncfgs::{ENABLE_COLUMN_PAGED_BATCHER, ENABLE_COLUMNAR_MERGE_BATCHER};
24use mz_dyncfg::ConfigSet;
25use mz_row_spine::ArcBatch;
26use mz_timely_util::containers::HeapSize;
27use mz_timely_util::operator::BatcherNew;
28use timely::Container;
29use timely::dataflow::Stream;
30use timely::dataflow::channels::pact::{Exchange, ParallelizationContract, Pipeline};
31use timely::dataflow::operators::Operator;
32use timely::progress::Timestamp;
33
34use crate::logging::compute::{
35 ArrangementHeapAllocations, ArrangementHeapCapacity, ArrangementHeapSize,
36 ArrangementHeapSizeOperator, ComputeEvent, ComputeEventBuilder,
37};
38use crate::typedefs::{
39 KeyAgent, KeyValAgent, MzArrangeData, MzData, MzTimestamp, RowAgent, RowRowAgent, RowValAgent,
40};
41
42pub enum ArrangementBatcher {
44 Columnation,
47 Columnar,
50 Chunked,
55}
56
57impl ArrangementBatcher {
58 pub fn from_config(config: &ConfigSet) -> Self {
66 if ENABLE_COLUMN_PAGED_BATCHER.get(config) {
67 Self::Chunked
68 } else if ENABLE_COLUMNAR_MERGE_BATCHER.get(config) {
69 Self::Columnar
70 } else {
71 Self::Columnation
72 }
73 }
74}
75
76pub trait MzArrange<'scope>: MzArrangeCore<'scope> {
78 fn mz_arrange<Ba, Tr>(self, name: &str) -> Arranged<'scope, TraceAgent<Tr>>
84 where
85 Ba: Batcher<Self::Container, Time = Self::Timestamp> + BatcherNew + 'static,
86 Ba::Output: Into<Tr::Batch>,
87 Tr: Trace<Time = Self::Timestamp> + 'static,
88 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize;
89}
90
91pub trait MzArrangeCore<'scope> {
93 type Timestamp: Timestamp + Lattice;
95 type Container: Container + Clone + 'static;
97
98 fn mz_arrange_core<P, Ba, Tr>(self, pact: P, name: &str) -> Arranged<'scope, TraceAgent<Tr>>
105 where
106 P: ParallelizationContract<Self::Timestamp, Self::Container>,
107 Ba: Batcher<Self::Container, Time = Self::Timestamp> + BatcherNew + 'static,
108 Ba::Output: Into<Tr::Batch>,
109 Tr: Trace<Time = Self::Timestamp> + 'static,
110 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize;
111}
112
113impl<'scope, T, C> MzArrangeCore<'scope> for Stream<'scope, T, C>
114where
115 T: Timestamp + Lattice,
116 C: Container + Clone + 'static,
117{
118 type Timestamp = T;
119 type Container = C;
120
121 fn mz_arrange_core<P, Ba, Tr>(self, pact: P, name: &str) -> Arranged<'scope, TraceAgent<Tr>>
122 where
123 P: ParallelizationContract<T, C>,
124 Ba: Batcher<C, Time = T> + BatcherNew + 'static,
125 Ba::Output: Into<Tr::Batch>,
126 Tr: Trace<Time = T> + 'static,
127 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize,
128 {
129 #[allow(clippy::disallowed_methods)]
131 arrange_core::<_, _, Ba, _>(self, pact, name, Ba::new_batcher).log_arrangement_size()
132 }
133}
134
135impl<'scope, T, K, V, R> MzArrange<'scope> for VecCollection<'scope, T, (K, V), R>
136where
137 T: Timestamp + Lattice,
138 K: ExchangeData + Hashable,
139 V: ExchangeData,
140 R: ExchangeData,
141{
142 fn mz_arrange<Ba, Tr>(self, name: &str) -> Arranged<'scope, TraceAgent<Tr>>
143 where
144 Ba: Batcher<Vec<((K, V), T, R)>, Time = T> + BatcherNew + 'static,
145 Ba::Output: Into<Tr::Batch>,
146 Tr: Trace<Time = T> + 'static,
147 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize,
148 {
149 let exchange = Exchange::new(move |update: &((K, V), T, R)| (update.0).0.hashed().into());
150 self.mz_arrange_core::<_, Ba, _>(exchange, name)
151 }
152}
153
154impl<'scope, T, C> MzArrangeCore<'scope> for Collection<'scope, T, C>
155where
156 T: Timestamp + Lattice,
157 C: Container + Clone + 'static,
158{
159 type Timestamp = T;
160 type Container = C;
161
162 fn mz_arrange_core<P, Ba, Tr>(self, pact: P, name: &str) -> Arranged<'scope, TraceAgent<Tr>>
163 where
164 P: ParallelizationContract<T, C>,
165 Ba: Batcher<C, Time = T> + BatcherNew + 'static,
166 Ba::Output: Into<Tr::Batch>,
167 Tr: Trace<Time = T> + 'static,
168 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize,
169 {
170 self.inner.mz_arrange_core::<_, Ba, _>(pact, name)
171 }
172}
173
174pub struct KeyCollection<'scope, T: Timestamp, K: 'static, R: 'static = usize>(
178 VecCollection<'scope, T, K, R>,
179);
180
181impl<'scope, T: Timestamp, K, R: Semigroup> From<VecCollection<'scope, T, K, R>>
182 for KeyCollection<'scope, T, K, R>
183{
184 fn from(value: VecCollection<'scope, T, K, R>) -> Self {
185 KeyCollection(value)
186 }
187}
188
189impl<'scope, T, K, R> MzArrange<'scope> for KeyCollection<'scope, T, K, R>
190where
191 T: Timestamp + Lattice,
192 K: ExchangeData + Hashable,
193 R: ExchangeData,
194{
195 fn mz_arrange<Ba, Tr>(self, name: &str) -> Arranged<'scope, TraceAgent<Tr>>
196 where
197 Ba: Batcher<Vec<((K, ()), T, R)>, Time = T> + BatcherNew + 'static,
198 Ba::Output: Into<Tr::Batch>,
199 Tr: Trace<Time = T> + 'static,
200 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize,
201 {
202 self.0.map(|d| (d, ())).mz_arrange::<Ba, _>(name)
203 }
204}
205
206impl<'scope, T, K, R> MzArrangeCore<'scope> for KeyCollection<'scope, T, K, R>
207where
208 T: Timestamp + Lattice,
209 K: Clone + 'static,
210 R: Clone + 'static,
211{
212 type Timestamp = T;
213 type Container = Vec<((K, ()), T, R)>;
214
215 fn mz_arrange_core<P, Ba, Tr>(self, pact: P, name: &str) -> Arranged<'scope, TraceAgent<Tr>>
216 where
217 P: ParallelizationContract<T, Vec<((K, ()), T, R)>>,
218 Ba: Batcher<Vec<((K, ()), T, R)>, Time = T> + BatcherNew + 'static,
219 Ba::Output: Into<Tr::Batch>,
220 Tr: Trace<Time = T> + 'static,
221 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize,
222 {
223 self.0
224 .map(|d| (d, ()))
225 .mz_arrange_core::<_, Ba, _>(pact, name)
226 }
227}
228
229pub trait ArrangementSize {
231 fn log_arrangement_size(self) -> Self;
233}
234
235fn log_arrangement_size_inner<'scope, B, L>(
245 arranged: Arranged<'scope, TraceAgent<Spine<ArcBatch<B>>>>,
246 mut logic: L,
247) -> Arranged<'scope, TraceAgent<Spine<ArcBatch<B>>>>
248where
249 B: SpineBatch + 'static,
250 L: FnMut(&B) -> (usize, usize, usize) + 'static,
251{
252 let scope = arranged.stream.scope();
253 let Some(logger) = scope
254 .worker()
255 .logger_for::<ComputeEventBuilder>("materialize/compute")
256 else {
257 return arranged;
258 };
259 let operator_id = arranged.trace.operator().global_id;
260 let trace = Rc::downgrade(&arranged.trace.trace_box_unstable());
261
262 let (mut old_size, mut old_capacity, mut old_allocations) = (0isize, 0isize, 0isize);
263
264 let stream = arranged
265 .stream
266 .unary(Pipeline, "ArrangementSize", |_cap, info| {
267 let address = info.address;
268 logger.log(&ComputeEvent::ArrangementHeapSizeOperator(
269 ArrangementHeapSizeOperator {
270 operator_id,
271 address: address.to_vec(),
272 },
273 ));
274
275 let mut batches: BTreeMap<*const B, (Weak<B>, (usize, usize, usize))> = BTreeMap::new();
281
282 move |input, output| {
283 input.for_each(|time, data| {
284 for span in data.iter() {
285 if let Some(batch) = &span.inner {
287 batches
288 .entry(Arc::as_ptr(&batch.0))
289 .or_insert_with(|| (Arc::downgrade(&batch.0), logic(&batch.0)));
290 }
291 }
292 output.session(&time).give_container(data);
293 });
294 let Some(trace) = trace.upgrade() else {
295 batches.clear();
302 return;
303 };
304
305 trace.borrow().trace().map_spans(|span| {
306 if let Some(batch) = &span.inner {
307 batches
308 .entry(Arc::as_ptr(&batch.0))
309 .or_insert_with(|| (Arc::downgrade(&batch.0), logic(&batch.0)));
310 }
311 });
312
313 let (mut size, mut capacity, mut allocations) = (0, 0, 0);
314 batches.retain(|_, (weak, cached)| {
315 if weak.strong_count() > 0 {
316 let (sz, c, a) = *cached;
317 (size += sz, capacity += c, allocations += a);
318 true
319 } else {
320 false
321 }
322 });
323
324 let size = size.try_into().expect("must fit");
325 if size != old_size {
326 logger.log(&ComputeEvent::ArrangementHeapSize(ArrangementHeapSize {
327 operator_id,
328 delta_size: size - old_size,
329 }));
330 }
331
332 let capacity = capacity.try_into().expect("must fit");
333 if capacity != old_capacity {
334 logger.log(&ComputeEvent::ArrangementHeapCapacity(
335 ArrangementHeapCapacity {
336 operator_id,
337 delta_capacity: capacity - old_capacity,
338 },
339 ));
340 }
341
342 let allocations = allocations.try_into().expect("must fit");
343 if allocations != old_allocations {
344 logger.log(&ComputeEvent::ArrangementHeapAllocations(
345 ArrangementHeapAllocations {
346 operator_id,
347 delta_allocations: allocations - old_allocations,
348 },
349 ));
350 }
351
352 old_size = size;
353 old_capacity = capacity;
354 old_allocations = allocations;
355 }
356 });
357 Arranged {
358 trace: arranged.trace,
359 stream,
360 }
361}
362
363impl<'scope, T, K, V, R> ArrangementSize for Arranged<'scope, KeyValAgent<K, V, T, R>>
364where
365 T: MzTimestamp,
366 K: Data + MzData,
367 V: Data + MzData,
368 R: Semigroup + Ord + MzData + 'static,
369{
370 fn log_arrangement_size(self) -> Self {
371 log_arrangement_size_inner(self, |batch| {
372 let (mut size, mut capacity, mut allocations) = (0, 0, 0);
373 let mut callback = |siz, cap| {
374 size += siz;
375 capacity += cap;
376 allocations += usize::from(cap > 0);
377 };
378 batch.storage.keys.heap_size(&mut callback);
379 batch.storage.vals.offs.heap_size(&mut callback);
380 batch.storage.vals.vals.heap_size(&mut callback);
381 batch.storage.upds.offs.heap_size(&mut callback);
382 batch.storage.upds.times.heap_size(&mut callback);
383 batch.storage.upds.diffs.heap_size(&mut callback);
384 (size, capacity, allocations)
385 })
386 }
387}
388
389impl<'scope, T, K, R> ArrangementSize for Arranged<'scope, KeyAgent<K, T, R>>
390where
391 T: MzTimestamp,
392 K: Data + MzArrangeData,
393 R: Semigroup + Ord + MzData + 'static,
394{
395 fn log_arrangement_size(self) -> Self {
396 log_arrangement_size_inner(self, |batch| {
397 let (mut size, mut capacity, mut allocations) = (0, 0, 0);
398 let mut callback = |siz, cap| {
399 size += siz;
400 capacity += cap;
401 allocations += usize::from(cap > 0);
402 };
403 batch.storage.keys.heap_size(&mut callback);
404 batch.storage.upds.offs.heap_size(&mut callback);
405 batch.storage.upds.times.heap_size(&mut callback);
406 batch.storage.upds.diffs.heap_size(&mut callback);
407 (size, capacity, allocations)
408 })
409 }
410}
411
412impl<'scope, T, V, R> ArrangementSize for Arranged<'scope, RowValAgent<V, T, R>>
413where
414 T: MzTimestamp,
415 V: Data + MzArrangeData,
416 R: Semigroup + Ord + MzArrangeData + 'static,
417{
418 fn log_arrangement_size(self) -> Self {
419 log_arrangement_size_inner(self, |batch| {
420 let (mut size, mut capacity, mut allocations) = (0, 0, 0);
421 let mut callback = |siz, cap| {
422 size += siz;
423 capacity += cap;
424 allocations += usize::from(cap > 0);
425 };
426 batch.storage.keys.heap_size(&mut callback);
427 batch.storage.vals.offs.heap_size(&mut callback);
428 batch.storage.vals.vals.heap_size(&mut callback);
429 batch.storage.upds.offs.heap_size(&mut callback);
430 batch.storage.upds.times.heap_size(&mut callback);
431 batch.storage.upds.diffs.heap_size(&mut callback);
432 (size, capacity, allocations)
433 })
434 }
435}
436
437impl<'scope, T, R> ArrangementSize for Arranged<'scope, RowRowAgent<T, R>>
438where
439 T: MzTimestamp,
440 R: Semigroup + Ord + MzArrangeData + 'static,
441{
442 fn log_arrangement_size(self) -> Self {
443 log_arrangement_size_inner(self, |batch| {
444 let (mut size, mut capacity, mut allocations) = (0, 0, 0);
445 let mut callback = |siz, cap| {
446 size += siz;
447 capacity += cap;
448 allocations += usize::from(cap > 0);
449 };
450 batch.storage.keys.heap_size(&mut callback);
451 batch.storage.vals.offs.heap_size(&mut callback);
452 batch.storage.vals.vals.heap_size(&mut callback);
453 batch.storage.upds.offs.heap_size(&mut callback);
454 batch.storage.upds.times.heap_size(&mut callback);
455 batch.storage.upds.diffs.heap_size(&mut callback);
456 (size, capacity, allocations)
457 })
458 }
459}
460
461impl<'scope, T, DC> ArrangementSize for Arranged<'scope, RowAgent<T, DC::Owned, DC>>
462where
463 T: MzTimestamp,
464 DC: BatchContainer<Owned: Semigroup + 'static> + HeapSize,
465{
466 fn log_arrangement_size(self) -> Self {
467 log_arrangement_size_inner(self, |batch| {
468 let (mut size, mut capacity, mut allocations) = (0, 0, 0);
469 let mut callback = |siz, cap| {
470 size += siz;
471 capacity += cap;
472 allocations += usize::from(cap > 0);
473 };
474 batch.storage.keys.heap_size(&mut callback);
475 batch.storage.upds.offs.heap_size(&mut callback);
476 batch.storage.upds.times.heap_size(&mut callback);
477 batch.storage.upds.diffs.heap_size(&mut callback);
478 (size, capacity, allocations)
479 })
480 }
481}