Skip to main content

differential_dataflow/trace/
mod.rs

1//! Traits and datastructures representing a collection trace.
2//!
3//! A collection trace is a set of updates of the form `(key, val, time, diff)`, which determine the contents
4//! of a collection at given times by accumulating updates whose time field is less or equal to the target field.
5//!
6//! The `Trace` trait describes those types and methods that a data structure must implement to be viewed as a
7//! collection trace. This trait allows operator implementations to be generic with respect to the type of trace,
8//! and allows various data structures to be interpretable as multiple different types of trace.
9
10pub mod chunk;
11pub mod cursor;
12pub mod description;
13pub mod implementations;
14pub mod wrappers;
15
16use timely::container::PushInto;
17use timely::progress::{Antichain, frontier::AntichainRef};
18use timely::progress::Timestamp;
19use crate::lattice::Lattice;
20
21use crate::logging::Logger;
22pub use self::cursor::Cursor;
23pub use self::cursor::Navigable;
24pub use self::cursor::{BatchCursor, BatchKey, BatchVal, BatchValOwn, BatchDiff, BatchDiffGat, BatchTimeGat};
25use self::cursor::CursorList;
26pub use self::description::Description;
27
28/// A type used to express how much effort a trace should exert even in the absence of updates.
29pub type ExertionLogic = std::sync::Arc<dyn for<'a> Fn(&'a [(usize, usize, usize)])->Option<usize>+Send+Sync>;
30
31/// A trace whose contents may be read.
32///
33/// This is a restricted interface to the more general `Trace` trait, which extends this trait with further methods
34/// to update the contents of the trace. These methods are used to examine the contents, and to update the reader's
35/// capabilities (which may release restrictions on the mutations to the underlying trace and cause work to happen).
36pub trait TraceReader {
37
38    /// The timestamp type of the trace's updates.
39    ///
40    /// Key/val/diff opinions live on the batches' cursors; the trace itself only needs time, to
41    /// bound its contents and to drive compaction.
42    type Time: Timestamp + Lattice;
43
44    /// The type of an immutable collection of updates.
45    type Batch:
46        'static +
47        Clone +
48        BatchReader<Time = Self::Time>;
49
50    /// Acquires the non-empty sequence of batches covering updates at times not greater or equal to an
51    /// element of `upper`.
52    ///
53    /// This is the sole primitive each `TraceReader` must implement to expose its contents: the
54    /// `cursor` and `cursor_through` methods assemble a [`CursorList`] over these batches' cursors,
55    /// for which the returned `Vec` serves as storage (the cursor borrows from it).
56    ///
57    /// This method is expected to work if called with an `upper` that (i) was an observed bound in batches from
58    /// the trace, and (ii) the trace has not been advanced beyond `upper`. Practically, the implementation should
59    /// be expected to look for a "clean cut" using `upper`, and if it finds such a cut can return the batches. This
60    /// should allow `upper` such as `&[]` as used by `self.cursor()`, though it is difficult to imagine other uses.
61    fn batches_through(&mut self, upper: AntichainRef<Self::Time>) -> Option<Vec<Self::Batch>>;
62
63    /// Provides a cursor over updates contained in the trace.
64    fn cursor(&mut self) -> (CursorList<<Self::Batch as Navigable>::Cursor>, Vec<Self::Batch>) where Self::Batch: Navigable {
65        if let Some(cursor) = self.cursor_through(Antichain::new().borrow()) {
66            cursor
67        }
68        else {
69            panic!("unable to acquire complete cursor for trace; is it closed?");
70        }
71    }
72
73    /// Acquires a cursor to the restriction of the collection's contents to updates at times not greater or
74    /// equal to an element of `upper`.
75    ///
76    /// The cursor is a [`CursorList`] that merges the cursors of the batches returned by
77    /// [`batches_through`](TraceReader::batches_through); see that method for the contract on `upper`.
78    fn cursor_through(&mut self, upper: AntichainRef<Self::Time>) -> Option<(CursorList<<Self::Batch as Navigable>::Cursor>, Vec<Self::Batch>)> where Self::Batch: Navigable {
79        Some(self::cursor::cursor_list(self.batches_through(upper)?))
80    }
81
82    /// Advances the frontier that constrains logical compaction.
83    ///
84    /// Logical compaction is the ability of the trace to change the times of the updates it contains.
85    /// Update times may be changed as long as their comparison to all query times beyond the logical compaction
86    /// frontier remains unchanged. Practically, this means that groups of timestamps not beyond the frontier can
87    /// be coalesced into fewer representative times.
88    ///
89    /// Logical compaction is important, as it allows the trace to forget historical distinctions between update
90    /// times, and maintain a compact memory footprint over an unbounded update history.
91    ///
92    /// By advancing the logical compaction frontier, the caller unblocks merging of otherwise equivalent updates,
93    /// but loses the ability to observe historical detail that is not beyond `frontier`.
94    ///
95    /// It is an error to call this method with a frontier not equal to or beyond the most recent arguments to
96    /// this method, or the initial value of `get_logical_compaction()` if this method has not yet been called.
97    fn set_logical_compaction(&mut self, frontier: AntichainRef<Self::Time>);
98
99    /// Reports the logical compaction frontier.
100    ///
101    /// All update times beyond this frontier will be presented with their original times, and all update times
102    /// not beyond this frontier will present as a time that compares identically with all query times beyond
103    /// this frontier. Practically, update times not beyond this frontier should not be taken to be accurate as
104    /// presented, and should be used carefully, only in accumulation to times that are beyond the frontier.
105    fn get_logical_compaction(&mut self) -> AntichainRef<'_, Self::Time>;
106
107    /// Advances the frontier that constrains physical compaction.
108    ///
109    /// Physical compaction is the ability of the trace to merge the batches of updates it maintains. Physical
110    /// compaction does not change the updates or their timestamps, although it is also the moment at which
111    /// logical compaction is most likely to happen.
112    ///
113    /// Physical compaction allows the trace to maintain a logarithmic number of batches of updates, which is
114    /// what allows the trace to provide efficient random access by keys and values.
115    ///
116    /// By advancing the physical compaction frontier, the caller unblocks the merging of batches of updates,
117    /// but loses the ability to create a cursor through any frontier not beyond `frontier`.
118    ///
119    /// It is an error to call this method with a frontier not equal to or beyond the most recent arguments to
120    /// this method, or the initial value of `get_physical_compaction()` if this method has not yet been called.
121    fn set_physical_compaction(&mut self, frontier: AntichainRef<'_, Self::Time>);
122
123    /// Reports the physical compaction frontier.
124    ///
125    /// All batches containing updates beyond this frontier will not be merged with other batches. This allows
126    /// the caller to create a cursor through any frontier beyond the physical compaction frontier, with the
127    /// `cursor_through()` method. This functionality is primarily of interest to the `join` operator, and any
128    /// other operators who need to take notice of the physical structure of update batches.
129    fn get_physical_compaction(&mut self) -> AntichainRef<'_, Self::Time>;
130
131    /// Maps logic across the non-empty sequence of batches in the trace.
132    ///
133    /// This is currently used only to extract historical data to prime late-starting operators who want to reproduce
134    /// the stream of batches moving past the trace. It could also be a fine basis for a default implementation of the
135    /// cursor methods, as they (by default) just move through batches accumulating cursors into a cursor list.
136    fn map_batches<F: FnMut(&Self::Batch)>(&self, f: F);
137
138    /// Reads the upper frontier of committed times.
139    ///
140    ///
141    #[inline]
142    fn read_upper(&mut self, target: &mut Antichain<Self::Time>) {
143        target.clear();
144        target.insert(<Self::Time as timely::progress::Timestamp>::minimum());
145        self.map_batches(|batch| {
146            target.clone_from(batch.upper());
147        });
148    }
149
150    /// Advances `upper` by any empty batches.
151    ///
152    /// An empty batch whose `batch.lower` bound equals the current
153    /// contents of `upper` will advance `upper` to `batch.upper`.
154    /// Taken across all batches, this should advance `upper` across
155    /// empty batch regions.
156    fn advance_upper(&mut self, upper: &mut Antichain<Self::Time>) {
157        self.map_batches(|batch| {
158            if batch.is_empty() && batch.lower() == upper {
159                upper.clone_from(batch.upper());
160            }
161        });
162    }
163
164}
165
166/// An append-only collection of `(key, val, time, diff)` tuples.
167///
168/// The trace must pretend to look like a collection of `(Key, Val, Time, isize)` tuples, but is permitted
169/// to introduce new types `KeyRef`, `ValRef`, and `TimeRef` which can be dereference to the types above.
170///
171/// The trace must be constructable from, and navigable by the `Key`, `Val`, `Time` types, but does not need
172/// to return them.
173pub trait Trace : TraceReader<Batch: Batch> {
174
175    /// Allocates a new empty trace.
176    fn new(
177        info: ::timely::dataflow::operators::generic::OperatorInfo,
178        logging: Option<crate::logging::Logger>,
179        activator: Option<timely::scheduling::activate::Activator>,
180    ) -> Self;
181
182    /// Exert merge effort, even without updates.
183    fn exert(&mut self);
184
185    /// Sets the logic for exertion in the absence of updates.
186    ///
187    /// The function receives an iterator over batch levels, from large to small, as triples `(level, count, length)`,
188    /// indicating the level, the number of batches, and their total length in updates. It should return a number of
189    /// updates to perform, or `None` if no work is required.
190    fn set_exert_logic(&mut self, logic: ExertionLogic);
191
192    /// Introduces a batch of updates to the trace.
193    ///
194    /// Batches describe the time intervals they contain, and they should be added to the trace in contiguous
195    /// intervals. If a batch arrives with a lower bound that does not equal the upper bound of the most recent
196    /// addition, the trace will add an empty batch. It is an error to then try to populate that region of time.
197    ///
198    /// This restriction could be relaxed, especially if we discover ways in which batch interval order could
199    /// commute. For now, the trace should complain, to the extent that it cares about contiguous intervals.
200    fn insert(&mut self, batch: Self::Batch);
201
202    /// Introduces an empty batch concluding the trace.
203    ///
204    /// This method should be logically equivalent to introducing an empty batch whose lower frontier equals
205    /// the upper frontier of the most recently introduced batch, and whose upper frontier is empty.
206    fn close(&mut self);
207}
208
209/// A batch of updates whose contents may be read.
210///
211/// This is a restricted interface to batches of updates, which support the reading of the batch's contents,
212/// but do not expose ways to construct the batches. This trait is appropriate for views of the batch, and is
213/// especially useful for views derived from other sources in ways that prevent the construction of batches
214/// from the type of data in the view (for example, filtered views, or views with extended time coordinates).
215pub trait BatchReader : Sized {
216
217    /// The timestamp type of the batch's updates.
218    ///
219    /// A batch carries only time; navigating its contents is the separate, optional [`Navigable`]
220    /// capability, which an operator requests with a `Self: Navigable` bound when it needs a cursor.
221    type Time: Timestamp + Lattice;
222
223    /// The number of updates in the batch.
224    fn len(&self) -> usize;
225    /// True if the batch is empty.
226    fn is_empty(&self) -> bool { self.len() == 0 }
227    /// Describes the times of the updates in the batch.
228    fn description(&self) -> &Description<Self::Time>;
229
230    /// All times in the batch are greater or equal to an element of `lower`.
231    fn lower(&self) -> &Antichain<Self::Time> { self.description().lower() }
232    /// All times in the batch are not greater or equal to any element of `upper`.
233    fn upper(&self) -> &Antichain<Self::Time> { self.description().upper() }
234}
235
236/// An immutable collection of updates.
237pub trait Batch : BatchReader + Sized {
238    /// A type used to progressively merge batches.
239    type Merger: Merger<Self>;
240
241    /// Initiates the merging of consecutive batches.
242    ///
243    /// The result of this method can be exercised to eventually produce the same result
244    /// that a call to `self.merge(other)` would produce, but it can be done in a measured
245    /// fashion. This can help to avoid latency spikes where a large merge needs to happen.
246    fn begin_merge(&self, other: &Self, compaction_frontier: AntichainRef<Self::Time>) -> Self::Merger {
247        Self::Merger::new(self, other, compaction_frontier)
248    }
249
250    /// Produce an empty batch over the indicated interval.
251    fn empty(lower: Antichain<Self::Time>, upper: Antichain<Self::Time>) -> Self;
252}
253
254/// Functionality for collecting and batching updates.
255///
256/// Accepts containers of type `Output` via [`PushInto`] and produces output batches of the same
257/// type. Callers are responsible for converting raw input data into `Output` containers (e.g.
258/// using a chunker) before pushing into the batcher.
259pub trait Batcher: PushInto<Self::Output> {
260    /// Type produced by the batcher, and also the type it consumes.
261    type Output: Default;
262    /// Times at which batches are formed.
263    type Time: Timestamp;
264    /// Allocates a new empty batcher.
265    fn new(logger: Option<Logger>, operator_id: usize) -> Self;
266    /// Returns all updates not greater or equal to an element of `upper`, as a sorted and
267    /// consolidated chain together with the description that bounds them.
268    ///
269    /// The returned chain is suitable to hand directly to [`Builder::seal`].
270    fn seal(&mut self, upper: Antichain<Self::Time>) -> (Vec<Self::Output>, Description<Self::Time>);
271    /// Returns the lower envelope of contained update times.
272    fn frontier(&mut self) -> AntichainRef<'_, Self::Time>;
273}
274
275/// Functionality for building batches from ordered update sequences.
276pub trait Builder: Sized {
277    /// Input item type.
278    type Input;
279    /// Timestamp type.
280    type Time: Timestamp;
281    /// Output batch type.
282    type Output;
283
284    /// Allocates an empty builder.
285    ///
286    /// Ideally we deprecate this and insist all non-trivial building happens via `with_capacity()`.
287    // #[deprecated]
288    fn new() -> Self { Self::with_capacity(0, 0, 0) }
289    /// Allocates an empty builder with capacity for the specified keys, values, and updates.
290    ///
291    /// They represent respectively the number of distinct `key`, `(key, val)`, and total updates.
292    fn with_capacity(keys: usize, vals: usize, upds: usize) -> Self;
293    /// Adds a chunk of elements to the batch.
294    ///
295    /// Adds all elements from `chunk` to the builder and leaves `chunk` in an undefined state.
296    fn push(&mut self, chunk: &mut Self::Input);
297    /// Completes building and returns the batch.
298    fn done(self, description: Description<Self::Time>) -> Self::Output;
299
300    /// Builds a batch from a chain of updates corresponding to the indicated lower and upper bounds.
301    ///
302    /// This method relies on the chain only containing updates greater or equal to the lower frontier,
303    /// and not greater or equal to the upper frontier, as encoded in the description. Chains must also
304    /// be sorted and consolidated.
305    fn seal(chain: &mut Vec<Self::Input>, description: Description<Self::Time>) -> Self::Output;
306}
307
308/// Represents a merge in progress.
309pub trait Merger<Output: Batch> {
310    /// Creates a new merger to merge the supplied batches, optionally compacting
311    /// up to the supplied frontier.
312    fn new(source1: &Output, source2: &Output, compaction_frontier: AntichainRef<Output::Time>) -> Self;
313    /// Perform some amount of work, decrementing `fuel`.
314    ///
315    /// If `fuel` is non-zero after the call, the merging is complete and
316    /// one should call `done` to extract the merged results.
317    fn work(&mut self, source1: &Output, source2: &Output, fuel: &mut isize);
318    /// Extracts merged results.
319    ///
320    /// This method should only be called after `work` has been called and
321    /// has not brought `fuel` to zero. Otherwise, the merge is still in
322    /// progress.
323    fn done(self) -> Output;
324}
325
326
327/// Blanket implementations for reference counted batches.
328pub mod rc_blanket_impls {
329
330    use std::rc::Rc;
331
332    use timely::progress::{Antichain, frontier::AntichainRef};
333    use super::{Batch, BatchReader, Builder, Merger, Navigable, Cursor, Description};
334
335    impl<B: BatchReader + Navigable> Navigable for Rc<B> {
336        /// The type used to enumerate the batch's contents.
337        type Cursor = RcBatchCursor<B::Cursor>;
338        /// Acquires a cursor to the batch's contents.
339        fn cursor(&self) -> Self::Cursor {
340            RcBatchCursor::new((**self).cursor())
341        }
342    }
343
344    impl<B: BatchReader> BatchReader for Rc<B> {
345
346        type Time = B::Time;
347        /// The number of updates in the batch.
348        fn len(&self) -> usize { (**self).len() }
349        /// Describes the times of the updates in the batch.
350        fn description(&self) -> &Description<Self::Time> { (**self).description() }
351    }
352
353    /// Wrapper to provide cursor to nested scope.
354    pub struct RcBatchCursor<C> {
355        cursor: C,
356    }
357
358    impl<C> RcBatchCursor<C> {
359        fn new(cursor: C) -> Self {
360            RcBatchCursor {
361                cursor,
362            }
363        }
364    }
365
366    impl<C: Cursor> Cursor for RcBatchCursor<C> {
367
368        type Storage = Rc<C::Storage>;
369
370        type Key<'a> = C::Key<'a>;
371        type ValOwn = C::ValOwn;
372        type Val<'a> = C::Val<'a>;
373        type Time = C::Time;
374        type TimeGat<'a> = C::TimeGat<'a>;
375        type Diff = C::Diff;
376        type DiffGat<'a> = C::DiffGat<'a>;
377        type KeyContainer = C::KeyContainer;
378        type ValContainer = C::ValContainer;
379        type TimeContainer = C::TimeContainer;
380        type DiffContainer = C::DiffContainer;
381
382        #[inline] fn key_valid(&self, storage: &Self::Storage) -> bool { self.cursor.key_valid(storage) }
383        #[inline] fn val_valid(&self, storage: &Self::Storage) -> bool { self.cursor.val_valid(storage) }
384
385        #[inline] fn key<'a>(&self, storage: &'a Self::Storage) -> Self::Key<'a> { self.cursor.key(storage) }
386        #[inline] fn val<'a>(&self, storage: &'a Self::Storage) -> Self::Val<'a> { self.cursor.val(storage) }
387
388        #[inline] fn get_key<'a>(&self, storage: &'a Self::Storage) -> Option<Self::Key<'a>> { self.cursor.get_key(storage) }
389        #[inline] fn get_val<'a>(&self, storage: &'a Self::Storage) -> Option<Self::Val<'a>> { self.cursor.get_val(storage) }
390
391        #[inline]
392        fn map_times<L: FnMut(Self::TimeGat<'_>, Self::DiffGat<'_>)>(&mut self, storage: &Self::Storage, logic: L) {
393            self.cursor.map_times(storage, logic)
394        }
395
396        #[inline] fn step_key(&mut self, storage: &Self::Storage) { self.cursor.step_key(storage) }
397        #[inline] fn seek_key(&mut self, storage: &Self::Storage, key: Self::Key<'_>) { self.cursor.seek_key(storage, key) }
398
399        #[inline] fn step_val(&mut self, storage: &Self::Storage) { self.cursor.step_val(storage) }
400        #[inline] fn seek_val(&mut self, storage: &Self::Storage, val: Self::Val<'_>) { self.cursor.seek_val(storage, val) }
401
402        #[inline] fn rewind_keys(&mut self, storage: &Self::Storage) { self.cursor.rewind_keys(storage) }
403        #[inline] fn rewind_vals(&mut self, storage: &Self::Storage) { self.cursor.rewind_vals(storage) }
404    }
405
406    /// An immutable collection of updates.
407    impl<B: Batch> Batch for Rc<B> {
408        type Merger = RcMerger<B>;
409        fn empty(lower: Antichain<Self::Time>, upper: Antichain<Self::Time>) -> Self {
410            Rc::new(B::empty(lower, upper))
411        }
412    }
413
414    /// Wrapper type for building reference counted batches.
415    pub struct RcBuilder<B: Builder> { builder: B }
416
417    /// Functionality for building batches from ordered update sequences.
418    impl<B: Builder> Builder for RcBuilder<B> {
419        type Input = B::Input;
420        type Time = B::Time;
421        type Output = Rc<B::Output>;
422        fn with_capacity(keys: usize, vals: usize, upds: usize) -> Self { RcBuilder { builder: B::with_capacity(keys, vals, upds) } }
423        fn push(&mut self, input: &mut Self::Input) { self.builder.push(input) }
424        fn done(self, description: Description<Self::Time>) -> Rc<B::Output> { Rc::new(self.builder.done(description)) }
425        fn seal(chain: &mut Vec<Self::Input>, description: Description<Self::Time>) -> Self::Output {
426            Rc::new(B::seal(chain, description))
427        }
428    }
429
430    /// Wrapper type for merging reference counted batches.
431    pub struct RcMerger<B:Batch> { merger: B::Merger }
432
433    /// Represents a merge in progress.
434    impl<B:Batch> Merger<Rc<B>> for RcMerger<B> {
435        fn new(source1: &Rc<B>, source2: &Rc<B>, compaction_frontier: AntichainRef<B::Time>) -> Self { RcMerger { merger: B::begin_merge(source1, source2, compaction_frontier) } }
436        fn work(&mut self, source1: &Rc<B>, source2: &Rc<B>, fuel: &mut isize) { self.merger.work(source1, source2, fuel) }
437        fn done(self) -> Rc<B> { Rc::new(self.merger.done()) }
438    }
439}