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}