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::progress::{Antichain, frontier::AntichainRef};
17use timely::progress::Timestamp;
18use crate::lattice::Lattice;
19
20pub use self::cursor::Cursor;
21pub use self::cursor::Navigable;
22pub use self::cursor::{BatchCursor, BatchKey, BatchVal, BatchValOwn, BatchDiff, BatchDiffGat, BatchTimeGat};
23pub use self::description::Description;
24pub use crate::batcher::Batcher;
25
26/// A type used to express how much effort a trace should exert even in the absence of updates.
27pub type ExertionLogic = std::sync::Arc<dyn for<'a> Fn(&'a [(usize, usize, usize)])->Option<usize>+Send+Sync>;
28
29/// An interval of a trace's history: a description of the times it covers, and the batch of
30/// updates within it, absent exactly when there are none.
31///
32/// The interval is never empty, but the batch may be missing; a span records that its times
33/// happened and brought no updates.
34#[derive(Clone, Debug)]
35pub struct Span<T, B> {
36    /// The lower and upper bounds of contained update times, and the compaction frontier.
37    pub desc: Description<T>,
38    /// The updates within the interval; absent exactly when there are none.
39    pub inner: Option<B>,
40}
41
42impl<T, B> Span<T, B> {
43    /// A span from a description and the batch within it, absent when there are no updates.
44    pub fn new(desc: Description<T>, inner: Option<B>) -> Self { Self { desc, inner } }
45    /// All times in the span are greater or equal to an element of `lower`.
46    pub fn lower(&self) -> &Antichain<T> { self.desc.lower() }
47    /// All times in the span are not greater or equal to any element of `upper`.
48    pub fn upper(&self) -> &Antichain<T> { self.desc.upper() }
49    /// True if the span carries a batch of updates.
50    ///
51    /// This is about the updates, not the interval, which is never empty.
52    pub fn has_updates(&self) -> bool { self.inner.is_some() }
53}
54
55impl<T: Timestamp, B> Span<T, B> {
56    /// A span over the indicated interval carrying no updates.
57    pub fn empty(lower: Antichain<T>, upper: Antichain<T>) -> Self {
58        Self { desc: Description::new(lower, upper, Antichain::from_elem(T::minimum())), inner: None }
59    }
60}
61
62/// The span type of a trace: [`Span`] instantiated at the trace's time and batch.
63pub type SpanOf<Tr> = Span<<Tr as TraceReader>::Time, <Tr as TraceReader>::Batch>;
64
65/// A trace whose contents may be read.
66///
67/// This is a restricted interface to the more general `Trace` trait, which extends this trait with further methods
68/// to update the contents of the trace. These methods are used to examine the contents, and to update the reader's
69/// capabilities (which may release restrictions on the mutations to the underlying trace and cause work to happen).
70pub trait TraceReader {
71
72    /// The timestamp type of the trace's updates.
73    ///
74    /// Key/val/diff opinions live on the batches' cursors; the trace itself only needs time, to
75    /// bound its contents and to drive compaction.
76    type Time: Timestamp + Lattice;
77
78    /// The batches of updates the trace's spans carry.
79    ///
80    /// A span is [`Span<Self::Time, Self::Batch>`](Span), pairing a description with a batch
81    /// when its interval brought any updates. The trace has no opinion on how a batch is read:
82    /// operators that want cursors add a [`Navigable`] bound of their own, and operators that
83    /// read batches some other way add none.
84    type Batch: 'static + Clone;
85
86    /// Acquires the non-empty sequence of spans covering updates at times not greater or equal to an
87    /// element of `upper`.
88    ///
89    /// This method is expected to work if called with an `upper` that (i) was an observed bound in spans from
90    /// the trace, and (ii) the trace has not been advanced beyond `upper`. Practically, the implementation should
91    /// be expected to look for a "clean cut" using `upper`, and if it finds such a cut can return the spans. This
92    /// should allow `upper` such as `&[]`, used to acquire all spans, though it is difficult to imagine other uses.
93    fn spans_through(&mut self, upper: AntichainRef<Self::Time>) -> Option<Vec<Span<Self::Time, Self::Batch>>>;
94
95    /// Acquires the batches of the spans covering times not greater or equal to an element
96    /// of `upper`.
97    ///
98    /// Spans with no updates contribute nothing, so this is what callers taking cursors want;
99    /// [`spans_through`](Self::spans_through) is for the drivers that also need the spans'
100    /// descriptions.
101    fn batches_through(&mut self, upper: AntichainRef<Self::Time>) -> Option<Vec<Self::Batch>> {
102        Some(self.spans_through(upper)?.into_iter().filter_map(|b| b.inner).collect())
103    }
104
105    /// Advances the frontier that constrains logical compaction.
106    ///
107    /// Logical compaction is the ability of the trace to change the times of the updates it contains.
108    /// Update times may be changed as long as their comparison to all query times beyond the logical compaction
109    /// frontier remains unchanged. Practically, this means that groups of timestamps not beyond the frontier can
110    /// be coalesced into fewer representative times.
111    ///
112    /// Logical compaction is important, as it allows the trace to forget historical distinctions between update
113    /// times, and maintain a compact memory footprint over an unbounded update history.
114    ///
115    /// By advancing the logical compaction frontier, the caller unblocks merging of otherwise equivalent updates,
116    /// but loses the ability to observe historical detail that is not beyond `frontier`.
117    ///
118    /// It is an error to call this method with a frontier not equal to or beyond the most recent arguments to
119    /// this method, or the initial value of `get_logical_compaction()` if this method has not yet been called.
120    fn set_logical_compaction(&mut self, frontier: AntichainRef<Self::Time>);
121
122    /// Reports the logical compaction frontier.
123    ///
124    /// All update times beyond this frontier will be presented with their original times, and all update times
125    /// not beyond this frontier will present as a time that compares identically with all query times beyond
126    /// this frontier. Practically, update times not beyond this frontier should not be taken to be accurate as
127    /// presented, and should be used carefully, only in accumulation to times that are beyond the frontier.
128    fn get_logical_compaction(&mut self) -> AntichainRef<'_, Self::Time>;
129
130    /// Advances the frontier that constrains physical compaction.
131    ///
132    /// Physical compaction is the ability of the trace to merge the batches of updates it maintains. Physical
133    /// compaction does not change the updates or their timestamps, although it is also the moment at which
134    /// logical compaction is most likely to happen.
135    ///
136    /// Physical compaction allows the trace to maintain a logarithmic number of batches of updates, which is
137    /// what allows the trace to provide efficient random access by keys and values.
138    ///
139    /// By advancing the physical compaction frontier, the caller unblocks the merging of batches of updates,
140    /// but loses the ability to create a cursor through any frontier not beyond `frontier`.
141    ///
142    /// It is an error to call this method with a frontier not equal to or beyond the most recent arguments to
143    /// this method, or the initial value of `get_physical_compaction()` if this method has not yet been called.
144    fn set_physical_compaction(&mut self, frontier: AntichainRef<'_, Self::Time>);
145
146    /// Reports the physical compaction frontier.
147    ///
148    /// All spans containing updates beyond this frontier will not be merged with other spans. This allows
149    /// the caller to acquire the spans through any frontier beyond the physical compaction frontier, with the
150    /// `spans_through()` method. This functionality is primarily of interest to the `join` operator, and any
151    /// other operators who need to take notice of the physical structure of update batches.
152    fn get_physical_compaction(&mut self) -> AntichainRef<'_, Self::Time>;
153
154    /// Maps logic across the non-empty sequence of spans in the trace.
155    ///
156    /// This is currently used only to extract historical data to prime late-starting operators who want to reproduce
157    /// the stream of spans moving past the trace.
158    fn map_spans<F: FnMut(&Span<Self::Time, Self::Batch>)>(&self, f: F);
159
160    /// Reads the upper frontier of committed times.
161    ///
162    ///
163    #[inline]
164    fn read_upper(&mut self, target: &mut Antichain<Self::Time>) {
165        target.clear();
166        target.insert(<Self::Time as timely::progress::Timestamp>::minimum());
167        self.map_spans(|span| {
168            target.clone_from(span.upper());
169        });
170    }
171
172    /// Advances `upper` across any spans with no updates.
173    ///
174    /// A span carrying no updates whose `lower` bound equals the current
175    /// contents of `upper` will advance `upper` to its `upper`.
176    /// Taken across all spans, this should advance `upper` across
177    /// update-free regions.
178    fn advance_upper(&mut self, upper: &mut Antichain<Self::Time>) {
179        self.map_spans(|span| {
180            if !span.has_updates() && span.lower() == upper {
181                upper.clone_from(span.upper());
182            }
183        });
184    }
185
186}
187
188/// An append-only collection of `(key, val, time, diff)` tuples.
189///
190/// The trace itself is opinionated only about `Time`, which bounds its contents and drives its compaction.
191/// Key, value, and diff opinions live on the batches, which the trace does not constrain.
192pub trait Trace : TraceReader {
193
194    /// Allocates a new empty trace.
195    fn new(
196        info: ::timely::dataflow::operators::generic::OperatorInfo,
197        logging: Option<crate::logging::Logger>,
198        activator: Option<timely::scheduling::activate::Activator>,
199    ) -> Self;
200
201    /// Exert merge effort, even without updates.
202    fn exert(&mut self);
203
204    /// Sets the logic for exertion in the absence of updates.
205    ///
206    /// The function receives an iterator over batch levels, from large to small, as triples `(level, count, length)`,
207    /// indicating the level, the number of batches, and their total length in updates. It should return a number of
208    /// updates to perform, or `None` if no work is required.
209    fn set_exert_logic(&mut self, logic: ExertionLogic);
210
211    /// Introduces a span of updates to the trace.
212    ///
213    /// Spans describe the time intervals they contain, and they should be added to the trace in contiguous
214    /// intervals. If a span arrives with a lower bound that does not equal the upper bound of the most recent
215    /// addition, the trace will add a span with no updates. It is an error to then try to populate that region
216    /// of time.
217    ///
218    /// This restriction could be relaxed, especially if we discover ways in which span interval order could
219    /// commute. For now, the trace should complain, to the extent that it cares about contiguous intervals.
220    fn insert(&mut self, span: Span<Self::Time, Self::Batch>);
221
222    /// Introduces an update-free span concluding the trace.
223    ///
224    /// This method should be logically equivalent to introducing a span with no updates whose lower frontier
225    /// equals the upper frontier of the most recently introduced span, and whose upper frontier is empty.
226    fn close(&mut self);
227}
228
229/// Functionality for building batches from ordered update sequences.
230///
231/// `Default` is the empty builder; a builder discovers its output as it is pushed, and so has
232/// no opportunity to size itself in advance.
233pub trait Builder: Default {
234    /// Input item type.
235    type Input;
236    /// Timestamp type.
237    type Time: Timestamp;
238    /// Output batch type.
239    type Output;
240
241    /// Adds a chunk of elements to the batch.
242    ///
243    /// Adds all elements from `chunk` to the builder and leaves `chunk` in an undefined state.
244    fn push(&mut self, chunk: &mut Self::Input);
245    /// Completes building and returns the batch, absent if no updates were pushed.
246    fn done(self) -> Option<Self::Output>;
247}