1use std::collections::BTreeMap;
11use std::rc::Rc;
12use std::sync::{Arc, Weak};
13
14use differential_dataflow::difference::Semigroup;
15use differential_dataflow::lattice::Lattice;
16use differential_dataflow::operators::arrange::arrangement::arrange_core;
17use differential_dataflow::operators::arrange::{Arranged, TraceAgent};
18use differential_dataflow::trace::implementations::spine_fueled::Spine;
19use differential_dataflow::trace::{Batch, Batcher, Builder, Trace, TraceReader};
20use differential_dataflow::{Collection, Data, ExchangeData, Hashable, VecCollection};
21use mz_row_spine::ArcBatch;
22use timely::Container;
23use timely::container::{ContainerBuilder, PushInto};
24use timely::dataflow::Stream;
25use timely::dataflow::channels::pact::{Exchange, ParallelizationContract, Pipeline};
26use timely::dataflow::operators::Operator;
27use timely::progress::Timestamp;
28
29use crate::logging::compute::{
30 ArrangementHeapAllocations, ArrangementHeapCapacity, ArrangementHeapSize,
31 ArrangementHeapSizeOperator, ComputeEvent, ComputeEventBuilder,
32};
33use crate::typedefs::{
34 KeyAgent, KeyValAgent, MzArrangeData, MzData, MzTimestamp, RowAgent, RowRowAgent, RowValAgent,
35};
36
37pub trait MzArrange<'scope>: MzArrangeCore<'scope> {
39 fn mz_arrange<Chu, Ba, Bu, Tr>(self, name: &str) -> Arranged<'scope, TraceAgent<Tr>>
45 where
46 Ba: Batcher<Time = Self::Timestamp> + 'static,
47 Chu: ContainerBuilder<Container = Ba::Output>
48 + for<'a> PushInto<&'a mut Self::Input>
49 + 'static,
50 Bu: Builder<Time = Self::Timestamp, Input = Ba::Output, Output = Tr::Batch>,
51 Tr: Trace + TraceReader<Time = Self::Timestamp> + 'static,
52 Tr::Batch: Batch,
53 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize;
54}
55
56pub trait MzArrangeCore<'scope> {
58 type Timestamp: Timestamp + Lattice;
60 type Input: Container + Clone + 'static;
62
63 fn mz_arrange_core<P, Chu, Ba, Bu, Tr>(
70 self,
71 pact: P,
72 name: &str,
73 ) -> Arranged<'scope, TraceAgent<Tr>>
74 where
75 P: ParallelizationContract<Self::Timestamp, Self::Input>,
76 Ba: Batcher<Time = Self::Timestamp> + 'static,
77 Chu: ContainerBuilder<Container = Ba::Output>
78 + for<'a> PushInto<&'a mut Self::Input>
79 + 'static,
80 Bu: Builder<Time = Self::Timestamp, Input = Ba::Output, Output = Tr::Batch>,
81 Tr: Trace + TraceReader<Time = Self::Timestamp> + 'static,
82 Tr::Batch: Batch,
83 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize;
84}
85
86impl<'scope, T, C> MzArrangeCore<'scope> for Stream<'scope, T, C>
87where
88 T: Timestamp + Lattice,
89 C: Container + Clone + 'static,
90{
91 type Timestamp = T;
92 type Input = C;
93
94 fn mz_arrange_core<P, Chu, Ba, Bu, Tr>(
95 self,
96 pact: P,
97 name: &str,
98 ) -> Arranged<'scope, TraceAgent<Tr>>
99 where
100 P: ParallelizationContract<T, Self::Input>,
101 Ba: Batcher<Time = T> + 'static,
102 Chu: ContainerBuilder<Container = Ba::Output>
103 + for<'a> PushInto<&'a mut Self::Input>
104 + 'static,
105 Bu: Builder<Time = T, Input = Ba::Output, Output = Tr::Batch>,
106 Tr: Trace + TraceReader<Time = T> + 'static,
107 Tr::Batch: Batch,
108 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize,
109 {
110 #[allow(clippy::disallowed_methods)]
112 arrange_core::<_, _, Chu, Ba, Bu, _>(self, pact, name).log_arrangement_size()
113 }
114}
115
116impl<'scope, T, K, V, R> MzArrange<'scope> for VecCollection<'scope, T, (K, V), R>
117where
118 T: Timestamp + Lattice,
119 K: ExchangeData + Hashable,
120 V: ExchangeData,
121 R: ExchangeData,
122{
123 fn mz_arrange<Chu, Ba, Bu, Tr>(self, name: &str) -> Arranged<'scope, TraceAgent<Tr>>
124 where
125 Ba: Batcher<Time = T> + 'static,
126 Chu: ContainerBuilder<Container = Ba::Output>
127 + for<'a> PushInto<&'a mut Self::Input>
128 + 'static,
129 Bu: Builder<Time = T, Input = Ba::Output, Output = Tr::Batch>,
130 Tr: Trace + TraceReader<Time = T> + 'static,
131 Tr::Batch: Batch,
132 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize,
133 {
134 let exchange = Exchange::new(move |update: &((K, V), T, R)| (update.0).0.hashed().into());
135 self.mz_arrange_core::<_, Chu, Ba, Bu, _>(exchange, name)
136 }
137}
138
139impl<'scope, T, C> MzArrangeCore<'scope> for Collection<'scope, T, C>
140where
141 T: Timestamp + Lattice,
142 C: Container + Clone + 'static,
143{
144 type Timestamp = T;
145 type Input = C;
146
147 fn mz_arrange_core<P, Chu, Ba, Bu, Tr>(
148 self,
149 pact: P,
150 name: &str,
151 ) -> Arranged<'scope, TraceAgent<Tr>>
152 where
153 P: ParallelizationContract<T, Self::Input>,
154 Ba: Batcher<Time = T> + 'static,
155 Chu: ContainerBuilder<Container = Ba::Output>
156 + for<'a> PushInto<&'a mut Self::Input>
157 + 'static,
158 Bu: Builder<Time = T, Input = Ba::Output, Output = Tr::Batch>,
159 Tr: Trace + TraceReader<Time = T> + 'static,
160 Tr::Batch: Batch,
161 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize,
162 {
163 self.inner.mz_arrange_core::<_, Chu, Ba, Bu, _>(pact, name)
164 }
165}
166
167pub struct KeyCollection<'scope, T: Timestamp, K: 'static, R: 'static = usize>(
171 VecCollection<'scope, T, K, R>,
172);
173
174impl<'scope, T: Timestamp, K, R: Semigroup> From<VecCollection<'scope, T, K, R>>
175 for KeyCollection<'scope, T, K, R>
176{
177 fn from(value: VecCollection<'scope, T, K, R>) -> Self {
178 KeyCollection(value)
179 }
180}
181
182impl<'scope, T, K, R> MzArrange<'scope> for KeyCollection<'scope, T, K, R>
183where
184 T: Timestamp + Lattice,
185 K: ExchangeData + Hashable,
186 R: ExchangeData,
187{
188 fn mz_arrange<Chu, Ba, Bu, Tr>(self, name: &str) -> Arranged<'scope, TraceAgent<Tr>>
189 where
190 Ba: Batcher<Time = T> + 'static,
191 Chu: ContainerBuilder<Container = Ba::Output>
192 + for<'a> PushInto<&'a mut Self::Input>
193 + 'static,
194 Bu: Builder<Time = T, Input = Ba::Output, Output = Tr::Batch>,
195 Tr: Trace + TraceReader<Time = T> + 'static,
196 Tr::Batch: Batch,
197 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize,
198 {
199 self.0.map(|d| (d, ())).mz_arrange::<Chu, Ba, Bu, _>(name)
200 }
201}
202
203impl<'scope, T, K, R> MzArrangeCore<'scope> for KeyCollection<'scope, T, K, R>
204where
205 T: Timestamp + Lattice,
206 K: Clone + 'static,
207 R: Clone + 'static,
208{
209 type Timestamp = T;
210 type Input = Vec<((K, ()), T, R)>;
211
212 fn mz_arrange_core<P, Chu, Ba, Bu, Tr>(
213 self,
214 pact: P,
215 name: &str,
216 ) -> Arranged<'scope, TraceAgent<Tr>>
217 where
218 P: ParallelizationContract<T, Self::Input>,
219 Ba: Batcher<Time = T> + 'static,
220 Chu: ContainerBuilder<Container = Ba::Output>
221 + for<'a> PushInto<&'a mut Self::Input>
222 + 'static,
223 Bu: Builder<Time = T, Input = Ba::Output, Output = Tr::Batch>,
224 Tr: Trace + TraceReader<Time = T> + 'static,
225 Tr::Batch: Batch,
226 Arranged<'scope, TraceAgent<Tr>>: ArrangementSize,
227 {
228 self.0
229 .map(|d| (d, ()))
230 .mz_arrange_core::<_, Chu, Ba, Bu, _>(pact, name)
231 }
232}
233
234pub trait ArrangementSize {
236 fn log_arrangement_size(self) -> Self;
238}
239
240fn log_arrangement_size_inner<'scope, B, L>(
250 arranged: Arranged<'scope, TraceAgent<Spine<ArcBatch<B>>>>,
251 mut logic: L,
252) -> Arranged<'scope, TraceAgent<Spine<ArcBatch<B>>>>
253where
254 B: Batch + 'static,
255 L: FnMut(&B) -> (usize, usize, usize) + 'static,
256{
257 let scope = arranged.stream.scope();
258 let Some(logger) = scope
259 .worker()
260 .logger_for::<ComputeEventBuilder>("materialize/compute")
261 else {
262 return arranged;
263 };
264 let operator_id = arranged.trace.operator().global_id;
265 let trace = Rc::downgrade(&arranged.trace.trace_box_unstable());
266
267 let (mut old_size, mut old_capacity, mut old_allocations) = (0isize, 0isize, 0isize);
268
269 let stream = arranged
270 .stream
271 .unary(Pipeline, "ArrangementSize", |_cap, info| {
272 let address = info.address;
273 logger.log(&ComputeEvent::ArrangementHeapSizeOperator(
274 ArrangementHeapSizeOperator {
275 operator_id,
276 address: address.to_vec(),
277 },
278 ));
279
280 let mut batches: BTreeMap<*const B, (Weak<B>, (usize, usize, usize))> = BTreeMap::new();
286
287 move |input, output| {
288 input.for_each(|time, data| {
289 for batch in data.iter() {
290 batches
291 .entry(Arc::as_ptr(&batch.0))
292 .or_insert_with(|| (Arc::downgrade(&batch.0), logic(&batch.0)));
293 }
294 output.session(&time).give_container(data);
295 });
296 let Some(trace) = trace.upgrade() else {
297 batches.clear();
304 return;
305 };
306
307 trace.borrow().trace().map_batches(|batch| {
308 batches
309 .entry(Arc::as_ptr(&batch.0))
310 .or_insert_with(|| (Arc::downgrade(&batch.0), logic(&batch.0)));
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, R> ArrangementSize for Arranged<'scope, RowAgent<T, R>>
462where
463 T: MzTimestamp,
464 R: Semigroup + Ord + MzArrangeData + 'static,
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}