Skip to main content

timely/progress/
operate.rs

1//! Methods which describe an operators topology, and the progress it makes.
2
3use std::rc::Rc;
4use std::cell::RefCell;
5
6use crate::scheduling::Schedule;
7use crate::progress::{Timestamp, ChangeBatch, Antichain};
8
9/// A dataflow operator that progress with a specific timestamp type.
10///
11/// This trait describes the methods necessary to present as a dataflow operator.
12/// This trait is a "builder" for operators, in that it reveals the structure of the operator
13/// and its requirements, but then (through `initialize`) consumes itself to produce a boxed
14/// schedulable object. At the moment of initialization, the values of the other methods are
15/// captured and frozen.
16pub trait Operate<T: Timestamp> {
17
18    /// Indicates if the operator is strictly local to this worker.
19    ///
20    /// A parent scope must understand whether the progress information returned by the worker
21    /// reflects only this worker's progress, so that it knows whether to send and receive the
22    /// corresponding progress messages to its peers. If the operator is strictly local, it must
23    /// exchange this information, whereas if the operator is itself implemented by the same set
24    /// of workers, the parent scope understands that progress information already reflects the
25    /// aggregate information among the workers.
26    ///
27    /// This is a coarse approximation to refined worker sets. In a future better world, operators
28    /// would explain how their implementations are partitioned, so that a parent scope knows what
29    /// progress information to exchange with which peers. Right now the two choices are either
30    /// "all" or "none", but it could be more detailed. In the more detailed case, this method
31    /// should / could return a pair (index, peers), indicating the group id of the worker out of
32    /// how many groups. This becomes complicated, as a full all-to-all exchange would result in
33    /// multiple copies of the same progress messages (but aggregated variously) arriving at
34    /// arbitrary times.
35    fn local(&self) -> bool { true }
36
37    /// The number of inputs.
38    fn inputs(&self) -> usize;
39    /// The number of outputs.
40    fn outputs(&self) -> usize;
41
42    /// Initializes the operator, converting the operator builder to a schedulable object.
43    ///
44    /// In addition, initialization produces internal connectivity, and a shared progress conduit
45    /// which must contain any initial output capabilities the operator would like to hold.
46    ///
47    /// The internal connectivity summarizes the operator by a map from pairs `(input, output)`
48    /// to an antichain of timestamp summaries, indicating how a timestamp on any of its inputs may
49    /// be transformed to timestamps on any of its outputs. The conservative and most common result
50    /// is full connectivity between all inputs and outputs, each with the identity summary.
51    ///
52    /// The shared progress object allows information to move between the host and the schedulable.
53    /// Importantly, it also indicates the initial internal capabilities for all of its outputs.
54    /// This must happen at this moment, as it is the only moment where an operator is allowed to
55    /// safely "create" capabilities without basing them on other, prior capabilities.
56    fn initialize(self: Box<Self>) -> (Connectivity<T::Summary>, Rc<RefCell<SharedProgress<T>>>, Box<dyn Schedule>);
57
58    /// Indicates for each input whether the operator should be invoked when that input's frontier changes.
59    ///
60    /// Returns a `Vec<FrontierInterest>` with one entry per input. Each entry describes whether
61    /// frontier changes on that input should cause the operator to be scheduled. The conservative
62    /// default is `Always` for each input.
63    fn notify_me(&self) -> &[FrontierInterest];// { &vec![FrontierInterest::Always; self.inputs()] }
64}
65
66/// The ways in which an operator can express interest in activation when an input frontier changes.
67#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Debug)]
68pub enum FrontierInterest {
69    /// Never interested in frontier changes, as for example the `map()` and `filter()` operators.
70    Never,
71    /// Interested when the operator holds capabilities.
72    IfCapability,
73    /// Always interested in frontier changes, as for example the `probe()` and `capture()` operators.
74    Always,
75}
76
77/// Operator internal connectivity, from inputs to outputs.
78pub type Connectivity<TS> = Vec<PortConnectivity<TS>>;
79
80/// Append-only accumulation of port summaries, prior to canonicalization.
81///
82/// Summaries may be introduced in any order, and repeatedly for the same port.
83/// The `freeze` method canonicalizes the accumulation into a `PortConnectivity`,
84/// which is the only way to read the contents back out.
85#[derive(Debug, Clone)]
86pub struct PortConnectivityBuilder<TS> {
87    /// Pairs of port and path summary antichain, in insertion order.
88    entries: Vec<(usize, Antichain<TS>)>,
89}
90
91impl<TS> Default for PortConnectivityBuilder<TS> {
92    fn default() -> Self {
93        Self { entries: Vec::new() }
94    }
95}
96
97impl<TS> PortConnectivityBuilder<TS> {
98    /// Inserts a summary element for `index`.
99    ///
100    /// Equivalent to `add_port` with a single-element antichain.
101    pub fn insert(&mut self, index: usize, element: TS) {
102        self.add_port(index, Antichain::from_elem(element));
103    }
104    /// Introduces a summary for `port`, which `freeze` will merge with any other
105    /// summaries for the same port.
106    ///
107    /// Summaries for the same port are merged by antichain insertion, and describe the
108    /// union of the claimed paths. Empty summaries are discarded.
109    pub fn add_port(&mut self, port: usize, summary: Antichain<TS>) {
110        if !summary.is_empty() {
111            self.entries.push((port, summary));
112        }
113    }
114    /// Canonicalizes the accumulated summaries into a readable `PortConnectivity`.
115    ///
116    /// Duplicate ports are merged by antichain insertion, whose result is independent of
117    /// the order in which elements were introduced.
118    pub fn freeze(mut self) -> PortConnectivity<TS> where TS : crate::PartialOrder {
119        self.entries.sort_unstable_by_key(|(port, _)| *port);
120        let mut entries: Vec<(usize, Antichain<TS>)> = Vec::with_capacity(self.entries.len());
121        for (port, summary) in self.entries {
122            match entries.last_mut() {
123                Some((last, antichain)) if *last == port => {
124                    for element in summary { antichain.insert(element); }
125                }
126                _ => { entries.push((port, summary)); }
127            }
128        }
129        PortConnectivity { entries }
130    }
131}
132
133impl<TS> FromIterator<(usize, Antichain<TS>)> for PortConnectivityBuilder<TS> {
134    fn from_iter<T>(iter: T) -> Self where T: IntoIterator<Item = (usize, Antichain<TS>)> {
135        Self { entries: iter.into_iter().filter(|(_,p)| !p.is_empty()).collect() }
136    }
137}
138
139/// Internal connectivity from one port to any number of opposing ports.
140///
141/// Always in canonical form: ports sorted and distinct, antichains non-empty.
142/// Values are constructed by `PortConnectivityBuilder::freeze` (or collected from
143/// an iterator), and offer no mutation.
144#[derive(serde::Serialize, serde::Deserialize, columnar::Columnar, Debug, Clone, Eq, PartialEq)]
145pub struct PortConnectivity<TS> {
146    /// Pairs of port and path summary antichain, sorted by distinct port.
147    entries: Vec<(usize, Antichain<TS>)>,
148}
149
150impl<TS> Default for PortConnectivity<TS> {
151    fn default() -> Self {
152        Self { entries: Vec::new() }
153    }
154}
155
156impl<TS> IntoIterator for PortConnectivity<TS> {
157    type Item = (usize, Antichain<TS>);
158    type IntoIter = std::vec::IntoIter<(usize, Antichain<TS>)>;
159    /// Consumes the connectivity, yielding each port and its antichain.
160    fn into_iter(self) -> Self::IntoIter { self.entries.into_iter() }
161}
162
163impl<TS> PortConnectivity<TS> {
164    /// Borrowing iterator of port identifiers and antichains.
165    pub fn iter_ports(&self) -> impl Iterator<Item = (usize, &Antichain<TS>)> {
166        self.entries.iter().map(|(o,p)| (*o, p))
167    }
168    /// Returns the associated path summary, if it exists.
169    pub fn get(&self, index: usize) -> Option<&Antichain<TS>> {
170        self.entries
171            .binary_search_by_key(&index, |(port, _)| *port)
172            .ok()
173            .map(|position| &self.entries[position].1)
174    }
175}
176
177impl<TS: crate::PartialOrder> FromIterator<(usize, Antichain<TS>)> for PortConnectivity<TS> {
178    fn from_iter<T>(iter: T) -> Self where T: IntoIterator<Item = (usize, Antichain<TS>)> {
179        iter.into_iter().collect::<PortConnectivityBuilder<TS>>().freeze()
180    }
181}
182
183/// Progress information shared between parent and child.
184#[derive(Debug)]
185pub struct SharedProgress<T: Timestamp> {
186    /// Frontier capability changes reported by the parent scope.
187    pub frontiers: Vec<ChangeBatch<T>>,
188    /// Consumed message changes reported by the child operator.
189    pub consumeds: Vec<ChangeBatch<T>>,
190    /// Internal capability changes reported by the child operator.
191    pub internals: Vec<ChangeBatch<T>>,
192    /// Produced message changes reported by the child operator.
193    pub produceds: Vec<ChangeBatch<T>>,
194}
195
196impl<T: Timestamp> SharedProgress<T> {
197    /// Allocates a new shared progress structure.
198    pub fn new(inputs: usize, outputs: usize) -> Self {
199        SharedProgress {
200            frontiers: vec![ChangeBatch::new(); inputs],
201            consumeds: vec![ChangeBatch::new(); inputs],
202            internals: vec![ChangeBatch::new(); outputs],
203            produceds: vec![ChangeBatch::new(); outputs],
204        }
205    }
206}