diff --git a/differential-dataflow/src/operators/int_proxy/history.rs b/differential-dataflow/src/operators/int_proxy/history.rs new file mode 100644 index 000000000..c2a8febe4 --- /dev/null +++ b/differential-dataflow/src/operators/int_proxy/history.rs @@ -0,0 +1,77 @@ +//! Time-ordered replay of proxy update histories, with meet-advancement. + +use crate::lattice::Lattice; + +/// A value history suitable for integer proxy values. +pub(in crate::operators) type IdHistory = crate::operators::ValueHistory; + +/// A value history minus the values (and diffs). +/// +/// This type mirrors ValueHistory, but traverses only the times and does not watch for +/// cancelation of data (and their times). We may learn that that pattern is preferred, +/// but for the moment the int-proxy tactic determines all interesting times before any +/// collection manipulation (and potential cancelation) occurs. +pub(crate) struct TimeHistory { + /// Un-replayed `(time, meet)`, sorted descending by time so popping replays ascending; + /// `meet` is the meet of this time with all times later in the replay. + history: Vec<(T, T)>, + /// Stepped-in times, advanced and deduplicated, sorted ascending. + buffer: Vec, +} + +impl TimeHistory { + pub fn new() -> Self { TimeHistory { history: Vec::new(), buffer: Vec::new() } } + + /// Load `times`, advancing each by `advance_by` if supplied, and organize the replay + /// (sort + suffix meets). + pub fn load(&mut self, times: impl Iterator, advance_by: Option<&T>) { + self.history.clear(); + self.buffer.clear(); + for mut time in times { + if let Some(m) = advance_by { + time = time.join(m); + } + self.history.push((time.clone(), time)); + } + self.history.sort_by(|x, y| y.0.cmp(&x.0)); + self.history.iter_mut().reduce(|prev, cur| { + cur.1.meet_assign(&prev.1); + cur + }); + } + + /// The next (least) un-replayed time. + pub fn time(&self) -> Option<&T> { + self.history.last().map(|x| &x.0) + } + /// The meet of all un-replayed times. + pub fn meet(&self) -> Option<&T> { + self.history.last().map(|x| &x.1) + } + + /// Step times while the next equals `time`; true iff any did. + pub fn step_while_time_is(&mut self, time: &T) -> bool { + let mut found = false; + while self.time() == Some(time) { + found = true; + let (t, _) = self.history.pop().unwrap(); + self.buffer.push(t); + } + found + } + + /// Advance buffered times by `meet` and deduplicate — the collapse that keeps replay + /// linear. + pub fn advance_buffer_by(&mut self, meet: &T) { + for time in self.buffer.iter_mut() { + *time = time.join(meet); + } + self.buffer.sort(); + self.buffer.dedup(); + } + + /// The buffered (stepped-in, advanced) times. + pub fn buffer(&self) -> &[T] { + &self.buffer + } +} diff --git a/differential-dataflow/src/operators/int_proxy/join.rs b/differential-dataflow/src/operators/int_proxy/join.rs new file mode 100644 index 000000000..dbda06db0 --- /dev/null +++ b/differential-dataflow/src/operators/int_proxy/join.rs @@ -0,0 +1,258 @@ +//! The proxy join framework. +//! +//! A conventional differential join against `(u64, u64)` values, which are provided by +//! and then interpreted by a backend, who is relieved of lattice-time reasoning. + +use timely::progress::{Antichain, Timestamp}; +use timely::progress::frontier::AntichainRef; + +use crate::difference::{Multiply, Semigroup}; +use crate::lattice::Lattice; +use crate::trace::BatchReader; +use super::ProxyBridge; +use crate::operators::join::{Fresh, JoinTactic}; + +use super::history::IdHistory; + +/// A unit of proxied join work, presented to the backend. +pub struct JoinInstance<'a, B0: BatchReader, B1: BatchReader