Skip to content

Commit 39ba5a7

Browse files
Convert Operate to a builder trait (#756)
* Documentation and formatting * Rename get_internal_summary to initialize * Make Operate a builder trait
1 parent b57c715 commit 39ba5a7

10 files changed

Lines changed: 67 additions & 73 deletions

File tree

timely/src/dataflow/operators/core/input.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,9 @@ impl<T:Timestamp> Operate<T> for Operator<T> {
204204
fn inputs(&self) -> usize { 0 }
205205
fn outputs(&self) -> usize { 1 }
206206

207-
fn get_internal_summary(&mut self) -> (Connectivity<<T as Timestamp>::Summary>, Rc<RefCell<SharedProgress<T>>>) {
207+
fn initialize(self: Box<Self>) -> (Connectivity<<T as Timestamp>::Summary>, Rc<RefCell<SharedProgress<T>>>, Box<dyn Schedule>) {
208208
self.shared_progress.borrow_mut().internals[0].update(T::minimum(), self.copies as i64);
209-
(Vec::new(), Rc::clone(&self.shared_progress))
209+
(Vec::new(), Rc::clone(&self.shared_progress), self)
210210
}
211211

212212
fn notify_me(&self) -> bool { false }

timely/src/dataflow/operators/core/probe.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ impl<G: Scope, C: Container> Probe<G, C> for Stream<G, C> {
9696
let (tee, stream) = builder.new_output();
9797
let mut output = PushCounter::new(tee);
9898

99+
// Conservatively introduce a minimal time to the handle.
100+
// This will be relaxed when the operator is first scheduled and can see its frontier.
99101
handle.frontier.borrow_mut().update_iter(std::iter::once((Timestamp::minimum(), 1)));
100102

101103
let shared_frontier = Rc::downgrade(&handle.frontier);
@@ -104,15 +106,17 @@ impl<G: Scope, C: Container> Probe<G, C> for Stream<G, C> {
104106
builder.build(
105107
move |progress| {
106108

107-
// surface all frontier changes to the shared frontier.
109+
// Mirror presented frontier changes into the shared handle.
108110
if let Some(shared_frontier) = shared_frontier.upgrade() {
109111
let mut borrow = shared_frontier.borrow_mut();
110112
borrow.update_iter(progress.frontiers[0].drain());
111113
}
112114

115+
// At initialization, we have a few tasks.
113116
if !started {
114-
// discard initial capability.
117+
// We must discard the capability held by `OpereratorCore`.
115118
progress.internals[0].update(G::Timestamp::minimum(), -1);
119+
// We must retract the conservative hold in the shared handle.
116120
if let Some(shared_frontier) = shared_frontier.upgrade() {
117121
let mut borrow = shared_frontier.borrow_mut();
118122
borrow.update_iter(std::iter::once((Timestamp::minimum(), -1)));

timely/src/dataflow/operators/core/unordered_input.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,11 @@ impl<T:Timestamp> Operate<T> for UnorderedOperator<T> {
133133
fn inputs(&self) -> usize { 0 }
134134
fn outputs(&self) -> usize { 1 }
135135

136-
fn get_internal_summary(&mut self) -> (Connectivity<<T as Timestamp>::Summary>, Rc<RefCell<SharedProgress<T>>>) {
137-
let mut borrow = self.internal.borrow_mut();
138-
for (time, count) in borrow.drain() {
136+
fn initialize(self: Box<Self>) -> (Connectivity<<T as Timestamp>::Summary>, Rc<RefCell<SharedProgress<T>>>, Box<dyn Schedule>) {
137+
for (time, count) in self.internal.borrow_mut().drain() {
139138
self.shared_progress.borrow_mut().internals[0].update(time, count * (self.peers as i64));
140139
}
141-
(Vec::new(), Rc::clone(&self.shared_progress))
140+
(Vec::new(), Rc::clone(&self.shared_progress), self)
142141
}
143142

144143
fn notify_me(&self) -> bool { false }

timely/src/dataflow/operators/generic/builder_raw.rs

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::dataflow::operators::generic::operator_info::OperatorInfo;
2424
pub struct OperatorShape {
2525
name: String, // A meaningful name for the operator.
2626
notify: bool, // Does the operator require progress notifications.
27-
peers: usize, // The total number of workers in the computation.
27+
peers: usize, // The total number of workers in the computation. Needed to initialize pointstamp counts with the correct magnitude.
2828
inputs: usize, // The number of input ports.
2929
outputs: usize, // The number of output ports.
3030
}
@@ -42,14 +42,10 @@ impl OperatorShape {
4242
}
4343

4444
/// The number of inputs of this operator
45-
pub fn inputs(&self) -> usize {
46-
self.inputs
47-
}
45+
pub fn inputs(&self) -> usize { self.inputs }
4846

4947
/// The number of outputs of this operator
50-
pub fn outputs(&self) -> usize {
51-
self.outputs
52-
}
48+
pub fn outputs(&self) -> usize { self.outputs }
5349
}
5450

5551
/// Builds operators with generic shape.
@@ -84,24 +80,16 @@ impl<G: Scope> OperatorBuilder<G> {
8480
}
8581

8682
/// The operator's scope-local index.
87-
pub fn index(&self) -> usize {
88-
self.index
89-
}
83+
pub fn index(&self) -> usize { self.index }
9084

9185
/// The operator's worker-unique identifier.
92-
pub fn global(&self) -> usize {
93-
self.global
94-
}
86+
pub fn global(&self) -> usize { self.global }
9587

9688
/// Return a reference to the operator's shape
97-
pub fn shape(&self) -> &OperatorShape {
98-
&self.shape
99-
}
89+
pub fn shape(&self) -> &OperatorShape { &self.shape }
10090

10191
/// Indicates whether the operator requires frontier information.
102-
pub fn set_notify(&mut self, notify: bool) {
103-
self.shape.notify = notify;
104-
}
92+
pub fn set_notify(&mut self, notify: bool) { self.shape.notify = notify; }
10593

10694
/// Adds a new input to a generic operator builder, returning the `Pull` implementor to use.
10795
pub fn new_input<C: Container, P>(&mut self, stream: Stream<G, C>, pact: P) -> P::Puller
@@ -134,7 +122,6 @@ impl<G: Scope> OperatorBuilder<G> {
134122

135123
/// Adds a new output to a generic operator builder, returning the `Push` implementor to use.
136124
pub fn new_output<C: Container>(&mut self) -> (Tee<G::Timestamp, C>, Stream<G, C>) {
137-
138125
let connection = (0 .. self.shape.inputs).map(|i| (i, Antichain::from_elem(Default::default())));
139126
self.new_output_connection(connection)
140127
}
@@ -218,7 +205,7 @@ where
218205
fn outputs(&self) -> usize { self.shape.outputs }
219206

220207
// announce internal topology as fully connected, and hold all default capabilities.
221-
fn get_internal_summary(&mut self) -> (Connectivity<T::Summary>, Rc<RefCell<SharedProgress<T>>>) {
208+
fn initialize(self: Box<Self>) -> (Connectivity<T::Summary>, Rc<RefCell<SharedProgress<T>>>, Box<dyn Schedule>) {
222209

223210
// Request the operator to be scheduled at least once.
224211
self.activations.borrow_mut().activate(&self.address[..]);
@@ -230,7 +217,7 @@ where
230217
.iter_mut()
231218
.for_each(|output| output.update(T::minimum(), self.shape.peers as i64));
232219

233-
(self.summary.clone(), Rc::clone(&self.shared_progress))
220+
(self.summary.clone(), Rc::clone(&self.shared_progress), self)
234221
}
235222

236223
fn notify_me(&self) -> bool { self.shape.notify }

timely/src/dataflow/operators/generic/builder_rc.rs

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@ impl<G: Scope> OperatorBuilder<G> {
4949
}
5050

5151
/// Indicates whether the operator requires frontier information.
52-
pub fn set_notify(&mut self, notify: bool) {
53-
self.builder.set_notify(notify);
54-
}
52+
pub fn set_notify(&mut self, notify: bool) { self.builder.set_notify(notify); }
5553

5654
/// Adds a new input to a generic operator builder, returning the `Pull` implementor to use.
5755
pub fn new_input<C: Container, P>(&mut self, stream: Stream<G, C>, pact: P) -> InputHandleCore<G::Timestamp, C, P::Puller>
@@ -197,24 +195,16 @@ impl<G: Scope> OperatorBuilder<G> {
197195
}
198196

199197
/// Get the identifier assigned to the operator being constructed
200-
pub fn index(&self) -> usize {
201-
self.builder.index()
202-
}
198+
pub fn index(&self) -> usize { self.builder.index() }
203199

204200
/// The operator's worker-unique identifier.
205-
pub fn global(&self) -> usize {
206-
self.builder.global()
207-
}
201+
pub fn global(&self) -> usize { self.builder.global() }
208202

209203
/// Return a reference to the operator's shape
210-
pub fn shape(&self) -> &OperatorShape {
211-
self.builder.shape()
212-
}
204+
pub fn shape(&self) -> &OperatorShape { self.builder.shape() }
213205

214206
/// Creates operator info for the operator.
215-
pub fn operator_info(&self) -> OperatorInfo {
216-
self.builder.operator_info()
217-
}
207+
pub fn operator_info(&self) -> OperatorInfo { self.builder.operator_info() }
218208
}
219209

220210

timely/src/progress/operate.rs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@ use std::cell::RefCell;
66
use crate::scheduling::Schedule;
77
use crate::progress::{Timestamp, ChangeBatch, Antichain};
88

9-
/// Methods for describing an operators topology, and the progress it makes.
10-
pub trait Operate<T: Timestamp> : Schedule {
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.
16+
pub trait Operate<T: Timestamp> {
1117

1218
/// Indicates if the operator is strictly local to this worker.
1319
///
@@ -33,20 +39,27 @@ pub trait Operate<T: Timestamp> : Schedule {
3339
/// The number of outputs.
3440
fn outputs(&self) -> usize;
3541

36-
/// Fetches summary information about internal structure of the operator.
42+
/// Initializes the operator, converting the operator builder to a schedulable object.
3743
///
38-
/// Each operator must summarize its internal structure by a map from pairs `(input, output)`
39-
/// to an antichain of timestamp summaries, indicating how a timestamp on any of its inputs may
40-
/// be transformed to timestamps on any of its outputs.
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.
4146
///
42-
/// Each operator must also indicate whether it initially holds any capabilities on any of its
43-
/// outputs, so that the parent operator can properly initialize its progress information.
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.
4451
///
45-
/// The default behavior is to indicate that timestamps on any input can emerge unchanged on
46-
/// any output, and no initial capabilities are held.
47-
fn get_internal_summary(&mut self) -> (Connectivity<T::Summary>, Rc<RefCell<SharedProgress<T>>>);
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>);
4857

49-
/// Indicates of whether the operator requires `push_external_progress` information or not.
58+
/// Indicates if the operator should be invoked on the basis of input frontier transitions.
59+
///
60+
/// This value is conservatively set to `true`, but operators that know they are oblivious to
61+
/// frontier information can indicate this with `false`, and they will not be scheduled on the
62+
/// basis of their input frontiers changing.
5063
fn notify_me(&self) -> bool { true }
5164
}
5265

timely/src/progress/reachability.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ pub struct Builder<T: Timestamp> {
130130
/// Internal connections within hosted operators.
131131
///
132132
/// Indexed by operator index, then input port, then output port. This is the
133-
/// same format returned by `get_internal_summary`, as if we simply appended
133+
/// same format returned by `initialize`, as if we simply appended
134134
/// all of the summaries for the hosted nodes.
135135
pub nodes: Vec<Connectivity<T::Summary>>,
136136
/// Direct connections from sources to targets.
@@ -359,7 +359,7 @@ pub struct Tracker<T:Timestamp> {
359359
/// Internal connections within hosted operators.
360360
///
361361
/// Indexed by operator index, then input port, then output port. This is the
362-
/// same format returned by `get_internal_summary`, as if we simply appended
362+
/// same format returned by `initialize`, as if we simply appended
363363
/// all of the summaries for the hosted nodes.
364364
nodes: Vec<Connectivity<T::Summary>>,
365365
/// Direct connections from sources to targets.

timely/src/progress/subgraph.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ where
134134

135135
/// Adds a new child to the subgraph.
136136
pub fn add_child(&mut self, child: Box<dyn Operate<TInner>>, index: usize, identifier: usize) {
137+
let child = PerOperatorState::new(child, index, identifier, self.logging.clone(), &mut self.summary_logging);
137138
if let Some(l) = &mut self.logging {
138139
let mut child_path = Vec::with_capacity(self.path.len() + 1);
139140
child_path.extend_from_slice(&self.path[..]);
@@ -142,10 +143,10 @@ where
142143
l.log(crate::logging::OperatesEvent {
143144
id: identifier,
144145
addr: child_path,
145-
name: child.name().to_owned(),
146+
name: child.name.to_owned(),
146147
});
147148
}
148-
self.children.push(PerOperatorState::new(child, index, identifier, self.logging.clone(), &mut self.summary_logging));
149+
self.children.push(child);
149150
}
150151

151152
/// Now that initialization is complete, actually build a subgraph.
@@ -545,7 +546,7 @@ where
545546

546547
// produces connectivity summaries from inputs to outputs, and reports initial internal
547548
// capabilities on each of the outputs (projecting capabilities from contained scopes).
548-
fn get_internal_summary(&mut self) -> (Connectivity<TOuter::Summary>, Rc<RefCell<SharedProgress<TOuter>>>) {
549+
fn initialize(mut self: Box<Self>) -> (Connectivity<TOuter::Summary>, Rc<RefCell<SharedProgress<TOuter>>>, Box<dyn Schedule>) {
549550

550551
// double-check that child 0 (the outside world) is correctly shaped.
551552
assert_eq!(self.children[0].outputs, self.inputs());
@@ -583,7 +584,7 @@ where
583584
self.propagate_pointstamps(); // Propagate expressed capabilities to output frontiers.
584585

585586
// Return summaries and shared progress information.
586-
(internal_summary, Rc::clone(&self.shared_progress))
587+
(internal_summary, Rc::clone(&self.shared_progress), self)
587588
}
588589
}
589590

@@ -598,13 +599,13 @@ struct PerOperatorState<T: Timestamp> {
598599
inputs: usize, // number of inputs to the operator
599600
outputs: usize, // number of outputs from the operator
600601

601-
operator: Option<Box<dyn Operate<T>>>,
602+
operator: Option<Box<dyn Schedule>>,
602603

603604
edges: Vec<Vec<Target>>, // edges from the outputs of the operator
604605

605606
shared_progress: Rc<RefCell<SharedProgress<T>>>,
606607

607-
internal_summary: Connectivity<T::Summary>, // cached result from get_internal_summary.
608+
internal_summary: Connectivity<T::Summary>, // cached result from initialize.
608609

609610
logging: Option<Logger>,
610611
}
@@ -632,7 +633,7 @@ impl<T: Timestamp> PerOperatorState<T> {
632633
}
633634

634635
pub fn new(
635-
mut scope: Box<dyn Operate<T>>,
636+
scope: Box<dyn Operate<T>>,
636637
index: usize,
637638
identifier: usize,
638639
logging: Option<Logger>,
@@ -644,7 +645,7 @@ impl<T: Timestamp> PerOperatorState<T> {
644645
let outputs = scope.outputs();
645646
let notify = scope.notify_me();
646647

647-
let (internal_summary, shared_progress) = scope.get_internal_summary();
648+
let (internal_summary, shared_progress, operator) = scope.initialize();
648649

649650
if let Some(l) = summary_logging {
650651
l.log(crate::logging::OperatesSummaryEvent {
@@ -666,8 +667,8 @@ impl<T: Timestamp> PerOperatorState<T> {
666667
);
667668

668669
PerOperatorState {
669-
name: scope.name().to_owned(),
670-
operator: Some(scope),
670+
name: operator.name().to_owned(),
671+
operator: Some(operator),
671672
index,
672673
id: identifier,
673674
local,

timely/src/scheduling/activate.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::sync::mpsc::{Sender, Receiver};
1818
/// There is no known harm to "spurious wake-ups" where a not-active path is
1919
/// returned through `extensions()`.
2020
pub trait Scheduler {
21-
/// Mark a path as immediately scheduleable.
21+
/// Mark a path as immediately schedulable.
2222
fn activate(&mut self, path: &[usize]);
2323
/// Populates `dest` with next identifiers on active extensions of `path`.
2424
///

timely/src/worker.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ impl<A: Allocate> Worker<A> {
668668
func(&mut resources, &mut builder)
669669
};
670670

671-
let mut operator = subscope.into_inner().build(self);
671+
let operator = subscope.into_inner().build(self);
672672

673673
if let Some(l) = logging.as_mut() {
674674
l.log(crate::logging::OperatesEvent {
@@ -679,15 +679,15 @@ impl<A: Allocate> Worker<A> {
679679
l.flush();
680680
}
681681

682-
operator.get_internal_summary();
682+
let (_, _, operator) = Box::new(operator).initialize();
683683

684684
let mut temp_channel_ids = self.temp_channel_ids.borrow_mut();
685685
let channel_ids = temp_channel_ids.drain(..).collect::<Vec<_>>();
686686

687687
let wrapper = Wrapper {
688688
logging,
689689
identifier,
690-
operate: Some(Box::new(operator)),
690+
operate: Some(operator),
691691
resources: Some(Box::new(resources)),
692692
channel_ids,
693693
};

0 commit comments

Comments
 (0)