Skip to main content

dbsp/circuit/
circuit_builder.rs

1//! API to construct circuits.
2//!
3//! The API exposes two abstractions: "circuits" and "streams", where a circuit
4//! is a possibly nested dataflow graph that consists of operators connected by
5//! streams:
6//!
7//!   * Circuits are represented by the [`Circuit`] trait, which has a single
8//!     implementation [`ChildCircuit<P>`], where `P` is the type of the parent
9//!     circuit.  For a root circuit, `P` is `()`, so the API provides
10//!     [`RootCircuit`] as a synonym for `ChildCircuit<()>`).
11//!
12//!     Use [`RootCircuit::build`] to create a new root circuit.  It takes a
13//!     user-provided callback, which it calls to set up operators and streams
14//!     inside the circuit, and then it returns the circuit. The circuit's
15//!     structure is fixed at construction and can't be changed afterward.
16//!
17//!   * Streams are represented by struct [`Stream<C, D>`], which is a stream
18//!     that carries values of type `D` within a circuit of type `C`.  Methods
19//!     and traits on `Stream` are the main way to assemble the structure of a
20//!     circuit within the [`RootCircuit::build`] callback.
21//!
22//! The API that this directly exposes runs the circuit in the context of the
23//! current thread.  To instead run the circuit in a collection of worker
24//! threads, use [`Runtime::init_circuit`].
25use crate::{
26    Error as DbspError, Position, Runtime, RuntimeError,
27    circuit::{
28        cache::{CircuitCache, CircuitStoreMarker},
29        fingerprinter::Fingerprinter,
30        metadata::OperatorMeta,
31        metrics::DBSP_OPERATOR_COMMIT_LATENCY_MICROSECONDS,
32        operator_traits::{
33            BinaryOperator, BinarySinkOperator, Data, ImportOperator, NaryOperator,
34            QuaternaryOperator, SinkOperator, SourceOperator, StrictUnaryOperator, TernaryOperator,
35            TernarySinkOperator, UnaryOperator,
36        },
37        runtime::Consensus,
38        schedule::{
39            CommitProgress, DynamicScheduler, Error as SchedulerError, Executor, IterativeExecutor,
40            OnceExecutor, Scheduler,
41        },
42        trace::{CircuitEvent, SchedulerEvent},
43    },
44    circuit_cache_key,
45    ir::LABEL_MIR_NODE_ID,
46    operator::dynamic::balance::{Balancer, BalancerError, BalancerHint, PartitioningPolicy},
47    time::{Timestamp, UnitTimestamp},
48};
49#[cfg(doc)]
50use crate::{
51    InputHandle, OutputHandle,
52    algebra::{IndexedZSet, ZSet},
53    operator::{Aggregator, Fold, Generator, Max, Min, time_series::RelRange},
54    trace::Batch,
55};
56use anyhow::Error as AnyError;
57use dyn_clone::{DynClone, clone_box};
58use feldera_ir::{LirCircuit, LirNodeId};
59use feldera_samply::Span;
60use feldera_storage::{FileCommitter, StoragePath};
61use itertools::Itertools;
62use nix::{
63    sys::time::TimeValLike,
64    time::{ClockId, clock_gettime},
65};
66use pin_project_lite::pin_project;
67use serde::{Deserialize, Serialize, Serializer, de::DeserializeOwned};
68use size_of::SizeOf;
69use std::{
70    any::{Any, TypeId, type_name_of_val},
71    borrow::Cow,
72    cell::{Cell, Ref, RefCell, RefMut},
73    collections::{BTreeMap, BTreeSet, HashMap},
74    fmt::{self, Debug, Display, Write},
75    future::Future,
76    io::ErrorKind,
77    marker::PhantomData,
78    mem::{take, transmute},
79    ops::{AddAssign, Deref},
80    panic::Location,
81    pin::Pin,
82    rc::Rc,
83    sync::Arc,
84    task::{Context, Poll},
85    thread::panicking,
86    time::{Duration, Instant},
87};
88use tokio::{runtime::Runtime as TokioRuntime, task::LocalSet};
89use tracing::debug;
90use typedmap::{TypedMap, TypedMapKey};
91
92use super::dbsp_handle::Mode;
93
94/// Label name used to store operator's persistent id,
95/// i.e., id stable across circuit modifications.
96const LABEL_PERSISTENT_OPERATOR_ID: &str = "persistent_id";
97
98/// Value stored in the stream.
99struct StreamValue<D> {
100    /// Value written to the stream at the current clock cycle;
101    /// `None` after the last consumer has retrieved the value from the stream.
102    val: Option<D>,
103
104    /// The number of consumers connected to the stream.  Each consumer
105    /// reads from the stream exactly once at every clock cycle.
106    ///
107    /// Controlled by the scheduler via `register_consumer` and `clear_consumer_count`.
108    consumers: usize,
109
110    /// The number of remaining consumers still expected to read from the stream
111    /// at the current clock cycle.  This value is reset to `consumers` when
112    /// a new value is written to the stream.  It is decremented on each access.
113    /// The last consumer to read from the stream (`tokens` drops to 0) obtains
114    /// an owned value rather than a borrow.  See description of
115    /// [ownership-aware scheduling](`OwnershipPreference`) for details.
116    tokens: Cell<usize>,
117}
118
119impl<D> StreamValue<D> {
120    const fn empty() -> Self {
121        Self {
122            val: None,
123            consumers: 0,
124            tokens: Cell::new(0),
125        }
126    }
127
128    fn put(&mut self, val: D) {
129        // Check that stream contents was consumed at the last clock cycle.
130        // This isn't strictly necessary for correctness, but can indicate a
131        // scheduling or token counting error.
132        debug_assert!(self.val.is_none());
133
134        // If the stream is not connected to any consumers, drop the output
135        // on the floor.
136        if self.consumers > 0 {
137            self.tokens = Cell::new(self.consumers);
138            self.val = Some(val);
139        }
140    }
141
142    /// Returns a reference to the value.
143    fn peek<R>(this: &R) -> &D
144    where
145        R: Deref<Target = Self>,
146    {
147        debug_assert_ne!(this.tokens.get(), 0);
148
149        this.val.as_ref().unwrap()
150    }
151
152    /// Returns the owned value, leaving `this` empty iff the number of remaining
153    /// tokens is 1; returns None otherwise.
154    fn take(this: &RefCell<Self>) -> Option<D>
155    where
156        D: Clone,
157    {
158        let tokens = this.borrow().tokens.get();
159        debug_assert_ne!(tokens, 0);
160
161        if tokens == 1 {
162            Some(this.borrow_mut().val.take().unwrap())
163        } else {
164            None
165        }
166    }
167
168    /// Must be called exactly once by each consumer of the stream at each clock cycle,
169    /// when the consumer has finished processing the contents of the stream. The consumer
170    /// is not allowed to access the value after calling this function. This guarantees that,
171    /// once the number of tokens drops to 1, only one active consumer remains and that
172    /// consumer can retrieve the value using `Self::take`.
173    fn consume_token(this: &RefCell<Self>) {
174        let this_ref = this.borrow();
175        debug_assert_ne!(this_ref.tokens.get(), 0);
176        this_ref.tokens.update(|tokens| tokens - 1);
177        if this_ref.tokens.get() == 0 {
178            // We're the last consumer. It's now safe to take a mutable reference to `this`.
179            drop(this_ref);
180            this.borrow_mut().val.take();
181        }
182    }
183}
184
185#[repr(transparent)]
186pub struct RefStreamValue<D>(Rc<RefCell<StreamValue<D>>>);
187
188impl<D> Clone for RefStreamValue<D> {
189    fn clone(&self) -> Self {
190        Self(self.0.clone())
191    }
192}
193
194impl<D> RefStreamValue<D> {
195    pub fn empty() -> Self {
196        Self(Rc::new(RefCell::new(StreamValue::empty())))
197    }
198
199    fn get_mut(&self) -> RefMut<'_, StreamValue<D>> {
200        self.0.borrow_mut()
201    }
202
203    fn get(&self) -> Ref<'_, StreamValue<D>> {
204        self.0.borrow()
205    }
206
207    /// Put a new value in the stream.
208    ///
209    /// # Panics
210    ///
211    /// Panics if someone is holding a reference to the stream value.
212    pub fn put(&self, d: D) {
213        let mut val = self.get_mut();
214        val.put(d);
215    }
216
217    unsafe fn transmute<D2>(&self) -> RefStreamValue<D2> {
218        unsafe {
219            RefStreamValue(std::mem::transmute::<
220                Rc<RefCell<StreamValue<D>>>,
221                Rc<RefCell<StreamValue<D2>>>,
222            >(self.0.clone()))
223        }
224    }
225}
226
227/// An object-safe interface to a stream.
228///
229/// The `Stream` type is parameterized with circuit and content types, and is not object-safe.
230/// This abstract trait abstracts away those types, allowing to pass streams around as trait objects.
231pub trait StreamMetadata: DynClone + 'static {
232    fn stream_id(&self) -> StreamId;
233    fn local_node_id(&self) -> NodeId;
234    fn origin_node_id(&self) -> &GlobalNodeId;
235    fn num_consumers(&self) -> usize;
236
237    /// Resets consumer count to 0.
238    fn clear_consumer_count(&self);
239
240    /// Invoked by the scheduler exactly once for each consumer operator attached
241    /// to the stream.
242    fn register_consumer(&self);
243
244    /// Invoked at each step once by each consumer of the stream.
245    /// When the token count drops to 1, the last consumer can retrieve the value using
246    /// `StreamValue::take`.
247    ///
248    /// Panics if called more times than there are tokens.
249    ///
250    /// All tokens must be consumed at each step; otherwise the circuit will panic
251    /// during the next step.
252    fn consume_token(&self);
253}
254
255dyn_clone::clone_trait_object!(StreamMetadata);
256
257/// A `Stream<C, D>` stores the output value of type `D` of an operator in a
258/// circuit with type `C`.
259///
260/// Circuits are synchronous, meaning that each value is produced and consumed
261/// in the same clock cycle, so there can be at most one value in the stream at
262/// any time.
263///
264/// The value type `D` may be any type, although most `Stream` methods impose
265/// additional requirements.  Since a stream must yield one data item per clock
266/// cycle, the rate at which data arrives is important to the choice of type.
267/// If, for example, an otherwise scalar input stream might not have new data on
268/// every cycle, an `Option` type could represent that, and to batch multiple
269/// pieces of data in a single step, one might use [`Vec`] or another collection
270/// type.
271///
272/// In practice, `D` is often a special collection type called an "indexed
273/// Z-set", represented as trait [`IndexedZSet`].  An indexed Z-set is
274/// conceptually a set of `(key, value, weight)` tuples.  Indexed Z-sets have a
275/// specialization called a "non-indexed Z-set" ([`ZSet`]) that contains `key`
276/// and `weight` only.  Indexed and non-indexed Z-sets are both subtraits of a
277/// higher-level [`Batch`] trait.  Many operators on streams work only with an
278/// indexed or non-indexed Z-set or another batch type as `D`.
279///
280/// # Data streams versus delta streams
281///
282/// A value in a `Stream` can represent data, or it can represent a delta (also
283/// called an "update").  Most streams carry data types that could have either
284/// meaning.  In particular, a stream of indexed or non-indexed Z-sets can carry
285/// either:
286///
287///   * In a stream of data, the `weight` indicates the multiplicity of a key. A
288///     negative `weight` has no natural interpretation and might indicate a
289///     bug.
290///
291///   * In a stream of deltas or updates, a positive `weight` represents
292///     insertions and a negative `weight` represents deletions.
293///
294/// There's no way to distinguish a data stream from a delta stream from just
295/// the type of the `Stream` since, as described above, a stream of Z-sets can
296/// be either one.  Some operators make sense for either kind of stream; for
297/// example, adding streams of Z-sets with [`plus`](`Stream::plus`) works
298/// equally well in either case, or even for adding a delta to data.  But other
299/// operations make sense only for streams of data or only for streams of
300/// deltas.  In these cases, `Stream` often provides an operator for each type
301/// of stream, and the programmer must choose the right one, since the types
302/// themselves don't help.
303///
304/// `Stream` refers to operators specifically for streams of data as
305/// "nonincremental".  These operators, which have `stream` in their name,
306/// e.g. `stream_join`, take streams of data as input and produce one as output.
307/// They act as "lifted scalar operators" that don't maintain state across
308/// invocations and only act on their immediate inputs, that is, each output is
309/// produced by independently applying the operator to the individual inputs
310/// received in the current step:
311///
312/// ```text
313///       ┌─────────────┐
314/// a ───►│    non-     │
315///       │ incremental ├───► c
316/// b ───►│  operator   │
317///       └─────────────┘
318/// ```
319///
320/// `Stream` refers to operators meant for streams of deltas as "incremental".
321/// These operators take streams of deltas as input and produces a stream of
322/// deltas as output.  Such operators could be implemented, inefficiently, in
323/// terms of a nonincremental version by putting an integration operator before
324/// each input and a differentiation operator after the output:
325///
326/// ```text
327///        ┌───┐      ┌─────────────┐
328/// Δa ───►│ I ├─────►│             │
329///        └───┘      │    non-     │    ┌───┐
330///                   │ incremental ├───►│ D ├───► Δc
331///        ┌───┐      │  operator   │    └───┘
332/// Δb ───►│ I ├─────►│             │
333///        └───┘      └─────────────┘
334/// ```
335///
336/// # Operator naming convention
337///
338/// `Stream` uses `_index` and `_generic` suffixes and
339/// `stream_` prefix to declare variations of basic operations, e.g., `map`,
340/// `map_index`, `map_generic`, `map_index_generic`, `join`, `stream_join`:
341///
342///   * `stream_` prefix: This prefix indicates that the operator is
343///     "nonincremental", that is, that it works with streams of data, rather
344///     than streams of deltas (see [Data streams versus delta streams], above).
345///
346///     [Data streams versus delta streams]:
347///     Stream#data-streams-versus-delta-streams
348///
349///   * `_generic` suffix: Most operators can assemble their outputs into any
350///     collection type that implements the [`Batch`] trait.  In practice, we
351///     typically use [`OrdIndexedZSet`](`crate::OrdIndexedZSet`) for indexed
352///     batches and [`OrdZSet`](`crate::OrdZSet`) for non-indexed batches.
353///     Methods without the `_generic` suffix return these concrete types,
354///     eliminating the need to type-annotate each invocation, while `_generic`
355///     methods can be used to return arbitrary custom `Batch` implementations.
356///
357///   * `_index` suffix: Methods without the `_index` suffix return non-indexed
358///     batches.  `<method>_index` methods combine the effects of `<method>` and
359///     [`index`](Self::index), e.g., `stream.map_index(closure)` is
360///     functionally equivalent, but more efficient than,
361///     `stream.map(closure).index()`.
362///
363/// # Catalog of stream operators
364///
365/// `Stream` methods are the main way to perform
366/// computations with streams.  The number of available methods can be
367/// overwhelming, so the subsections below categorize them into functionally
368/// related groups.
369///
370/// ## Input operators
371///
372/// Most streams are obtained via methods or traits that operate on `Stream`s.
373/// The input operators create the initial input streams for these other methods
374/// to work with.
375///
376/// [`Circuit::add_source`] is the fundamental way to add an input stream.
377/// Using it directly makes sense for cases like generating input using a
378/// function (perhaps using [`Generator`]) or reading data from a file.  More
379/// commonly, [`RootCircuit`] offers the `add_input_*` functions as convenience
380/// wrappers for `add_source`.  Each one returns a tuple of:
381///
382///   * A `Stream` that can be attached as input to operators in the circuit
383///     (within the constructor function passed to `RootCircuit::build` only).
384///
385///   * An input handle that can be used to add input to the stream from outside
386///     the circuit.  In a typical scenario, the closure passed to build will
387///     return all of its input handles, which are used at runtime to feed new
388///     inputs to the circuit at each step.  Different functions return
389///     different kinds of input handles.
390///
391/// Use [`RootCircuit::add_input_indexed_zset`] or
392/// [`RootCircuit::add_input_zset`] to create an (indexed) Z-set input
393/// stream. There's also [`RootCircuit::add_input_set`] and
394/// [`RootCircuit::add_input_map`] to simplify cases where a regular set or
395/// map is easier to use than a Z-set.  The latter functions maintain an extra
396/// internal table tracking the contents of the set or map, so they're a second
397/// choice.
398///
399/// For special cases, there's also [`RootCircuit::add_input_stream<T>`].  The
400/// [`InputHandle`] that it returns needs to be told which workers to feed the
401/// data to, which makes it harder to use.  It might be useful for feeding
402/// non-relational data to the circuit, such as the current physical time.  DBSP
403/// does not know how to automatically distribute such values across workers,
404/// so the caller must decide whether to send the value to one specific worker
405/// or to broadcast it to everyone.
406///
407/// It's common to pass explicit type arguments to the functions that
408/// create input streams, e.g.:
409///
410/// ```ignore
411/// circuit.add_input_indexed_zset::<KeyType, ValueType, WeightType>()
412/// ```
413///
414/// ## Output and debugging operators
415///
416/// There's not much value in computations whose output can't be seen in the
417/// outside world.  Use [`Stream::output`] to obtain an [`OutputHandle`] that
418/// exports a stream's data.  The constructor function passed to
419/// [`RootCircuit::build`] should return the `OutputHandle` (in addition to all
420/// the input handles as described above).  After each step, the client code
421/// should take the new data from the `OutputHandle`, typically by calling
422/// [`OutputHandle::consolidate`].
423///
424/// Use [`Stream::inspect`] to apply a callback function to each data item in a
425/// stream.  The callback function has no return value and is executed only for
426/// its side effects, such as printing the data item to stdout.  The `inspect`
427/// operator yields the same stream on its output.
428///
429/// It is not an operator, but [`Circuit::region`] can help with debugging by
430/// grouping operators into a named collection.
431///
432/// ## Record-by-record mapping operators
433///
434/// These operators map one kind of batch to another, allowing the client to
435/// pass in a function that looks at individual records in a Z-set or other
436/// batch.  These functions apply to both streams of deltas and streams of data.
437///
438/// The following methods are available for streams of indexed and non-indexed
439/// Z-sets.  Each of these takes a function that accepts a key (for non-indexed
440/// Z-sets) or a key-value pair (for indexed Z-sets):
441///
442///   * Use [`Stream::map`] to output a non-indexed Z-set using an
443///     arbitrary mapping function, or [`Stream::map_index`] to map to
444///     an indexed Z-set.
445///
446///   * Use [`Stream::filter`] to drop records that do not satisfy a
447///     predicate function.  The output stream has the same type as the input.
448///
449///   * Use [`Stream::flat_map`] to output a Z-set that maps each
450///     input record to any number of records, or
451///     [`Stream::flat_map_index`] for indexed Z-set output.  These
452///     methods also work as a `filter_map` equivalent.
453///
454/// ## Value-by-value mapping operators
455///
456/// Sometimes it makes sense to map a stream's whole data item instead of
457/// breaking Z-sets down into records.  Unlike the record-by-record functions,
458/// these functions works with streams that carry a type other than a Z-set or
459/// batch.  These functions apply to both streams of deltas and streams of data.
460///
461/// Use [`Stream::apply`] to apply a mapping function to each item in a given
462/// stream.  [`Stream::apply_named`], [`Stream::apply_owned`], and
463/// [`Stream::apply_owned_named`] offer small variations.
464///
465/// Use [`Stream::apply2`] or [`Stream::apply2_owned`] to apply a mapping
466/// function to pairs of items drawn from two different input streams.
467///
468/// ## Addition and subtraction operators
469///
470/// Arithmetic operators work on Z-sets (and batches) by operating on weights.
471/// For example, adding two Z-sets adds the weights of records with the same
472/// key-value pair.  They also work on streams of arithmetic types.
473///
474/// Use [`Stream::neg`] to map a stream to its negation, that is, to negate the
475/// weights for a Z-set.
476///
477/// Use [`Stream::plus`] to add two streams and [`Stream::sum`] to add an
478/// arbitrary number of streams.  Use [`Stream::minus`] to subtract streams.
479///
480/// There aren't any multiplication or division operators, because there is no
481/// clear interpretation of them for Z-sets.  You can use [`Stream::apply`] and
482/// [`Stream::apply2`], as already described, to do arbitrary arithmetic on one
483/// or two streams of arithmetic types.
484///
485/// ## Stream type conversion operators
486///
487/// These operators convert among streams of deltas, streams of data, and
488/// streams of "upserts".  Client code can use them, but they're more often
489/// useful for testing (for example, for checking that incremental operators are
490/// equivalent to non-incremental ones) or for building other operators.
491///
492/// Use [`Stream::integrate`] to sum up the values within an input stream.  The
493/// first output value is the first input value, the second output value is the
494/// sum of the first two inputs, and so on.  This effectively converts a stream
495/// of deltas into a stream of data.
496///
497/// [`Stream::stream_fold`] generalizes integration.  On each step, it calls a
498/// function to fold the input value with an accumulator and outputs the
499/// accumulator.  The client provides the function and the initial value of the
500/// accumulator.
501///
502/// Use [`Stream::differentiate`] to calculate differences between subsequent
503/// values in an input stream.  The first output is the first input value, the
504/// second output is the second input value minus the first input value, and so
505/// on.  This effectively converts a stream of data into a stream of deltas.
506/// You shouldn't ordinarily need this operator, at least not for Z-sets,
507/// because DBSP operators are fully incremental.
508///
509/// Use [`Stream::upsert`] to convert a stream of "upserts" into a stream of
510/// deltas.  The input stream carries "upserts", or assignments of values to
511/// keys such that a subsequent assignment to a key assigned earlier replaces
512/// the earlier value.  `upsert` turns these into a stream of deltas by
513/// internally tracking upserts that have already been seen.
514///
515/// ## Weighting and counting operators
516///
517/// Use [`Stream::dyn_weigh`] to multiply the weights in an indexed Z-set by a
518/// user-provided function of the key and value.  This is equally appropriate
519/// for streams of data or deltas.  This method outputs a non-indexed Z-set with
520/// just the keys from the input, discarding the values, which also means that
521/// weights will be added together in the case of equal input keys.
522///
523/// `Stream` provides methods to count the number of values per key in an
524/// indexed Z-set:
525///
526///   * To sum the weights for each value within a key, use
527///     [`Stream::dyn_weighted_count`] for streams of deltas or
528///     [`Stream::dyn_stream_weighted_count`] for streams of data.
529///
530///   * To count each value only once even for a weight greater than one, use
531///     [`Stream::dyn_distinct_count`] for streams of deltas or
532///     [`Stream::dyn_stream_distinct_count`] for streams of data.
533///
534/// The "distinct" operator on a Z-set maps positive weights to 1 and all other
535/// weights to 0.  `Stream` has two implementations:
536///
537///   * Use [`Stream::distinct`] to incrementally process a stream of deltas. If
538///     the output stream were to be integrated, it only contain records with
539///     weight 0 or 1.  This operator internally integrates the stream of
540///     deltas, which means its memory consumption is proportional to the
541///     integrated data size.
542///
543///   * Use [`Stream::stream_distinct`] to non-incrementally process a stream of
544///     data.  It sets each record's weight to 1 if it is positive and drops the
545///     others.
546///
547/// ## Join on equal keys
548///
549/// A DBSP equi-join takes batches `a` and `b` as input, finds all pairs of a
550/// record in `a` and a record in `b` with the same key, applies a given
551/// function `F` to those records' key and values, and outputs a Z-set with
552/// `F`'s output.
553///
554/// DBSP implements two kinds of joins:
555///
556///   * Joins of delta streams ("incremental" joins) for indexed Z-sets with the
557///     same key type.  Use [`Stream::join`] for non-indexed Z-set output, or
558///     [`Stream::join_index`] for indexed Z-set output.
559///
560///   * Joins of data streams ("nonincremental" joins), which work with any
561///     indexed batches.  Use [`Stream::stream_join`], which outputs a
562///     non-indexed Z-set.
563///
564///     `stream_join` also works for joining a stream of deltas with an
565///     invariant stream of data where the latter is used as a lookup table.
566///
567///     If the output of the join function grows monotonically as `(k, v1, v2)`
568///     tuples are fed to it in lexicographic order, then
569///     [`Stream::monotonic_stream_join`] is more efficient.  One such monotonic
570///     function is a join function that returns `(k, v1, v2)` itself.
571///
572/// One way to implement a Cartesian product is to map unindexed Z-set inputs
573/// into indexed Z-sets with a unit key type, e.g. `input.index_with(|k| ((),
574/// k))`, and then use `join` or `stream_join`, as appropriate.
575///
576/// ## Other kinds of joins
577///
578/// Use [`Stream::antijoin`] for antijoins of delta streams.  It takes indexed
579/// Z-set `a` and Z-set `b` with the same key type and yields the subset of `a`
580/// whose keys do not appear in `b`.  `b` may be indexed or non-indexed and its
581/// value type does not matter.
582///
583/// Use [`Stream::dyn_semijoin_stream`] for semi-joins of data streams.  It
584/// takes a batch `a` and non-indexed batch `b` with the same key type as `a`.
585/// It outputs a non-indexed Z-set of key-value tuples that contains all the
586/// pairs from `a` for which a key appears in `b`.
587///
588/// Use [`Stream::outer_join`] or [`Stream::outer_join_default`] for outer joins
589/// of delta streams.  The former takes three functions, one for each of the
590/// cases (common keys, left key only, right key only), and the latter
591/// simplifies it by taking only a function for common keys and passing in the
592/// default for the missing value.
593///
594/// DBSP implements "range join" of data streams, which joins keys in `a`
595/// against ranges of keys in `b`.  [`Stream::dyn_stream_join_range`] implements
596/// range join with non-indexed Z-set output,
597/// [`Stream::dyn_stream_join_range_index`] with indexed output.
598///
599/// ## Aggregation
600///
601/// Aggregation applies a function (the "aggregation function") to all of the
602/// values for a given key in an input stream, and outputs an indexed Z-set with
603/// the same keys as the input and the function's output as values.  The output
604/// of aggregation usually has fewer records than its input, because it outputs
605/// only one record per input key, regardless of the number of key-value pairs
606/// with that key.
607///
608/// DBSP implements two kinds of aggregation:
609///
610///   * [`Stream::dyn_aggregate`] aggregates delta streams.  It takes an
611///     aggregation function as an [`Aggregator`], e.g. [`Min`], [`Max`],
612///     [`Fold`], or one written by the client.
613///
614///     [`Stream::dyn_aggregate_linear`] is cheaper for linear aggregation
615///     functions.  It's also a little easier to use with a custom aggregation
616///     function, because it only takes a function rather than an [`Aggregator`]
617///     object.
618///
619///     [`Stream::dyn_average`] calculates the average over the values for each
620///     key.
621///
622///   * [`Stream::dyn_stream_aggregate`] aggregates data streams.  Each batch
623///     from the input is separately aggregated and written to the output
624///     stream.
625///
626///     [`Stream::dyn_stream_aggregate_linear`] applies a linear aggregation
627///     function to a data stream.
628///
629/// These aggregation functions all partition the aggregation by key, like GROUP
630/// BY in SQL.  To aggregate all records in a non-indexed Z-set, map to an
631/// indexed Z-set with a unit key `()` before aggregating, then map again to
632/// remove the index if necessary, e.g.:
633///
634/// ```ignore
635/// let max_auction_count = auction_counts
636///     .map_index(|(_auction, count)| ((), *count))
637///     .aggregate(Max)
638///     .map(|((), max_count)| *max_count);
639/// ```
640///
641/// ## Rolling aggregates
642///
643/// DBSP supports rolling aggregation of time series data over a
644/// client-specified "rolling window" range.  For this purpose, Rust unsigned
645/// integer types model times, larger integers corresponding to later times.
646/// The unit of time in use is relevant only for specifying the width of the
647/// aggregation window, with [`RelRange`].
648///
649/// The DBSP logical time concept is unrelated to times used in rolling
650/// aggregation and other time-series operators. The former is used to establish
651/// the ordering in which updates are consumed by DBSP, while the latter model
652/// physical times when the corresponding events were generated, observed, or
653/// processed. In particular, the ordering of physical and logical timestamps
654/// doesn't have to match. In other words, DBSP can process events out-of-order.
655///
656/// Rolling aggregation takes place within a "partition", which is any
657/// convenient division of the data.  It might correspond to a tenant ID, for
658/// example, if each tenant's data is to be separately aggregated.  To represent
659/// partitioning, rolling aggregation introduces the
660/// [`OrdPartitionedIndexedZSet`](`crate::OrdPartitionedIndexedZSet`)
661/// type, which is an `IndexedZSet` with an arbitrary key type that specifies
662/// the partition (it may be `()` if all data is to be within a single
663/// partition) and a value type of the form `(TS, V)` where `TS` is the type
664/// used for time and `V` is the client's value type.
665///
666/// Rolling aggregation does not reduce the size of data.  It outputs one record
667/// for each input record.
668///
669/// DBSP has two kinds of rolling aggregation functions that differ based on
670/// their tolerance for updating aggregation results when new data arrives for
671/// an old moment in time:
672///
673///   * If the application must tolerate data arriving entirely out-of-order,
674///     use [`Stream::partitioned_rolling_aggregate`].  It operates on a
675///     `PartitionedIndexedZSet` and takes an [`Aggregator`] and a [`RelRange`]
676///     that specifies the span of the window.  It returns another
677///     `PartitionedIndexedZSet` with the results.  This operator must buffer
678///     old data indefinitely since old output is always subject to revision.
679///
680///     [`Stream::partitioned_rolling_aggregate_linear`] is cheaper for linear
681///     aggregation functions.
682///
683///     [`Stream::partitioned_rolling_average`] calculates the rolling average
684///     over a partition.
685///
686///   * If the application can discard data that arrives too out-of-order, use
687///     [`Stream::partitioned_rolling_aggregate_with_waterline`], which can be
688///     more memory-efficient.  This form of rolling aggregation requires a
689///     "waterline" stream, which is a stream of times (scalars, not batches or
690///     Z sets) that reports the earliest time that can be updated.  Use
691///     [`Stream::waterline_monotonic`] to conveniently produce the waterline
692///     stream.
693///
694///     [`Stream::partitioned_rolling_aggregate_with_waterline`] operates on an
695///     `IndexedZSet` and, in addition to the aggregrator, range, and waterline
696///     stream, it takes a function to map a record to a partition. It discards
697///     input before the waterline, partitions it, aggregates it, and returns
698///     the result as a `PartitionedIndexedZSet`.
699///
700/// ## Windowing
701///
702/// Use [`Stream::dyn_window`] to extract a stream of deltas to windows from a
703/// stream of deltas.  This can be useful for windowing outside the context of
704/// rolling aggregation.
705pub struct Stream<C, D> {
706    /// Globally unique ID of the stream.
707    stream_id: StreamId,
708    /// Id of the operator within the local circuit that writes to the stream.
709    local_node_id: NodeId,
710    /// Global id of the node that writes to this stream.
711    origin_node_id: GlobalNodeId,
712    /// Circuit that this stream belongs to.
713    circuit: C,
714    /// Value stored in the stream (there can be at most one since our
715    /// circuits are synchronous).
716    val: RefStreamValue<D>,
717}
718
719impl<C, D> StreamMetadata for Stream<C, D>
720where
721    C: Clone + 'static,
722    D: 'static,
723{
724    fn stream_id(&self) -> StreamId {
725        self.stream_id
726    }
727    fn local_node_id(&self) -> NodeId {
728        self.local_node_id
729    }
730    fn origin_node_id(&self) -> &GlobalNodeId {
731        &self.origin_node_id
732    }
733    fn clear_consumer_count(&self) {
734        self.val.get_mut().consumers = 0;
735    }
736    fn num_consumers(&self) -> usize {
737        self.val.get().consumers
738    }
739    fn register_consumer(&self) {
740        self.val.get_mut().consumers += 1;
741    }
742    fn consume_token(&self) {
743        StreamValue::consume_token(self.val());
744    }
745}
746
747impl<C, D> Clone for Stream<C, D>
748where
749    C: Clone,
750{
751    fn clone(&self) -> Self {
752        Self {
753            stream_id: self.stream_id,
754            local_node_id: self.local_node_id,
755            origin_node_id: self.origin_node_id.clone(),
756            circuit: self.circuit.clone(),
757            val: self.val.clone(),
758        }
759    }
760}
761
762impl<C, D> Stream<C, D>
763where
764    C: Clone,
765{
766    /// Transmute a stream of `D` into a stream of `D2`.
767    ///
768    /// This is unsafe and dangerous for the same reasons [`std::mem:transmute`]
769    /// is dangerous and should be used with care.
770    ///
771    /// # Safety
772    ///
773    /// Transmuting `D` into `D2` should be safe.
774    pub(crate) unsafe fn transmute_payload<D2>(&self) -> Stream<C, D2> {
775        unsafe {
776            Stream {
777                stream_id: self.stream_id,
778                local_node_id: self.local_node_id,
779                origin_node_id: self.origin_node_id.clone(),
780                circuit: self.circuit.clone(),
781                val: self.val.transmute::<D2>(),
782            }
783        }
784    }
785}
786
787impl<C, D> Stream<C, D> {
788    /// Returns local node id of the operator or subcircuit that writes to
789    /// this stream.
790    ///
791    /// If the stream originates in a subcircuit, returns the id of the
792    /// subcircuit node.
793    pub fn local_node_id(&self) -> NodeId {
794        self.local_node_id
795    }
796
797    /// Returns global id of the operator that writes to this stream.
798    ///
799    /// If the stream originates in a subcircuit, returns id of the operator
800    /// inside the subcircuit (or one of its subcircuits) that produces the
801    /// contents of the stream.
802    pub fn origin_node_id(&self) -> &GlobalNodeId {
803        &self.origin_node_id
804    }
805
806    pub fn stream_id(&self) -> StreamId {
807        self.stream_id
808    }
809
810    /// Reference to the circuit the stream belongs to.
811    pub fn circuit(&self) -> &C {
812        &self.circuit
813    }
814
815    pub fn ptr_eq<D2>(&self, other: &Stream<C, D2>) -> bool {
816        self.stream_id() == other.stream_id()
817    }
818}
819
820// Internal streams API only used inside this module.
821impl<C, D> Stream<C, D>
822where
823    C: Circuit,
824{
825    /// Create a new stream within the given circuit, connected to the specified
826    /// node id.
827    pub(crate) fn new(circuit: C, node_id: NodeId) -> Self {
828        Self {
829            stream_id: circuit.allocate_stream_id(),
830            local_node_id: node_id,
831            origin_node_id: GlobalNodeId::child_of(&circuit, node_id),
832            circuit,
833            val: RefStreamValue::empty(),
834        }
835    }
836
837    /// Create a stream out of an existing [`RefStreamValue`] with `node_id` as the source.
838    pub fn with_value(circuit: C, node_id: NodeId, val: RefStreamValue<D>) -> Self {
839        Self {
840            stream_id: circuit.allocate_stream_id(),
841            local_node_id: node_id,
842            origin_node_id: GlobalNodeId::child_of(&circuit, node_id),
843            circuit,
844            val,
845        }
846    }
847
848    pub fn value(&self) -> RefStreamValue<D> {
849        self.val.clone()
850    }
851
852    /// Export stream to the parent circuit.
853    ///
854    /// Creates a stream in the parent circuit that contains the last value in
855    /// `self` when the child circuit terminates.
856    ///
857    /// This method currently only works for streams connected to a feedback
858    /// `Z1` operator and will panic for other streams.
859    pub fn export(&self) -> Stream<C::Parent, D>
860    where
861        C::Parent: Circuit,
862        D: 'static,
863    {
864        self.circuit()
865            .cache_get_or_insert_with(ExportId::new(self.stream_id()), || unimplemented!())
866            .clone()
867    }
868
869    /// Call `set_label` on the node that produces this stream.
870    pub fn set_label(&self, key: &str, val: &str) -> Self {
871        self.circuit.set_node_label(&self.origin_node_id, key, val);
872        self.clone()
873    }
874
875    /// Call `get_label` on the node that produces this stream.
876    pub fn get_label(&self, key: &str) -> Option<String> {
877        self.circuit.get_node_label(&self.origin_node_id, key)
878    }
879
880    /// Set persistent id for the operator that produces this stream.
881    pub fn set_persistent_id(&self, name: Option<&str>) -> Self {
882        if let Some(name) = name {
883            self.set_label(LABEL_PERSISTENT_OPERATOR_ID, name)
884        } else {
885            self.clone()
886        }
887    }
888
889    /// Get persistent id for the operator that produces this stream.
890    pub fn get_persistent_id(&self) -> Option<String> {
891        self.get_label(LABEL_PERSISTENT_OPERATOR_ID)
892    }
893}
894
895impl<C, D> Stream<C, D> {
896    /// Create a stream whose origin differs from its node id.
897    fn with_origin(
898        circuit: C,
899        stream_id: StreamId,
900        node_id: NodeId,
901        origin_node_id: GlobalNodeId,
902    ) -> Self {
903        Self {
904            stream_id,
905            local_node_id: node_id,
906            origin_node_id,
907            circuit,
908            val: RefStreamValue::empty(),
909        }
910    }
911}
912
913impl<C, D> Stream<C, D> {
914    pub(crate) fn map_value<T>(&self, f: impl Fn(&D) -> T) -> T {
915        f(StreamValue::peek(&self.get()))
916    }
917
918    fn get(&self) -> Ref<'_, StreamValue<D>> {
919        self.val.get()
920    }
921
922    fn val(&self) -> &RefCell<StreamValue<D>> {
923        &self.val.0
924    }
925
926    /// Puts a value in the stream, overwriting the previous value if any.
927    ///
928    /// # Panics
929    ///
930    /// The caller must have exclusive access to the current stream;
931    /// otherwise the method will panic.
932    pub(crate) fn put(&self, d: D) {
933        self.val.put(d);
934    }
935}
936
937/// Stream whose final value is exported to the parent circuit.
938///
939/// The struct bundles a pair of streams emitted by a
940/// [`StrictOperator`](`crate::circuit::operator_traits::StrictOperator`):
941/// a `local` stream inside operator's local circuit and an
942/// export stream available to the parent of the local circuit.
943/// The export stream contains the final value computed by the
944/// operator before `clock_end`.
945pub struct ExportStream<C, D>
946where
947    C: Circuit,
948{
949    pub local: Stream<C, D>,
950    pub export: Stream<C::Parent, D>,
951}
952
953/// Relative location of a circuit in the hierarchy of nested circuits.
954///
955/// `0` refers to the local circuit that a given node or operator belongs
956/// to, `1` - to the parent of the local, circuit, `2` - to the parent's
957/// parent, etc.
958pub type Scope = u16;
959
960/// Node in a circuit.  A node wraps an operator with strongly typed
961/// input and output streams.
962pub trait Node: Any {
963    /// Node id unique within its parent circuit.
964    fn local_id(&self) -> NodeId;
965
966    /// Global node id.
967    fn global_id(&self) -> &GlobalNodeId;
968
969    /// Persistent node id that remains stable across circuit restarts.  This Id can be used
970    /// to pick up operator state from a checkpoint after restart.
971    ///
972    /// * In ephemeral mode, this id is derived from the global node id. Such an Id can be used
973    ///   to recover the operator after a failure, when the circuit is identical to the one that was
974    ///   running before the failure.
975    ///
976    /// * In persistent mode, this id is derived from the operator's persistent Id assigned to it
977    ///   by the compiler during circuit construction.  This Id will remain stable across circuit
978    ///   restarts even if the circuit changes, as long as all ancestors of the node remain the
979    ///   same. The function will return `None` in persistent mode if the operator
980    ///   does not have a compiler-assigned persistent Id.
981    fn persistent_id(&self) -> Option<String> {
982        let worker_index = Runtime::worker_index();
983
984        match Runtime::mode() {
985            Mode::Ephemeral => Some(format!(
986                "{worker_index}-{}",
987                self.global_id().path_as_string()
988            )),
989            Mode::Persistent => self
990                .get_label(LABEL_PERSISTENT_OPERATOR_ID)
991                .map(|operator_id| format!("{worker_index}-{operator_id}")),
992        }
993    }
994
995    /// Operator name, e.g., "Map", "Join", etc.
996    fn name(&self) -> Cow<'static, str>;
997
998    fn is_circuit(&self) -> bool {
999        false
1000    }
1001
1002    /// Is this an input node?
1003    fn is_input(&self) -> bool;
1004
1005    /// `true` if the node encapsulates an asynchronous operator (see
1006    /// [`Operator::is_async()`](super::operator_traits::Operator::is_async)).
1007    /// `false` for synchronous operators and subcircuits.
1008    fn is_async(&self) -> bool;
1009
1010    /// `true` if the node is ready to execute (see
1011    /// [`Operator::ready()`](super::operator_traits::Operator::ready)).
1012    /// Always returns `true` for synchronous operators and subcircuits.
1013    fn ready(&self) -> bool;
1014
1015    /// Register callback to be invoked when an asynchronous operator becomes
1016    /// ready (see
1017    /// [`super::operator_traits::Operator::register_ready_callback`]).
1018    fn register_ready_callback(&mut self, _cb: Box<dyn Fn() + Send + Sync>) {}
1019
1020    /// Evaluate the operator.  Reads one value from each input stream
1021    /// and pushes a new value to the output stream (except for sink
1022    /// operators, which don't have an output stream).
1023    fn eval<'a>(
1024        &'a mut self,
1025    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>>;
1026
1027    fn import<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
1028        Box::pin(async {})
1029    }
1030
1031    /// Notify the operator that the circuit is starting a transaction.
1032    fn start_transaction(&mut self);
1033
1034    /// Notify the node about start of a transaction.
1035    /// Call `Operator::flush` on the operator.
1036    fn flush(&mut self);
1037
1038    /// Call `Operator::flush_complete` on the operator.
1039    fn is_flush_complete(&self) -> bool;
1040
1041    /// Notify the node about start of a clock epoch.
1042    ///
1043    /// The node should forward the notification to its inner operator.
1044    ///
1045    /// # Arguments
1046    ///
1047    /// * `scope` - the scope whose clock is restarting. A node gets notified
1048    ///   about clock events in its local circuit (scope 0) and all its
1049    ///   ancestors.
1050    fn clock_start(&mut self, scope: Scope);
1051
1052    /// Notify the node about the end of a clock epoch.
1053    ///
1054    /// The node should forward the notification to its inner operator.
1055    ///
1056    /// # Arguments
1057    ///
1058    /// * `scope` - the scope whose clock is ending.
1059    fn clock_end(&mut self, scope: Scope);
1060
1061    fn init(&mut self) {}
1062
1063    fn metadata(&self, output: &mut OperatorMeta);
1064
1065    fn fixedpoint(&self, scope: Scope) -> bool;
1066
1067    /// Invoke closure on all children of `self`, terminate on the first
1068    /// error.
1069    fn map_nodes_recursive(
1070        &self,
1071        _f: &mut dyn FnMut(&dyn Node) -> Result<(), DbspError>,
1072    ) -> Result<(), DbspError> {
1073        Ok(())
1074    }
1075
1076    /// Invoke closure on all children of `self`, terminate on the first
1077    /// error.
1078    fn map_nodes_recursive_mut(
1079        &self,
1080        _f: &mut dyn FnMut(&mut dyn Node) -> Result<(), DbspError>,
1081    ) -> Result<(), DbspError> {
1082        Ok(())
1083    }
1084
1085    /// Instructs the node to write the state of its inner operator to
1086    /// persistent storage within directory `base`.
1087    ///
1088    /// The node shouldn't commit the state to stable storage; rather, it should
1089    /// append the files to be committed to `files` for later commit.
1090    fn checkpoint(
1091        &mut self,
1092        base: &StoragePath,
1093        files: &mut Vec<Arc<dyn FileCommitter>>,
1094    ) -> Result<(), DbspError>;
1095
1096    /// Instructs the node to restore the state of its inner operator to
1097    /// the given checkpoint in directory `base`.
1098    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError>;
1099
1100    /// Reset the state of the operator to default.
1101    ///
1102    /// Used during replay, when the replay algorithm determines
1103    /// that the operator that may have previously picked up its state
1104    /// from a checkpoint must be backfilled from clean state.
1105    fn clear_state(&mut self) -> Result<(), DbspError>;
1106
1107    /// Call [`Operator::start_compaction`](super::operator_traits::Operator::start_compaction) on the operator this node encapsulates.
1108    fn start_compaction(&mut self);
1109
1110    /// Call [`Operator::is_compaction_complete`](super::operator_traits::Operator::is_compaction_complete) on the operator this node encapsulates.
1111    fn is_compaction_complete(&self) -> bool;
1112
1113    /// Place operator in the replay mode.
1114    ///
1115    /// In the replay mode the operator streams its stored state to a temporary
1116    /// replay stream.
1117    ///
1118    /// # Panics
1119    ///
1120    /// Panics for operators that don't support replay.
1121    fn start_replay(&mut self) -> Result<(), DbspError>;
1122
1123    /// Check if the operator has finished replaying its stored state.
1124    ///
1125    /// # Panics
1126    ///
1127    /// Panics for operators that don't support replay.
1128    fn is_replay_complete(&self) -> bool;
1129
1130    /// Notify the operator that the circuit is exiting the replay mode.
1131    ///
1132    /// The operator can cleanup any state needed for replay at this point.
1133    ///
1134    /// # Panics
1135    ///
1136    /// Panics for operators that don't support replay.
1137    fn end_replay(&mut self) -> Result<(), DbspError>;
1138
1139    /// Takes a fingerprint of the node's inner operator adds it to `fip`.
1140    fn fingerprint(&self, fip: &mut Fingerprinter) {
1141        fip.hash(type_name_of_val(self));
1142    }
1143
1144    /// Tag the node with a text label.
1145    fn set_label(&mut self, key: &str, value: &str);
1146
1147    /// Get the label associated with the given key.
1148    fn get_label(&self, key: &str) -> Option<&str>;
1149
1150    fn labels(&self) -> &BTreeMap<String, String>;
1151
1152    /// Apply closure to a child node of `self`.
1153    fn map_child(&self, _path: &[NodeId], _f: &mut dyn FnMut(&dyn Node)) {
1154        panic!("map_child: not a circuit node")
1155    }
1156
1157    /// Apply closure to a child node of `self`.
1158    fn map_child_mut(&self, _path: &[NodeId], _f: &mut dyn FnMut(&mut dyn Node)) {
1159        panic!("map_child_mut: not a circuit node")
1160    }
1161
1162    fn as_circuit(&self) -> Option<&dyn CircuitBase> {
1163        None
1164    }
1165
1166    fn as_any(&self) -> &dyn Any;
1167}
1168
1169/// Globally unique id of a stream.
1170#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
1171#[repr(transparent)]
1172pub struct StreamId(usize);
1173
1174impl StreamId {
1175    pub fn new(id: usize) -> Self {
1176        Self(id)
1177    }
1178
1179    /// Extracts numeric representation of the stream id.
1180    pub fn id(&self) -> usize {
1181        self.0
1182    }
1183}
1184
1185impl Display for StreamId {
1186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1187        f.write_char('s')?;
1188        Debug::fmt(&self.0, f)
1189    }
1190}
1191
1192/// Id of an operator, guaranteed to be unique within a circuit.
1193#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1194#[repr(transparent)]
1195pub struct NodeId(usize);
1196
1197impl NodeId {
1198    pub fn new(id: usize) -> Self {
1199        Self(id)
1200    }
1201
1202    /// Extracts numeric representation of the node id.
1203    pub fn id(&self) -> usize {
1204        self.0
1205    }
1206
1207    pub fn root() -> Self {
1208        Self(0)
1209    }
1210}
1211
1212impl Display for NodeId {
1213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1214        f.write_char('n')?;
1215        Debug::fmt(&self.0, f)
1216    }
1217}
1218
1219/// Identifies a circuit region by name and a numeric ID; each pair is supposed to be unique.
1220#[derive(Debug, Clone, Eq, Hash, PartialEq)]
1221pub struct RegionName {
1222    /// ID for a region used to distinguish multiple regions with the same name.
1223    id: u64,
1224    pub name: Cow<'static, str>,
1225}
1226
1227impl RegionName {
1228    fn new(name: &str, id: u64) -> Self {
1229        Self {
1230            id,
1231            name: Cow::Owned(name.to_string()),
1232        }
1233    }
1234
1235    /// Returns the identifier for this region.
1236    pub fn id(&self) -> u64 {
1237        self.id
1238    }
1239
1240    /// Returns the name of this region.
1241    pub fn name(&self) -> &str {
1242        &self.name
1243    }
1244}
1245
1246impl Display for RegionName {
1247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1248        f.write_str(self.name.as_ref())?;
1249        f.write_char(':')?;
1250        write!(f, "{}", self.id)
1251    }
1252}
1253
1254/// Globally unique id of a node (operator or subcircuit).
1255///
1256/// The identifier consists of a path from the top-level circuit to the node.
1257/// The top-level circuit has global id `[]`, an operator in the top-level
1258/// circuit or a sub-circuit nested inside the top-level circuit will have a
1259/// path of length 1, e.g., `[5]`, an operator inside the nested circuit
1260/// will have a path of length 2, e.g., `[5, 1]`, etc.
1261#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1262#[repr(transparent)]
1263pub struct GlobalNodeId(Vec<NodeId>);
1264
1265impl Serialize for GlobalNodeId {
1266    /// Serialize as a string containing all node ids
1267    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1268    where
1269        S: Serializer,
1270    {
1271        let s = self.node_identifier().to_string();
1272        serializer.serialize_str(&s)
1273    }
1274}
1275
1276impl Display for GlobalNodeId {
1277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1278        f.write_str("[")?;
1279        let path = self.path();
1280        for i in 0..path.len() {
1281            f.write_str(&path[i].0.to_string())?;
1282            if i < path.len() - 1 {
1283                f.write_str(".")?;
1284            }
1285        }
1286        f.write_str("]")
1287    }
1288}
1289
1290impl GlobalNodeId {
1291    /// Generate global node id from path.
1292    pub fn from_path(path: &[NodeId]) -> Self {
1293        Self(path.to_owned())
1294    }
1295
1296    /// Generate global node id from path.
1297    pub fn from_path_vec(path: Vec<NodeId>) -> Self {
1298        Self(path)
1299    }
1300
1301    pub fn root() -> Self {
1302        Self(Vec::new())
1303    }
1304
1305    /// Generate global node id by appending `child_id` to `self`.
1306    pub fn child(&self, child_id: NodeId) -> Self {
1307        let mut path = Vec::with_capacity(self.path().len() + 1);
1308        for id in self.path() {
1309            path.push(*id);
1310        }
1311        path.push(child_id);
1312        Self(path)
1313    }
1314
1315    /// Generate global node id for a child node of `circuit`.
1316    pub fn child_of<C>(circuit: &C, node_id: NodeId) -> Self
1317    where
1318        C: Circuit,
1319    {
1320        let mut ids = circuit.global_node_id().path().to_owned();
1321        ids.push(node_id);
1322        Self(ids)
1323    }
1324
1325    /// Generate unique name to use as a node label in a visual graph.
1326    pub fn node_identifier(&self) -> impl Display {
1327        struct NodeIdentifier<'a>(&'a GlobalNodeId);
1328        impl<'a> Display for NodeIdentifier<'a> {
1329            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1330                write!(f, "n{}", self.0.path().iter().format("_"))
1331            }
1332        }
1333        NodeIdentifier(self)
1334    }
1335
1336    /// Returns local node id of `self` or `None` if `self` is the root node.
1337    pub fn local_node_id(&self) -> Option<NodeId> {
1338        self.0.last().cloned()
1339    }
1340
1341    /// Returns parent id of `self` or `None` if `self` is the root node.
1342    pub fn parent_id(&self) -> Option<Self> {
1343        self.0
1344            .split_last()
1345            .map(|(_, prefix)| GlobalNodeId::from_path(prefix))
1346    }
1347
1348    /// Returns `true` if `self` is a child of `parent`.
1349    pub fn is_child_of(&self, parent: &Self) -> bool {
1350        self.parent_id().as_ref() == Some(parent)
1351    }
1352
1353    /// Get the path from global.
1354    pub fn path(&self) -> &[NodeId] {
1355        &self.0
1356    }
1357
1358    /// Ancestor of `self` in the top-level circuit.
1359    ///
1360    /// For a top-level node, its node id. For a nested node,
1361    /// returns the node id of the ancestor whose parent is the root node.
1362    ///
1363    /// # Panic
1364    ///
1365    /// Panics if `self` is the root node.
1366    pub fn top_level_ancestor(&self) -> NodeId {
1367        self.0[0]
1368    }
1369
1370    /// Convert to string in the `x-y-z` format.
1371    pub(crate) fn path_as_string(&self) -> String {
1372        self.0
1373            .iter()
1374            .map(|node_id| node_id.0.to_string())
1375            .collect::<Vec<_>>()
1376            .join("-")
1377    }
1378
1379    /// Format global node id as LIR node id.
1380    pub fn lir_node_id(&self) -> LirNodeId {
1381        LirNodeId::new(&self.path_as_string())
1382    }
1383}
1384
1385type CircuitEventHandler = Box<dyn Fn(&CircuitEvent)>;
1386type SchedulerEventHandler = Box<dyn FnMut(&SchedulerEvent<'_>)>;
1387type CircuitEventHandlers = Rc<RefCell<HashMap<String, CircuitEventHandler>>>;
1388type SchedulerEventHandlers = Rc<RefCell<HashMap<String, SchedulerEventHandler>>>;
1389
1390/// Operator's preference to consume input data by value.
1391///
1392/// # Background
1393///
1394/// A stream in a circuit can be connected to multiple consumers.  It is
1395/// therefore generally impossible to provide each consumer with an owned copy
1396/// of the data without cloning it.  At the same time, many operators can be
1397/// more efficient when working with owned inputs.  For instance, when computing
1398/// a sum of two z-sets, if one of the input z-sets is owned we can just add
1399/// values from the other z-set to it without cloning the first z-set.  If both
1400/// inputs are owned then we additionally do not need to clone key/value pairs
1401/// when inserting them.  Furthermore, the implementation can choose to add the
1402/// contents of the smaller z-set to the larger one.
1403///
1404/// # Ownership-aware scheduling
1405///
1406/// To leverage such optimizations, we adopt the best-effort approach: operators
1407/// consume streaming data by-value when possible while falling back to
1408/// pass-by-reference otherwise.  In a synchronous circuit, each operator reads
1409/// its input stream precisely once in each clock cycle. It is therefore
1410/// possible to determine the last consumer at each clock cycle and give it the
1411/// owned value from the channel.  It is furthermore possible for the scheduler
1412/// to schedule operators that strongly prefer owned values last.
1413///
1414/// We capture ownership preferences at two levels.  First, each individual
1415/// operator that consumes one or more streams exposes its preferences on a
1416/// per-stream basis via an API method (e.g.,
1417/// [`UnaryOperator::input_preference`]).  The [`Circuit`] API allows the
1418/// circuit builder to override these preferences when instantiating an
1419/// operator, taking into account circuit topology and workload.  We express
1420/// preference as a numeric value.
1421///
1422/// These preferences are associated with each edge in the circuit graph.  The
1423/// schedulers we have built so far implement a limited form of ownership-aware
1424/// scheduling.  They only consider strong preferences
1425/// ([`OwnershipPreference::STRONGLY_PREFER_OWNED`] and stronger) and model them
1426/// internally as hard constraints that must be satisfied for the circuit to be
1427/// schedulable.  Weaker preferences are ignored.
1428#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
1429#[repr(transparent)]
1430pub struct OwnershipPreference(usize);
1431
1432impl OwnershipPreference {
1433    /// Create a new instance with given numeric preference value (higher
1434    /// value means stronger preference).
1435    pub const fn new(val: usize) -> Self {
1436        Self(val)
1437    }
1438
1439    /// The operator does not gain any speed up from consuming an owned value.
1440    pub const INDIFFERENT: Self = Self::new(0);
1441
1442    /// The operator is likely to run faster provided an owned input, but
1443    /// shouldn't be prioritized over more impactful operators
1444    ///
1445    /// This gives a lower priority than [`Self::PREFER_OWNED`] so that
1446    /// operators who need ownership can get it when available
1447    pub const WEAKLY_PREFER_OWNED: Self = Self::new(40);
1448
1449    /// The operator is likely to run faster provided an owned input.
1450    ///
1451    /// Preference levels above `PREFER_OWNED` should not be used by operators
1452    /// and are reserved for use by circuit builders through the [`Circuit`]
1453    /// API.
1454    pub const PREFER_OWNED: Self = Self::new(50);
1455
1456    /// The circuit will suffer a significant performance hit if the operator
1457    /// cannot consume data in the channel by-value.
1458    pub const STRONGLY_PREFER_OWNED: Self = Self::new(100);
1459
1460    /// Returns the numeric value of the preference.
1461    pub const fn raw(&self) -> usize {
1462        self.0
1463    }
1464}
1465
1466impl Default for OwnershipPreference {
1467    #[inline]
1468    fn default() -> Self {
1469        Self::INDIFFERENT
1470    }
1471}
1472
1473impl Display for OwnershipPreference {
1474    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1475        match *self {
1476            Self::INDIFFERENT => f.write_str("Indifferent"),
1477            Self::WEAKLY_PREFER_OWNED => f.write_str("WeaklyPreferOwned"),
1478            Self::PREFER_OWNED => f.write_str("PreferOwned"),
1479            Self::STRONGLY_PREFER_OWNED => f.write_str("StronglyPreferOwned"),
1480            Self(preference) => write!(f, "Preference({preference})"),
1481        }
1482    }
1483}
1484
1485/// An edge in a circuit graph represents a stream connecting two
1486/// operators or a dependency (i.e., a requirement that one operator
1487/// must be evaluated before the other even if they are not connected
1488/// by a stream).
1489#[derive(Clone)]
1490pub struct Edge {
1491    /// Source node.
1492    pub from: NodeId,
1493    /// Destination node.
1494    pub to: NodeId,
1495    /// Origin node that generates the stream.  If the origin belongs
1496    /// to the local circuit, this is just the full path to the `from`
1497    /// node.
1498    pub origin: GlobalNodeId,
1499    /// Stream associated with the edge, if any.  If `None`, this is a
1500    /// dependency edge.
1501    pub stream: Option<Box<dyn StreamMetadata>>,
1502    /// Ownership preference associated with the consumer of this
1503    /// stream or `None` if this is a dependency edge.
1504    pub ownership_preference: Option<OwnershipPreference>,
1505}
1506
1507#[allow(dead_code)]
1508impl Edge {
1509    /// `true` if `self` is a dependency edge.
1510    pub(crate) fn is_dependency(&self) -> bool {
1511        self.ownership_preference.is_none()
1512    }
1513
1514    /// `true` if `self` is a stream edge.
1515    pub(crate) fn is_stream(&self) -> bool {
1516        self.stream.is_some()
1517    }
1518
1519    pub(crate) fn stream_id(&self) -> Option<StreamId> {
1520        self.stream.as_ref().map(|meta| meta.stream_id())
1521    }
1522}
1523
1524circuit_cache_key!(ExportId<C, D>(StreamId => Stream<C, D>));
1525
1526// `stream` => `replay_stream` mapping designates `replay_stream`
1527// as a replay source for `stream`.
1528circuit_cache_key!(ReplaySource(StreamId => Box<dyn StreamMetadata>));
1529
1530/// Register `replay_stream` as a replay source for `stream`.
1531pub(crate) fn register_replay_stream<C, B>(
1532    circuit: &C,
1533    stream: &Stream<C, B>,
1534    replay_stream: &Stream<C, B>,
1535) where
1536    C: Circuit,
1537    B: 'static,
1538{
1539    // We currently only support using operators in the top-level circuit
1540    // as replay sources.
1541    if TypeId::of::<()>() == TypeId::of::<C::Time>() {
1542        // If a replay source already exists, don't overwrite it. This normally shouldn't
1543        // happen as we should not have more than one integral for each stream. One situation
1544        // where this does happen today is for input streams that have an integral without
1545        // an accumulator as part of input_upsert, and another integral with an accumulator
1546        // created by a downstream join or aggregate. In this case, we want to use the former
1547        // for replay, as the latter may have been added in the new version of the program
1548        // and may be empty, while the former can have state (conversely, if the input integral
1549        // is empty, the downstream integral is guaranteed to be empty too).
1550        if !circuit.cache_contains(&ReplaySource::new(stream.stream_id())) {
1551            circuit.cache_insert(
1552                ReplaySource::new(stream.stream_id()),
1553                Box::new(replay_stream.clone()),
1554            );
1555        }
1556    }
1557}
1558
1559/// Trait for an object that has a clock associated with it.
1560/// This is implemented trivially for root circuits.
1561pub trait WithClock {
1562    /// `()` for a trivial zero-dimensional clock that doesn't need to count
1563    /// ticks.
1564    type Time: Timestamp;
1565
1566    /// Current time.
1567    fn time(&self) -> Self::Time;
1568}
1569
1570/// This `impl` is only needed to bootstrap the
1571/// recursive definition of `WithClock` for `ChildCircuit`.
1572/// It is never actually used at runtime.
1573impl WithClock for () {
1574    type Time = UnitTimestamp;
1575
1576    fn time(&self) -> Self::Time {
1577        UnitTimestamp
1578    }
1579}
1580
1581impl<P, T> WithClock for ChildCircuit<P, T>
1582where
1583    P: 'static,
1584    T: Timestamp,
1585{
1586    type Time = T;
1587
1588    fn time(&self) -> Self::Time {
1589        self.time.borrow().clone()
1590    }
1591}
1592
1593// TODO: use a better type than serde_json::Value.
1594#[derive(Default, Debug, Clone, Serialize, Deserialize)]
1595pub struct CircuitMetadata {
1596    metadata: HashMap<NodeId, serde_json::Value>,
1597}
1598
1599#[derive(Default, Debug)]
1600pub struct MetadataExchangeInner {
1601    /// Metadata registered by operators running in the current worker.
1602    local_metadata: RefCell<CircuitMetadata>,
1603
1604    /// Metadata received from peers. All workers have identical metadata snapshots during a step.
1605    /// This is a vector of metadata snapshots from all workers, one for each worker.
1606    global_metadata: RefCell<Vec<CircuitMetadata>>,
1607}
1608
1609/// Metadata exchange.
1610///
1611/// Allows the worker to exchange arbitrary semi-structured data with its peers.
1612///
1613/// Every operator in the circuit can update its local metadata.
1614/// Before every step, the circuit broadcasts its metadata to all other workers
1615/// and receives their metadata. As a result all workers have identical metadata
1616/// snapshots during the step and can make deterministic decisions based on it, such
1617/// as choosing a balancing policy for a stream.
1618#[derive(Default, Debug, Clone)]
1619pub struct MetadataExchange {
1620    inner: Rc<MetadataExchangeInner>,
1621}
1622
1623impl MetadataExchange {
1624    fn new() -> Self {
1625        Self::default()
1626    }
1627
1628    /// Get the current snapshot of the local metadata registered by operators running in the current worker.
1629    pub fn local_metadata(&self) -> CircuitMetadata {
1630        self.inner.local_metadata.borrow().clone()
1631    }
1632
1633    /// Update the local metadata for the operator with the given id.
1634    pub fn set_local_operator_metadata(&self, id: NodeId, metadata: serde_json::Value) {
1635        self.inner
1636            .local_metadata
1637            .borrow_mut()
1638            .metadata
1639            .insert(id, metadata.clone());
1640    }
1641
1642    /// Clear the local metadata for the operator with the given id.
1643    pub fn clear_local_operator_metadata(&self, id: NodeId) {
1644        self.inner.local_metadata.borrow_mut().metadata.remove(&id);
1645    }
1646
1647    /// Update the local metadata for the operator with the given id by serializing `metadata` to a JSON value.
1648    pub fn set_local_operator_metadata_typed<T>(&self, id: NodeId, metadata: T)
1649    where
1650        T: Serialize,
1651    {
1652        self.inner
1653            .local_metadata
1654            .borrow_mut()
1655            .metadata
1656            .insert(id, serde_json::to_value(metadata).unwrap());
1657    }
1658
1659    /// Get the current snapshot of the local metadata for the operator with the given id.
1660    pub fn get_local_operator_metadata(&self, id: NodeId) -> Option<serde_json::Value> {
1661        self.inner
1662            .local_metadata
1663            .borrow()
1664            .metadata
1665            .get(&id)
1666            .cloned()
1667    }
1668
1669    pub fn get_local_operator_metadata_typed<T>(&self, id: NodeId) -> Option<T>
1670    where
1671        T: DeserializeOwned,
1672    {
1673        self.get_local_operator_metadata(id)
1674            .map(|val| serde_json::from_value::<T>(val).unwrap())
1675    }
1676
1677    /// Set the global metadata received from peers (invoked by the scheduler).
1678    pub fn set_global_metadata(&self, global_metadata: Vec<CircuitMetadata>) {
1679        *self.inner.global_metadata.borrow_mut() = global_metadata;
1680    }
1681
1682    pub fn get_global_metadata(&self) -> Vec<CircuitMetadata> {
1683        self.inner.global_metadata.borrow().clone()
1684    }
1685
1686    /// Get metadata for the operator with the given id received from all workers before the current step.
1687    pub fn get_global_operator_metadata(&self, id: NodeId) -> Vec<Option<serde_json::Value>> {
1688        self.inner
1689            .global_metadata
1690            .borrow()
1691            .iter()
1692            .map(|global_metadata| global_metadata.metadata.get(&id).cloned())
1693            .collect()
1694    }
1695
1696    /// Get metadata for the operator with the given id received from all workers before the current step.
1697    /// Deserialize it from a JSON value to a strongly typed representation `T`.
1698    ///
1699    /// # Panic
1700    ///
1701    /// Panics if the JSON value cannot be deserialized to a strongly typed representation `T`.
1702    pub fn get_global_operator_metadata_typed<T>(&self, id: NodeId) -> Vec<Option<T>>
1703    where
1704        T: DeserializeOwned,
1705    {
1706        self.inner
1707            .global_metadata
1708            .borrow()
1709            .iter()
1710            .map(|global_metadata| {
1711                global_metadata
1712                    .metadata
1713                    .get(&id)
1714                    .cloned()
1715                    .map(|val| serde_json::from_value::<T>(val).unwrap())
1716            })
1717            .collect()
1718    }
1719}
1720
1721/// An object-safe subset of the circuit API.
1722pub trait CircuitBase: 'static {
1723    fn edges(&self) -> Ref<'_, Edges>;
1724
1725    fn edges_mut(&self) -> RefMut<'_, Edges>;
1726
1727    /// Global id of the circuit node.
1728    ///
1729    /// Returns [`GlobalNodeId::root()`] for the root circuit.
1730    fn global_id(&self) -> &GlobalNodeId;
1731
1732    /// Number of nodes in the circuit.
1733    fn num_nodes(&self) -> usize;
1734
1735    /// Returns vector of local node ids in the circuit.
1736    fn node_ids(&self) -> Vec<NodeId>;
1737
1738    fn lookup_local_node_by_persistent_id(
1739        &self,
1740        persistent_id: &str,
1741    ) -> Result<GlobalNodeId, DbspError>;
1742
1743    fn import_nodes(&self) -> Vec<NodeId>;
1744
1745    fn clear(&mut self);
1746
1747    /// Register a dependency between `from` and `to` nodes.  A dependency tells
1748    /// the scheduler that `from` must be evaluated before `to` in each
1749    /// clock cycle even though there may not be an edge or a path
1750    /// connecting them.
1751    fn add_dependency(&self, from: NodeId, to: NodeId);
1752
1753    /// The set of transitive ancestors for each node in the circuit,
1754    /// i.e., the set of all nodes that precede it (transitively) in the `Edges` relationship.
1755    fn transitive_ancestors(&self) -> BTreeMap<NodeId, BTreeSet<NodeId>>;
1756
1757    /// Allocate a new globally unique stream id.  This method can be invoked on any circuit in the pipeline,
1758    /// since all of them maintain a shared global counter.
1759    fn allocate_stream_id(&self) -> StreamId;
1760
1761    /// Reference to the global counter shared by all circuits.
1762    fn last_stream_id(&self) -> RefCell<StreamId>;
1763
1764    /// Relative depth of `self` from the root circuit.
1765    ///
1766    /// Returns 0 if `self` is the root circuit, 1 if `self` is an immediate
1767    /// child of the root circuit, etc.
1768    fn root_scope(&self) -> Scope;
1769
1770    /// Circuit's node id within the parent circuit.
1771    fn node_id(&self) -> NodeId;
1772
1773    /// Circuit's global node id.
1774    fn global_node_id(&self) -> GlobalNodeId;
1775
1776    /// Apply `f` to the node with the specified `path` relative to `self`.
1777    fn map_node_relative(&self, path: &[NodeId], f: &mut dyn FnMut(&dyn Node));
1778
1779    /// Apply `f` to the node with the specified `path` relative to `self`.
1780    fn map_node_mut_relative(&self, path: &[NodeId], f: &mut dyn FnMut(&mut dyn Node));
1781
1782    /// Recursively apply `f` to all nodes in `self` and its children.
1783    ///
1784    /// Stop at the first error.
1785    fn map_nodes_recursive(
1786        &self,
1787        f: &mut dyn FnMut(&dyn Node) -> Result<(), DbspError>,
1788    ) -> Result<(), DbspError>;
1789
1790    /// Recursively apply `f` to all nodes in `self` and its children mutably.
1791    ///
1792    /// Stop at the first error.
1793    fn map_nodes_recursive_mut(
1794        &mut self,
1795        f: &mut dyn FnMut(&mut dyn Node) -> Result<(), DbspError>,
1796    ) -> Result<(), DbspError>;
1797
1798    /// Apply `f` to all immediate children of `self`.
1799    ///
1800    /// Stop at the first error.
1801    fn map_local_nodes(
1802        &self,
1803        f: &mut dyn FnMut(&dyn Node) -> Result<(), DbspError>,
1804    ) -> Result<(), DbspError>;
1805
1806    /// Apply `f` to all immediate children of `self`.
1807    fn map_local_nodes_mut(
1808        &self,
1809        f: &mut dyn FnMut(&mut dyn Node) -> Result<(), DbspError>,
1810    ) -> Result<(), DbspError>;
1811
1812    /// Apply closure `f` to a node with specified node id.
1813    ///
1814    /// # Panic
1815    ///
1816    /// Panics if `id` is not a valid Id of a node in `self`.
1817    fn apply_local_node_mut(&self, id: NodeId, f: &mut dyn FnMut(&mut dyn Node));
1818
1819    /// Apply `f` to all immediate subcircuits of `self`.
1820    ///
1821    /// Stop at the first error.
1822    fn map_subcircuits(
1823        &self,
1824        f: &mut dyn FnMut(&dyn CircuitBase) -> Result<(), DbspError>,
1825    ) -> Result<(), DbspError>;
1826
1827    /// Tag the node with a text label.
1828    ///
1829    /// # Panic
1830    ///
1831    /// Panics if `id` is not a valid Id of a node in `self` or one of its children or
1832    /// if there is another mutable or immutable reference to the node.
1833    fn set_node_label(&self, id: &GlobalNodeId, key: &str, val: &str);
1834
1835    fn set_persistent_node_id(&self, id: &GlobalNodeId, persistent_id: Option<&str>) {
1836        if let Some(persistent_id) = persistent_id {
1837            self.set_node_label(id, LABEL_PERSISTENT_OPERATOR_ID, persistent_id);
1838        }
1839    }
1840
1841    fn set_mir_node_id(&self, id: &GlobalNodeId, mir_id: Option<&str>) {
1842        if let Some(mir_id) = mir_id {
1843            self.set_node_label(id, LABEL_MIR_NODE_ID, mir_id);
1844        }
1845    }
1846
1847    /// Get node label.
1848    ///
1849    /// # Panic
1850    ///
1851    /// Panics if `id` is not a valid Id of a node in `self` or one of its children or
1852    /// if there is another mutable or immutable reference to the node.
1853    fn get_node_label(&self, id: &GlobalNodeId, key: &str) -> Option<String>;
1854
1855    /// Node label for persistent operator id.
1856    fn get_persistent_node_id(&self, id: &GlobalNodeId) -> Option<String> {
1857        self.get_node_label(id, LABEL_PERSISTENT_OPERATOR_ID)
1858    }
1859
1860    fn check_fixedpoint(&self, scope: Scope) -> bool;
1861
1862    /// Return the metadata exchange object associated with the circuit.
1863    fn metadata_exchange(&self) -> &MetadataExchange;
1864
1865    /// Return the balancer object associated with the circuit.
1866    fn balancer(&self) -> &Balancer;
1867
1868    /// Set the auto-rebalancing flag for the circuit.
1869    fn set_auto_rebalance(&self, enable: bool) -> Result<(), DbspError>;
1870
1871    /// Set the balancer hint for the operator with the given global node id.
1872    ///
1873    /// # Errors
1874    ///
1875    /// Returns an error if the operator with the given global node id is not found
1876    /// or if the hint contradicts the current balancer policy.
1877    fn set_balancer_hint_by_global_id(
1878        &self,
1879        global_node_id: &GlobalNodeId,
1880        hint: BalancerHint,
1881    ) -> Result<(), DbspError>;
1882
1883    fn set_balancer_hint(&self, persistent_id: &str, hint: BalancerHint) -> Result<(), DbspError>;
1884
1885    /// Get the current balancer policy for all streams managed by the balancer.
1886    fn get_current_balancer_policies(&self) -> BTreeMap<NodeId, PartitioningPolicy>;
1887
1888    fn get_current_balancer_policy(
1889        &self,
1890        persistent_id: &str,
1891    ) -> Result<PartitioningPolicy, DbspError>;
1892
1893    fn rebalance(&self);
1894
1895    fn start_compaction(&self);
1896
1897    /// Returns `true` when all operators' background compaction has fully
1898    /// converged.
1899    fn is_compaction_complete(&self) -> bool;
1900}
1901
1902/// The circuit interface.  All DBSP computation takes place within a circuit.
1903///
1904/// Circuits can nest.  The nesting hierarchy must be known statically at
1905/// compile time via the `Parent` associated type, which must be `()` for a root
1906/// circuit and otherwise the parent circuit's type.
1907///
1908/// A circuit has a clock represented by the `Time` associated type obtained via
1909/// the `WithClock` supertrait.  For a root circuit, this is a trivial
1910/// zero-dimensional clock that doesn't need to count ticks.
1911///
1912/// There is only one implementation, [`ChildCircuit<P>`], whose `Parent` type
1913/// is `P`.  [`RootCircuit`] is a synonym for `ChildCircuit<()>`.
1914pub trait Circuit: CircuitBase + Clone + WithClock {
1915    /// Parent circuit type or `()` for the root circuit.
1916    type Parent;
1917
1918    /// Returns the parent circuit of `self`.
1919    fn parent(&self) -> Self::Parent;
1920
1921    /// Return the root of the circuit tree.
1922    fn root_circuit(&self) -> RootCircuit;
1923
1924    /// Check if `this` and `other` refer to the same circuit instance.
1925    fn ptr_eq(this: &Self, other: &Self) -> bool;
1926
1927    /// Returns circuit event handlers attached to the circuit.
1928    fn circuit_event_handlers(&self) -> CircuitEventHandlers;
1929
1930    /// Returns scheduler event handlers attached to the circuit.
1931    fn scheduler_event_handlers(&self) -> SchedulerEventHandlers;
1932
1933    /// Deliver `event` to all circuit event handlers.
1934    fn log_circuit_event(&self, event: &CircuitEvent);
1935
1936    /// Deliver `event` to all scheduler event handlers.
1937    fn log_scheduler_event(&self, event: &SchedulerEvent<'_>);
1938
1939    /// Apply closure `f` to a node with specified global id.
1940    ///
1941    /// # Panic
1942    ///
1943    /// Panics if `id` is not a valid Id of a node in `self` or one of its children or
1944    /// if there is another mutable reference to the node.
1945    fn map_node<T>(&self, id: &GlobalNodeId, f: &mut dyn FnMut(&dyn Node) -> T) -> T;
1946
1947    /// Apply closure `f` to a node with specified global id.
1948    ///
1949    /// # Panic
1950    ///
1951    /// Panics if `id` is not a valid Id of a node in `self` or one of its children or
1952    /// if there is another mutable or immutable reference to the node.
1953    fn map_node_mut<T>(&self, id: &GlobalNodeId, f: &mut dyn FnMut(&mut dyn Node) -> T) -> T;
1954
1955    /// Apply closure `f` to a node with specified node id.
1956    ///
1957    /// # Panic
1958    ///
1959    /// Panics if `id` is not a valid Id of a node in `self`.
1960    fn map_local_node_mut<T>(&self, id: NodeId, f: &mut dyn FnMut(&mut dyn Node) -> T) -> T;
1961
1962    /// Lookup a value in the circuit cache or create and insert a new value
1963    /// if it does not exist.
1964    ///
1965    /// See [`cache`](`crate::circuit::cache`) module documentation for details.
1966    fn cache_get_or_insert_with<K, F>(&self, key: K, f: F) -> RefMut<'_, K::Value>
1967    where
1968        K: 'static + TypedMapKey<CircuitStoreMarker>,
1969        F: FnMut() -> K::Value;
1970
1971    /// Invoked by the scheduler at the end of a clock cycle, after all circuit
1972    /// operators have been evaluated.
1973    fn tick(&self);
1974
1975    /// Deliver `clock_start` notification to all nodes in the circuit.
1976    fn clock_start(&self, scope: Scope);
1977
1978    /// Deliver `clock_end` notification to all nodes in the circuit.
1979    fn clock_end(&self, scope: Scope);
1980
1981    /// `true` if the specified node is ready to execute (see
1982    /// [`Operator::ready()`](super::operator_traits::Operator::ready)).
1983    fn ready(&self, id: NodeId) -> bool;
1984
1985    /// Insert a value to the circuit cache, overwriting any existing value.
1986    ///
1987    /// See [`cache`](`crate::circuit::cache`) module documentation for
1988    /// details.
1989    fn cache_insert<K>(&self, key: K, val: K::Value)
1990    where
1991        K: TypedMapKey<CircuitStoreMarker> + 'static;
1992
1993    fn cache_contains<K>(&self, key: &K) -> bool
1994    where
1995        K: TypedMapKey<CircuitStoreMarker> + 'static;
1996
1997    fn cache_get<K>(&self, key: &K) -> Option<K::Value>
1998    where
1999        K: TypedMapKey<CircuitStoreMarker> + 'static,
2000        K::Value: Clone;
2001
2002    /// Check if a stream can be replayed, if so, return the replay stream that can be used to
2003    /// replay the contents of `stream_id`.
2004    fn get_replay_source(&self, stream_id: StreamId) -> Option<Box<dyn StreamMetadata>> {
2005        self.cache_get(&ReplaySource::new(stream_id))
2006    }
2007
2008    /// For every edge in `self` with stream id equal to `stream_id`, create an additional replay edge with
2009    /// the same destination node attached to `replay_stream`.
2010    fn add_replay_edges(&self, stream_id: StreamId, replay_stream: &dyn StreamMetadata);
2011
2012    /// Connect `stream` as input to `to`.
2013    fn connect_stream<T: 'static>(
2014        &self,
2015        stream: &Stream<Self, T>,
2016        to: NodeId,
2017        ownership_preference: OwnershipPreference,
2018    );
2019
2020    fn register_ready_callback(&self, id: NodeId, cb: Box<dyn Fn() + Send + Sync>);
2021
2022    fn is_async_node(&self, id: NodeId) -> bool;
2023
2024    /// Evaluate operator with the given id.
2025    ///
2026    /// This method should only be used by schedulers.
2027    fn eval_node(
2028        &self,
2029        id: NodeId,
2030    ) -> impl Future<Output = Result<Option<Position>, SchedulerError>>;
2031
2032    /// Evaluate import node to pull inputs from the parent circuit.
2033    fn eval_import_node(&self, id: NodeId) -> impl Future<Output = ()>;
2034
2035    fn flush_node(&self, id: NodeId);
2036
2037    fn is_flush_complete(&self, id: NodeId) -> bool;
2038
2039    /// Evaluate closure `f` inside a new circuit region.
2040    ///
2041    /// A region is a logical grouping of circuit nodes.  Regions are used
2042    /// exclusively for debugging and do not affect scheduling or evaluation
2043    /// of the circuit.  This function creates a new region and executes
2044    /// closure `f` inside it.  Any operators or subcircuits created by
2045    /// `f` will belong to the new region.
2046    #[track_caller]
2047    fn region<F, T>(&self, name: &str, f: F) -> T
2048    where
2049        F: FnOnce() -> T;
2050
2051    /// Create a named region identifier for incremental region construction.
2052    /// 'name' is the name displayed for the region.
2053    /// 'id' is an identifier which distinguishes between regions with the same name.
2054    ///
2055    /// Returns a [`RegionName`] that can be passed to [`Circuit::open_region`]
2056    /// and [`Circuit::close_region`] to associate operators with the region
2057    /// from multiple call sites.
2058    fn create_region_name(&self, name: &str, id: u64) -> RegionName {
2059        RegionName::new(name, id)
2060    }
2061
2062    /// Open the circuit region identified by `name`.
2063    ///
2064    /// Emits an `OpenRegion` event.  Every operator or subcircuit created after
2065    /// this call (in the same thread) will be inserted in the named region until
2066    /// [`Circuit::close_region`] is called with the same `name`.  Saves the previously
2067    /// active region.
2068    ///
2069    /// # Panics
2070    ///
2071    /// The caller is responsible for pairing every `open_region` with a
2072    /// matching `close_region`; failing to do so will leave the region stack
2073    /// in an inconsistent state.
2074    #[track_caller]
2075    fn open_region(&self, name: RegionName);
2076
2077    /// Close the circuit region identified by `name`.  Restores the previously
2078    /// active region saved by [`Circuit::open_region`].
2079    ///
2080    /// Emits a `CloseRegion` event which should use the same name as the most recent
2081    /// [`Circuit::open_region`] call.
2082    fn close_region(&self, name: RegionName);
2083
2084    /// Add a dependency from `preprocessor_node_id` to all input operators in the
2085    /// circuit, making sure that this node and all its predecessors
2086    /// are evaluated before the rest of the circuit.
2087    fn add_preprocessor(&self, preprocessor_node_id: NodeId);
2088
2089    /// Add a source operator to the circuit.  See [`SourceOperator`].
2090    fn add_source<O, Op>(&self, operator: Op) -> Stream<Self, O>
2091    where
2092        O: Data,
2093        Op: SourceOperator<O>;
2094
2095    /// Add a pair of operators that implement cross-worker communication.
2096    ///
2097    /// Operators that exchange data across workers are split into two
2098    /// operators: the **sender** responsible for partitioning values read
2099    /// from the input stream and distributing them across workers and the
2100    /// **receiver**, which receives and reassembles data received from its
2101    /// peers.  Splitting communication into two halves allows the scheduler
2102    /// to schedule useful work in between them instead of blocking to wait
2103    /// for the receiver.
2104    ///
2105    /// Exchange operators use some form of IPC or shared memory instead of
2106    /// streams to communicate.  Therefore, the sender must implement trait
2107    /// [`SinkOperator`], while the receiver implements [`SourceOperator`].
2108    ///
2109    /// This function adds both operators to the circuit and registers a
2110    /// dependency between them, making sure that the scheduler will
2111    /// evaluate the sender before the receiver even though there is no
2112    /// explicit stream connecting them.
2113    ///
2114    /// Returns the output stream produced by the receiver operator.
2115    ///
2116    /// # Arguments
2117    ///
2118    /// * `sender` - the sender half of the pair.  The sender must be a sink
2119    ///   operator
2120    /// * `receiver` - the receiver half of the pair.  Must be a source
2121    /// * `input_stream` - stream to connect as input to the `sender`.
2122    fn add_exchange<I, SndOp, O, RcvOp>(
2123        &self,
2124        sender: SndOp,
2125        receiver: RcvOp,
2126        input_stream: &Stream<Self, I>,
2127    ) -> Stream<Self, O>
2128    where
2129        I: Data,
2130        O: Data,
2131        SndOp: SinkOperator<I>,
2132        RcvOp: SourceOperator<O>;
2133
2134    /// Like [`Self::add_exchange`], but overrides the ownership
2135    /// preference on the input stream with `input_preference`.
2136    fn add_exchange_with_preference<I, SndOp, O, RcvOp>(
2137        &self,
2138        sender: SndOp,
2139        receiver: RcvOp,
2140        input_stream: &Stream<Self, I>,
2141        input_preference: OwnershipPreference,
2142    ) -> Stream<Self, O>
2143    where
2144        I: Data,
2145        O: Data,
2146        SndOp: SinkOperator<I>,
2147        RcvOp: SourceOperator<O>;
2148
2149    /// Add a sink operator (see [`SinkOperator`]).
2150    fn add_sink<I, Op>(&self, operator: Op, input_stream: &Stream<Self, I>) -> GlobalNodeId
2151    where
2152        I: Data,
2153        Op: SinkOperator<I>;
2154
2155    /// Like [`Self::add_sink`], but overrides the ownership preference on the
2156    /// input stream with `input_preference`.
2157    fn add_sink_with_preference<I, Op>(
2158        &self,
2159        operator: Op,
2160        input_stream: &Stream<Self, I>,
2161        input_preference: OwnershipPreference,
2162    ) -> GlobalNodeId
2163    where
2164        I: Data,
2165        Op: SinkOperator<I>;
2166
2167    /// Add a binary sink operator (see [`BinarySinkOperator`]).
2168    fn add_binary_sink<I1, I2, Op>(
2169        &self,
2170        operator: Op,
2171        input_stream1: &Stream<Self, I1>,
2172        input_stream2: &Stream<Self, I2>,
2173    ) where
2174        I1: Data,
2175        I2: Data,
2176        Op: BinarySinkOperator<I1, I2>;
2177
2178    /// Like [`Self::add_binary_sink`], but overrides the ownership preferences
2179    /// on both input streams with `input_preference1` and
2180    /// `input_preference2`.
2181    fn add_binary_sink_with_preference<I1, I2, Op>(
2182        &self,
2183        operator: Op,
2184        input_stream1: (&Stream<Self, I1>, OwnershipPreference),
2185        input_stream2: (&Stream<Self, I2>, OwnershipPreference),
2186    ) where
2187        I1: Data,
2188        I2: Data,
2189        Op: BinarySinkOperator<I1, I2>;
2190
2191    /// Add a ternary sink operator (see [`TernarySinkOperator`]).
2192    fn add_ternary_sink<I1, I2, I3, Op>(
2193        &self,
2194        operator: Op,
2195        input_stream1: &Stream<Self, I1>,
2196        input_stream2: &Stream<Self, I2>,
2197        input_stream3: &Stream<Self, I3>,
2198    ) -> GlobalNodeId
2199    where
2200        I1: Data,
2201        I2: Data,
2202        I3: Data,
2203        Op: TernarySinkOperator<I1, I2, I3>;
2204
2205    /// Like [`Self::add_ternary_sink`], but overrides the ownership preferences
2206    /// on input streams.
2207    fn add_ternary_sink_with_preference<I1, I2, I3, Op>(
2208        &self,
2209        operator: Op,
2210        input_stream1: (&Stream<Self, I1>, OwnershipPreference),
2211        input_stream2: (&Stream<Self, I2>, OwnershipPreference),
2212        input_stream3: (&Stream<Self, I3>, OwnershipPreference),
2213    ) -> GlobalNodeId
2214    where
2215        I1: Data,
2216        I2: Data,
2217        I3: Data,
2218        Op: TernarySinkOperator<I1, I2, I3>;
2219
2220    /// Add a unary operator (see [`UnaryOperator`]).
2221    fn add_unary_operator<I, O, Op>(
2222        &self,
2223        operator: Op,
2224        input_stream: &Stream<Self, I>,
2225    ) -> Stream<Self, O>
2226    where
2227        I: Data,
2228        O: Data,
2229        Op: UnaryOperator<I, O>;
2230
2231    /// Like [`Self::add_unary_operator`], but overrides the ownership
2232    /// preference on the input stream with `input_preference`.
2233    fn add_unary_operator_with_preference<I, O, Op>(
2234        &self,
2235        operator: Op,
2236        input_stream: &Stream<Self, I>,
2237        input_preference: OwnershipPreference,
2238    ) -> Stream<Self, O>
2239    where
2240        I: Data,
2241        O: Data,
2242        Op: UnaryOperator<I, O>;
2243
2244    /// Add a binary operator (see [`BinaryOperator`]).
2245    fn add_binary_operator<I1, I2, O, Op>(
2246        &self,
2247        operator: Op,
2248        input_stream1: &Stream<Self, I1>,
2249        input_stream2: &Stream<Self, I2>,
2250    ) -> Stream<Self, O>
2251    where
2252        I1: Data,
2253        I2: Data,
2254        O: Data,
2255        Op: BinaryOperator<I1, I2, O>;
2256
2257    /// Like [`Self::add_binary_operator`], but overrides the ownership
2258    /// preference on both input streams with `input_preference1` and
2259    /// `input_preference2` respectively.
2260    fn add_binary_operator_with_preference<I1, I2, O, Op>(
2261        &self,
2262        operator: Op,
2263        input_stream1: (&Stream<Self, I1>, OwnershipPreference),
2264        input_stream2: (&Stream<Self, I2>, OwnershipPreference),
2265    ) -> Stream<Self, O>
2266    where
2267        I1: Data,
2268        I2: Data,
2269        O: Data,
2270        Op: BinaryOperator<I1, I2, O>;
2271
2272    /// Add a ternary operator (see [`TernaryOperator`]).
2273    fn add_ternary_operator<I1, I2, I3, O, Op>(
2274        &self,
2275        operator: Op,
2276        input_stream1: &Stream<Self, I1>,
2277        input_stream2: &Stream<Self, I2>,
2278        input_stream3: &Stream<Self, I3>,
2279    ) -> Stream<Self, O>
2280    where
2281        I1: Data,
2282        I2: Data,
2283        I3: Data,
2284        O: Data,
2285        Op: TernaryOperator<I1, I2, I3, O>;
2286
2287    /// Like [`Self::add_ternary_operator`], but overrides the ownership
2288    /// preference on the input streams with specified values.
2289    #[allow(clippy::too_many_arguments)]
2290    fn add_ternary_operator_with_preference<I1, I2, I3, O, Op>(
2291        &self,
2292        operator: Op,
2293        input_stream1: (&Stream<Self, I1>, OwnershipPreference),
2294        input_stream2: (&Stream<Self, I2>, OwnershipPreference),
2295        input_stream3: (&Stream<Self, I3>, OwnershipPreference),
2296    ) -> Stream<Self, O>
2297    where
2298        I1: Data,
2299        I2: Data,
2300        I3: Data,
2301        O: Data,
2302        Op: TernaryOperator<I1, I2, I3, O>;
2303
2304    /// Add a quaternary operator (see [`QuaternaryOperator`]).
2305    fn add_quaternary_operator<I1, I2, I3, I4, O, Op>(
2306        &self,
2307        operator: Op,
2308        input_stream1: &Stream<Self, I1>,
2309        input_stream2: &Stream<Self, I2>,
2310        input_stream3: &Stream<Self, I3>,
2311        input_stream4: &Stream<Self, I4>,
2312    ) -> Stream<Self, O>
2313    where
2314        I1: Data,
2315        I2: Data,
2316        I3: Data,
2317        I4: Data,
2318        O: Data,
2319        Op: QuaternaryOperator<I1, I2, I3, I4, O>;
2320
2321    /// Like [`Self::add_quaternary_operator`], but overrides the ownership
2322    /// preference on the input streams with specified values.
2323    #[allow(clippy::too_many_arguments)]
2324    fn add_quaternary_operator_with_preference<I1, I2, I3, I4, O, Op>(
2325        &self,
2326        operator: Op,
2327        input_stream1: (&Stream<Self, I1>, OwnershipPreference),
2328        input_stream2: (&Stream<Self, I2>, OwnershipPreference),
2329        input_stream3: (&Stream<Self, I3>, OwnershipPreference),
2330        input_stream4: (&Stream<Self, I4>, OwnershipPreference),
2331    ) -> Stream<Self, O>
2332    where
2333        I1: Data,
2334        I2: Data,
2335        I3: Data,
2336        I4: Data,
2337        O: Data,
2338        Op: QuaternaryOperator<I1, I2, I3, I4, O>;
2339
2340    /// Add a N-ary operator (see [`NaryOperator`]).
2341    fn add_nary_operator<'a, I, O, Op, Iter>(
2342        &'a self,
2343        operator: Op,
2344        input_streams: Iter,
2345    ) -> Stream<Self, O>
2346    where
2347        I: Data,
2348        O: Data,
2349        Op: NaryOperator<I, O>,
2350        Iter: IntoIterator<Item = &'a Stream<Self, I>>;
2351
2352    /// Like [`Self::add_nary_operator`], but overrides the ownership
2353    /// preference with `input_preference`.
2354    fn add_nary_operator_with_preference<'a, I, O, Op, Iter>(
2355        &'a self,
2356        operator: Op,
2357        input_streams: Iter,
2358        input_preference: OwnershipPreference,
2359    ) -> Stream<Self, O>
2360    where
2361        I: Data,
2362        O: Data,
2363        Op: NaryOperator<I, O>,
2364        Iter: IntoIterator<Item = &'a Stream<Self, I>>;
2365
2366    /// Use a constructor function to create a node.
2367    ///
2368    /// Used to add operators that do not implement any of the operator traits.
2369    /// The `constructor` closure is responsible for creating the node, its output stream,
2370    /// and connecting any input streams to the node.
2371    fn add_custom_node<N: Node, R>(
2372        &self,
2373        name: Cow<'static, str>,
2374        constructor: impl FnOnce(NodeId) -> (N, R),
2375    ) -> R;
2376
2377    /// Add a feedback loop to the circuit.
2378    ///
2379    /// Other methods in this API only support the construction of acyclic
2380    /// graphs, as they require the input stream to exist before nodes that
2381    /// consumes it are created.  This method instantiates an operator whose
2382    /// input stream can be connected later, and thus may depend on
2383    /// the operator's output.  This enables the construction of feedback loops.
2384    /// Since all loops in a well-formed circuit must include a [strict
2385    /// operator](`crate::circuit::operator_traits::StrictOperator`), `operator`
2386    /// must be [strict](`crate::circuit::operator_traits::StrictOperator`).
2387    ///
2388    /// Returns the output stream of the operator and an object that can be used
2389    /// to later connect its input.
2390    ///
2391    /// # Examples
2392    /// We build the following circuit to compute the sum of input values
2393    /// received from `source`. `z1` stores the sum accumulated during
2394    /// previous timestamps.  At every timestamp, the (`+`) operator
2395    /// computes the sum of the new value received from source and the value
2396    /// stored in `z1`.
2397    ///
2398    /// ```text
2399    ///                ┌─┐
2400    /// source ───────►│+├───┬─►
2401    ///           ┌───►└─┘   │
2402    ///           │          │ z1_feedback
2403    /// z1_output │    ┌──┐  │
2404    ///           └────┤z1│◄─┘
2405    ///                └──┘
2406    /// ```
2407    ///
2408    /// ```
2409    /// # use dbsp::{
2410    /// #     operator::{Z1, Generator},
2411    /// #     Circuit, RootCircuit,
2412    /// # };
2413    /// # let circuit = RootCircuit::build(|circuit| {
2414    /// // Create a data source.
2415    /// let source = circuit.add_source(Generator::new(|| 10));
2416    /// // Create z1.  `z1_output` will contain the output stream of `z1`; `z1_feedback`
2417    /// // is a placeholder where we can later plug the input to `z1`.
2418    /// let (z1_output, z1_feedback) = circuit.add_feedback(Z1::new(0));
2419    /// // Connect outputs of `source` and `z1` to the plus operator.
2420    /// let plus = source.apply2(&z1_output, |n1: &usize, n2: &usize| n1 + n2);
2421    /// // Connect the output of `+` as input to `z1`.
2422    /// z1_feedback.connect(&plus);
2423    /// Ok(())
2424    /// # });
2425    /// ```
2426    fn add_feedback<I, O, Op>(
2427        &self,
2428        operator: Op,
2429    ) -> (Stream<Self, O>, FeedbackConnector<Self, I, O, Op>)
2430    where
2431        I: Data,
2432        O: Data,
2433        Op: StrictUnaryOperator<I, O>;
2434
2435    /// Like `add_feedback`, but also assigns persistent id to the output half of the the strict operator.
2436    fn add_feedback_persistent<I, O, Op>(
2437        &self,
2438        persistent_id: Option<&str>,
2439        operator: Op,
2440    ) -> (Stream<Self, O>, FeedbackConnector<Self, I, O, Op>)
2441    where
2442        I: Data,
2443        O: Data,
2444        Op: StrictUnaryOperator<I, O>,
2445    {
2446        let (output, feedback) = self.add_feedback(operator);
2447
2448        output.set_persistent_id(persistent_id);
2449
2450        (output, feedback)
2451    }
2452
2453    /// Like `add_feedback`, but additionally makes the output of the operator
2454    /// available to the parent circuit.
2455    ///
2456    /// Normally a [strict
2457    /// operator](`crate::circuit::operator_traits::StrictOperator`) writes a
2458    /// value computed based on inputs from previous clock cycles to its
2459    /// output stream at the start of each new clock cycle.  When the local
2460    /// clock epoch ends, the last value computed by the operator (that
2461    /// would otherwise be dropped) is written to the export stream instead.
2462    ///
2463    /// # Examples
2464    ///
2465    /// See example in the [`Self::iterate`] method.
2466    fn add_feedback_with_export<I, O, Op>(
2467        &self,
2468        operator: Op,
2469    ) -> (ExportStream<Self, O>, FeedbackConnector<Self, I, O, Op>)
2470    where
2471        I: Data,
2472        O: Data,
2473        Op: StrictUnaryOperator<I, O>;
2474
2475    /// Like `add_feedback_with_export`, but also assigns persistent id to the output half of the strict operator.
2476    fn add_feedback_with_export_persistent<I, O, Op>(
2477        &self,
2478        persistent_id: Option<&str>,
2479        operator: Op,
2480    ) -> (ExportStream<Self, O>, FeedbackConnector<Self, I, O, Op>)
2481    where
2482        I: Data,
2483        O: Data,
2484        Op: StrictUnaryOperator<I, O>,
2485    {
2486        let (export, feedback) = self.add_feedback_with_export(operator);
2487
2488        export.local.set_persistent_id(persistent_id);
2489
2490        (export, feedback)
2491    }
2492
2493    fn connect_feedback_with_preference<I, O, Op>(
2494        &self,
2495        output_node_id: NodeId,
2496        operator: Rc<RefCell<Op>>,
2497        input_stream: &Stream<Self, I>,
2498        input_preference: OwnershipPreference,
2499    ) where
2500        I: Data,
2501        O: Data,
2502        Op: StrictUnaryOperator<I, O>;
2503
2504    /// Add an iterative child circuit.
2505    ///
2506    /// Creates a child circuit with a nested logical clock.
2507    ///
2508    /// Creates an empty circuit with `self` as parent and invokes
2509    /// `child_constructor` to populate the circuit.  `child_constructor`
2510    /// typically captures some of the streams in `self` and connects them
2511    /// to source nodes of the child circuit.  It is also responsible for
2512    /// attaching an executor to the child circuit.  The return type `T`
2513    /// will typically contain output streams of the child.
2514    ///
2515    /// Most users should invoke higher-level APIs like [`Circuit::iterate`]
2516    /// instead of using this method directly.
2517    fn iterative_subcircuit<F, T, E>(&self, child_constructor: F) -> Result<T, SchedulerError>
2518    where
2519        F: FnOnce(&mut IterativeCircuit<Self>) -> Result<(T, E), SchedulerError>,
2520        E: Executor<IterativeCircuit<Self>>;
2521
2522    /// Like `iterative_subcircuit`, but creates a child circuit that runs on the same
2523    /// clock as the parent.
2524    fn non_iterative_subcircuit<F, T, E>(&self, child_constructor: F) -> Result<T, SchedulerError>
2525    where
2526        F: FnOnce(&mut NonIterativeCircuit<Self>) -> Result<(T, E), SchedulerError>,
2527        E: Executor<NonIterativeCircuit<Self>>;
2528
2529    /// Add an iteratively scheduled child circuit.
2530    ///
2531    /// Add a child circuit with a nested clock.  The child will execute
2532    /// multiple times for each parent timestamp, until its termination
2533    /// condition is satisfied.  Every time the child circuit is activated
2534    /// by the parent (once per parent timestamp), the executor calls
2535    /// [`clock_start`](`super::operator_traits::Operator::clock_start`)
2536    /// on each child operator.  It then calls `eval` on all
2537    /// child operators in a causal order and checks if the termination
2538    /// condition is satisfied.  If the condition is `false`, the
2539    /// executor `eval`s all operators again.  Once the termination
2540    /// condition is `true`, the executor calls `clock_end` on all child
2541    /// operators and returns control back to the parent scheduler.
2542    ///
2543    /// The `constructor` closure populates the child circuit and returns a
2544    /// closure that checks the termination condition and an arbitrary
2545    /// user-defined return value that typically contains output streams
2546    /// of the child.
2547    ///
2548    /// # Examples
2549    ///
2550    /// ```
2551    /// # use std::{cell::RefCell, rc::Rc};
2552    /// use dbsp::{
2553    ///     operator::{Generator, Z1},
2554    ///     Circuit, RootCircuit,
2555    ///     Error as DbspError,
2556    /// };
2557    ///
2558    /// let (circuit_handle, output_handle) = RootCircuit::build(|root_circuit| {
2559    ///     // Generate sequence 0, 1, 2, ...
2560    ///     let mut n: usize = 0;
2561    ///     let source = root_circuit.add_source(Generator::new(move || {
2562    ///         let result = n;
2563    ///         n += 1;
2564    ///         result
2565    ///     }));
2566    ///     // Compute factorial of each number in the sequence.
2567    ///     let fact = root_circuit
2568    ///         .iterate(|child_circuit| {
2569    ///             let counter = Rc::new(RefCell::new(1));
2570    ///             let counter_clone = counter.clone();
2571    ///             let countdown = source.delta0(child_circuit).apply(move |parent_val| {
2572    ///                 let mut counter_borrow = counter_clone.borrow_mut();
2573    ///                 *counter_borrow += *parent_val;
2574    ///                 let res = *counter_borrow;
2575    ///                 *counter_borrow -= 1;
2576    ///                 res
2577    ///             });
2578    ///             let (z1_output, z1_feedback) = child_circuit.add_feedback_with_export(Z1::new(1));
2579    ///             let mul = countdown.apply2(&z1_output.local, |n1: &usize, n2: &usize| n1 * n2);
2580    ///             z1_feedback.connect(&mul);
2581    ///             // Stop iterating when the countdown reaches 0.
2582    ///             Ok((async move || Ok(*counter.borrow() == 0), z1_output.export))
2583    ///         })?;
2584    ///     Ok(fact.output())
2585    /// })?;
2586    ///
2587    /// let factorial = |n: usize| (1..=n).product::<usize>();
2588    /// const ITERATIONS: usize = 10;
2589    /// for i in 0..ITERATIONS {
2590    ///     circuit_handle.transaction()?;
2591    ///     let result = output_handle.take_from_all();
2592    ///     let result = result.first().unwrap();
2593    ///     println!("Iteration {:3}: {:3}! = {}", i + 1, i, result);
2594    ///     assert_eq!(*result, factorial(i));
2595    /// }
2596    ///
2597    /// Ok::<(), DbspError>(())
2598    /// ```
2599    fn iterate<F, C, T>(&self, constructor: F) -> Result<T, SchedulerError>
2600    where
2601        F: FnOnce(&mut IterativeCircuit<Self>) -> Result<(C, T), SchedulerError>,
2602        C: AsyncFn() -> Result<bool, SchedulerError> + 'static;
2603
2604    /// Add an iteratively scheduled child circuit.
2605    ///
2606    /// Similar to [`iterate`](`Self::iterate`), but with a user-specified
2607    /// [`Scheduler`] implementation.
2608    fn iterate_with_scheduler<F, C, T, S>(&self, constructor: F) -> Result<T, SchedulerError>
2609    where
2610        F: FnOnce(&mut IterativeCircuit<Self>) -> Result<(C, T), SchedulerError>,
2611        C: AsyncFn() -> Result<bool, SchedulerError> + 'static,
2612        S: Scheduler + 'static;
2613
2614    /// Add a child circuit that will iterate to a fixed point.
2615    ///
2616    /// For each parent clock cycle, the child circuit will iterate until
2617    /// reaching a fixed point, i.e., a state where the outputs of all
2618    /// operators are guaranteed to remain the same, should the nested clock
2619    /// continue ticking.
2620    ///
2621    /// The fixed point check is implemented by checking the following
2622    /// condition:
2623    ///
2624    /// * All operators in the circuit are in such a state that, if their inputs
2625    ///   remain constant (i.e., all future inputs are identical to the last
2626    ///   input), then their outputs remain constant too.
2627    ///
2628    /// This is a necessary and sufficient condition that is also easy to check
2629    /// by asking each operator if it is in a stable state (via the
2630    /// [`Operator::fixedpoint`](`super::operator_traits::Operator::fixedpoint`)
2631    /// API.
2632    ///
2633    /// # Warning
2634    ///
2635    /// The cost of checking this condition precisely can be high for some
2636    /// operators, which implement approximate checks instead.  For instance,
2637    /// delay operators ([`Z1`](`crate::operator::Z1`) and
2638    /// [`Z1Nested`](`crate::operator::Z1Nested`)) require storing the last
2639    /// two versions of the state instead of one and comparing them at each
2640    /// cycle.  Instead, they conservatively check for _specific_ fixed points,
2641    /// namely fixed points where both input and output of the operator are zero
2642    /// As a result, the circuit may fail to detect other fixed points and may
2643    /// iterate forever.
2644    ///
2645    /// The goal is to evolve the design so that circuits created using the
2646    /// high-level API (`Stream::xxx` methods) implement accurate fixed point
2647    /// checks, but there are currently no guardrails in the system against
2648    /// constructing non-compliant circuits.
2649    fn fixedpoint<F, T>(&self, constructor: F) -> Result<T, SchedulerError>
2650    where
2651        F: FnOnce(&mut IterativeCircuit<Self>) -> Result<T, SchedulerError>;
2652
2653    /// Add a child circuit that will iterate to a fixed point.
2654    ///
2655    /// Similar to [`fixedpoint`](`Self::fixedpoint`), but with a user-specified
2656    /// [`Scheduler`] implementation.
2657    fn fixedpoint_with_scheduler<F, T, S>(&self, constructor: F) -> Result<T, SchedulerError>
2658    where
2659        F: FnOnce(&mut IterativeCircuit<Self>) -> Result<T, SchedulerError>,
2660        S: Scheduler + 'static;
2661
2662    /// Make the contents of `parent_stream` available in the nested circuit
2663    /// via an [`ImportOperator`].
2664    ///
2665    /// Typically invoked via a convenience wrapper, e.g., [`Stream::delta0`].
2666    fn import_stream<I, O, Op>(
2667        &self,
2668        operator: Op,
2669        parent_stream: &Stream<Self::Parent, I>,
2670    ) -> Stream<Self, O>
2671    where
2672        Self::Parent: Circuit,
2673        I: Data,
2674        O: Data,
2675        Op: ImportOperator<I, O>;
2676
2677    /// Like [`Self::import_stream`] but overrides the ownership
2678    /// preference on the input stream with `input_preference.
2679    fn import_stream_with_preference<I, O, Op>(
2680        &self,
2681        operator: Op,
2682        parent_stream: &Stream<Self::Parent, I>,
2683        input_preference: OwnershipPreference,
2684    ) -> Stream<Self, O>
2685    where
2686        Self::Parent: Circuit,
2687        I: Data,
2688        O: Data,
2689        Op: ImportOperator<I, O>;
2690}
2691
2692/// A collection of edges in a circuit, indexed by source, destination, and stream id.
2693pub struct Edges {
2694    by_source: BTreeMap<NodeId, Vec<Rc<Edge>>>,
2695    by_destination: BTreeMap<NodeId, Vec<Rc<Edge>>>,
2696    by_stream: BTreeMap<Option<StreamId>, Vec<Rc<Edge>>>,
2697}
2698
2699impl Edges {
2700    fn new() -> Self {
2701        Self {
2702            by_source: BTreeMap::new(),
2703            by_destination: BTreeMap::new(),
2704            by_stream: BTreeMap::new(),
2705        }
2706    }
2707
2708    fn add_edge(&mut self, edge: Edge) {
2709        let edge = Rc::new(edge);
2710
2711        self.by_source
2712            .entry(edge.from)
2713            .or_default()
2714            .push(edge.clone());
2715        self.by_destination
2716            .entry(edge.to)
2717            .or_default()
2718            .push(edge.clone());
2719
2720        self.by_stream
2721            .entry(edge.stream.as_ref().map(|s| s.stream_id()))
2722            .or_default()
2723            .push(edge);
2724    }
2725
2726    fn extend<I>(&mut self, edges: I)
2727    where
2728        I: IntoIterator<Item = Edge>,
2729    {
2730        for edge in edges {
2731            self.add_edge(edge)
2732        }
2733    }
2734
2735    pub(crate) fn iter(&self) -> impl Iterator<Item = &Edge> {
2736        self.by_source
2737            .values()
2738            .flat_map(|edges| edges.iter().map(|edge| edge.as_ref()))
2739    }
2740
2741    pub(crate) fn get_by_stream_id(&self, stream_id: &Option<StreamId>) -> Option<&[Rc<Edge>]> {
2742        self.by_stream.get(stream_id).map(|v| v.as_slice())
2743    }
2744
2745    fn delete_stream(&mut self, stream_id: StreamId) {
2746        if let Some(edges) = self.by_stream.remove(&Some(stream_id)) {
2747            for edge in edges {
2748                if let Some(v) = self.by_source.get_mut(&edge.from) {
2749                    v.retain(|e| e.stream_id() != Some(stream_id))
2750                }
2751                if let Some(v) = self.by_destination.get_mut(&edge.to) {
2752                    v.retain(|e| e.stream_id() != Some(stream_id))
2753                }
2754            }
2755        }
2756    }
2757
2758    pub(crate) fn inputs_of(&self, node_id: NodeId) -> impl Iterator<Item = &Edge> {
2759        self.by_destination
2760            .get(&node_id)
2761            .into_iter()
2762            .flatten()
2763            .map(|edge| edge.as_ref())
2764    }
2765
2766    /// Nodes that depend on node_id directly.
2767    ///
2768    /// Nodes that have an incoming _dependency_ edge from `node_id`.
2769    pub(crate) fn depend_on(&self, node_id: NodeId) -> impl Iterator<Item = &Edge> {
2770        self.by_source.get(&node_id).into_iter().flat_map(|edges| {
2771            edges.iter().filter_map(|edge| {
2772                if edge.is_dependency() {
2773                    Some(edge.as_ref())
2774                } else {
2775                    None
2776                }
2777            })
2778        })
2779    }
2780
2781    /// Nodes that `node_id` depends on directly.
2782    ///
2783    /// Nodes that have an outgoing _dependency_ edge to `node_id`.
2784    pub(crate) fn dependencies_of(&self, node_id: NodeId) -> impl Iterator<Item = &Edge> {
2785        self.by_destination
2786            .get(&node_id)
2787            .into_iter()
2788            .flat_map(|edges| {
2789                edges.iter().filter_map(|edge| {
2790                    if edge.is_dependency() {
2791                        Some(edge.as_ref())
2792                    } else {
2793                        None
2794                    }
2795                })
2796            })
2797    }
2798
2799    fn clear(&mut self) {
2800        *self = Self::new();
2801    }
2802}
2803
2804/// A circuit consists of nodes and edges.  An edge from
2805/// node1 to node2 indicates that the output stream of node1
2806/// is connected to an input of node2.
2807struct CircuitInner<P>
2808where
2809    P: 'static,
2810{
2811    parent: P,
2812
2813    /// Root of the circuit tree.  `None` if this is the root circuit.
2814    root: Option<RootCircuit>,
2815
2816    root_scope: Scope,
2817
2818    /// Circuit's node id within the parent circuit.
2819    node_id: NodeId,
2820    global_node_id: GlobalNodeId,
2821    nodes: RefCell<Vec<RefCell<Box<dyn Node>>>>,
2822    edges: RefCell<Edges>,
2823    import_nodes: RefCell<Vec<NodeId>>,
2824    circuit_event_handlers: CircuitEventHandlers,
2825    scheduler_event_handlers: SchedulerEventHandlers,
2826    store: RefCell<CircuitCache>,
2827    last_stream_id: RefCell<StreamId>,
2828    metadata_exchange: MetadataExchange,
2829    balancer: Rc<Balancer>,
2830}
2831
2832impl<P> CircuitInner<P>
2833where
2834    P: 'static,
2835{
2836    #[allow(clippy::too_many_arguments)]
2837    fn new(
2838        parent: P,
2839        root: Option<RootCircuit>,
2840        root_scope: Scope,
2841        node_id: NodeId,
2842        global_node_id: GlobalNodeId,
2843        circuit_event_handlers: CircuitEventHandlers,
2844        scheduler_event_handlers: SchedulerEventHandlers,
2845        last_stream_id: RefCell<StreamId>,
2846    ) -> Self {
2847        let metadata_exchange = MetadataExchange::new();
2848
2849        Self {
2850            parent,
2851            root,
2852            root_scope,
2853            node_id,
2854            global_node_id,
2855            nodes: RefCell::new(Vec::new()),
2856            edges: RefCell::new(Edges::new()),
2857            import_nodes: RefCell::new(Vec::new()),
2858            circuit_event_handlers,
2859            scheduler_event_handlers,
2860            store: RefCell::new(TypedMap::new()),
2861            last_stream_id,
2862            metadata_exchange: metadata_exchange.clone(),
2863            balancer: Rc::new(Balancer::new(&metadata_exchange)),
2864        }
2865    }
2866
2867    fn add_edge(&self, edge: Edge) {
2868        self.edges.borrow_mut().add_edge(edge);
2869    }
2870
2871    fn add_node<N>(&self, mut node: N)
2872    where
2873        N: Node + 'static,
2874    {
2875        node.init();
2876        self.nodes
2877            .borrow_mut()
2878            .push(RefCell::new(Box::new(node) as Box<dyn Node>));
2879    }
2880
2881    fn add_import_node(&self, node_id: NodeId) {
2882        self.import_nodes.borrow_mut().push(node_id);
2883    }
2884
2885    fn import_nodes(&self) -> Vec<NodeId> {
2886        self.import_nodes.borrow().clone()
2887    }
2888
2889    fn clear(&self) {
2890        self.nodes.borrow_mut().clear();
2891        self.edges.borrow_mut().clear();
2892        self.store.borrow_mut().clear();
2893    }
2894
2895    fn register_circuit_event_handler<F>(&self, name: &str, handler: F)
2896    where
2897        F: Fn(&CircuitEvent) + 'static,
2898    {
2899        self.circuit_event_handlers.borrow_mut().insert(
2900            name.to_string(),
2901            Box::new(handler) as Box<dyn Fn(&CircuitEvent)>,
2902        );
2903    }
2904
2905    fn unregister_circuit_event_handler(&self, name: &str) -> bool {
2906        self.circuit_event_handlers
2907            .borrow_mut()
2908            .remove(name)
2909            .is_some()
2910    }
2911
2912    fn register_scheduler_event_handler<F>(&self, name: &str, handler: F)
2913    where
2914        F: FnMut(&SchedulerEvent<'_>) + 'static,
2915    {
2916        self.scheduler_event_handlers.borrow_mut().insert(
2917            name.to_string(),
2918            Box::new(handler) as Box<dyn FnMut(&SchedulerEvent<'_>)>,
2919        );
2920    }
2921
2922    fn unregister_scheduler_event_handler(&self, name: &str) -> bool {
2923        self.scheduler_event_handlers
2924            .borrow_mut()
2925            .remove(name)
2926            .is_some()
2927    }
2928
2929    fn log_circuit_event(&self, event: &CircuitEvent) {
2930        for (_, handler) in self.circuit_event_handlers.borrow().iter() {
2931            handler(event)
2932        }
2933    }
2934
2935    fn log_scheduler_event(&self, event: &SchedulerEvent<'_>) {
2936        for (_, handler) in self.scheduler_event_handlers.borrow_mut().iter_mut() {
2937            handler(event)
2938        }
2939    }
2940
2941    fn check_fixedpoint(&self, scope: Scope) -> bool {
2942        self.nodes.borrow().iter().all(|node| {
2943            /*
2944            if !res {
2945                let n = node.borrow();
2946                let n1 = n.deref();
2947                eprintln!("not fixed {:?} {}", n1.global_id(), n1.name());
2948            };
2949            */
2950            node.borrow().fixedpoint(scope)
2951        })
2952    }
2953
2954    fn lookup_local_node_by_persistent_id(
2955        &self,
2956        persistent_id: &str,
2957    ) -> Result<GlobalNodeId, DbspError> {
2958        self.nodes
2959            .borrow()
2960            .iter()
2961            .find_map(|node| {
2962                if node.borrow().get_label(LABEL_PERSISTENT_OPERATOR_ID) == Some(persistent_id) {
2963                    Some(node.borrow().global_id().clone())
2964                } else {
2965                    None
2966                }
2967            })
2968            .ok_or_else(|| {
2969                DbspError::Runtime(RuntimeError::UnknownPersistentId(persistent_id.to_string()))
2970            })
2971    }
2972}
2973
2974/// A circuit.
2975///
2976/// A single implementation that can operate as the top-level
2977/// circuit when instantiated with `P = ()` or a nested circuit,
2978/// with `P = ChildCircuit<..>` designating the parent circuit type.
2979pub struct ChildCircuit<P, T>
2980where
2981    P: 'static,
2982    T: Timestamp,
2983{
2984    inner: Rc<CircuitInner<P>>,
2985    time: Rc<RefCell<T>>,
2986}
2987
2988/// Top-level circuit.
2989///
2990/// `RootCircuit` is a specialization of [`ChildCircuit<P>`] with `P = ()`.  It
2991/// forms the top level of a possibly nested DBSP circuit.  Every use of DBSP
2992/// needs a top-level circuit and non-recursive queries, including all of
2993/// standard SQL, only needs a top-level circuit.
2994///
2995/// Input enters a circuit through the top level circuit only.  `RootCircuit`
2996/// has `add_input_*` methods for setting up input operators, which can only be
2997/// called within the callback passed to `RootCircuit::build`.  The data from
2998/// the input operators is represented as a [`Stream`], which may be in turn be
2999/// used as input for further operators, which are primarily instantiated via
3000/// methods on [`Stream`].  Stream output may be made available outside the
3001/// bounds of a circuit using [`Stream::output`].
3002pub type RootCircuit = ChildCircuit<(), ()>;
3003
3004pub type NestedCircuit = ChildCircuit<RootCircuit, <() as Timestamp>::Nested>;
3005
3006/// A child circuit with a nested clock.
3007pub type IterativeCircuit<P> = ChildCircuit<P, <<P as WithClock>::Time as Timestamp>::Nested>;
3008
3009/// A child circuit that runs on the same clock as the parent.
3010pub type NonIterativeCircuit<P> = ChildCircuit<P, <P as WithClock>::Time>;
3011
3012impl<P, T> Clone for ChildCircuit<P, T>
3013where
3014    P: 'static,
3015    T: Timestamp,
3016{
3017    fn clone(&self) -> Self {
3018        Self {
3019            inner: self.inner.clone(),
3020            time: self.time.clone(),
3021        }
3022    }
3023}
3024
3025impl<P, T> ChildCircuit<P, T>
3026where
3027    P: 'static,
3028    T: Timestamp,
3029{
3030    /// Immutably borrow the inner circuit.
3031    fn inner(&self) -> &CircuitInner<P> {
3032        &self.inner
3033    }
3034}
3035
3036impl RootCircuit {
3037    /// Creates a circuit and prepares it for execution by calling
3038    /// `constructor`.  The constructor should create input operators by calling
3039    /// [`RootCircuit::dyn_add_input_zset`] and related methods.  Each of these
3040    /// calls returns an input handle and a [`Stream`].  The `constructor` can
3041    /// call [`Stream`] methods to do computation, each of which yields further
3042    /// [`Stream`]s.  It can also use [`Stream::output`] to obtain an output
3043    /// handle.
3044    ///
3045    /// Returns a [`CircuitHandle`] with which the caller can control the
3046    /// circuit, plus a user-defined value returned by the constructor.  The
3047    /// `constructor` should use the latter to return the input and output
3048    /// handles it obtains, because these allow the caller to feed input into
3049    /// the circuit and read output from the circuit.
3050    ///
3051    /// The default scheduler, currently [`DynamicScheduler`], will decide the
3052    /// order in which to evaluate operators.  (This scheduler does not schedule
3053    /// processes or threads.)
3054    ///
3055    /// A client may use the returned [`CircuitHandle`] to run the circuit in
3056    /// the context of the current thread.  To instead run the circuit in a
3057    /// collection of worker threads, call [`Runtime::init_circuit`]
3058    /// instead.
3059    ///
3060    /// # Example
3061    ///
3062    /// ```
3063    /// use dbsp::{
3064    ///     operator::{Generator, Inspect},
3065    ///     Circuit, RootCircuit,
3066    /// };
3067    ///
3068    /// let circuit = RootCircuit::build(|circuit| {
3069    ///     // Add a source operator.
3070    ///     let source_stream = circuit.add_source(Generator::new(|| "Hello, world!".to_owned()));
3071    ///
3072    ///     // Add a unary operator and wire the source directly to it.
3073    ///     circuit.add_unary_operator(
3074    ///         Inspect::new(|n| println!("New output: {}", n)),
3075    ///         &source_stream,
3076    ///     );
3077    ///     Ok(())
3078    /// });
3079    /// ```
3080    pub fn build<F, T>(constructor: F) -> Result<(CircuitHandle, T), DbspError>
3081    where
3082        F: FnOnce(&mut RootCircuit) -> Result<T, AnyError>,
3083    {
3084        Self::build_with_scheduler::<F, T, DynamicScheduler>(constructor)
3085    }
3086
3087    /// Create a circuit and prepare it for execution.
3088    ///
3089    /// Similar to [`build`](`Self::build`), but with a user-specified
3090    /// [`Scheduler`] implementation that decides the order in which to evaluate
3091    /// operators.  (This scheduler does not schedule processes or threads.)
3092    pub fn build_with_scheduler<F, T, S>(constructor: F) -> Result<(CircuitHandle, T), DbspError>
3093    where
3094        F: FnOnce(&mut RootCircuit) -> Result<T, AnyError>,
3095        S: Scheduler + 'static,
3096    {
3097        // TODO: user LocalRuntime instead of Runtime + LocalSet when
3098        // tokio::LocalRuntime is stable.
3099        // Local tokio runtime that schedules operators on the current worker thread.
3100        let tokio_runtime = tokio::runtime::Builder::new_current_thread()
3101            .build()
3102            .map_err(|e| {
3103                DbspError::Scheduler(SchedulerError::TokioError {
3104                    error: e.to_string(),
3105                })
3106            })?;
3107
3108        let mut circuit = RootCircuit::new();
3109        let res = constructor(&mut circuit).map_err(DbspError::Constructor)?;
3110        let mut executor = Box::new(<OnceExecutor<S>>::new()) as Box<dyn Executor<RootCircuit>>;
3111        executor.prepare(&circuit, None)?;
3112
3113        // if Runtime::worker_index() == 0 {
3114        //     circuit.to_dot_file(
3115        //         |node| {
3116        //             Some(crate::utils::DotNodeAttributes::new().with_label(&format!(
3117        //                 "{}-{}-{}",
3118        //                 node.local_id(),
3119        //                 node.name(),
3120        //                 node.persistent_id().unwrap_or_default()
3121        //             )))
3122        //         },
3123        //         |edge| {
3124        //             let style = if edge.is_dependency() {
3125        //                 Some("dotted".to_string())
3126        //             } else {
3127        //                 None
3128        //             };
3129        //             let label = if let Some(stream) = &edge.stream {
3130        //                 Some(format!("consumers: {}", stream.num_consumers()))
3131        //             } else {
3132        //                 None
3133        //             };
3134        //             Some(
3135        //                 crate::utils::DotEdgeAttributes::new(edge.stream_id())
3136        //                     .with_style(style)
3137        //                     .with_label(label),
3138        //             )
3139        //         },
3140        //         "circuit.dot",
3141        //     );
3142        //     println!("circuit written to circuit.dot");
3143        // }
3144
3145        // Alternatively, `CircuitHandle` should expose `clock_start` and `clock_end`
3146        // APIs, so that the user can reset the circuit at runtime and start
3147        // evaluation from clean state without having to rebuild it from
3148        // scratch.
3149        circuit.log_scheduler_event(&SchedulerEvent::clock_start());
3150        circuit.clock_start(0);
3151        Ok((
3152            CircuitHandle {
3153                circuit,
3154                executor,
3155                tokio_runtime,
3156                replay_info: None,
3157            },
3158            res,
3159        ))
3160    }
3161}
3162
3163impl RootCircuit {
3164    // Create new top-level circuit.  Clients invoke this via the
3165    // [`RootCircuit::build`] API.
3166    fn new() -> Self {
3167        Self {
3168            inner: Rc::new(CircuitInner::new(
3169                (),
3170                None,
3171                0,
3172                NodeId::root(),
3173                GlobalNodeId::root(),
3174                Rc::new(RefCell::new(HashMap::new())),
3175                Rc::new(RefCell::new(HashMap::new())),
3176                RefCell::new(StreamId::new(0)),
3177            )),
3178            time: Rc::new(RefCell::new(())),
3179        }
3180    }
3181}
3182
3183impl RootCircuit {
3184    /// Attach a circuit event handler to the top-level circuit (see
3185    /// [`super::trace::CircuitEvent`] for a description of circuit events).
3186    ///
3187    /// This method should normally be called inside the closure passed to
3188    /// [`RootCircuit::build`] before adding any operators to the circuit, so
3189    /// that the handler gets to observe all nodes, edges, and subcircuits
3190    /// added to the circuit.
3191    ///
3192    /// `name` - user-readable name assigned to the handler.  If a handler with
3193    /// the same name exists, it will be replaced by the new handler.
3194    ///
3195    /// `handler` - user callback invoked on each circuit event (see
3196    /// [`super::trace::CircuitEvent`]).
3197    ///
3198    /// # Examples
3199    ///
3200    /// ```text
3201    /// TODO
3202    /// ```
3203    pub fn register_circuit_event_handler<F>(&self, name: &str, handler: F)
3204    where
3205        F: Fn(&CircuitEvent) + 'static,
3206    {
3207        self.inner().register_circuit_event_handler(name, handler);
3208    }
3209
3210    /// Remove a circuit event handler.  Returns `true` if a handler with the
3211    /// specified name had previously been registered and `false` otherwise.
3212    pub fn unregister_circuit_event_handler(&self, name: &str) -> bool {
3213        self.inner().unregister_circuit_event_handler(name)
3214    }
3215
3216    /// Attach a scheduler event handler to the top-level circuit (see
3217    /// [`super::trace::SchedulerEvent`] for a description of scheduler
3218    /// events).
3219    ///
3220    /// This method can be used during circuit construction, inside the closure
3221    /// provided to [`RootCircuit::build`].  Use
3222    /// [`CircuitHandle::register_scheduler_event_handler`],
3223    /// [`CircuitHandle::unregister_scheduler_event_handler`] to manipulate
3224    /// scheduler callbacks at runtime.
3225    ///
3226    /// `name` - user-readable name assigned to the handler.  If a handler with
3227    /// the same name exists, it will be replaced by the new handler.
3228    ///
3229    /// `handler` - user callback invoked on each scheduler event.
3230    pub fn register_scheduler_event_handler<F>(&self, name: &str, handler: F)
3231    where
3232        F: FnMut(&SchedulerEvent<'_>) + 'static,
3233    {
3234        self.inner().register_scheduler_event_handler(name, handler);
3235    }
3236
3237    /// Remove a scheduler event handler.  Returns `true` if a handler with the
3238    /// specified name had previously been registered and `false` otherwise.
3239    pub fn unregister_scheduler_event_handler(&self, name: &str) -> bool {
3240        self.inner().unregister_scheduler_event_handler(name)
3241    }
3242}
3243
3244impl<P, T> ChildCircuit<P, T>
3245where
3246    P: Circuit,
3247    T: Timestamp,
3248{
3249    /// Create an empty nested circuit of `parent`.
3250    fn with_parent(parent: P, id: NodeId) -> Self {
3251        let global_node_id = parent.global_node_id().child(id);
3252        let circuit_handlers = parent.circuit_event_handlers();
3253        let sched_handlers = parent.scheduler_event_handlers();
3254        let root_scope = parent.root_scope() + 1;
3255        let last_stream_id = parent.last_stream_id();
3256
3257        let root = parent.root_circuit();
3258
3259        ChildCircuit {
3260            inner: Rc::new(CircuitInner::new(
3261                parent,
3262                Some(root),
3263                root_scope,
3264                id,
3265                global_node_id,
3266                circuit_handlers,
3267                sched_handlers,
3268                last_stream_id,
3269            )),
3270            time: Rc::new(RefCell::new(Timestamp::clock_start())),
3271        }
3272    }
3273
3274    /// `true` if `self` is a subcircuit of `other`.
3275    pub fn is_child_of(&self, other: &P) -> bool {
3276        P::ptr_eq(&self.inner().parent, other)
3277    }
3278}
3279
3280// Internal API.
3281impl<P, T> ChildCircuit<P, T>
3282where
3283    P: 'static,
3284    T: Timestamp,
3285    Self: Circuit,
3286{
3287    /// Circuit's node id within the parent circuit.
3288    fn node_id(&self) -> NodeId {
3289        self.inner().node_id
3290    }
3291
3292    /// Add a node to the circuit.
3293    ///
3294    /// Allocates a new node id and invokes a user callback to create a new node
3295    /// instance. The callback may use the node id, e.g., to add an edge to
3296    /// this node.
3297    fn add_node<F, N, V>(&self, f: F) -> V
3298    where
3299        F: FnOnce(NodeId) -> (N, V),
3300        N: Node + 'static,
3301    {
3302        let id = self.inner().nodes.borrow().len();
3303
3304        // We don't hold a reference to `self.inner()` while calling `f`, so it can
3305        // safely modify the circuit, e.g., add edges.
3306        let (node, res) = f(NodeId(id));
3307        self.inner().add_node(node);
3308        res
3309    }
3310
3311    fn add_import_node(&self, node_id: NodeId) {
3312        self.inner().add_import_node(node_id);
3313    }
3314
3315    /// Like `add_node`, but the node is not created if the closure fails.
3316    fn try_add_node<F, N, V, E>(&self, f: F) -> Result<V, E>
3317    where
3318        F: FnOnce(NodeId) -> Result<(N, V), E>,
3319        N: Node + 'static,
3320    {
3321        let id = self.inner().nodes.borrow().len();
3322
3323        // We don't hold a reference to `self.inner()` while calling `f`, so it can
3324        // safely modify the circuit, e.g., add edges.
3325        let (node, res) = f(NodeId(id))?;
3326        self.inner().add_node(node);
3327        Ok(res)
3328    }
3329
3330    /// Send the specified `CircuitEvent` to all handlers attached to the
3331    /// circuit.
3332    fn log_circuit_event(&self, event: &CircuitEvent) {
3333        self.inner().log_circuit_event(event);
3334    }
3335
3336    /// Send the specified `SchedulerEvent` to all handlers attached to the
3337    /// circuit.
3338    pub(super) fn log_scheduler_event(&self, event: &SchedulerEvent<'_>) {
3339        self.inner().log_scheduler_event(event);
3340    }
3341}
3342
3343impl<P, T> CircuitBase for ChildCircuit<P, T>
3344where
3345    P: Clone + 'static,
3346    T: Timestamp,
3347{
3348    fn edges(&self) -> Ref<'_, Edges> {
3349        self.inner().edges.borrow()
3350    }
3351
3352    fn transitive_ancestors(&self) -> BTreeMap<NodeId, BTreeSet<NodeId>> {
3353        let edges = self.edges();
3354        let mut result = BTreeMap::new();
3355
3356        // For each node, compute its transitive ancestors using BFS
3357        for node_id in self.node_ids() {
3358            let mut ancestors = BTreeSet::new();
3359            let mut queue = vec![node_id];
3360
3361            // BFS to find all transitive ancestors
3362            while let Some(current) = queue.pop() {
3363                for edge in edges.inputs_of(current) {
3364                    let ancestor_node = edge.from;
3365                    if ancestors.insert(ancestor_node) {
3366                        queue.push(ancestor_node);
3367                    }
3368                }
3369            }
3370
3371            result.insert(node_id, ancestors);
3372        }
3373
3374        result
3375    }
3376
3377    fn edges_mut(&self) -> RefMut<'_, Edges> {
3378        self.inner().edges.borrow_mut()
3379    }
3380
3381    fn num_nodes(&self) -> usize {
3382        self.inner().nodes.borrow().len()
3383    }
3384
3385    fn clear(&mut self) {
3386        self.inner().clear();
3387    }
3388
3389    fn add_dependency(&self, from: NodeId, to: NodeId) {
3390        self.log_circuit_event(&CircuitEvent::dependency(
3391            self.global_node_id().child(from),
3392            self.global_node_id().child(to),
3393        ));
3394
3395        let origin = self.global_node_id().child(from);
3396        self.inner().add_edge(Edge {
3397            from,
3398            to,
3399            origin,
3400            stream: None,
3401            ownership_preference: None,
3402        });
3403    }
3404
3405    /// Apply `f` to the node with the specified `path` relative to `self`.
3406    fn map_node_relative(&self, path: &[NodeId], f: &mut dyn FnMut(&dyn Node)) {
3407        let nodes = self.inner().nodes.borrow();
3408        let node = nodes[path[0].0].borrow();
3409        if path.len() == 1 {
3410            f(node.as_ref())
3411        } else {
3412            node.map_child(&path[1..], &mut |node| f(node));
3413        }
3414    }
3415
3416    /// Apply `f` to the node with the specified `path` relative to `self`.
3417    fn map_node_mut_relative(&self, path: &[NodeId], f: &mut dyn FnMut(&mut dyn Node)) {
3418        let nodes = self.inner().nodes.borrow();
3419        let mut node = nodes[path[0].0].borrow_mut();
3420        if path.len() == 1 {
3421            f(node.as_mut())
3422        } else {
3423            node.map_child_mut(&path[1..], &mut |node| f(node));
3424        }
3425    }
3426
3427    fn map_nodes_recursive(
3428        &self,
3429        f: &mut dyn FnMut(&dyn Node) -> Result<(), DbspError>,
3430    ) -> Result<(), DbspError> {
3431        for node in self.inner().nodes.borrow().iter() {
3432            f(node.borrow().as_ref())?;
3433            node.borrow().map_nodes_recursive(f)?;
3434        }
3435        Ok(())
3436    }
3437
3438    fn map_nodes_recursive_mut(
3439        &mut self,
3440        f: &mut dyn FnMut(&mut dyn Node) -> Result<(), DbspError>,
3441    ) -> Result<(), DbspError> {
3442        for node in self.inner().nodes.borrow_mut().iter_mut() {
3443            f(node.borrow_mut().as_mut())?;
3444            node.borrow_mut().map_nodes_recursive_mut(f)?;
3445        }
3446
3447        Ok(())
3448    }
3449
3450    fn map_local_nodes(
3451        &self,
3452        f: &mut dyn FnMut(&dyn Node) -> Result<(), DbspError>,
3453    ) -> Result<(), DbspError> {
3454        for node in self.inner().nodes.borrow().iter() {
3455            f(node.borrow().as_ref())?;
3456        }
3457        Ok(())
3458    }
3459
3460    fn map_local_nodes_mut(
3461        &self,
3462        f: &mut dyn FnMut(&mut dyn Node) -> Result<(), DbspError>,
3463    ) -> Result<(), DbspError> {
3464        for node in self.inner().nodes.borrow_mut().iter_mut() {
3465            f(node.borrow_mut().as_mut())?;
3466        }
3467
3468        Ok(())
3469    }
3470
3471    fn apply_local_node_mut(&self, id: NodeId, f: &mut dyn FnMut(&mut dyn Node)) {
3472        self.map_node_mut_relative(&[id], &mut |node| f(node));
3473    }
3474
3475    fn map_subcircuits(
3476        &self,
3477        f: &mut dyn FnMut(&dyn CircuitBase) -> Result<(), DbspError>,
3478    ) -> Result<(), DbspError> {
3479        for node in self.inner().nodes.borrow().iter() {
3480            let node = node.borrow();
3481            if let Some(child_circuit) = node.as_circuit() {
3482                f(child_circuit)?;
3483            }
3484        }
3485        Ok(())
3486    }
3487
3488    fn set_node_label(&self, id: &GlobalNodeId, key: &str, val: &str) {
3489        self.map_node_mut(id, &mut |node| node.set_label(key, val));
3490    }
3491
3492    fn get_node_label(&self, id: &GlobalNodeId, key: &str) -> Option<String> {
3493        self.map_node(id, &mut |node| node.get_label(key).map(str::to_string))
3494    }
3495
3496    fn global_id(&self) -> &GlobalNodeId {
3497        &self.inner().global_node_id
3498    }
3499
3500    /// Returns vector of local node ids in the circuit.
3501    fn node_ids(&self) -> Vec<NodeId> {
3502        self.inner()
3503            .nodes
3504            .borrow()
3505            .iter()
3506            .map(|node| node.borrow().local_id())
3507            .collect()
3508    }
3509
3510    fn lookup_local_node_by_persistent_id(
3511        &self,
3512        persistent_id: &str,
3513    ) -> Result<GlobalNodeId, DbspError> {
3514        self.inner()
3515            .lookup_local_node_by_persistent_id(persistent_id)
3516    }
3517
3518    fn import_nodes(&self) -> Vec<NodeId> {
3519        self.inner().import_nodes()
3520    }
3521
3522    fn allocate_stream_id(&self) -> StreamId {
3523        let circuit = self.inner();
3524        let mut last_stream_id = circuit.last_stream_id.borrow_mut();
3525        last_stream_id.0 += 1;
3526        *last_stream_id
3527    }
3528
3529    fn last_stream_id(&self) -> RefCell<StreamId> {
3530        self.inner().last_stream_id.clone()
3531    }
3532
3533    fn root_scope(&self) -> Scope {
3534        self.inner().root_scope
3535    }
3536
3537    fn node_id(&self) -> NodeId {
3538        self.inner().node_id
3539    }
3540
3541    fn global_node_id(&self) -> GlobalNodeId {
3542        self.inner().global_node_id.clone()
3543    }
3544
3545    fn check_fixedpoint(&self, scope: Scope) -> bool {
3546        self.inner().check_fixedpoint(scope)
3547    }
3548
3549    fn metadata_exchange(&self) -> &MetadataExchange {
3550        &self.inner().metadata_exchange
3551    }
3552
3553    fn balancer(&self) -> &Balancer {
3554        &self.inner().balancer
3555    }
3556
3557    fn set_auto_rebalance(&self, enable: bool) -> Result<(), DbspError> {
3558        self.inner().balancer.set_auto_rebalance(enable)
3559    }
3560
3561    fn set_balancer_hint_by_global_id(
3562        &self,
3563        global_node_id: &GlobalNodeId,
3564        hint: BalancerHint,
3565    ) -> Result<(), DbspError> {
3566        if global_node_id.parent_id() != Some(GlobalNodeId::root()) {
3567            return Err(DbspError::Balancer(BalancerError::NonTopLevelNode(
3568                global_node_id.clone(),
3569            )));
3570        }
3571
3572        self.inner()
3573            .balancer
3574            .set_hint(global_node_id.local_node_id().unwrap(), hint)
3575    }
3576
3577    fn set_balancer_hint(&self, persistent_id: &str, hint: BalancerHint) -> Result<(), DbspError> {
3578        let global_node_id = self.lookup_local_node_by_persistent_id(persistent_id)?;
3579        self.set_balancer_hint_by_global_id(&global_node_id, hint)
3580    }
3581
3582    fn get_current_balancer_policies(&self) -> BTreeMap<NodeId, PartitioningPolicy> {
3583        self.inner().balancer.get_policy()
3584    }
3585
3586    fn get_current_balancer_policy(
3587        &self,
3588        persistent_id: &str,
3589    ) -> Result<PartitioningPolicy, DbspError> {
3590        let global_node_id = self.lookup_local_node_by_persistent_id(persistent_id)?;
3591        let local_node_id = global_node_id.local_node_id().unwrap();
3592        self.inner()
3593            .balancer
3594            .get_policy_for_stream(local_node_id)
3595            .ok_or_else(|| {
3596                DbspError::Balancer(BalancerError::NotRegisteredWithBalancer(local_node_id))
3597            })
3598    }
3599
3600    fn rebalance(&self) {
3601        self.inner().balancer.rebalance()
3602    }
3603
3604    fn start_compaction(&self) {
3605        let _ = self.map_local_nodes_mut(&mut |node| {
3606            node.start_compaction();
3607            Ok(())
3608        });
3609    }
3610
3611    fn is_compaction_complete(&self) -> bool {
3612        let mut complete = true;
3613        let _ = self.map_local_nodes(&mut |node| {
3614            complete &= node.is_compaction_complete();
3615            Ok(())
3616        });
3617        complete
3618    }
3619}
3620
3621impl<P, T> Circuit for ChildCircuit<P, T>
3622where
3623    P: Clone + 'static,
3624    T: Timestamp,
3625{
3626    type Parent = P;
3627
3628    fn parent(&self) -> P {
3629        self.inner().parent.clone()
3630    }
3631
3632    fn root_circuit(&self) -> RootCircuit {
3633        if <dyn Any>::is::<RootCircuit>(self) {
3634            unsafe { transmute::<&Self, &RootCircuit>(self) }.clone()
3635        } else {
3636            self.inner().root.as_ref().unwrap().clone()
3637        }
3638    }
3639
3640    fn map_node<V>(&self, id: &GlobalNodeId, f: &mut dyn FnMut(&dyn Node) -> V) -> V {
3641        let path = id.path();
3642        let mut result: Option<V> = None;
3643
3644        assert!(path.starts_with(self.global_id().path()));
3645
3646        self.map_node_relative(
3647            path.strip_prefix(self.global_id().path()).unwrap(),
3648            &mut |node| result = Some(f(node)),
3649        );
3650        result.unwrap()
3651    }
3652
3653    fn map_node_mut<V>(&self, id: &GlobalNodeId, f: &mut dyn FnMut(&mut dyn Node) -> V) -> V {
3654        let path = id.path();
3655        let mut result: Option<V> = None;
3656
3657        assert!(path.starts_with(self.global_id().path()));
3658
3659        self.map_node_mut_relative(
3660            path.strip_prefix(self.global_id().path()).unwrap(),
3661            &mut |node| result = Some(f(node)),
3662        );
3663        result.unwrap()
3664    }
3665
3666    fn map_local_node_mut<V>(&self, id: NodeId, f: &mut dyn FnMut(&mut dyn Node) -> V) -> V {
3667        let mut result: Option<V> = None;
3668
3669        self.map_node_mut_relative(&[id], &mut |node| result = Some(f(node)));
3670        result.unwrap()
3671    }
3672
3673    fn ptr_eq(this: &Self, other: &Self) -> bool {
3674        Rc::ptr_eq(&this.inner, &other.inner)
3675    }
3676
3677    fn circuit_event_handlers(&self) -> CircuitEventHandlers {
3678        self.inner().circuit_event_handlers.clone()
3679    }
3680
3681    fn scheduler_event_handlers(&self) -> SchedulerEventHandlers {
3682        self.inner().scheduler_event_handlers.clone()
3683    }
3684
3685    fn log_circuit_event(&self, event: &CircuitEvent) {
3686        self.inner().log_circuit_event(event);
3687    }
3688
3689    fn log_scheduler_event(&self, event: &SchedulerEvent<'_>) {
3690        self.inner().log_scheduler_event(event);
3691    }
3692
3693    fn cache_get_or_insert_with<K, F>(&self, key: K, mut f: F) -> RefMut<'_, K::Value>
3694    where
3695        K: 'static + TypedMapKey<CircuitStoreMarker>,
3696        F: FnMut() -> K::Value,
3697    {
3698        // Don't use `store.entry()`, since `f` may need to perform
3699        // its own cache lookup.
3700        if self.inner().store.borrow().contains_key(&key) {
3701            return RefMut::map(self.inner().store.borrow_mut(), |store| {
3702                store.get_mut(&key).unwrap()
3703            });
3704        }
3705
3706        let new = f();
3707
3708        // TODO: Use `RefMut::filter_map()` to only perform one lookup in the happy path
3709        //       https://github.com/rust-lang/rust/issues/81061
3710        RefMut::map(self.inner().store.borrow_mut(), |store| {
3711            store.entry(key).or_insert(new)
3712        })
3713    }
3714
3715    fn connect_stream<V: 'static>(
3716        &self,
3717        stream: &Stream<Self, V>,
3718        to: NodeId,
3719        ownership_preference: OwnershipPreference,
3720    ) {
3721        self.log_circuit_event(&CircuitEvent::stream(
3722            stream.origin_node_id().clone(),
3723            self.global_node_id().child(to),
3724            ownership_preference,
3725        ));
3726
3727        debug_assert_eq!(self.global_node_id(), stream.circuit.global_node_id());
3728        self.inner().add_edge(Edge {
3729            from: stream.local_node_id(),
3730            to,
3731            origin: stream.origin_node_id().clone(),
3732            stream: Some(Box::new(stream.clone())),
3733            ownership_preference: Some(ownership_preference),
3734        });
3735    }
3736
3737    fn tick(&self) {
3738        let mut time = self.time.borrow_mut();
3739        *time = time.advance(0);
3740    }
3741
3742    fn clock_start(&self, scope: Scope) {
3743        for node in self.inner().nodes.borrow_mut().iter_mut() {
3744            node.borrow_mut().clock_start(scope);
3745        }
3746    }
3747
3748    fn clock_end(&self, scope: Scope) {
3749        for node in self.inner().nodes.borrow_mut().iter_mut() {
3750            node.borrow_mut().clock_end(scope);
3751        }
3752
3753        let mut time = self.time.borrow_mut();
3754        *time = time.advance(scope + 1);
3755    }
3756
3757    fn ready(&self, id: NodeId) -> bool {
3758        self.inner().nodes.borrow()[id.0].borrow().ready()
3759    }
3760
3761    fn cache_insert<K>(&self, key: K, val: K::Value)
3762    where
3763        K: TypedMapKey<CircuitStoreMarker> + 'static,
3764    {
3765        self.inner().store.borrow_mut().insert(key, val);
3766    }
3767
3768    fn cache_contains<K>(&self, key: &K) -> bool
3769    where
3770        K: TypedMapKey<CircuitStoreMarker> + 'static,
3771    {
3772        self.inner().store.borrow().contains_key(key)
3773    }
3774
3775    fn cache_get<K>(&self, key: &K) -> Option<K::Value>
3776    where
3777        K: TypedMapKey<CircuitStoreMarker> + 'static,
3778        K::Value: Clone,
3779    {
3780        self.inner().store.borrow().get(key).cloned()
3781    }
3782
3783    fn register_ready_callback(&self, id: NodeId, cb: Box<dyn Fn() + Send + Sync>) {
3784        self.inner().nodes.borrow()[id.0]
3785            .borrow_mut()
3786            .register_ready_callback(cb);
3787    }
3788
3789    fn is_async_node(&self, id: NodeId) -> bool {
3790        self.inner().nodes.borrow()[id.0].borrow().is_async()
3791    }
3792
3793    // Justification: the scheduler must not call `eval()` on a node twice.
3794    #[allow(clippy::await_holding_refcell_ref)]
3795    async fn eval_node(&self, id: NodeId) -> Result<Option<Position>, SchedulerError> {
3796        let circuit = self.inner();
3797        debug_assert!(id.0 < circuit.nodes.borrow().len());
3798
3799        // Notify loggers while holding a reference to the inner circuit.
3800        // We normally avoid this, since a nested call from event handler
3801        // will panic in `self.inner()`, but we do it here as an
3802        // optimization.
3803        circuit.log_scheduler_event(&SchedulerEvent::eval_start(
3804            circuit.nodes.borrow()[id.0].borrow().as_ref(),
3805        ));
3806
3807        let span = Span::new("eval").with_category("Operator");
3808        let (result, elapsed_time) =
3809            Timed::new(circuit.nodes.borrow()[id.0].borrow_mut().eval()).await;
3810        let progress = result?;
3811        span.with_tooltip(|| {
3812            let nodes = circuit.nodes.borrow();
3813            let node = nodes[id.0].borrow();
3814            format!(
3815                "{} {} used {}μs real time, {}μs CPU time",
3816                node.name(),
3817                node.global_id().node_identifier(),
3818                elapsed_time.real.as_micros(),
3819                elapsed_time.cpu.as_micros(),
3820            )
3821        })
3822        .record();
3823
3824        circuit.log_scheduler_event(&SchedulerEvent::eval_end(
3825            circuit.nodes.borrow()[id.0].borrow().as_ref(),
3826            elapsed_time,
3827        ));
3828
3829        Ok(progress)
3830    }
3831
3832    // Justification: the scheduler must not call `eval()` on a node twice.
3833    #[allow(clippy::await_holding_refcell_ref)]
3834    async fn eval_import_node(&self, id: NodeId) {
3835        let circuit = self.inner();
3836        debug_assert!(id.0 < circuit.nodes.borrow().len());
3837        debug_assert!(circuit.import_nodes().contains(&id));
3838
3839        // `circuit.nodes.borrow()` is safe across the await point because the
3840        // scheduler only borrows `circuit.nodes` during a step and never
3841        // borrows it mutably.
3842        //
3843        // `circuit.nodes.borrow()[id.0].borrow_mut()` is safe across the await
3844        // point because the scheduler never evaluates a node reentrantly.
3845        circuit.nodes.borrow()[id.0].borrow_mut().import().await;
3846    }
3847
3848    fn flush_node(&self, id: NodeId) {
3849        let circuit = self.inner();
3850        debug_assert!(id.0 < circuit.nodes.borrow().len());
3851
3852        circuit.nodes.borrow()[id.0].borrow_mut().flush();
3853    }
3854
3855    fn is_flush_complete(&self, id: NodeId) -> bool {
3856        let circuit = self.inner();
3857        debug_assert!(id.0 < circuit.nodes.borrow().len());
3858
3859        circuit.nodes.borrow()[id.0].borrow().is_flush_complete()
3860    }
3861
3862    #[track_caller]
3863    fn region<F, V>(&self, name: &str, f: F) -> V
3864    where
3865        F: FnOnce() -> V,
3866    {
3867        self.log_circuit_event(&CircuitEvent::push_region(name, Some(Location::caller())));
3868        let res = f();
3869        self.log_circuit_event(&CircuitEvent::pop_region());
3870        res
3871    }
3872
3873    #[track_caller]
3874    fn open_region(&self, name: RegionName) {
3875        self.log_circuit_event(&CircuitEvent::open_region(name, Some(Location::caller())));
3876    }
3877
3878    fn close_region(&self, name: RegionName) {
3879        self.log_circuit_event(&CircuitEvent::close_region(name));
3880    }
3881
3882    fn add_preprocessor(&self, preprocessor_node_id: NodeId) {
3883        for node in self.inner().nodes.borrow_mut().iter() {
3884            if node.borrow().is_input() {
3885                self.add_dependency(preprocessor_node_id, node.borrow().local_id());
3886            }
3887        }
3888    }
3889
3890    /// Add a source operator to the circuit.  See [`SourceOperator`].
3891    fn add_source<O, Op>(&self, operator: Op) -> Stream<Self, O>
3892    where
3893        O: Data,
3894        Op: SourceOperator<O>,
3895    {
3896        self.add_node(|id| {
3897            self.log_circuit_event(&CircuitEvent::operator(
3898                GlobalNodeId::child_of(self, id),
3899                operator.name(),
3900                operator.location(),
3901            ));
3902
3903            let node = SourceNode::new(operator, self.clone(), id);
3904            let output_stream = node.output_stream();
3905            (node, output_stream)
3906        })
3907    }
3908
3909    fn add_exchange<I, SndOp, O, RcvOp>(
3910        &self,
3911        sender: SndOp,
3912        receiver: RcvOp,
3913        input_stream: &Stream<Self, I>,
3914    ) -> Stream<Self, O>
3915    where
3916        I: Data,
3917        O: Data,
3918        SndOp: SinkOperator<I>,
3919        RcvOp: SourceOperator<O>,
3920    {
3921        let preference = sender.input_preference();
3922        self.add_exchange_with_preference(sender, receiver, input_stream, preference)
3923    }
3924
3925    fn add_exchange_with_preference<I, SndOp, O, RcvOp>(
3926        &self,
3927        sender: SndOp,
3928        receiver: RcvOp,
3929        input_stream: &Stream<Self, I>,
3930        input_preference: OwnershipPreference,
3931    ) -> Stream<Self, O>
3932    where
3933        I: Data,
3934        O: Data,
3935        SndOp: SinkOperator<I>,
3936        RcvOp: SourceOperator<O>,
3937    {
3938        let sender_id = self.add_node(|id| {
3939            self.log_circuit_event(&CircuitEvent::operator(
3940                GlobalNodeId::child_of(self, id),
3941                sender.name(),
3942                sender.location(),
3943            ));
3944
3945            let node = SinkNode::new(sender, input_stream.clone(), self.clone(), id);
3946            self.connect_stream(input_stream, id, input_preference);
3947            (node, id)
3948        });
3949
3950        let output_stream = self.add_node(|id| {
3951            self.log_circuit_event(&CircuitEvent::operator(
3952                GlobalNodeId::child_of(self, id),
3953                receiver.name(),
3954                receiver.location(),
3955            ));
3956
3957            let node = SourceNode::new(receiver, self.clone(), id);
3958            let output_stream = node.output_stream();
3959            (node, output_stream)
3960        });
3961
3962        self.add_dependency(sender_id, output_stream.local_node_id());
3963        output_stream
3964    }
3965
3966    fn add_sink<I, Op>(&self, operator: Op, input_stream: &Stream<Self, I>) -> GlobalNodeId
3967    where
3968        I: Data,
3969        Op: SinkOperator<I>,
3970    {
3971        let preference = operator.input_preference();
3972        self.add_sink_with_preference(operator, input_stream, preference)
3973    }
3974
3975    fn add_sink_with_preference<I, Op>(
3976        &self,
3977        operator: Op,
3978        input_stream: &Stream<Self, I>,
3979        input_preference: OwnershipPreference,
3980    ) -> GlobalNodeId
3981    where
3982        I: Data,
3983        Op: SinkOperator<I>,
3984    {
3985        self.add_node(|id| {
3986            let global_node_id = GlobalNodeId::child_of(self, id);
3987            // Log the operator event before the connection event, so that handlers
3988            // don't observe edges that connect to nodes they haven't seen yet.
3989            self.log_circuit_event(&CircuitEvent::operator(
3990                global_node_id.clone(),
3991                operator.name(),
3992                operator.location(),
3993            ));
3994
3995            self.connect_stream(input_stream, id, input_preference);
3996            (
3997                SinkNode::new(operator, input_stream.clone(), self.clone(), id),
3998                global_node_id,
3999            )
4000        })
4001    }
4002
4003    /// Add a binary sink operator (see [`BinarySinkOperator`]).
4004    fn add_binary_sink<I1, I2, Op>(
4005        &self,
4006        operator: Op,
4007        input_stream1: &Stream<Self, I1>,
4008        input_stream2: &Stream<Self, I2>,
4009    ) where
4010        I1: Data,
4011        I2: Data,
4012        Op: BinarySinkOperator<I1, I2>,
4013    {
4014        let (preference1, preference2) = operator.input_preference();
4015        self.add_binary_sink_with_preference(
4016            operator,
4017            (input_stream1, preference1),
4018            (input_stream2, preference2),
4019        )
4020    }
4021
4022    fn add_binary_sink_with_preference<I1, I2, Op>(
4023        &self,
4024        operator: Op,
4025        input_stream1: (&Stream<Self, I1>, OwnershipPreference),
4026        input_stream2: (&Stream<Self, I2>, OwnershipPreference),
4027    ) where
4028        I1: Data,
4029        I2: Data,
4030        Op: BinarySinkOperator<I1, I2>,
4031    {
4032        let (input_stream1, input_preference1) = input_stream1;
4033        let (input_stream2, input_preference2) = input_stream2;
4034
4035        self.add_node(|id| {
4036            self.log_circuit_event(&CircuitEvent::operator(
4037                GlobalNodeId::child_of(self, id),
4038                operator.name(),
4039                operator.location(),
4040            ));
4041
4042            let node = BinarySinkNode::new(
4043                operator,
4044                input_stream1.clone(),
4045                input_stream2.clone(),
4046                self.clone(),
4047                id,
4048            );
4049            self.connect_stream(input_stream1, id, input_preference1);
4050            self.connect_stream(input_stream2, id, input_preference2);
4051            (node, ())
4052        });
4053    }
4054
4055    /// Add a ternary sink operator (see [`TernarySinkOperator`]).
4056    fn add_ternary_sink<I1, I2, I3, Op>(
4057        &self,
4058        operator: Op,
4059        input_stream1: &Stream<Self, I1>,
4060        input_stream2: &Stream<Self, I2>,
4061        input_stream3: &Stream<Self, I3>,
4062    ) -> GlobalNodeId
4063    where
4064        I1: Data,
4065        I2: Data,
4066        I3: Data,
4067        Op: TernarySinkOperator<I1, I2, I3>,
4068    {
4069        let (preference1, preference2, preference3) = operator.input_preference();
4070        self.add_ternary_sink_with_preference(
4071            operator,
4072            (input_stream1, preference1),
4073            (input_stream2, preference2),
4074            (input_stream3, preference3),
4075        )
4076    }
4077
4078    fn add_ternary_sink_with_preference<I1, I2, I3, Op>(
4079        &self,
4080        operator: Op,
4081        input_stream1: (&Stream<Self, I1>, OwnershipPreference),
4082        input_stream2: (&Stream<Self, I2>, OwnershipPreference),
4083        input_stream3: (&Stream<Self, I3>, OwnershipPreference),
4084    ) -> GlobalNodeId
4085    where
4086        I1: Data,
4087        I2: Data,
4088        I3: Data,
4089        Op: TernarySinkOperator<I1, I2, I3>,
4090    {
4091        let (input_stream1, input_preference1) = input_stream1;
4092        let (input_stream2, input_preference2) = input_stream2;
4093        let (input_stream3, input_preference3) = input_stream3;
4094
4095        self.add_node(|id| {
4096            let global_node_id = GlobalNodeId::child_of(self, id);
4097
4098            self.log_circuit_event(&CircuitEvent::operator(
4099                GlobalNodeId::child_of(self, id),
4100                operator.name(),
4101                operator.location(),
4102            ));
4103
4104            let node = TernarySinkNode::new(
4105                operator,
4106                input_stream1.clone(),
4107                input_stream2.clone(),
4108                input_stream3.clone(),
4109                self.clone(),
4110                id,
4111            );
4112            self.connect_stream(input_stream1, id, input_preference1);
4113            self.connect_stream(input_stream2, id, input_preference2);
4114            self.connect_stream(input_stream3, id, input_preference3);
4115            (node, global_node_id)
4116        })
4117    }
4118
4119    fn add_unary_operator<I, O, Op>(
4120        &self,
4121        operator: Op,
4122        input_stream: &Stream<Self, I>,
4123    ) -> Stream<Self, O>
4124    where
4125        I: Data,
4126        O: Data,
4127        Op: UnaryOperator<I, O>,
4128    {
4129        let preference = operator.input_preference();
4130        self.add_unary_operator_with_preference(operator, input_stream, preference)
4131    }
4132
4133    fn add_unary_operator_with_preference<I, O, Op>(
4134        &self,
4135        operator: Op,
4136        input_stream: &Stream<Self, I>,
4137        input_preference: OwnershipPreference,
4138    ) -> Stream<Self, O>
4139    where
4140        I: Data,
4141        O: Data,
4142        Op: UnaryOperator<I, O>,
4143    {
4144        self.add_node(|id| {
4145            self.log_circuit_event(&CircuitEvent::operator(
4146                GlobalNodeId::child_of(self, id),
4147                operator.name(),
4148                operator.location(),
4149            ));
4150
4151            let node = UnaryNode::new(operator, input_stream.clone(), self.clone(), id);
4152            let output_stream = node.output_stream();
4153            self.connect_stream(input_stream, id, input_preference);
4154            (node, output_stream)
4155        })
4156    }
4157
4158    fn add_binary_operator<I1, I2, O, Op>(
4159        &self,
4160        operator: Op,
4161        input_stream1: &Stream<Self, I1>,
4162        input_stream2: &Stream<Self, I2>,
4163    ) -> Stream<Self, O>
4164    where
4165        I1: Data,
4166        I2: Data,
4167        O: Data,
4168        Op: BinaryOperator<I1, I2, O>,
4169    {
4170        let (pref1, pref2) = operator.input_preference();
4171        self.add_binary_operator_with_preference(
4172            operator,
4173            (input_stream1, pref1),
4174            (input_stream2, pref2),
4175        )
4176    }
4177
4178    fn add_binary_operator_with_preference<I1, I2, O, Op>(
4179        &self,
4180        operator: Op,
4181        input_stream1: (&Stream<Self, I1>, OwnershipPreference),
4182        input_stream2: (&Stream<Self, I2>, OwnershipPreference),
4183    ) -> Stream<Self, O>
4184    where
4185        I1: Data,
4186        I2: Data,
4187        O: Data,
4188        Op: BinaryOperator<I1, I2, O>,
4189    {
4190        let (input_stream1, input_preference1) = input_stream1;
4191        let (input_stream2, input_preference2) = input_stream2;
4192
4193        self.add_node(|id| {
4194            self.log_circuit_event(&CircuitEvent::operator(
4195                GlobalNodeId::child_of(self, id),
4196                operator.name(),
4197                operator.location(),
4198            ));
4199
4200            let node = BinaryNode::new(
4201                operator,
4202                input_stream1.clone(),
4203                input_stream2.clone(),
4204                self.clone(),
4205                id,
4206            );
4207            let output_stream = node.output_stream();
4208            self.connect_stream(input_stream1, id, input_preference1);
4209            self.connect_stream(input_stream2, id, input_preference2);
4210            (node, output_stream)
4211        })
4212    }
4213
4214    fn add_ternary_operator<I1, I2, I3, O, Op>(
4215        &self,
4216        operator: Op,
4217        input_stream1: &Stream<Self, I1>,
4218        input_stream2: &Stream<Self, I2>,
4219        input_stream3: &Stream<Self, I3>,
4220    ) -> Stream<Self, O>
4221    where
4222        I1: Data,
4223        I2: Data,
4224        I3: Data,
4225        O: Data,
4226        Op: TernaryOperator<I1, I2, I3, O>,
4227    {
4228        let (pref1, pref2, pref3) = operator.input_preference();
4229        self.add_ternary_operator_with_preference(
4230            operator,
4231            (input_stream1, pref1),
4232            (input_stream2, pref2),
4233            (input_stream3, pref3),
4234        )
4235    }
4236
4237    #[allow(clippy::too_many_arguments)]
4238    fn add_ternary_operator_with_preference<I1, I2, I3, O, Op>(
4239        &self,
4240        operator: Op,
4241        input_stream1: (&Stream<Self, I1>, OwnershipPreference),
4242        input_stream2: (&Stream<Self, I2>, OwnershipPreference),
4243        input_stream3: (&Stream<Self, I3>, OwnershipPreference),
4244    ) -> Stream<Self, O>
4245    where
4246        I1: Data,
4247        I2: Data,
4248        I3: Data,
4249        O: Data,
4250        Op: TernaryOperator<I1, I2, I3, O>,
4251    {
4252        let (input_stream1, input_preference1) = input_stream1;
4253        let (input_stream2, input_preference2) = input_stream2;
4254        let (input_stream3, input_preference3) = input_stream3;
4255
4256        self.add_node(|id| {
4257            self.log_circuit_event(&CircuitEvent::operator(
4258                GlobalNodeId::child_of(self, id),
4259                operator.name(),
4260                operator.location(),
4261            ));
4262
4263            let node = TernaryNode::new(
4264                operator,
4265                input_stream1.clone(),
4266                input_stream2.clone(),
4267                input_stream3.clone(),
4268                self.clone(),
4269                id,
4270            );
4271            let output_stream = node.output_stream();
4272            self.connect_stream(input_stream1, id, input_preference1);
4273            self.connect_stream(input_stream2, id, input_preference2);
4274            self.connect_stream(input_stream3, id, input_preference3);
4275            (node, output_stream)
4276        })
4277    }
4278
4279    fn add_quaternary_operator<I1, I2, I3, I4, O, Op>(
4280        &self,
4281        operator: Op,
4282        input_stream1: &Stream<Self, I1>,
4283        input_stream2: &Stream<Self, I2>,
4284        input_stream3: &Stream<Self, I3>,
4285        input_stream4: &Stream<Self, I4>,
4286    ) -> Stream<Self, O>
4287    where
4288        I1: Data,
4289        I2: Data,
4290        I3: Data,
4291        I4: Data,
4292        O: Data,
4293        Op: QuaternaryOperator<I1, I2, I3, I4, O>,
4294    {
4295        let (pref1, pref2, pref3, pref4) = operator.input_preference();
4296        self.add_quaternary_operator_with_preference(
4297            operator,
4298            (input_stream1, pref1),
4299            (input_stream2, pref2),
4300            (input_stream3, pref3),
4301            (input_stream4, pref4),
4302        )
4303    }
4304
4305    #[allow(clippy::too_many_arguments)]
4306    fn add_quaternary_operator_with_preference<I1, I2, I3, I4, O, Op>(
4307        &self,
4308        operator: Op,
4309        input_stream1: (&Stream<Self, I1>, OwnershipPreference),
4310        input_stream2: (&Stream<Self, I2>, OwnershipPreference),
4311        input_stream3: (&Stream<Self, I3>, OwnershipPreference),
4312        input_stream4: (&Stream<Self, I4>, OwnershipPreference),
4313    ) -> Stream<Self, O>
4314    where
4315        I1: Data,
4316        I2: Data,
4317        I3: Data,
4318        I4: Data,
4319        O: Data,
4320        Op: QuaternaryOperator<I1, I2, I3, I4, O>,
4321    {
4322        let (input_stream1, input_preference1) = input_stream1;
4323        let (input_stream2, input_preference2) = input_stream2;
4324        let (input_stream3, input_preference3) = input_stream3;
4325        let (input_stream4, input_preference4) = input_stream4;
4326
4327        self.add_node(|id| {
4328            self.log_circuit_event(&CircuitEvent::operator(
4329                GlobalNodeId::child_of(self, id),
4330                operator.name(),
4331                operator.location(),
4332            ));
4333
4334            let node = QuaternaryNode::new(
4335                operator,
4336                input_stream1.clone(),
4337                input_stream2.clone(),
4338                input_stream3.clone(),
4339                input_stream4.clone(),
4340                self.clone(),
4341                id,
4342            );
4343            let output_stream = node.output_stream();
4344            self.connect_stream(input_stream1, id, input_preference1);
4345            self.connect_stream(input_stream2, id, input_preference2);
4346            self.connect_stream(input_stream3, id, input_preference3);
4347            self.connect_stream(input_stream4, id, input_preference4);
4348            (node, output_stream)
4349        })
4350    }
4351
4352    fn add_nary_operator<'a, I, O, Op, Iter>(
4353        &'a self,
4354        operator: Op,
4355        input_streams: Iter,
4356    ) -> Stream<Self, O>
4357    where
4358        I: Data,
4359        O: Data,
4360        Op: NaryOperator<I, O>,
4361        Iter: IntoIterator<Item = &'a Stream<Self, I>>,
4362    {
4363        let pref = operator.input_preference();
4364        self.add_nary_operator_with_preference(operator, input_streams, pref)
4365    }
4366
4367    fn add_nary_operator_with_preference<'a, I, O, Op, Iter>(
4368        &'a self,
4369        operator: Op,
4370        input_streams: Iter,
4371        input_preference: OwnershipPreference,
4372    ) -> Stream<Self, O>
4373    where
4374        I: Data,
4375        O: Data,
4376        Op: NaryOperator<I, O>,
4377        Iter: IntoIterator<Item = &'a Stream<Self, I>>,
4378    {
4379        let input_streams: Vec<Stream<_, _>> = input_streams.into_iter().cloned().collect();
4380        self.add_node(|id| {
4381            self.log_circuit_event(&CircuitEvent::operator(
4382                GlobalNodeId::child_of(self, id),
4383                operator.name(),
4384                operator.location(),
4385            ));
4386
4387            let node = NaryNode::new(operator, input_streams.clone(), self.clone(), id);
4388            let output_stream = node.output_stream();
4389            for stream in input_streams.iter() {
4390                self.connect_stream(stream, id, input_preference);
4391            }
4392            (node, output_stream)
4393        })
4394    }
4395
4396    #[track_caller]
4397    fn add_custom_node<N: Node, R>(
4398        &self,
4399        name: Cow<'static, str>,
4400        constructor: impl FnOnce(NodeId) -> (N, R),
4401    ) -> R {
4402        self.add_node(|id| {
4403            // This must be called before the node is created, so that the node can connect input streams to
4404            // itself without causing a panic.
4405            self.log_circuit_event(&CircuitEvent::operator(
4406                GlobalNodeId::child_of(self, id),
4407                name,
4408                Some(Location::caller()),
4409            ));
4410            let (node, res) = constructor(id);
4411            (node, res)
4412        })
4413    }
4414
4415    fn add_feedback<I, O, Op>(
4416        &self,
4417        operator: Op,
4418    ) -> (Stream<Self, O>, FeedbackConnector<Self, I, O, Op>)
4419    where
4420        I: Data,
4421        O: Data,
4422        Op: StrictUnaryOperator<I, O>,
4423    {
4424        self.add_node(|id| {
4425            self.log_circuit_event(&CircuitEvent::strict_operator_output(
4426                GlobalNodeId::child_of(self, id),
4427                operator.name(),
4428                operator.location(),
4429            ));
4430
4431            let operator = Rc::new(RefCell::new(operator));
4432            let connector = FeedbackConnector::new(id, self.clone(), operator.clone());
4433            let output_node = FeedbackOutputNode::new(operator, self.clone(), id);
4434            let local = output_node.output_stream();
4435            (output_node, (local, connector))
4436        })
4437    }
4438
4439    fn add_feedback_with_export<I, O, Op>(
4440        &self,
4441        operator: Op,
4442    ) -> (ExportStream<Self, O>, FeedbackConnector<Self, I, O, Op>)
4443    where
4444        I: Data,
4445        O: Data,
4446        Op: StrictUnaryOperator<I, O>,
4447    {
4448        self.add_node(|id| {
4449            self.log_circuit_event(&CircuitEvent::strict_operator_output(
4450                GlobalNodeId::child_of(self, id),
4451                operator.name(),
4452                operator.location(),
4453            ));
4454
4455            let operator = Rc::new(RefCell::new(operator));
4456            let connector = FeedbackConnector::new(id, self.clone(), operator.clone());
4457            let output_node = FeedbackOutputNode::with_export(operator, self.clone(), id);
4458            let local = output_node.output_stream();
4459            let export = output_node.export_stream.clone().unwrap();
4460            (output_node, (ExportStream { local, export }, connector))
4461        })
4462    }
4463
4464    /// Connect feedback loop.
4465    ///
4466    /// Returns node id of the input half of Z-1.
4467    fn connect_feedback_with_preference<I, O, Op>(
4468        &self,
4469        output_node_id: NodeId,
4470        operator: Rc<RefCell<Op>>,
4471        input_stream: &Stream<Self, I>,
4472        input_preference: OwnershipPreference,
4473    ) where
4474        I: Data,
4475        O: Data,
4476        Op: StrictUnaryOperator<I, O>,
4477    {
4478        self.add_node(|id| {
4479            self.log_circuit_event(&CircuitEvent::strict_operator_input(
4480                GlobalNodeId::child_of(self, id),
4481                output_node_id,
4482            ));
4483
4484            let output_node = FeedbackInputNode::new(operator, input_stream.clone(), id);
4485            self.connect_stream(input_stream, id, input_preference);
4486            self.add_dependency(output_node_id, id);
4487            (output_node, ())
4488        })
4489    }
4490
4491    fn iterative_subcircuit<F, V, E>(&self, child_constructor: F) -> Result<V, SchedulerError>
4492    where
4493        F: FnOnce(&mut IterativeCircuit<Self>) -> Result<(V, E), SchedulerError>,
4494        E: Executor<IterativeCircuit<Self>>,
4495    {
4496        self.try_add_node(|id| {
4497            let global_id = GlobalNodeId::child_of(self, id);
4498            self.log_circuit_event(&CircuitEvent::subcircuit(global_id.clone(), true));
4499            let mut child_circuit = ChildCircuit::with_parent(self.clone(), id);
4500            let (res, executor) = child_constructor(&mut child_circuit)?;
4501            let child = <ChildNode<IterativeCircuit<Self>>>::new::<E>(child_circuit, 1, executor);
4502            self.log_circuit_event(&CircuitEvent::subcircuit_complete(global_id));
4503            Ok((child, res))
4504        })
4505    }
4506
4507    fn non_iterative_subcircuit<F, V, E>(&self, child_constructor: F) -> Result<V, SchedulerError>
4508    where
4509        F: FnOnce(&mut NonIterativeCircuit<Self>) -> Result<(V, E), SchedulerError>,
4510        E: Executor<NonIterativeCircuit<Self>>,
4511    {
4512        self.try_add_node(|id| {
4513            let global_id = GlobalNodeId::child_of(self, id);
4514            self.log_circuit_event(&CircuitEvent::subcircuit(global_id.clone(), false));
4515            let mut child_circuit = ChildCircuit::with_parent(self.clone(), id);
4516            let (res, executor) = child_constructor(&mut child_circuit)?;
4517            let child =
4518                <ChildNode<NonIterativeCircuit<Self>>>::new::<E>(child_circuit, 0, executor);
4519            self.log_circuit_event(&CircuitEvent::subcircuit_complete(global_id));
4520            Ok((child, res))
4521        })
4522    }
4523
4524    fn iterate<F, C, V>(&self, constructor: F) -> Result<V, SchedulerError>
4525    where
4526        F: FnOnce(&mut IterativeCircuit<Self>) -> Result<(C, V), SchedulerError>,
4527        C: AsyncFn() -> Result<bool, SchedulerError> + 'static,
4528    {
4529        self.iterate_with_scheduler::<F, C, V, DynamicScheduler>(constructor)
4530    }
4531
4532    /// Add an iteratively scheduled child circuit.
4533    ///
4534    /// Similar to [`iterate`](`Self::iterate`), but with a user-specified
4535    /// [`Scheduler`] implementation.
4536    fn iterate_with_scheduler<F, C, V, S>(&self, constructor: F) -> Result<V, SchedulerError>
4537    where
4538        F: FnOnce(&mut IterativeCircuit<Self>) -> Result<(C, V), SchedulerError>,
4539        C: AsyncFn() -> Result<bool, SchedulerError> + 'static,
4540        S: Scheduler + 'static,
4541    {
4542        self.iterative_subcircuit(|child| {
4543            let (termination_check, res) = constructor(child)?;
4544            let mut executor = <IterativeExecutor<_, S>>::new(termination_check);
4545            executor.prepare(child, None)?;
4546            Ok((res, executor))
4547        })
4548    }
4549
4550    fn fixedpoint<F, V>(&self, constructor: F) -> Result<V, SchedulerError>
4551    where
4552        F: FnOnce(&mut IterativeCircuit<Self>) -> Result<V, SchedulerError>,
4553    {
4554        self.fixedpoint_with_scheduler::<F, V, DynamicScheduler>(constructor)
4555    }
4556
4557    fn fixedpoint_with_scheduler<F, V, S>(&self, constructor: F) -> Result<V, SchedulerError>
4558    where
4559        F: FnOnce(&mut IterativeCircuit<Self>) -> Result<V, SchedulerError>,
4560        S: Scheduler + 'static,
4561    {
4562        self.iterative_subcircuit(|child| {
4563            let res = constructor(child)?;
4564            let child_clone = child.clone();
4565
4566            let consensus = Consensus::new("fixed point");
4567
4568            let termination_check = async move || {
4569                // Send local fixed point status to all peers.
4570                let local_fixedpoint = child_clone.inner().check_fixedpoint(0);
4571                consensus.check(local_fixedpoint).await
4572            };
4573            let mut executor = <IterativeExecutor<_, S>>::new(termination_check);
4574            executor.prepare(child, None)?;
4575            Ok((res, executor))
4576        })
4577    }
4578
4579    fn import_stream<I, O, Op>(&self, operator: Op, parent_stream: &Stream<P, I>) -> Stream<Self, O>
4580    where
4581        Self::Parent: Circuit,
4582        I: Data,
4583        O: Data,
4584        Op: ImportOperator<I, O>,
4585    {
4586        let preference = operator.input_preference();
4587        self.import_stream_with_preference(operator, parent_stream, preference)
4588    }
4589
4590    fn import_stream_with_preference<I, O, Op>(
4591        &self,
4592        operator: Op,
4593        parent_stream: &Stream<P, I>,
4594        input_preference: OwnershipPreference,
4595    ) -> Stream<Self, O>
4596    where
4597        Self::Parent: Circuit,
4598        I: Data,
4599        O: Data,
4600        Op: ImportOperator<I, O>,
4601    {
4602        assert!(self.is_child_of(parent_stream.circuit()));
4603
4604        let output_stream = self.add_node(|id| {
4605            let node_id = self.global_node_id().child(id);
4606            self.log_circuit_event(&CircuitEvent::operator(
4607                node_id.clone(),
4608                operator.name(),
4609                operator.location(),
4610            ));
4611            let node = ImportNode::new(operator, self.clone(), parent_stream.clone(), id);
4612            // Note: here the edge points to the sub-circuit, and not to the ImportNode itself.
4613            self.parent()
4614                .connect_stream(parent_stream, self.node_id(), input_preference);
4615            // Log the actual edge going to the inner node as well
4616            self.parent().log_circuit_event(&CircuitEvent::stream(
4617                parent_stream.origin_node_id().clone(),
4618                node_id.clone(),
4619                input_preference,
4620            ));
4621            let output_stream = node.output_stream();
4622            (node, output_stream)
4623        });
4624
4625        self.add_import_node(output_stream.local_node_id());
4626
4627        output_stream
4628    }
4629
4630    fn add_replay_edges(&self, stream_id: StreamId, replay_stream: &dyn StreamMetadata) {
4631        let mut edges = self.edges_mut();
4632        let mut new_edges = Vec::new();
4633
4634        let Some(edges_to_replay) = edges.get_by_stream_id(&Some(stream_id)) else {
4635            return;
4636        };
4637
4638        for edge in edges_to_replay {
4639            // println!(
4640            //     "Adding replay edge ({}) {} -> {}",
4641            //     replay_stream.origin_node_id(),
4642            //     replay_stream.local_node_id(),
4643            //     edge.to
4644            // );
4645            new_edges.push(Edge {
4646                from: replay_stream.local_node_id(),
4647                to: edge.to,
4648                origin: replay_stream.origin_node_id().clone(),
4649                stream: Some(clone_box(replay_stream)),
4650                ownership_preference: edge.ownership_preference,
4651            });
4652        }
4653
4654        edges.extend(new_edges);
4655    }
4656}
4657struct ImportNode<C, I, O, Op>
4658where
4659    C: Circuit,
4660{
4661    id: GlobalNodeId,
4662    operator: Op,
4663    parent_stream: Stream<C::Parent, I>,
4664    output_stream: Stream<C, O>,
4665    labels: BTreeMap<String, String>,
4666}
4667
4668impl<C, I, O, Op> ImportNode<C, I, O, Op>
4669where
4670    C: Circuit,
4671    C::Parent: Circuit,
4672    I: Clone + 'static,
4673    O: Clone + 'static,
4674    Op: ImportOperator<I, O>,
4675{
4676    fn new(operator: Op, circuit: C, parent_stream: Stream<C::Parent, I>, id: NodeId) -> Self {
4677        assert!(Circuit::ptr_eq(&circuit.parent(), parent_stream.circuit()));
4678
4679        Self {
4680            id: circuit.global_node_id().child(id),
4681            operator,
4682            parent_stream,
4683            output_stream: Stream::new(circuit, id),
4684            labels: BTreeMap::new(),
4685        }
4686    }
4687
4688    fn output_stream(&self) -> Stream<C, O> {
4689        self.output_stream.clone()
4690    }
4691}
4692
4693impl<C, I, O, Op> Node for ImportNode<C, I, O, Op>
4694where
4695    C: Circuit,
4696    C::Parent: Circuit,
4697    I: Clone + 'static,
4698    O: Clone + 'static,
4699    Op: ImportOperator<I, O>,
4700{
4701    fn name(&self) -> Cow<'static, str> {
4702        self.operator.name()
4703    }
4704
4705    fn local_id(&self) -> NodeId {
4706        self.id.local_node_id().unwrap()
4707    }
4708
4709    fn global_id(&self) -> &GlobalNodeId {
4710        &self.id
4711    }
4712
4713    fn is_async(&self) -> bool {
4714        self.operator.is_async()
4715    }
4716
4717    fn is_input(&self) -> bool {
4718        self.operator.is_input()
4719    }
4720
4721    fn ready(&self) -> bool {
4722        self.operator.ready()
4723    }
4724
4725    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
4726        self.operator.register_ready_callback(cb);
4727    }
4728
4729    fn eval<'a>(
4730        &'a mut self,
4731    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
4732        Box::pin(async {
4733            self.output_stream.put(self.operator.eval().await);
4734            Ok(self.operator.flush_progress())
4735        })
4736    }
4737
4738    // It is safe to hold `self.parent_stream.get()` across the await point
4739    // because the operator is not evaluated reentrantly.
4740    #[allow(clippy::await_holding_refcell_ref)]
4741    fn import<'a>(&'a mut self) -> Pin<Box<dyn Future<Output = ()> + 'a>> {
4742        Box::pin(async {
4743            match StreamValue::take(self.parent_stream.val()) {
4744                None => {
4745                    self.operator
4746                        .import(StreamValue::peek(&self.parent_stream.get()))
4747                        .await
4748                }
4749                Some(val) => self.operator.import_owned(val).await,
4750            }
4751
4752            StreamValue::consume_token(self.parent_stream.val());
4753        })
4754    }
4755
4756    fn start_transaction(&mut self) {
4757        self.operator.start_transaction();
4758    }
4759
4760    fn flush(&mut self) {
4761        self.operator.flush();
4762    }
4763
4764    fn is_flush_complete(&self) -> bool {
4765        self.operator.is_flush_complete()
4766    }
4767
4768    fn clock_start(&mut self, scope: Scope) {
4769        self.operator.clock_start(scope);
4770    }
4771
4772    fn clock_end(&mut self, scope: Scope) {
4773        self.operator.clock_end(scope);
4774    }
4775
4776    fn init(&mut self) {
4777        self.operator.init(&self.id);
4778    }
4779
4780    fn metadata(&self, output: &mut OperatorMeta) {
4781        self.operator.metadata(output);
4782    }
4783
4784    fn fixedpoint(&self, scope: Scope) -> bool {
4785        self.operator.fixedpoint(scope)
4786    }
4787
4788    fn checkpoint(
4789        &mut self,
4790        base: &StoragePath,
4791        files: &mut Vec<Arc<dyn FileCommitter>>,
4792    ) -> Result<(), DbspError> {
4793        self.operator
4794            .checkpoint(base, self.persistent_id().as_deref(), files)
4795    }
4796
4797    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
4798        self.operator.restore(base, self.persistent_id().as_deref())
4799    }
4800
4801    fn start_compaction(&mut self) {
4802        self.operator.start_compaction()
4803    }
4804
4805    fn is_compaction_complete(&self) -> bool {
4806        self.operator.is_compaction_complete()
4807    }
4808
4809    fn clear_state(&mut self) -> Result<(), DbspError> {
4810        self.operator.clear_state()
4811    }
4812
4813    fn start_replay(&mut self) -> Result<(), DbspError> {
4814        self.operator.start_replay()
4815    }
4816
4817    fn end_replay(&mut self) -> Result<(), DbspError> {
4818        self.operator.end_replay()
4819    }
4820
4821    fn is_replay_complete(&self) -> bool {
4822        self.operator.is_replay_complete()
4823    }
4824
4825    fn set_label(&mut self, key: &str, value: &str) {
4826        self.labels.insert(key.to_string(), value.to_string());
4827    }
4828
4829    fn get_label(&self, key: &str) -> Option<&str> {
4830        self.labels.get(key).map(|s| s.as_str())
4831    }
4832
4833    fn labels(&self) -> &BTreeMap<String, String> {
4834        &self.labels
4835    }
4836
4837    fn as_any(&self) -> &dyn Any {
4838        self
4839    }
4840}
4841
4842struct SourceNode<C, O, Op> {
4843    id: GlobalNodeId,
4844    operator: Op,
4845    output_stream: Stream<C, O>,
4846    labels: BTreeMap<String, String>,
4847}
4848
4849impl<C, O, Op> SourceNode<C, O, Op>
4850where
4851    Op: SourceOperator<O>,
4852    C: Circuit,
4853{
4854    fn new(operator: Op, circuit: C, id: NodeId) -> Self {
4855        Self {
4856            id: circuit.global_node_id().child(id),
4857            operator,
4858            output_stream: Stream::new(circuit, id),
4859            labels: BTreeMap::new(),
4860        }
4861    }
4862
4863    fn output_stream(&self) -> Stream<C, O> {
4864        self.output_stream.clone()
4865    }
4866}
4867
4868impl<C, O, Op> Node for SourceNode<C, O, Op>
4869where
4870    C: Circuit,
4871    O: Clone + 'static,
4872    Op: SourceOperator<O>,
4873{
4874    fn name(&self) -> Cow<'static, str> {
4875        self.operator.name()
4876    }
4877
4878    fn local_id(&self) -> NodeId {
4879        self.id.local_node_id().unwrap()
4880    }
4881
4882    fn global_id(&self) -> &GlobalNodeId {
4883        &self.id
4884    }
4885
4886    fn is_async(&self) -> bool {
4887        self.operator.is_async()
4888    }
4889
4890    fn is_input(&self) -> bool {
4891        self.operator.is_input()
4892    }
4893
4894    fn ready(&self) -> bool {
4895        self.operator.ready()
4896    }
4897
4898    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
4899        self.operator.register_ready_callback(cb);
4900    }
4901
4902    fn eval<'a>(
4903        &'a mut self,
4904    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
4905        Box::pin(async {
4906            self.output_stream.put(self.operator.eval().await);
4907            Ok(self.operator.flush_progress())
4908        })
4909    }
4910
4911    fn start_transaction(&mut self) {
4912        self.operator.start_transaction();
4913    }
4914
4915    fn flush(&mut self) {
4916        self.operator.flush();
4917    }
4918
4919    fn is_flush_complete(&self) -> bool {
4920        self.operator.is_flush_complete()
4921    }
4922
4923    fn clock_start(&mut self, scope: Scope) {
4924        self.operator.clock_start(scope);
4925    }
4926
4927    fn clock_end(&mut self, scope: Scope) {
4928        self.operator.clock_end(scope);
4929    }
4930
4931    fn init(&mut self) {
4932        self.operator.init(&self.id);
4933    }
4934
4935    fn metadata(&self, output: &mut OperatorMeta) {
4936        self.operator.metadata(output);
4937    }
4938
4939    fn fixedpoint(&self, scope: Scope) -> bool {
4940        self.operator.fixedpoint(scope)
4941    }
4942
4943    fn checkpoint(
4944        &mut self,
4945        base: &StoragePath,
4946        files: &mut Vec<Arc<dyn FileCommitter>>,
4947    ) -> Result<(), DbspError> {
4948        self.operator
4949            .checkpoint(base, self.persistent_id().as_deref(), files)
4950    }
4951
4952    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
4953        self.operator.restore(base, self.persistent_id().as_deref())
4954    }
4955
4956    fn start_compaction(&mut self) {
4957        self.operator.start_compaction()
4958    }
4959
4960    fn is_compaction_complete(&self) -> bool {
4961        self.operator.is_compaction_complete()
4962    }
4963
4964    fn clear_state(&mut self) -> Result<(), DbspError> {
4965        self.operator.clear_state()
4966    }
4967
4968    fn start_replay(&mut self) -> Result<(), DbspError> {
4969        self.operator.start_replay()
4970    }
4971
4972    fn is_replay_complete(&self) -> bool {
4973        self.operator.is_replay_complete()
4974    }
4975
4976    fn end_replay(&mut self) -> Result<(), DbspError> {
4977        self.operator.end_replay()
4978    }
4979
4980    fn set_label(&mut self, key: &str, value: &str) {
4981        self.labels.insert(key.to_string(), value.to_string());
4982    }
4983
4984    fn get_label(&self, key: &str) -> Option<&str> {
4985        self.labels.get(key).map(|s| s.as_str())
4986    }
4987
4988    fn labels(&self) -> &BTreeMap<String, String> {
4989        &self.labels
4990    }
4991
4992    fn as_any(&self) -> &dyn Any {
4993        self
4994    }
4995}
4996
4997struct UnaryNode<C, I, O, Op> {
4998    id: GlobalNodeId,
4999    operator: Op,
5000    input_stream: Stream<C, I>,
5001    output_stream: Stream<C, O>,
5002    labels: BTreeMap<String, String>,
5003}
5004
5005impl<C, I, O, Op> UnaryNode<C, I, O, Op>
5006where
5007    Op: UnaryOperator<I, O>,
5008    C: Circuit,
5009{
5010    fn new(operator: Op, input_stream: Stream<C, I>, circuit: C, id: NodeId) -> Self {
5011        Self {
5012            id: circuit.global_node_id().child(id),
5013            operator,
5014            input_stream,
5015            output_stream: Stream::new(circuit, id),
5016            labels: BTreeMap::new(),
5017        }
5018    }
5019
5020    fn output_stream(&self) -> Stream<C, O> {
5021        self.output_stream.clone()
5022    }
5023}
5024
5025impl<C, I, O, Op> Node for UnaryNode<C, I, O, Op>
5026where
5027    C: Circuit,
5028    I: Clone + 'static,
5029    O: Clone + 'static,
5030    Op: UnaryOperator<I, O>,
5031{
5032    fn name(&self) -> Cow<'static, str> {
5033        self.operator.name()
5034    }
5035
5036    fn local_id(&self) -> NodeId {
5037        self.id.local_node_id().unwrap()
5038    }
5039
5040    fn global_id(&self) -> &GlobalNodeId {
5041        &self.id
5042    }
5043
5044    fn is_async(&self) -> bool {
5045        self.operator.is_async()
5046    }
5047
5048    fn is_input(&self) -> bool {
5049        self.operator.is_input()
5050    }
5051
5052    fn ready(&self) -> bool {
5053        self.operator.ready()
5054    }
5055
5056    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
5057        self.operator.register_ready_callback(cb);
5058    }
5059
5060    // Justification: see StreamValue::take() comment.
5061    #[allow(clippy::await_holding_refcell_ref)]
5062    fn eval<'a>(
5063        &'a mut self,
5064    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
5065        Box::pin(async {
5066            self.output_stream
5067                .put(match StreamValue::take(self.input_stream.val()) {
5068                    Some(v) => self.operator.eval_owned(v).await,
5069                    None => {
5070                        self.operator
5071                            .eval(StreamValue::peek(&self.input_stream.get()))
5072                            .await
5073                    }
5074                });
5075            StreamValue::consume_token(self.input_stream.val());
5076            Ok(self.operator.flush_progress())
5077        })
5078    }
5079
5080    fn start_transaction(&mut self) {
5081        self.operator.start_transaction();
5082    }
5083
5084    fn flush(&mut self) {
5085        self.operator.flush();
5086    }
5087
5088    fn is_flush_complete(&self) -> bool {
5089        self.operator.is_flush_complete()
5090    }
5091
5092    fn clock_start(&mut self, scope: Scope) {
5093        self.operator.clock_start(scope);
5094    }
5095
5096    fn clock_end(&mut self, scope: Scope) {
5097        self.operator.clock_end(scope);
5098    }
5099
5100    fn init(&mut self) {
5101        self.operator.init(&self.id);
5102    }
5103
5104    fn metadata(&self, output: &mut OperatorMeta) {
5105        self.operator.metadata(output);
5106    }
5107
5108    fn fixedpoint(&self, scope: Scope) -> bool {
5109        self.operator.fixedpoint(scope)
5110    }
5111
5112    fn checkpoint(
5113        &mut self,
5114        base: &StoragePath,
5115        files: &mut Vec<Arc<dyn FileCommitter>>,
5116    ) -> Result<(), DbspError> {
5117        self.operator
5118            .checkpoint(base, self.persistent_id().as_deref(), files)
5119    }
5120
5121    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
5122        self.operator.restore(base, self.persistent_id().as_deref())
5123    }
5124
5125    fn start_compaction(&mut self) {
5126        self.operator.start_compaction()
5127    }
5128
5129    fn is_compaction_complete(&self) -> bool {
5130        self.operator.is_compaction_complete()
5131    }
5132
5133    fn clear_state(&mut self) -> Result<(), DbspError> {
5134        self.operator.clear_state()
5135    }
5136
5137    fn start_replay(&mut self) -> Result<(), DbspError> {
5138        self.operator.start_replay()
5139    }
5140
5141    fn is_replay_complete(&self) -> bool {
5142        self.operator.is_replay_complete()
5143    }
5144
5145    fn end_replay(&mut self) -> Result<(), DbspError> {
5146        self.operator.end_replay()
5147    }
5148
5149    fn set_label(&mut self, key: &str, value: &str) {
5150        self.labels.insert(key.to_string(), value.to_string());
5151    }
5152
5153    fn get_label(&self, key: &str) -> Option<&str> {
5154        self.labels.get(key).map(|s| s.as_str())
5155    }
5156
5157    fn labels(&self) -> &BTreeMap<String, String> {
5158        &self.labels
5159    }
5160
5161    fn as_any(&self) -> &dyn Any {
5162        self
5163    }
5164}
5165
5166struct SinkNode<C, I, Op> {
5167    id: GlobalNodeId,
5168    operator: Op,
5169    input_stream: Stream<C, I>,
5170    labels: BTreeMap<String, String>,
5171}
5172
5173impl<C, I, Op> SinkNode<C, I, Op>
5174where
5175    Op: SinkOperator<I>,
5176    C: Circuit,
5177{
5178    fn new(operator: Op, input_stream: Stream<C, I>, circuit: C, id: NodeId) -> Self {
5179        Self {
5180            id: circuit.global_node_id().child(id),
5181            operator,
5182            input_stream,
5183            labels: BTreeMap::new(),
5184        }
5185    }
5186}
5187
5188impl<C, I, Op> Node for SinkNode<C, I, Op>
5189where
5190    C: Circuit,
5191    I: Clone + 'static,
5192    Op: SinkOperator<I>,
5193{
5194    fn name(&self) -> Cow<'static, str> {
5195        self.operator.name()
5196    }
5197
5198    fn local_id(&self) -> NodeId {
5199        self.id.local_node_id().unwrap()
5200    }
5201
5202    fn global_id(&self) -> &GlobalNodeId {
5203        &self.id
5204    }
5205
5206    fn is_async(&self) -> bool {
5207        self.operator.is_async()
5208    }
5209
5210    fn is_input(&self) -> bool {
5211        self.operator.is_input()
5212    }
5213
5214    fn ready(&self) -> bool {
5215        self.operator.ready()
5216    }
5217
5218    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
5219        self.operator.register_ready_callback(cb);
5220    }
5221
5222    // Justification: see StreamValue::take() comment.
5223    #[allow(clippy::await_holding_refcell_ref)]
5224    fn eval<'a>(
5225        &'a mut self,
5226    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
5227        Box::pin(async {
5228            match StreamValue::take(self.input_stream.val()) {
5229                Some(v) => self.operator.eval_owned(v).await,
5230                None => {
5231                    self.operator
5232                        .eval(StreamValue::peek(&self.input_stream.get()))
5233                        .await
5234                }
5235            };
5236            StreamValue::consume_token(self.input_stream.val());
5237
5238            Ok(self.operator.flush_progress())
5239        })
5240    }
5241
5242    fn start_transaction(&mut self) {
5243        self.operator.start_transaction();
5244    }
5245
5246    fn flush(&mut self) {
5247        self.operator.flush();
5248    }
5249
5250    fn is_flush_complete(&self) -> bool {
5251        self.operator.is_flush_complete()
5252    }
5253
5254    fn clock_start(&mut self, scope: Scope) {
5255        self.operator.clock_start(scope);
5256    }
5257
5258    fn clock_end(&mut self, scope: Scope) {
5259        self.operator.clock_end(scope);
5260    }
5261
5262    fn init(&mut self) {
5263        self.operator.init(&self.id);
5264    }
5265
5266    fn metadata(&self, output: &mut OperatorMeta) {
5267        self.operator.metadata(output);
5268    }
5269
5270    fn fixedpoint(&self, scope: Scope) -> bool {
5271        self.operator.fixedpoint(scope)
5272    }
5273
5274    fn checkpoint(
5275        &mut self,
5276        base: &StoragePath,
5277        files: &mut Vec<Arc<dyn FileCommitter>>,
5278    ) -> Result<(), DbspError> {
5279        self.operator
5280            .checkpoint(base, self.persistent_id().as_deref(), files)
5281    }
5282
5283    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
5284        self.operator.restore(base, self.persistent_id().as_deref())
5285    }
5286
5287    fn start_compaction(&mut self) {
5288        self.operator.start_compaction()
5289    }
5290
5291    fn is_compaction_complete(&self) -> bool {
5292        self.operator.is_compaction_complete()
5293    }
5294
5295    fn clear_state(&mut self) -> Result<(), DbspError> {
5296        self.operator.clear_state()
5297    }
5298
5299    fn start_replay(&mut self) -> Result<(), DbspError> {
5300        self.operator.start_replay()
5301    }
5302
5303    fn is_replay_complete(&self) -> bool {
5304        self.operator.is_replay_complete()
5305    }
5306
5307    fn end_replay(&mut self) -> Result<(), DbspError> {
5308        self.operator.end_replay()
5309    }
5310
5311    fn set_label(&mut self, key: &str, value: &str) {
5312        self.labels.insert(key.to_string(), value.to_string());
5313    }
5314
5315    fn get_label(&self, key: &str) -> Option<&str> {
5316        self.labels.get(key).map(|s| s.as_str())
5317    }
5318
5319    fn labels(&self) -> &BTreeMap<String, String> {
5320        &self.labels
5321    }
5322
5323    fn as_any(&self) -> &dyn Any {
5324        self
5325    }
5326}
5327
5328struct BinarySinkNode<C, I1, I2, Op> {
5329    id: GlobalNodeId,
5330    operator: Op,
5331    input_stream1: Stream<C, I1>,
5332    input_stream2: Stream<C, I2>,
5333    // `true` if both input streams are aliases of the same stream.
5334    is_alias: bool,
5335    labels: BTreeMap<String, String>,
5336}
5337
5338impl<C, I1, I2, Op> BinarySinkNode<C, I1, I2, Op>
5339where
5340    I1: Clone,
5341    I2: Clone,
5342    Op: BinarySinkOperator<I1, I2>,
5343    C: Circuit,
5344{
5345    fn new(
5346        operator: Op,
5347        input_stream1: Stream<C, I1>,
5348        input_stream2: Stream<C, I2>,
5349        circuit: C,
5350        id: NodeId,
5351    ) -> Self {
5352        let is_alias = input_stream1.ptr_eq(&input_stream2);
5353        Self {
5354            id: circuit.global_node_id().child(id),
5355            operator,
5356            input_stream1,
5357            input_stream2,
5358            is_alias,
5359            labels: BTreeMap::new(),
5360        }
5361    }
5362}
5363
5364impl<C, I1, I2, Op> Node for BinarySinkNode<C, I1, I2, Op>
5365where
5366    C: Circuit,
5367    I1: Clone + 'static,
5368    I2: Clone + 'static,
5369    Op: BinarySinkOperator<I1, I2>,
5370{
5371    fn name(&self) -> Cow<'static, str> {
5372        self.operator.name()
5373    }
5374
5375    fn local_id(&self) -> NodeId {
5376        self.id.local_node_id().unwrap()
5377    }
5378
5379    fn global_id(&self) -> &GlobalNodeId {
5380        &self.id
5381    }
5382
5383    fn is_async(&self) -> bool {
5384        self.operator.is_async()
5385    }
5386
5387    fn is_input(&self) -> bool {
5388        self.operator.is_input()
5389    }
5390
5391    fn ready(&self) -> bool {
5392        self.operator.ready()
5393    }
5394
5395    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
5396        self.operator.register_ready_callback(cb);
5397    }
5398
5399    // Justification: see StreamValue::take() comment.
5400    #[allow(clippy::await_holding_refcell_ref)]
5401    fn eval<'a>(
5402        &'a mut self,
5403    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
5404        Box::pin(async {
5405            if self.is_alias {
5406                {
5407                    let val1 = self.input_stream1.get();
5408                    let val2 = self.input_stream2.get();
5409                    self.operator
5410                        .eval(
5411                            Cow::Borrowed(StreamValue::peek(&val1)),
5412                            Cow::Borrowed(StreamValue::peek(&val2)),
5413                        )
5414                        .await;
5415                }
5416
5417                StreamValue::consume_token(self.input_stream1.val());
5418                StreamValue::consume_token(self.input_stream2.val());
5419            } else {
5420                let val1 = StreamValue::take(self.input_stream1.val());
5421                let val2 = StreamValue::take(self.input_stream2.val());
5422
5423                match (val1, val2) {
5424                    (Some(val1), Some(val2)) => {
5425                        self.operator.eval(Cow::Owned(val1), Cow::Owned(val2)).await;
5426                    }
5427                    (Some(val1), None) => {
5428                        self.operator
5429                            .eval(
5430                                Cow::Owned(val1),
5431                                Cow::Borrowed(StreamValue::peek(&self.input_stream2.get())),
5432                            )
5433                            .await;
5434                    }
5435                    (None, Some(val2)) => {
5436                        self.operator
5437                            .eval(
5438                                Cow::Borrowed(StreamValue::peek(&self.input_stream1.get())),
5439                                Cow::Owned(val2),
5440                            )
5441                            .await;
5442                    }
5443                    (None, None) => {
5444                        self.operator
5445                            .eval(
5446                                Cow::Borrowed(StreamValue::peek(&self.input_stream1.get())),
5447                                Cow::Borrowed(StreamValue::peek(&self.input_stream2.get())),
5448                            )
5449                            .await;
5450                    }
5451                }
5452
5453                StreamValue::consume_token(self.input_stream1.val());
5454                StreamValue::consume_token(self.input_stream2.val());
5455            };
5456
5457            Ok(self.operator.flush_progress())
5458        })
5459    }
5460
5461    fn start_transaction(&mut self) {
5462        self.operator.start_transaction();
5463    }
5464
5465    fn flush(&mut self) {
5466        self.operator.flush();
5467    }
5468
5469    fn is_flush_complete(&self) -> bool {
5470        self.operator.is_flush_complete()
5471    }
5472
5473    fn clock_start(&mut self, scope: Scope) {
5474        self.operator.clock_start(scope);
5475    }
5476
5477    fn clock_end(&mut self, scope: Scope) {
5478        self.operator.clock_end(scope);
5479    }
5480
5481    fn init(&mut self) {
5482        self.operator.init(&self.id);
5483    }
5484
5485    fn metadata(&self, output: &mut OperatorMeta) {
5486        self.operator.metadata(output);
5487    }
5488
5489    fn fixedpoint(&self, scope: Scope) -> bool {
5490        self.operator.fixedpoint(scope)
5491    }
5492
5493    fn checkpoint(
5494        &mut self,
5495        base: &StoragePath,
5496        files: &mut Vec<Arc<dyn FileCommitter>>,
5497    ) -> Result<(), DbspError> {
5498        self.operator
5499            .checkpoint(base, self.persistent_id().as_deref(), files)
5500    }
5501
5502    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
5503        self.operator.restore(base, self.persistent_id().as_deref())
5504    }
5505
5506    fn start_compaction(&mut self) {
5507        self.operator.start_compaction()
5508    }
5509
5510    fn is_compaction_complete(&self) -> bool {
5511        self.operator.is_compaction_complete()
5512    }
5513
5514    fn clear_state(&mut self) -> Result<(), DbspError> {
5515        self.operator.clear_state()
5516    }
5517
5518    fn start_replay(&mut self) -> Result<(), DbspError> {
5519        self.operator.start_replay()
5520    }
5521
5522    fn is_replay_complete(&self) -> bool {
5523        self.operator.is_replay_complete()
5524    }
5525
5526    fn end_replay(&mut self) -> Result<(), DbspError> {
5527        self.operator.end_replay()
5528    }
5529
5530    fn set_label(&mut self, key: &str, value: &str) {
5531        self.labels.insert(key.to_string(), value.to_string());
5532    }
5533
5534    fn get_label(&self, key: &str) -> Option<&str> {
5535        self.labels.get(key).map(|s| s.as_str())
5536    }
5537
5538    fn labels(&self) -> &BTreeMap<String, String> {
5539        &self.labels
5540    }
5541
5542    fn as_any(&self) -> &dyn Any {
5543        self
5544    }
5545}
5546
5547struct TernarySinkNode<C, I1, I2, I3, Op> {
5548    id: GlobalNodeId,
5549    operator: Op,
5550    input_stream1: Stream<C, I1>,
5551    input_stream2: Stream<C, I2>,
5552    input_stream3: Stream<C, I3>,
5553    labels: BTreeMap<String, String>,
5554}
5555
5556impl<C, I1, I2, I3, Op> TernarySinkNode<C, I1, I2, I3, Op>
5557where
5558    I1: Clone,
5559    I2: Clone,
5560    I3: Clone,
5561    Op: TernarySinkOperator<I1, I2, I3>,
5562    C: Circuit,
5563{
5564    fn new(
5565        operator: Op,
5566        input_stream1: Stream<C, I1>,
5567        input_stream2: Stream<C, I2>,
5568        input_stream3: Stream<C, I3>,
5569        circuit: C,
5570        id: NodeId,
5571    ) -> Self {
5572        assert!(!input_stream1.ptr_eq(&input_stream2));
5573        assert!(!input_stream1.ptr_eq(&input_stream3));
5574        assert!(!input_stream2.ptr_eq(&input_stream3));
5575
5576        Self {
5577            id: circuit.global_node_id().child(id),
5578            operator,
5579            input_stream1,
5580            input_stream2,
5581            input_stream3,
5582            labels: BTreeMap::new(),
5583        }
5584    }
5585}
5586
5587impl<C, I1, I2, I3, Op> Node for TernarySinkNode<C, I1, I2, I3, Op>
5588where
5589    C: Circuit,
5590    I1: Clone + 'static,
5591    I2: Clone + 'static,
5592    I3: Clone + 'static,
5593    Op: TernarySinkOperator<I1, I2, I3>,
5594{
5595    fn name(&self) -> Cow<'static, str> {
5596        self.operator.name()
5597    }
5598
5599    fn local_id(&self) -> NodeId {
5600        self.id.local_node_id().unwrap()
5601    }
5602
5603    fn global_id(&self) -> &GlobalNodeId {
5604        &self.id
5605    }
5606
5607    fn is_async(&self) -> bool {
5608        self.operator.is_async()
5609    }
5610
5611    fn is_input(&self) -> bool {
5612        self.operator.is_input()
5613    }
5614
5615    fn ready(&self) -> bool {
5616        self.operator.ready()
5617    }
5618
5619    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
5620        self.operator.register_ready_callback(cb);
5621    }
5622
5623    // Justification: see StreamValue::take() comment.
5624    #[allow(clippy::await_holding_refcell_ref)]
5625    fn eval<'a>(
5626        &'a mut self,
5627    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
5628        Box::pin(async {
5629            let val1 = StreamValue::take(self.input_stream1.val()).map(|val| Cow::Owned(val));
5630            let r1 = self.input_stream1.get();
5631            let val2 = StreamValue::take(self.input_stream2.val()).map(|val| Cow::Owned(val));
5632            let r2 = self.input_stream2.get();
5633            let val3 = StreamValue::take(self.input_stream3.val()).map(|val| Cow::Owned(val));
5634            let r3 = self.input_stream3.get();
5635
5636            self.operator
5637                .eval(
5638                    val1.unwrap_or_else(|| Cow::Borrowed(StreamValue::peek(&r1))),
5639                    val2.unwrap_or_else(|| Cow::Borrowed(StreamValue::peek(&r2))),
5640                    val3.unwrap_or_else(|| Cow::Borrowed(StreamValue::peek(&r3))),
5641                )
5642                .await;
5643
5644            drop(r1);
5645            drop(r2);
5646            drop(r3);
5647
5648            StreamValue::consume_token(self.input_stream1.val());
5649            StreamValue::consume_token(self.input_stream2.val());
5650            StreamValue::consume_token(self.input_stream3.val());
5651
5652            Ok(self.operator.flush_progress())
5653        })
5654    }
5655
5656    fn start_transaction(&mut self) {
5657        self.operator.start_transaction();
5658    }
5659
5660    fn flush(&mut self) {
5661        self.operator.flush();
5662    }
5663
5664    fn is_flush_complete(&self) -> bool {
5665        self.operator.is_flush_complete()
5666    }
5667
5668    fn clock_start(&mut self, scope: Scope) {
5669        self.operator.clock_start(scope);
5670    }
5671
5672    fn clock_end(&mut self, scope: Scope) {
5673        self.operator.clock_end(scope);
5674    }
5675
5676    fn init(&mut self) {
5677        self.operator.init(&self.id);
5678    }
5679
5680    fn metadata(&self, output: &mut OperatorMeta) {
5681        self.operator.metadata(output);
5682    }
5683
5684    fn fixedpoint(&self, scope: Scope) -> bool {
5685        self.operator.fixedpoint(scope)
5686    }
5687
5688    fn checkpoint(
5689        &mut self,
5690        base: &StoragePath,
5691        files: &mut Vec<Arc<dyn FileCommitter>>,
5692    ) -> Result<(), DbspError> {
5693        self.operator
5694            .checkpoint(base, self.persistent_id().as_deref(), files)
5695    }
5696
5697    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
5698        self.operator.restore(base, self.persistent_id().as_deref())
5699    }
5700
5701    fn start_compaction(&mut self) {
5702        self.operator.start_compaction()
5703    }
5704
5705    fn is_compaction_complete(&self) -> bool {
5706        self.operator.is_compaction_complete()
5707    }
5708
5709    fn clear_state(&mut self) -> Result<(), DbspError> {
5710        self.operator.clear_state()
5711    }
5712
5713    fn start_replay(&mut self) -> Result<(), DbspError> {
5714        self.operator.start_replay()
5715    }
5716
5717    fn is_replay_complete(&self) -> bool {
5718        self.operator.is_replay_complete()
5719    }
5720
5721    fn end_replay(&mut self) -> Result<(), DbspError> {
5722        self.operator.end_replay()
5723    }
5724
5725    fn set_label(&mut self, key: &str, value: &str) {
5726        self.labels.insert(key.to_string(), value.to_string());
5727    }
5728
5729    fn get_label(&self, key: &str) -> Option<&str> {
5730        self.labels.get(key).map(|s| s.as_str())
5731    }
5732
5733    fn labels(&self) -> &BTreeMap<String, String> {
5734        &self.labels
5735    }
5736
5737    fn as_any(&self) -> &dyn Any {
5738        self
5739    }
5740}
5741
5742struct BinaryNode<C, I1, I2, O, Op> {
5743    id: GlobalNodeId,
5744    operator: Op,
5745    input_stream1: Stream<C, I1>,
5746    input_stream2: Stream<C, I2>,
5747    output_stream: Stream<C, O>,
5748    // `true` if both input streams are aliases of the same stream.
5749    is_alias: bool,
5750    labels: BTreeMap<String, String>,
5751}
5752
5753impl<C, I1, I2, O, Op> BinaryNode<C, I1, I2, O, Op>
5754where
5755    Op: BinaryOperator<I1, I2, O>,
5756    C: Circuit,
5757{
5758    fn new(
5759        operator: Op,
5760        input_stream1: Stream<C, I1>,
5761        input_stream2: Stream<C, I2>,
5762        circuit: C,
5763        id: NodeId,
5764    ) -> Self {
5765        let is_alias = input_stream1.ptr_eq(&input_stream2);
5766        Self {
5767            id: circuit.global_node_id().child(id),
5768            operator,
5769            input_stream1,
5770            input_stream2,
5771            is_alias,
5772            output_stream: Stream::new(circuit, id),
5773            labels: BTreeMap::new(),
5774        }
5775    }
5776
5777    fn output_stream(&self) -> Stream<C, O> {
5778        self.output_stream.clone()
5779    }
5780}
5781
5782impl<C, I1, I2, O, Op> Node for BinaryNode<C, I1, I2, O, Op>
5783where
5784    C: Circuit,
5785    I1: Clone + 'static,
5786    I2: Clone + 'static,
5787    O: Clone + 'static,
5788    Op: BinaryOperator<I1, I2, O>,
5789{
5790    fn name(&self) -> Cow<'static, str> {
5791        self.operator.name()
5792    }
5793
5794    fn local_id(&self) -> NodeId {
5795        self.id.local_node_id().unwrap()
5796    }
5797
5798    fn global_id(&self) -> &GlobalNodeId {
5799        &self.id
5800    }
5801
5802    fn is_async(&self) -> bool {
5803        self.operator.is_async()
5804    }
5805
5806    fn is_input(&self) -> bool {
5807        self.operator.is_input()
5808    }
5809
5810    fn ready(&self) -> bool {
5811        self.operator.ready()
5812    }
5813
5814    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
5815        self.operator.register_ready_callback(cb);
5816    }
5817
5818    // Justification: see StreamValue::take() comment.
5819    #[allow(clippy::await_holding_refcell_ref)]
5820    fn eval<'a>(
5821        &'a mut self,
5822    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
5823        Box::pin(async {
5824            // If the two input streams are aliases, we cannot remove the owned
5825            // value from `input_stream2`, as this will invalidate the borrow
5826            // from `input_stream1`.  Instead use `peek` to obtain the value by
5827            // reference.
5828            if self.is_alias {
5829                {
5830                    let val1 = self.input_stream1.get();
5831                    let val2 = self.input_stream2.get();
5832
5833                    self.output_stream.put(
5834                        self.operator
5835                            .eval(StreamValue::peek(&val1), StreamValue::peek(&val2))
5836                            .await,
5837                    );
5838                }
5839                // It is now safe to call `take`, and we must do so to decrement
5840                // the ref counter.
5841                StreamValue::consume_token(self.input_stream1.val());
5842                StreamValue::consume_token(self.input_stream2.val());
5843            } else {
5844                let val1 = StreamValue::take(self.input_stream1.val());
5845                let val2 = StreamValue::take(self.input_stream2.val());
5846
5847                self.output_stream.put(match (val1, val2) {
5848                    (Some(val1), Some(val2)) => self.operator.eval_owned(val1, val2).await,
5849                    (Some(val1), None) => {
5850                        self.operator
5851                            .eval_owned_and_ref(val1, StreamValue::peek(&self.input_stream2.get()))
5852                            .await
5853                    }
5854                    (None, Some(val2)) => {
5855                        self.operator
5856                            .eval_ref_and_owned(StreamValue::peek(&self.input_stream1.get()), val2)
5857                            .await
5858                    }
5859                    (None, None) => {
5860                        self.operator
5861                            .eval(
5862                                StreamValue::peek(&self.input_stream1.get()),
5863                                StreamValue::peek(&self.input_stream2.get()),
5864                            )
5865                            .await
5866                    }
5867                });
5868                StreamValue::consume_token(self.input_stream1.val());
5869                StreamValue::consume_token(self.input_stream2.val());
5870            }
5871            Ok(self.operator.flush_progress())
5872        })
5873    }
5874
5875    fn start_transaction(&mut self) {
5876        self.operator.start_transaction();
5877    }
5878
5879    fn flush(&mut self) {
5880        self.operator.flush();
5881    }
5882
5883    fn is_flush_complete(&self) -> bool {
5884        self.operator.is_flush_complete()
5885    }
5886
5887    fn clock_start(&mut self, scope: Scope) {
5888        self.operator.clock_start(scope);
5889    }
5890
5891    fn clock_end(&mut self, scope: Scope) {
5892        self.operator.clock_end(scope);
5893    }
5894
5895    fn init(&mut self) {
5896        self.operator.init(&self.id);
5897    }
5898
5899    fn metadata(&self, output: &mut OperatorMeta) {
5900        self.operator.metadata(output);
5901    }
5902
5903    fn fixedpoint(&self, scope: Scope) -> bool {
5904        self.operator.fixedpoint(scope)
5905    }
5906
5907    fn checkpoint(
5908        &mut self,
5909        base: &StoragePath,
5910        files: &mut Vec<Arc<dyn FileCommitter>>,
5911    ) -> Result<(), DbspError> {
5912        self.operator
5913            .checkpoint(base, self.persistent_id().as_deref(), files)
5914    }
5915
5916    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
5917        self.operator.restore(base, self.persistent_id().as_deref())
5918    }
5919
5920    fn start_compaction(&mut self) {
5921        self.operator.start_compaction()
5922    }
5923
5924    fn is_compaction_complete(&self) -> bool {
5925        self.operator.is_compaction_complete()
5926    }
5927
5928    fn clear_state(&mut self) -> Result<(), DbspError> {
5929        self.operator.clear_state()
5930    }
5931
5932    fn start_replay(&mut self) -> Result<(), DbspError> {
5933        self.operator.start_replay()
5934    }
5935
5936    fn is_replay_complete(&self) -> bool {
5937        self.operator.is_replay_complete()
5938    }
5939
5940    fn end_replay(&mut self) -> Result<(), DbspError> {
5941        self.operator.end_replay()
5942    }
5943
5944    fn set_label(&mut self, key: &str, value: &str) {
5945        self.labels.insert(key.to_string(), value.to_string());
5946    }
5947
5948    fn get_label(&self, key: &str) -> Option<&str> {
5949        self.labels.get(key).map(|s| s.as_str())
5950    }
5951
5952    fn labels(&self) -> &BTreeMap<String, String> {
5953        &self.labels
5954    }
5955
5956    fn as_any(&self) -> &dyn Any {
5957        self
5958    }
5959}
5960
5961struct TernaryNode<C, I1, I2, I3, O, Op> {
5962    id: GlobalNodeId,
5963    operator: Op,
5964    input_stream1: Stream<C, I1>,
5965    input_stream2: Stream<C, I2>,
5966    input_stream3: Stream<C, I3>,
5967    output_stream: Stream<C, O>,
5968    labels: BTreeMap<String, String>,
5969}
5970
5971impl<C, I1, I2, I3, O, Op> TernaryNode<C, I1, I2, I3, O, Op>
5972where
5973    I1: Clone,
5974    I2: Clone,
5975    I3: Clone,
5976    Op: TernaryOperator<I1, I2, I3, O>,
5977    C: Circuit,
5978{
5979    fn new(
5980        operator: Op,
5981        input_stream1: Stream<C, I1>,
5982        input_stream2: Stream<C, I2>,
5983        input_stream3: Stream<C, I3>,
5984        circuit: C,
5985        id: NodeId,
5986    ) -> Self {
5987        Self {
5988            id: circuit.global_node_id().child(id),
5989            operator,
5990            input_stream1,
5991            input_stream2,
5992            input_stream3,
5993            // is_alias1,
5994            // is_alias2,
5995            output_stream: Stream::new(circuit, id),
5996            labels: BTreeMap::new(),
5997        }
5998    }
5999
6000    fn output_stream(&self) -> Stream<C, O> {
6001        self.output_stream.clone()
6002    }
6003}
6004
6005impl<C, I1, I2, I3, O, Op> Node for TernaryNode<C, I1, I2, I3, O, Op>
6006where
6007    C: Circuit,
6008    I1: Clone + 'static,
6009    I2: Clone + 'static,
6010    I3: Clone + 'static,
6011    O: Clone + 'static,
6012    Op: TernaryOperator<I1, I2, I3, O>,
6013{
6014    fn name(&self) -> Cow<'static, str> {
6015        self.operator.name()
6016    }
6017
6018    fn local_id(&self) -> NodeId {
6019        self.id.local_node_id().unwrap()
6020    }
6021
6022    fn global_id(&self) -> &GlobalNodeId {
6023        &self.id
6024    }
6025
6026    fn is_async(&self) -> bool {
6027        self.operator.is_async()
6028    }
6029
6030    fn is_input(&self) -> bool {
6031        self.operator.is_input()
6032    }
6033
6034    fn ready(&self) -> bool {
6035        self.operator.ready()
6036    }
6037
6038    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
6039        self.operator.register_ready_callback(cb);
6040    }
6041
6042    // Justification: see StreamValue::take() comment.
6043    #[allow(clippy::await_holding_refcell_ref)]
6044    fn eval<'a>(
6045        &'a mut self,
6046    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
6047        Box::pin(async {
6048            {
6049                self.output_stream.put(
6050                    self.operator
6051                        .eval(
6052                            Cow::Borrowed(StreamValue::peek(&self.input_stream1.get())),
6053                            Cow::Borrowed(StreamValue::peek(&self.input_stream2.get())),
6054                            Cow::Borrowed(StreamValue::peek(&self.input_stream3.get())),
6055                        )
6056                        .await,
6057                );
6058            }
6059
6060            StreamValue::consume_token(self.input_stream1.val());
6061            StreamValue::consume_token(self.input_stream2.val());
6062            StreamValue::consume_token(self.input_stream3.val());
6063
6064            Ok(self.operator.flush_progress())
6065        })
6066    }
6067
6068    fn start_transaction(&mut self) {
6069        self.operator.start_transaction();
6070    }
6071
6072    fn flush(&mut self) {
6073        self.operator.flush();
6074    }
6075
6076    fn is_flush_complete(&self) -> bool {
6077        self.operator.is_flush_complete()
6078    }
6079
6080    fn clock_start(&mut self, scope: Scope) {
6081        self.operator.clock_start(scope);
6082    }
6083
6084    fn clock_end(&mut self, scope: Scope) {
6085        self.operator.clock_end(scope);
6086    }
6087
6088    fn init(&mut self) {
6089        self.operator.init(&self.id);
6090    }
6091
6092    fn metadata(&self, output: &mut OperatorMeta) {
6093        self.operator.metadata(output);
6094    }
6095
6096    fn fixedpoint(&self, scope: Scope) -> bool {
6097        self.operator.fixedpoint(scope)
6098    }
6099
6100    fn checkpoint(
6101        &mut self,
6102        base: &StoragePath,
6103        files: &mut Vec<Arc<dyn FileCommitter>>,
6104    ) -> Result<(), DbspError> {
6105        self.operator
6106            .checkpoint(base, self.persistent_id().as_deref(), files)
6107    }
6108
6109    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
6110        self.operator.restore(base, self.persistent_id().as_deref())
6111    }
6112
6113    fn start_compaction(&mut self) {
6114        self.operator.start_compaction()
6115    }
6116
6117    fn is_compaction_complete(&self) -> bool {
6118        self.operator.is_compaction_complete()
6119    }
6120
6121    fn clear_state(&mut self) -> Result<(), DbspError> {
6122        self.operator.clear_state()
6123    }
6124
6125    fn start_replay(&mut self) -> Result<(), DbspError> {
6126        self.operator.start_replay()
6127    }
6128
6129    fn is_replay_complete(&self) -> bool {
6130        self.operator.is_replay_complete()
6131    }
6132
6133    fn end_replay(&mut self) -> Result<(), DbspError> {
6134        self.operator.end_replay()
6135    }
6136
6137    fn set_label(&mut self, key: &str, value: &str) {
6138        self.labels.insert(key.to_string(), value.to_string());
6139    }
6140
6141    fn get_label(&self, key: &str) -> Option<&str> {
6142        self.labels.get(key).map(|s| s.as_str())
6143    }
6144
6145    fn labels(&self) -> &BTreeMap<String, String> {
6146        &self.labels
6147    }
6148
6149    fn as_any(&self) -> &dyn Any {
6150        self
6151    }
6152}
6153
6154struct QuaternaryNode<C, I1, I2, I3, I4, O, Op> {
6155    id: GlobalNodeId,
6156    operator: Op,
6157    input_stream1: Stream<C, I1>,
6158    input_stream2: Stream<C, I2>,
6159    input_stream3: Stream<C, I3>,
6160    input_stream4: Stream<C, I4>,
6161    output_stream: Stream<C, O>,
6162    labels: BTreeMap<String, String>,
6163    // // `true` if `input_stream1` is an alias to `input_stream2`, `input_stream3` or
6164    // // `input_stream4`.
6165    // is_alias1: bool,
6166    // // `true` if `input_stream2` is an alias to `input_stream3` or `input_stream4`.
6167    // is_alias2: bool,
6168    // // `true` if `input_stream3` is an alias to `input_stream4`.
6169    // is_alias3: bool,
6170}
6171
6172impl<C, I1, I2, I3, I4, O, Op> QuaternaryNode<C, I1, I2, I3, I4, O, Op>
6173where
6174    I1: Clone,
6175    I2: Clone,
6176    I3: Clone,
6177    I4: Clone,
6178    Op: QuaternaryOperator<I1, I2, I3, I4, O>,
6179    C: Circuit,
6180{
6181    fn new(
6182        operator: Op,
6183        input_stream1: Stream<C, I1>,
6184        input_stream2: Stream<C, I2>,
6185        input_stream3: Stream<C, I3>,
6186        input_stream4: Stream<C, I4>,
6187        circuit: C,
6188        id: NodeId,
6189    ) -> Self {
6190        // let is_alias1 = input_stream1.ptr_eq(&input_stream2)
6191        //     || input_stream1.ptr_eq(&input_stream3)
6192        //     || input_stream1.ptr_eq(&input_stream4);
6193        // let is_alias2 =
6194        //     input_stream2.ptr_eq(&input_stream3) || input_stream2.ptr_eq(&input_stream4);
6195        // let is_alias3 = input_stream3.ptr_eq(&input_stream4);
6196        Self {
6197            id: circuit.global_node_id().child(id),
6198            operator,
6199            input_stream1,
6200            input_stream2,
6201            input_stream3,
6202            input_stream4,
6203            // is_alias1,
6204            // is_alias2,
6205            // is_alias3,
6206            output_stream: Stream::new(circuit, id),
6207            labels: BTreeMap::new(),
6208        }
6209    }
6210
6211    fn output_stream(&self) -> Stream<C, O> {
6212        self.output_stream.clone()
6213    }
6214}
6215
6216impl<C, I1, I2, I3, I4, O, Op> Node for QuaternaryNode<C, I1, I2, I3, I4, O, Op>
6217where
6218    C: Circuit,
6219    I1: Clone + 'static,
6220    I2: Clone + 'static,
6221    I3: Clone + 'static,
6222    I4: Clone + 'static,
6223    O: Clone + 'static,
6224    Op: QuaternaryOperator<I1, I2, I3, I4, O>,
6225{
6226    fn name(&self) -> Cow<'static, str> {
6227        self.operator.name()
6228    }
6229
6230    fn local_id(&self) -> NodeId {
6231        self.id.local_node_id().unwrap()
6232    }
6233
6234    fn global_id(&self) -> &GlobalNodeId {
6235        &self.id
6236    }
6237
6238    fn is_async(&self) -> bool {
6239        self.operator.is_async()
6240    }
6241
6242    fn is_input(&self) -> bool {
6243        self.operator.is_input()
6244    }
6245
6246    fn ready(&self) -> bool {
6247        self.operator.ready()
6248    }
6249
6250    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
6251        self.operator.register_ready_callback(cb);
6252    }
6253
6254    // Justification: see StreamValue::take() comment.
6255    #[allow(clippy::await_holding_refcell_ref)]
6256    fn eval<'a>(
6257        &'a mut self,
6258    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
6259        Box::pin(async {
6260            {
6261                self.output_stream.put(
6262                    self.operator
6263                        .eval(
6264                            Cow::Borrowed(StreamValue::peek(&self.input_stream1.get())),
6265                            Cow::Borrowed(StreamValue::peek(&self.input_stream2.get())),
6266                            Cow::Borrowed(StreamValue::peek(&self.input_stream3.get())),
6267                            Cow::Borrowed(StreamValue::peek(&self.input_stream4.get())),
6268                        )
6269                        .await,
6270                );
6271            }
6272
6273            StreamValue::consume_token(self.input_stream1.val());
6274            StreamValue::consume_token(self.input_stream2.val());
6275            StreamValue::consume_token(self.input_stream3.val());
6276            StreamValue::consume_token(self.input_stream4.val());
6277
6278            Ok(self.operator.flush_progress())
6279        })
6280    }
6281
6282    fn start_transaction(&mut self) {
6283        self.operator.start_transaction();
6284    }
6285
6286    fn flush(&mut self) {
6287        self.operator.flush();
6288    }
6289
6290    fn is_flush_complete(&self) -> bool {
6291        self.operator.is_flush_complete()
6292    }
6293
6294    fn clock_start(&mut self, scope: Scope) {
6295        self.operator.clock_start(scope);
6296    }
6297
6298    fn clock_end(&mut self, scope: Scope) {
6299        self.operator.clock_end(scope);
6300    }
6301
6302    fn init(&mut self) {
6303        self.operator.init(&self.id);
6304    }
6305
6306    fn metadata(&self, output: &mut OperatorMeta) {
6307        self.operator.metadata(output);
6308    }
6309
6310    fn fixedpoint(&self, scope: Scope) -> bool {
6311        self.operator.fixedpoint(scope)
6312    }
6313
6314    fn checkpoint(
6315        &mut self,
6316        base: &StoragePath,
6317        files: &mut Vec<Arc<dyn FileCommitter>>,
6318    ) -> Result<(), DbspError> {
6319        self.operator
6320            .checkpoint(base, self.persistent_id().as_deref(), files)
6321    }
6322
6323    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
6324        self.operator.restore(base, self.persistent_id().as_deref())
6325    }
6326
6327    fn start_compaction(&mut self) {
6328        self.operator.start_compaction()
6329    }
6330
6331    fn is_compaction_complete(&self) -> bool {
6332        self.operator.is_compaction_complete()
6333    }
6334
6335    fn clear_state(&mut self) -> Result<(), DbspError> {
6336        self.operator.clear_state()
6337    }
6338
6339    fn start_replay(&mut self) -> Result<(), DbspError> {
6340        self.operator.start_replay()
6341    }
6342
6343    fn is_replay_complete(&self) -> bool {
6344        self.operator.is_replay_complete()
6345    }
6346
6347    fn end_replay(&mut self) -> Result<(), DbspError> {
6348        self.operator.end_replay()
6349    }
6350
6351    fn set_label(&mut self, key: &str, value: &str) {
6352        self.labels.insert(key.to_string(), value.to_string());
6353    }
6354
6355    fn get_label(&self, key: &str) -> Option<&str> {
6356        self.labels.get(key).map(|s| s.as_str())
6357    }
6358
6359    fn labels(&self) -> &BTreeMap<String, String> {
6360        &self.labels
6361    }
6362
6363    fn as_any(&self) -> &dyn Any {
6364        self
6365    }
6366}
6367
6368struct NaryNode<C, I, O, Op>
6369where
6370    I: Clone + 'static,
6371{
6372    id: GlobalNodeId,
6373    operator: Op,
6374    // The second field of the tuple indicates if the stream is an
6375    // alias to an earlier stream.
6376    input_streams: Vec<Stream<C, I>>,
6377    // // Streams that are aliases.
6378    // aliases: Vec<usize>,
6379    output_stream: Stream<C, O>,
6380    labels: BTreeMap<String, String>,
6381}
6382
6383impl<C, I, O, Op> NaryNode<C, I, O, Op>
6384where
6385    I: Clone + 'static,
6386    Op: NaryOperator<I, O>,
6387    C: Circuit,
6388{
6389    fn new<Iter>(operator: Op, input_streams: Iter, circuit: C, id: NodeId) -> Self
6390    where
6391        Iter: IntoIterator<Item = Stream<C, I>>,
6392    {
6393        let mut input_streams: Vec<_> = input_streams.into_iter().collect();
6394        // let mut aliases = Vec::new();
6395        // for i in 0..input_streams.len() {
6396        //     for j in 0..i {
6397        //         if input_streams[i].0.ptr_eq(&input_streams[j].0) {
6398        //             input_streams[i].1 = true;
6399        //             aliases.push(i);
6400        //             break;
6401        //         }
6402        //     }
6403        // }
6404        //aliases.shrink_to_fit();
6405        input_streams.shrink_to_fit();
6406        Self {
6407            id: circuit.global_node_id().child(id),
6408            operator,
6409            input_streams,
6410            //aliases,
6411            output_stream: Stream::new(circuit, id),
6412            labels: BTreeMap::new(),
6413        }
6414    }
6415
6416    fn output_stream(&self) -> Stream<C, O> {
6417        self.output_stream.clone()
6418    }
6419}
6420
6421impl<C, I, O, Op> Node for NaryNode<C, I, O, Op>
6422where
6423    C: Circuit,
6424    I: Clone,
6425    O: Clone + 'static,
6426    Op: NaryOperator<I, O>,
6427{
6428    fn name(&self) -> Cow<'static, str> {
6429        self.operator.name()
6430    }
6431
6432    fn local_id(&self) -> NodeId {
6433        self.id.local_node_id().unwrap()
6434    }
6435
6436    fn global_id(&self) -> &GlobalNodeId {
6437        &self.id
6438    }
6439
6440    fn is_async(&self) -> bool {
6441        self.operator.is_async()
6442    }
6443
6444    fn is_input(&self) -> bool {
6445        self.operator.is_input()
6446    }
6447
6448    fn ready(&self) -> bool {
6449        self.operator.ready()
6450    }
6451
6452    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
6453        self.operator.register_ready_callback(cb);
6454    }
6455
6456    fn eval<'a>(
6457        &'a mut self,
6458    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
6459        Box::pin(async {
6460            let refs = self
6461                .input_streams
6462                .iter()
6463                .map(|stream| stream.get())
6464                .collect::<Vec<_>>();
6465
6466            self.output_stream.put(
6467                self.operator
6468                    .eval(refs.iter().map(|r| Cow::Borrowed(StreamValue::peek(r))))
6469                    .await,
6470            );
6471
6472            std::mem::drop(refs);
6473
6474            for i in self.input_streams.iter() {
6475                StreamValue::consume_token(i.val());
6476            }
6477            Ok(self.operator.flush_progress())
6478        })
6479    }
6480
6481    fn start_transaction(&mut self) {
6482        self.operator.start_transaction();
6483    }
6484
6485    fn flush(&mut self) {
6486        self.operator.flush();
6487    }
6488
6489    fn is_flush_complete(&self) -> bool {
6490        self.operator.is_flush_complete()
6491    }
6492
6493    fn clock_start(&mut self, scope: Scope) {
6494        self.operator.clock_start(scope);
6495    }
6496
6497    fn clock_end(&mut self, scope: Scope) {
6498        self.operator.clock_end(scope);
6499    }
6500
6501    fn init(&mut self) {
6502        self.operator.init(&self.id);
6503    }
6504
6505    fn metadata(&self, output: &mut OperatorMeta) {
6506        self.operator.metadata(output);
6507    }
6508
6509    fn fixedpoint(&self, scope: Scope) -> bool {
6510        self.operator.fixedpoint(scope)
6511    }
6512
6513    fn checkpoint(
6514        &mut self,
6515        base: &StoragePath,
6516        files: &mut Vec<Arc<dyn FileCommitter>>,
6517    ) -> Result<(), DbspError> {
6518        self.operator
6519            .checkpoint(base, self.persistent_id().as_deref(), files)
6520    }
6521
6522    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
6523        self.operator.restore(base, self.persistent_id().as_deref())
6524    }
6525
6526    fn start_compaction(&mut self) {
6527        self.operator.start_compaction()
6528    }
6529
6530    fn is_compaction_complete(&self) -> bool {
6531        self.operator.is_compaction_complete()
6532    }
6533
6534    fn clear_state(&mut self) -> Result<(), DbspError> {
6535        self.operator.clear_state()
6536    }
6537
6538    fn start_replay(&mut self) -> Result<(), DbspError> {
6539        self.operator.start_replay()
6540    }
6541
6542    fn is_replay_complete(&self) -> bool {
6543        self.operator.is_replay_complete()
6544    }
6545
6546    fn end_replay(&mut self) -> Result<(), DbspError> {
6547        self.operator.end_replay()
6548    }
6549
6550    fn set_label(&mut self, key: &str, value: &str) {
6551        self.labels.insert(key.to_string(), value.to_string());
6552    }
6553
6554    fn get_label(&self, key: &str) -> Option<&str> {
6555        self.labels.get(key).map(|s| s.as_str())
6556    }
6557
6558    fn labels(&self) -> &BTreeMap<String, String> {
6559        &self.labels
6560    }
6561
6562    fn as_any(&self) -> &dyn Any {
6563        self
6564    }
6565}
6566
6567// The output half of a feedback node.  We implement a feedback node using a
6568// pair of nodes: `FeedbackOutputNode` is connected to the circuit as a source
6569// node (i.e., it does not have an input stream) and thus gets evaluated first
6570// in each time stamp.  `FeedbackInputNode` is a sink node.  This way the
6571// circuit graph remains acyclic and can be scheduled in a topological order.
6572struct FeedbackOutputNode<C, I, O, Op>
6573where
6574    C: Circuit,
6575{
6576    id: GlobalNodeId,
6577    operator: Rc<RefCell<Op>>,
6578    output_stream: Stream<C, O>,
6579    export_stream: Option<Stream<C::Parent, O>>,
6580    phantom_input: PhantomData<I>,
6581    labels: BTreeMap<String, String>,
6582}
6583
6584impl<C, I, O, Op> FeedbackOutputNode<C, I, O, Op>
6585where
6586    C: Circuit,
6587    Op: StrictUnaryOperator<I, O>,
6588{
6589    fn new(operator: Rc<RefCell<Op>>, circuit: C, id: NodeId) -> Self {
6590        Self {
6591            id: circuit.global_node_id().child(id),
6592            operator,
6593            output_stream: Stream::new(circuit.clone(), id),
6594            export_stream: None,
6595            phantom_input: PhantomData,
6596            labels: BTreeMap::new(),
6597        }
6598    }
6599
6600    fn with_export(operator: Rc<RefCell<Op>>, circuit: C, id: NodeId) -> Self {
6601        let mut result = Self::new(operator, circuit.clone(), id);
6602        result.export_stream = Some(Stream::with_origin(
6603            circuit.parent(),
6604            circuit.allocate_stream_id(),
6605            circuit.node_id(),
6606            GlobalNodeId::child_of(&circuit, id),
6607        ));
6608        result
6609    }
6610
6611    fn output_stream(&self) -> Stream<C, O> {
6612        self.output_stream.clone()
6613    }
6614}
6615
6616impl<C, I, O, Op> Node for FeedbackOutputNode<C, I, O, Op>
6617where
6618    C: Circuit,
6619    I: Data,
6620    O: Clone + 'static,
6621    Op: StrictUnaryOperator<I, O>,
6622{
6623    fn local_id(&self) -> NodeId {
6624        self.id.local_node_id().unwrap()
6625    }
6626
6627    fn global_id(&self) -> &GlobalNodeId {
6628        &self.id
6629    }
6630
6631    fn name(&self) -> Cow<'static, str> {
6632        self.operator.borrow().name()
6633    }
6634
6635    fn is_async(&self) -> bool {
6636        self.operator.borrow().is_async()
6637    }
6638
6639    fn is_input(&self) -> bool {
6640        self.operator.borrow().is_input()
6641    }
6642
6643    fn ready(&self) -> bool {
6644        self.operator.borrow().ready()
6645    }
6646
6647    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
6648        self.operator.borrow_mut().register_ready_callback(cb);
6649    }
6650
6651    fn eval<'a>(
6652        &'a mut self,
6653    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
6654        Box::pin(async {
6655            self.output_stream
6656                .put(self.operator.borrow_mut().get_output());
6657            Ok(None)
6658        })
6659    }
6660
6661    fn start_transaction(&mut self) {
6662        self.operator.borrow_mut().start_transaction();
6663    }
6664
6665    fn flush(&mut self) {
6666        self.operator.borrow_mut().flush();
6667    }
6668
6669    fn is_flush_complete(&self) -> bool {
6670        self.operator.borrow().is_flush_complete()
6671    }
6672
6673    fn clock_start(&mut self, scope: Scope) {
6674        self.operator.borrow_mut().clock_start(scope)
6675    }
6676
6677    fn clock_end(&mut self, scope: Scope) {
6678        if scope == 0
6679            && let Some(export_stream) = &mut self.export_stream
6680        {
6681            export_stream.put(self.operator.borrow_mut().get_final_output());
6682        }
6683        self.operator.borrow_mut().clock_end(scope);
6684    }
6685
6686    fn init(&mut self) {
6687        self.operator.borrow_mut().init(&self.id);
6688    }
6689
6690    fn metadata(&self, _output: &mut OperatorMeta) {
6691        // Avoid producing duplicate metadata for input and output parts of the operator;
6692        // otherwise it will be double-counted in circuit-level metrics.
6693    }
6694
6695    fn fixedpoint(&self, scope: Scope) -> bool {
6696        self.operator.borrow().fixedpoint(scope)
6697    }
6698
6699    fn checkpoint(
6700        &mut self,
6701        base: &StoragePath,
6702        files: &mut Vec<Arc<dyn FileCommitter>>,
6703    ) -> Result<(), DbspError> {
6704        self.operator
6705            .borrow_mut()
6706            .checkpoint(base, self.persistent_id().as_deref(), files)
6707    }
6708
6709    fn restore(&mut self, base: &StoragePath) -> Result<(), DbspError> {
6710        self.operator
6711            .borrow_mut()
6712            .restore(base, self.persistent_id().as_deref())
6713    }
6714
6715    fn start_compaction(&mut self) {
6716        self.operator.borrow_mut().start_compaction()
6717    }
6718
6719    fn is_compaction_complete(&self) -> bool {
6720        self.operator.borrow().is_compaction_complete()
6721    }
6722
6723    fn clear_state(&mut self) -> Result<(), DbspError> {
6724        self.operator.borrow_mut().clear_state()
6725    }
6726
6727    fn start_replay(&mut self) -> Result<(), DbspError> {
6728        self.operator.borrow_mut().start_replay()
6729    }
6730
6731    fn is_replay_complete(&self) -> bool {
6732        self.operator.borrow().is_replay_complete()
6733    }
6734
6735    fn end_replay(&mut self) -> Result<(), DbspError> {
6736        self.operator.borrow_mut().end_replay()
6737    }
6738
6739    fn set_label(&mut self, key: &str, value: &str) {
6740        self.labels.insert(key.to_string(), value.to_string());
6741    }
6742
6743    fn get_label(&self, key: &str) -> Option<&str> {
6744        self.labels.get(key).map(|s| s.as_str())
6745    }
6746
6747    fn labels(&self) -> &BTreeMap<String, String> {
6748        &self.labels
6749    }
6750
6751    fn as_any(&self) -> &dyn Any {
6752        self
6753    }
6754}
6755
6756/// The input half of a feedback node
6757struct FeedbackInputNode<C, I, O, Op> {
6758    // Id of this node (the input half).
6759    id: GlobalNodeId,
6760    operator: Rc<RefCell<Op>>,
6761    input_stream: Stream<C, I>,
6762    phantom_output: PhantomData<O>,
6763    labels: BTreeMap<String, String>,
6764}
6765
6766impl<C, I, O, Op> FeedbackInputNode<C, I, O, Op>
6767where
6768    Op: StrictUnaryOperator<I, O>,
6769    C: Circuit,
6770{
6771    fn new(operator: Rc<RefCell<Op>>, input_stream: Stream<C, I>, id: NodeId) -> Self {
6772        Self {
6773            id: input_stream.circuit().global_node_id().child(id),
6774            operator,
6775            input_stream,
6776            phantom_output: PhantomData,
6777            labels: BTreeMap::new(),
6778        }
6779    }
6780}
6781
6782impl<C, I, O, Op> Node for FeedbackInputNode<C, I, O, Op>
6783where
6784    Op: StrictUnaryOperator<I, O>,
6785    I: Data,
6786    O: 'static,
6787    C: Clone + 'static,
6788{
6789    fn name(&self) -> Cow<'static, str> {
6790        self.operator.borrow().name()
6791    }
6792
6793    fn local_id(&self) -> NodeId {
6794        self.id.local_node_id().unwrap()
6795    }
6796
6797    fn global_id(&self) -> &GlobalNodeId {
6798        &self.id
6799    }
6800
6801    fn is_async(&self) -> bool {
6802        self.operator.borrow().is_async()
6803    }
6804
6805    fn is_input(&self) -> bool {
6806        self.operator.borrow().is_input()
6807    }
6808
6809    fn ready(&self) -> bool {
6810        self.operator.borrow().ready()
6811    }
6812
6813    fn register_ready_callback(&mut self, cb: Box<dyn Fn() + Send + Sync>) {
6814        self.operator.borrow_mut().register_ready_callback(cb);
6815    }
6816
6817    // Justification: see StreamValue::take() comment.
6818    #[allow(clippy::await_holding_refcell_ref)]
6819    fn eval<'a>(
6820        &'a mut self,
6821    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
6822        Box::pin(async {
6823            match StreamValue::take(self.input_stream.val()) {
6824                Some(v) => self.operator.borrow_mut().eval_strict_owned(v).await,
6825                None => {
6826                    self.operator
6827                        .borrow_mut()
6828                        .eval_strict(StreamValue::peek(&self.input_stream.get()))
6829                        .await
6830                }
6831            };
6832
6833            StreamValue::consume_token(self.input_stream.val());
6834
6835            Ok(None)
6836        })
6837    }
6838
6839    fn start_transaction(&mut self) {
6840        self.operator.borrow_mut().start_transaction();
6841    }
6842
6843    fn flush(&mut self) {
6844        self.operator.borrow_mut().flush_input();
6845    }
6846
6847    fn is_flush_complete(&self) -> bool {
6848        self.operator.borrow().is_flush_input_complete()
6849    }
6850
6851    // Don't call `clock_start`/`clock_end` on the operator.  `FeedbackOutputNode`
6852    // will do that.
6853    fn clock_start(&mut self, _scope: Scope) {}
6854
6855    fn clock_end(&mut self, _scope: Scope) {}
6856
6857    fn init(&mut self) {
6858        self.operator.borrow_mut().init(&self.id);
6859    }
6860
6861    fn metadata(&self, output: &mut OperatorMeta) {
6862        self.operator.borrow().metadata(output)
6863    }
6864
6865    fn fixedpoint(&self, scope: Scope) -> bool {
6866        self.operator.borrow().fixedpoint(scope)
6867    }
6868
6869    fn checkpoint(
6870        &mut self,
6871        _base: &StoragePath,
6872        _files: &mut Vec<Arc<dyn FileCommitter>>,
6873    ) -> Result<(), DbspError> {
6874        // The Z-1 operator consists of two logical parts.
6875        // The first part gets invoked at the start of a clock cycle to retrieve the
6876        // state stored at the previous clock tick. The second one gets invoked
6877        // to store the updated state inside the operator. We only want to
6878        // invoke commit on one of them, doesn't matter which (so we
6879        // do it in FeedbackOutputNode)
6880        Ok(())
6881    }
6882
6883    fn restore(&mut self, _base: &StoragePath) -> Result<(), DbspError> {
6884        // See comment in `commit`.
6885        Ok(())
6886    }
6887
6888    fn start_compaction(&mut self) {}
6889
6890    fn is_compaction_complete(&self) -> bool {
6891        true
6892    }
6893
6894    fn clear_state(&mut self) -> Result<(), DbspError> {
6895        Ok(())
6896    }
6897
6898    fn start_replay(&mut self) -> Result<(), DbspError> {
6899        self.operator.borrow_mut().start_replay()
6900    }
6901
6902    fn is_replay_complete(&self) -> bool {
6903        self.operator.borrow().is_replay_complete()
6904    }
6905
6906    fn end_replay(&mut self) -> Result<(), DbspError> {
6907        self.operator.borrow_mut().end_replay()
6908    }
6909
6910    fn set_label(&mut self, key: &str, value: &str) {
6911        self.labels.insert(key.to_string(), value.to_string());
6912    }
6913
6914    fn get_label(&self, key: &str) -> Option<&str> {
6915        self.labels.get(key).map(|s| s.as_str())
6916    }
6917
6918    fn labels(&self) -> &BTreeMap<String, String> {
6919        &self.labels
6920    }
6921
6922    fn as_any(&self) -> &dyn Any {
6923        self
6924    }
6925}
6926
6927/// Input connector of a feedback operator.
6928///
6929/// This struct is part of the mechanism for constructing a feedback loop in a
6930/// circuit. It is returned by [`Circuit::add_feedback`] and represents the
6931/// input port of an operator whose input stream does not exist yet.  Once the
6932/// input stream has been created, it can be connected to the operator using
6933/// [`FeedbackConnector::connect`]. See [`Circuit::add_feedback`] for details.
6934pub struct FeedbackConnector<C, I, O, Op> {
6935    output_node_id: NodeId,
6936    circuit: C,
6937    operator: Rc<RefCell<Op>>,
6938    phantom_input: PhantomData<I>,
6939    phantom_output: PhantomData<O>,
6940}
6941
6942impl<C, I, O, Op> FeedbackConnector<C, I, O, Op>
6943where
6944    Op: StrictUnaryOperator<I, O>,
6945{
6946    fn new(output_node_id: NodeId, circuit: C, operator: Rc<RefCell<Op>>) -> Self {
6947        Self {
6948            output_node_id,
6949            circuit,
6950            operator,
6951            phantom_input: PhantomData,
6952            phantom_output: PhantomData,
6953        }
6954    }
6955}
6956
6957impl<C, I, O, Op> FeedbackConnector<C, I, O, Op>
6958where
6959    Op: StrictUnaryOperator<I, O>,
6960    I: Data,
6961    O: Data,
6962    C: Circuit,
6963{
6964    pub fn operator_mut(&self) -> RefMut<'_, Op> {
6965        self.operator.borrow_mut()
6966    }
6967
6968    /// Connect `input_stream` as input to the operator.
6969    ///
6970    /// See [`Circuit::add_feedback`] for details.
6971    /// Returns node id of the input node.
6972    pub fn connect(self, input_stream: &Stream<C, I>) {
6973        self.connect_with_preference(input_stream, OwnershipPreference::INDIFFERENT)
6974    }
6975
6976    pub fn connect_with_preference(
6977        self,
6978        input_stream: &Stream<C, I>,
6979        input_preference: OwnershipPreference,
6980    ) {
6981        self.circuit.connect_feedback_with_preference(
6982            self.output_node_id,
6983            self.operator,
6984            input_stream,
6985            input_preference,
6986        )
6987    }
6988}
6989
6990// A nested circuit instantiated as a node in a parent circuit.
6991struct ChildNode<C>
6992where
6993    C: Circuit,
6994{
6995    id: GlobalNodeId,
6996    circuit: C,
6997    executor: Box<dyn Executor<C>>,
6998    labels: BTreeMap<String, String>,
6999    nesting_depth: Scope,
7000}
7001
7002impl<C> Drop for ChildNode<C>
7003where
7004    C: Circuit,
7005{
7006    fn drop(&mut self) {
7007        // Explicitly deallocate all nodes in the circuit to break
7008        // cyclic `Rc` references between circuits and streams.
7009        self.circuit.clear();
7010    }
7011}
7012
7013impl<C> ChildNode<C>
7014where
7015    C: Circuit,
7016{
7017    fn new<E>(circuit: C, nesting_depth: Scope, executor: E) -> Self
7018    where
7019        E: Executor<C>,
7020    {
7021        Self {
7022            id: circuit.global_node_id(),
7023            circuit,
7024            executor: Box::new(executor) as Box<dyn Executor<C>>,
7025            labels: BTreeMap::new(),
7026            nesting_depth,
7027        }
7028    }
7029}
7030
7031impl<C> Node for ChildNode<C>
7032where
7033    C: Circuit,
7034{
7035    fn name(&self) -> Cow<'static, str> {
7036        Cow::Borrowed("Subcircuit")
7037    }
7038
7039    fn local_id(&self) -> NodeId {
7040        self.id.local_node_id().unwrap()
7041    }
7042
7043    fn global_id(&self) -> &GlobalNodeId {
7044        &self.id
7045    }
7046
7047    fn is_circuit(&self) -> bool {
7048        true
7049    }
7050
7051    fn is_async(&self) -> bool {
7052        false
7053    }
7054
7055    fn is_input(&self) -> bool {
7056        false
7057    }
7058
7059    fn ready(&self) -> bool {
7060        true
7061    }
7062
7063    fn eval<'a>(
7064        &'a mut self,
7065    ) -> Pin<Box<dyn Future<Output = Result<Option<Position>, SchedulerError>> + 'a>> {
7066        Box::pin(async {
7067            // We may want to make the executor responsible for evaluating import nodes
7068            // if there is a need for customizing this behavior.
7069            for node_id in self.circuit.import_nodes() {
7070                self.circuit.eval_import_node(node_id).await;
7071            }
7072            self.executor.transaction(&self.circuit).await?;
7073            Ok(None)
7074        })
7075    }
7076
7077    fn start_transaction(&mut self) {
7078        // Nested circuit has its own transactions.
7079    }
7080
7081    fn flush(&mut self) {
7082        self.executor.flush();
7083    }
7084
7085    fn is_flush_complete(&self) -> bool {
7086        self.executor.is_flush_complete()
7087    }
7088
7089    fn clock_start(&mut self, scope: Scope) {
7090        self.circuit.clock_start(scope + self.nesting_depth);
7091    }
7092
7093    fn clock_end(&mut self, scope: Scope) {
7094        self.circuit.clock_end(scope + self.nesting_depth);
7095    }
7096
7097    fn metadata(&self, _meta: &mut OperatorMeta) {}
7098
7099    fn fixedpoint(&self, scope: Scope) -> bool {
7100        self.circuit.check_fixedpoint(scope + self.nesting_depth)
7101    }
7102
7103    fn map_nodes_recursive(
7104        &self,
7105        f: &mut dyn FnMut(&dyn Node) -> Result<(), DbspError>,
7106    ) -> Result<(), DbspError> {
7107        self.circuit.map_nodes_recursive(f)
7108    }
7109
7110    fn checkpoint(
7111        &mut self,
7112        _base: &StoragePath,
7113        _files: &mut Vec<Arc<dyn FileCommitter>>,
7114    ) -> Result<(), DbspError> {
7115        Ok(())
7116    }
7117
7118    fn restore(&mut self, _base: &StoragePath) -> Result<(), DbspError> {
7119        Ok(())
7120    }
7121
7122    fn start_compaction(&mut self) {
7123        self.circuit.start_compaction();
7124    }
7125
7126    fn is_compaction_complete(&self) -> bool {
7127        self.circuit.is_compaction_complete()
7128    }
7129
7130    fn clear_state(&mut self) -> Result<(), DbspError> {
7131        self.circuit
7132            .map_local_nodes_mut(&mut |node| node.clear_state())
7133    }
7134
7135    fn start_replay(&mut self) -> Result<(), DbspError> {
7136        Ok(())
7137    }
7138
7139    fn is_replay_complete(&self) -> bool {
7140        true
7141    }
7142
7143    fn end_replay(&mut self) -> Result<(), DbspError> {
7144        Ok(())
7145    }
7146
7147    fn set_label(&mut self, key: &str, value: &str) {
7148        self.labels.insert(key.to_string(), value.to_string());
7149    }
7150
7151    fn get_label(&self, key: &str) -> Option<&str> {
7152        self.labels.get(key).map(|s| s.as_str())
7153    }
7154
7155    fn labels(&self) -> &BTreeMap<String, String> {
7156        &self.labels
7157    }
7158
7159    fn map_child(&self, path: &[NodeId], f: &mut dyn FnMut(&dyn Node)) {
7160        self.circuit.map_node_relative(path, f);
7161    }
7162
7163    fn map_child_mut(&self, path: &[NodeId], f: &mut dyn FnMut(&mut dyn Node)) {
7164        self.circuit.map_node_mut_relative(path, f);
7165    }
7166
7167    fn as_any(&self) -> &dyn Any {
7168        self
7169    }
7170
7171    fn as_circuit(&self) -> Option<&dyn CircuitBase> {
7172        Some(&self.circuit)
7173    }
7174}
7175
7176/// Top-level circuit with executor.
7177///
7178/// This is the interface to a circuit created with [`RootCircuit::build`].
7179/// Call [`CircuitHandle::transaction`] to run the circuit in the context of the
7180/// current thread.
7181pub struct CircuitHandle {
7182    circuit: RootCircuit,
7183    executor: Box<dyn Executor<RootCircuit>>,
7184    tokio_runtime: TokioRuntime,
7185    replay_info: Option<BootstrapInfo>,
7186}
7187
7188impl Drop for CircuitHandle {
7189    fn drop(&mut self) {
7190        self.circuit
7191            .log_scheduler_event(&SchedulerEvent::clock_end());
7192
7193        // Prevent nested panic when `drop` is invoked while panicking
7194        // and `clock_end` triggers another panic due to violated invariants
7195        // since the original panic interrupted normal execution.
7196        if !panicking() {
7197            self.circuit.clock_end(0)
7198        }
7199
7200        // We must explicitly deallocate all nodes in the circuit to break
7201        // cyclic `Rc` references between circuits and streams.  Alternatively,
7202        // we could use weak references to break cycles, but we'd have to
7203        // pay the cost of upgrading weak references on each access.
7204        self.circuit.clear();
7205    }
7206}
7207
7208/// Operators involved in the replay phase of a circuit.
7209#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
7210pub struct BootstrapInfo {
7211    /// Operators that will replay their contents during the replay phase.
7212    pub replay_sources: BTreeMap<NodeId, StreamId>,
7213
7214    /// Operators that require backfill from upstream nodes, including their persistent IDs.
7215    #[allow(dead_code)]
7216    pub need_backfill: BTreeMap<NodeId, Option<String>>,
7217}
7218
7219impl CircuitHandle {
7220    /// Start and instantly commit a transaction, waiting for the commit to complete.
7221    pub fn transaction(&self) -> Result<(), DbspError> {
7222        self.tokio_runtime
7223            .block_on(async {
7224                let local_set = LocalSet::new();
7225                local_set
7226                    .run_until(async { self.executor.transaction(&self.circuit).await })
7227                    .await
7228            })
7229            .map_err(DbspError::Scheduler)
7230    }
7231
7232    /// Start a transaction.
7233    ///
7234    /// A transaction consists of a sequence of steps that evaluate a set of inputs for a single logical
7235    /// clock tick.
7236    pub fn start_transaction(&self) -> Result<(), DbspError> {
7237        self.tokio_runtime
7238            .block_on(async {
7239                let local_set = LocalSet::new();
7240                local_set
7241                    .run_until(async { self.executor.start_transaction(&self.circuit).await })
7242                    .await
7243            })
7244            .map_err(DbspError::Scheduler)
7245    }
7246
7247    /// Start committing the current transaction by forcing all operators to process
7248    /// their inputs to completion.
7249    ///
7250    /// The caller must invoke `step` repeatedly until the commit is complete.
7251    pub fn start_commit_transaction(&self) -> Result<(), DbspError> {
7252        self.executor
7253            .start_commit_transaction()
7254            .map_err(DbspError::Scheduler)
7255    }
7256
7257    pub fn is_commit_complete(&self) -> bool {
7258        self.executor.is_commit_complete()
7259    }
7260
7261    pub fn commit_progress(&self) -> CommitProgress {
7262        self.executor.commit_progress()
7263    }
7264
7265    /// Evaluate the circuit for a single step.
7266    pub fn step(&self) -> Result<(), DbspError> {
7267        self.tokio_runtime
7268            .block_on(async {
7269                let local_set = LocalSet::new();
7270                local_set
7271                    .run_until(async { self.executor.step(&self.circuit).await })
7272                    .await
7273            })
7274            .map_err(DbspError::Scheduler)
7275    }
7276
7277    pub fn checkpoint(
7278        &mut self,
7279        base: &StoragePath,
7280        files: &mut Vec<Arc<dyn FileCommitter>>,
7281    ) -> Result<(), DbspError> {
7282        // if Runtime::worker_index() == 0 {
7283        //     self.circuit.to_dot_file(
7284        //         |node| {
7285        //             Some(crate::utils::DotNodeAttributes::new().with_label(&format!(
7286        //                 "{}-{}-{}",
7287        //                 node.local_id(),
7288        //                 node.name(),
7289        //                 node.persistent_id().unwrap_or_default()
7290        //             )))
7291        //         },
7292        //         |edge| {
7293        //             let style = if edge.is_dependency() {
7294        //                 Some("dotted".to_string())
7295        //             } else {
7296        //                 None
7297        //             };
7298        //             let label = if let Some(stream) = &edge.stream {
7299        //                 Some(format!("consumers: {}", stream.num_consumers()))
7300        //             } else {
7301        //                 None
7302        //             };
7303        //             Some(
7304        //                 crate::utils::DotEdgeAttributes::new(edge.stream_id())
7305        //                     .with_style(style)
7306        //                     .with_label(label),
7307        //             )
7308        //         },
7309        //         "commit.dot",
7310        //     );
7311        //     info!("CircuitHandle::commit: circuit written to commit.dot");
7312        // }
7313
7314        self.circuit
7315            .map_nodes_recursive_mut(&mut |node: &mut dyn Node| {
7316                let _span = Span::new("operator")
7317                    .with_category("Checkpoint")
7318                    .with_tooltip(|| format!("{} {}", node.name(), node.global_id()));
7319                DBSP_OPERATOR_COMMIT_LATENCY_MICROSECONDS
7320                    .record_callback(|| node.checkpoint(base, files))
7321            })
7322    }
7323
7324    /// Restores the circuit from a checkpoint.
7325    ///
7326    /// Restore the circuit from a checkpoint and prepare it to backfill new and
7327    /// modified parts of the circuit if necessary.
7328    ///
7329    /// 1. Find and restore the checkpointed state of each operator.
7330    /// 2. Identify stateful operators (such as integrals and output nodes) that don't have
7331    ///    a checkpoint and require backfill (the `need_backfill` set).
7332    /// 3. Iterate backward from `need_backfill` nodes to find all operators that
7333    ///    should participate in the replay phase of the circuit. Iteration stops when
7334    ///    reaching a stream whose contents can be replayed from an existing node
7335    ///    that has a checkpoint or an input node.
7336    /// 4. If the circuit requires backfill, prepare the circuit for replay by
7337    ///    configuring the scheduler to only schedule nodes that participate in
7338    ///    backfill.
7339    ///
7340    /// Returns `None` if the circuit does not require backfill; returns info about
7341    /// nodes that participate in backfill otherwise.
7342    ///
7343    /// * After calling this function, the client can invoke `step` repeatedly for replay to make progress.
7344    /// * Use `is_replay_complete` to determine whether the circuit has finished the replay.
7345    /// * Use `complete_replay` to finalize the replay phase and prepare the circuit for normal operation after replay is complete.
7346    pub fn restore(&mut self, base: &StoragePath) -> Result<Option<BootstrapInfo>, DbspError> {
7347        // Nodes that will act as replay sources during the replay phase of the circuit.
7348        let mut replay_sources: BTreeMap<NodeId, StreamId> = BTreeMap::new();
7349
7350        // Nodes that require backfill from upstream nodes.
7351        let mut need_backfill: BTreeSet<GlobalNodeId> = BTreeSet::new();
7352
7353        // debug!("CircuitHandle::restore: restoring from checkpoint {}", base);
7354
7355        // In persistent mode, refuse to silently treat a vanished state
7356        // file as backfill. dependencies.json records every state file at
7357        // commit time; if any are gone, the corruption is structural and
7358        // operators need to see it. A missing backend in persistent mode is
7359        // an invariant violation, surface it rather than skipping verify.
7360        if Runtime::mode() == Mode::Persistent {
7361            let backend = Runtime::storage_backend()?;
7362            crate::circuit::checkpointer::Checkpointer::verify_checkpoint_intact(
7363                backend.as_ref(),
7364                base,
7365            )?;
7366        }
7367
7368        // Initialize `need_backfill` to operators without a checkpoint.
7369        // Fail if there are any errors other than NotFound.
7370        // By the end of this, `need_backfill` will contain all new integrals and output
7371        // nodes that need backfill.
7372        self.circuit.map_nodes_recursive_mut(
7373            &mut |node: &mut dyn Node| match node.restore(base) {
7374                Err(e) if Runtime::mode() == Mode::Ephemeral => Err(e),
7375                Err(DbspError::Storage(ioerror)) if ioerror.kind() == ErrorKind::NotFound => {
7376                    need_backfill.insert(node.global_id().clone());
7377                    Ok(())
7378                }
7379                Err(DbspError::IO(ioerror)) if ioerror.kind() == ErrorKind::NotFound => {
7380                    need_backfill.insert(node.global_id().clone());
7381                    Ok(())
7382                }
7383                Err(e) => Err(e),
7384                Ok(()) => Ok(()),
7385            },
7386        )?;
7387
7388        // Additional nodes that must be backfilled to keep the balancer state consistent.
7389        let additional_need_backfill: BTreeSet<GlobalNodeId> =
7390            self.invalidate_balancer_clusters(&need_backfill);
7391        if Runtime::worker_index() == 0 {
7392            debug!(
7393                "CircuitHandle::restore: additional need backfill: {:?}",
7394                additional_need_backfill
7395            );
7396        }
7397        need_backfill.extend(additional_need_backfill);
7398
7399        debug!(
7400            "worker {}: CircuitHandle::restore: found {} operators that require backfill: {:?}",
7401            Runtime::worker_index(),
7402            need_backfill.len(),
7403            need_backfill.iter().cloned().collect::<Vec<GlobalNodeId>>()
7404        );
7405
7406        // We can only backfill a nested circuit as a whole, so if we encounter at least
7407        // one node in a nested circuit that needs backfill, we backfill the
7408        // entire circuit.
7409        let need_backfill = need_backfill
7410            .into_iter()
7411            .map(|gid| gid.top_level_ancestor())
7412            .collect::<BTreeSet<_>>();
7413
7414        // Iterate backward from `need_backfill` nodes to find all operators that
7415        // should participate in the replay.
7416
7417        // All nodes that participate in the replay phase, including replay sources and
7418        // nodes that need backfilling.
7419        let mut participate_in_backfill = need_backfill.clone();
7420
7421        // New nodes computed at each iteration.
7422        let mut participate_in_backfill_new = need_backfill.clone();
7423
7424        while !participate_in_backfill_new.is_empty() {
7425            participate_in_backfill_new = self.compute_replay_nodes_step(
7426                &mut replay_sources,
7427                &need_backfill,
7428                participate_in_backfill_new,
7429                &mut participate_in_backfill,
7430            )?;
7431        }
7432
7433        debug!(
7434            "worker {}: CircuitHandle::restore: replaying {} operators: {:?}\n  backfilling {} operators: {:?}\n  replay circuit consists of {} operators: {:?}",
7435            Runtime::worker_index(),
7436            replay_sources.len(),
7437            replay_sources.keys().cloned().collect::<Vec<NodeId>>(),
7438            need_backfill.len(),
7439            need_backfill.iter().cloned().collect::<Vec<NodeId>>(),
7440            participate_in_backfill.len(),
7441            participate_in_backfill
7442                .iter()
7443                .cloned()
7444                .collect::<Vec<NodeId>>()
7445        );
7446
7447        let replay_nodes = replay_sources.keys().cloned().collect::<BTreeSet<_>>();
7448
7449        assert!(
7450            replay_nodes
7451                .intersection(&need_backfill)
7452                .collect::<Vec<_>>()
7453                .is_empty()
7454        );
7455
7456        // Nodes that will be backfilled from upstream nodes, including need_backfill nodes
7457        // and their transitive ancestors.
7458        let nodes_to_backfill = participate_in_backfill
7459            .difference(&replay_nodes)
7460            .cloned()
7461            .collect::<BTreeSet<_>>();
7462
7463        if !participate_in_backfill.is_empty() {
7464            // Configure all `replay_nodes` to run in replay mode.
7465            for node_id in replay_nodes.iter() {
7466                self.circuit
7467                    .map_local_node_mut(*node_id, &mut |node| node.start_replay())?;
7468            }
7469
7470            // Clear the state of `need_backfill` nodes.
7471            for node_id in nodes_to_backfill.iter() {
7472                self.circuit
7473                    .map_local_node_mut(*node_id, &mut |node| node.clear_state())?;
7474            }
7475
7476            // Prepare the scheduler to only run `participate_in_backfill`.
7477            self.executor
7478                .prepare(&self.circuit, Some(&participate_in_backfill))?;
7479
7480            // info!("CircuitHandle::restore: replay circuit is ready");
7481
7482            // if Runtime::worker_index() == 0 {
7483            //     self.circuit.to_dot_file(
7484            //         |node| {
7485            //             if !node.global_id().is_child_of(self.circuit.global_id()) {
7486            //                 return None;
7487            //             }
7488            //             let color = if replay_sources.contains_key(&node.local_id()) {
7489            //                 Some(0xff5555)
7490            //             } else if participate_in_backfill.contains(&node.local_id()) {
7491            //                 Some(0x5555ff)
7492            //             } else {
7493            //                 None
7494            //             };
7495            //             Some(
7496            //                 crate::utils::DotNodeAttributes::new()
7497            //                     .with_color(color)
7498            //                     .with_label(&format!("{}: {}", node.global_id(), node.name())),
7499            //             )
7500            //         },
7501            //         |edge| {
7502            //             let style = if edge.is_dependency() {
7503            //                 Some("dotted".to_string())
7504            //             } else {
7505            //                 None
7506            //             };
7507            //             let label = if let Some(stream) = &edge.stream {
7508            //                 Some(format!("consumers: {}", stream.num_consumers()))
7509            //             } else {
7510            //                 None
7511            //             };
7512            //             Some(
7513            //                 crate::utils::DotEdgeAttributes::new(edge.stream_id())
7514            //                     .with_style(style)
7515            //                     .with_label(label),
7516            //             )
7517            //         },
7518            //         "replay.dot",
7519            //     );
7520            //     println!("CircuitHandle::restore: replay circuit is written to replay.dot");
7521            // }
7522
7523            // Add persistent IDs to the need_backfill set.
7524            let need_backfill = nodes_to_backfill
7525                .iter()
7526                .map(|node_id| {
7527                    let pid = self.circuit.map_local_node_mut(*node_id, &mut |node| {
7528                        node.get_label(LABEL_PERSISTENT_OPERATOR_ID)
7529                            .map(|s| s.to_string())
7530                    });
7531
7532                    (*node_id, pid)
7533                })
7534                .collect::<BTreeMap<_, _>>();
7535
7536            let replay_info = BootstrapInfo {
7537                replay_sources: replay_sources.clone(),
7538                need_backfill,
7539            };
7540
7541            self.replay_info = Some(replay_info.clone());
7542
7543            Ok(Some(replay_info))
7544        } else {
7545            Ok(None)
7546        }
7547    }
7548
7549    /// Compute additional nodes whose state must be discarded and backfilled in order
7550    /// to ensure that the balancer state is consistent.
7551    ///
7552    /// The new version of the circuit may have a different join graph in which the partitioning
7553    /// policy used to create the checkpoint may no longer be valid.
7554    ///
7555    /// Example:
7556    ///
7557    /// Consider the following checkpointed circuit:
7558    ///
7559    /// s1 = join(a, b)
7560    /// s2 = join(c, d)
7561    ///
7562    /// where streams a and c are partitioned using PartitioningPolicy::Broadcast, and
7563    /// b and d are partitioned using PartitioningPolicy::Shard.
7564    ///
7565    /// The new circuit adds
7566    ///
7567    /// s3 = join(a, c)
7568    ///
7569    /// This new circuit is in an inconsistent state since we can't join two broadcast streams.
7570    ///
7571    /// This function computes a conservative approximation of the set of nodes whose state must
7572    /// be discarded and backfilled to avoid such inconsistencies:
7573    ///
7574    /// 1. For each join cluster, check if there exists a solution that extends the partitioning
7575    ///    policies of the nodes restores from the checkpoint.
7576    /// 2. If no solution exists, mark all nodes in the cluster and their transitive successors
7577    ///    as needing backfill.
7578    fn invalidate_balancer_clusters(
7579        &self,
7580        need_backfill: &BTreeSet<GlobalNodeId>,
7581    ) -> BTreeSet<GlobalNodeId> {
7582        // Convert GlobalNodeId to top-level NodeId for comparison with balancer data.
7583        let need_backfill_node_ids: BTreeSet<NodeId> = need_backfill
7584            .iter()
7585            .map(|gid| gid.top_level_ancestor())
7586            .collect();
7587
7588        // Compute exchange sender nodes that need to be discarded and backfilled.
7589        let additional_need_backfill = self
7590            .circuit
7591            .balancer()
7592            .invalidate_clusters_for_bootstrapping(&need_backfill_node_ids);
7593
7594        // Invalidate all successors as well; otherwise we can end up with nodes that are not
7595        // marked for backfill but their ancestors are.
7596        let nodes_to_add = self.propagate_need_backfill_forward(
7597            additional_need_backfill
7598                .difference(&need_backfill_node_ids)
7599                .cloned()
7600                .collect(),
7601        );
7602
7603        // Convert NodeIds back to GlobalNodeIds and add to additional_need_backfill
7604        nodes_to_add
7605            .into_iter()
7606            .map(|node_id| GlobalNodeId::root().child(node_id))
7607            .collect()
7608    }
7609
7610    /// Compute all transitive successors of need_backfill nodes.
7611    fn propagate_need_backfill_forward(
7612        &self,
7613        mut need_backfill: BTreeSet<NodeId>,
7614    ) -> BTreeSet<NodeId> {
7615        // Recursively add all successors of these exchange sender nodes
7616        let mut worklist: Vec<NodeId> = need_backfill.iter().cloned().collect();
7617        let mut visited = BTreeSet::new();
7618
7619        while let Some(node_id) = worklist.pop() {
7620            if visited.contains(&node_id) {
7621                continue;
7622            }
7623            visited.insert(node_id);
7624
7625            // Get all successors of this node
7626            let successors: Vec<NodeId> = self
7627                .circuit
7628                .edges()
7629                .by_source
7630                .get(&node_id)
7631                .into_iter()
7632                .flat_map(|edges| edges.iter().map(|edge| edge.to))
7633                .collect();
7634
7635            for successor in successors {
7636                if !visited.contains(&successor) {
7637                    worklist.push(successor);
7638                    need_backfill.insert(successor);
7639                }
7640            }
7641
7642            // Add all dependencies of this node. Makes sure that the output half of Z-1 is marked for backfill.
7643            let dependencies: Vec<NodeId> = self
7644                .circuit
7645                .edges()
7646                .by_destination
7647                .get(&node_id)
7648                .into_iter()
7649                .flat_map(|edges| edges.iter())
7650                .filter(|edge| edge.is_dependency())
7651                .map(|edge| edge.from)
7652                .collect();
7653
7654            for dependency in dependencies {
7655                if !visited.contains(&dependency) {
7656                    worklist.push(dependency);
7657                    need_backfill.insert(dependency);
7658                }
7659            }
7660        }
7661
7662        need_backfill
7663    }
7664
7665    /// Iterative step of computing the set of nodes that participate in the replay phase.
7666    ///
7667    /// Find all input streams of nodes in `participate_in_backfill_new`.
7668    ///
7669    /// * For streams that can be replayed from a replay source:
7670    ///  - add the replay source to `replay_sources` and `participate_in_backfill`.
7671    ///  - create replay streams from the replay source to each consumer of the original stream.
7672    /// * For other streams, add their origin nodes to `participate_in_backfill`.
7673    ///
7674    /// Return the set of nodes newly added to `participate_in_backfill`.
7675    fn compute_replay_nodes_step(
7676        &self,
7677        replay_sources: &mut BTreeMap<NodeId, StreamId>,
7678        need_backfill: &BTreeSet<NodeId>,
7679        participate_in_backfill_new: BTreeSet<NodeId>,
7680        participate_in_backfill: &mut BTreeSet<NodeId>,
7681    ) -> Result<BTreeSet<NodeId>, DbspError> {
7682        let mut inputs = BTreeSet::new();
7683
7684        for node_id in participate_in_backfill_new.iter() {
7685            // Compute immediate ancestors of node_id, including:
7686            // 1. Nodes connected to node_id by a stream.
7687            // 2. Nodes that depend on node_id -- makes sure that if we schedule the output half of a strict operator,
7688            //    we will also schedule the input half.
7689            // 3. Nodes that node_id depends on -- Makes sure that if we schedule the output of an exchange operator,
7690            //    we will schedule the input part as well.
7691
7692            // 1.
7693            let node_inputs = self
7694                .circuit
7695                .edges()
7696                .by_destination
7697                .get(node_id)
7698                .iter()
7699                .flat_map(|edges| edges.iter())
7700                .filter(|edge| edge.is_stream())
7701                .map(|edge| {
7702                    // If the origin of the stream is a node inside the nested circuit, we will clear and replay the
7703                    // entire nested circuit, as we don't currently have a way to replay state from inside a nested
7704                    // circuit into a parent circuit.
7705                    (Some(edge.stream_id().unwrap()), edge.from)
7706                })
7707                .collect::<Vec<_>>();
7708
7709            for input in node_inputs.into_iter() {
7710                inputs.insert(input);
7711            }
7712
7713            // 2.
7714            for edge in self.circuit.edges().dependencies_of(*node_id) {
7715                inputs.insert((None, edge.from));
7716            }
7717
7718            // 3.
7719            for edge in self.circuit.edges().depend_on(*node_id) {
7720                inputs.insert((None, edge.to));
7721            }
7722        }
7723
7724        let mut participate_in_backfill_new = BTreeSet::new();
7725
7726        let mut replay_streams = BTreeMap::new();
7727
7728        for (stream_id, mut node_id) in inputs.into_iter() {
7729            // println!("replay needed for ({stream_id}, {node_id})");
7730
7731            // Add all ancestors of `participate_in_backfill_new` to the `participate_in_backfill` set, except streams
7732            // that can be replayed from a different node.
7733            if let Some(stream_id) = stream_id
7734                && let Some(replay_source) = self.circuit.get_replay_source(stream_id)
7735            {
7736                // If the replay source is itself in the need_backfill set (i.e., it's an integral without
7737                // a checkpoint), it cannot be used for replay.
7738                if !need_backfill.contains(&replay_source.local_node_id()) {
7739                    replay_streams.insert(stream_id, replay_source.clone());
7740                    // trace!(
7741                    //     "worker {}: Replacing node_id {node_id} with replay source {}",
7742                    //     Runtime::worker_index(),
7743                    //     replay_source.origin_node_id()
7744                    // );
7745
7746                    // Replace the node_id with the replay source.
7747                    node_id = replay_source.local_node_id();
7748                }
7749            }
7750
7751            if !participate_in_backfill.contains(&node_id) {
7752                // println!("Adding {gid} to participate_in_backfill via {stream_id:?}");
7753                participate_in_backfill.insert(node_id);
7754                participate_in_backfill_new.insert(node_id);
7755            }
7756        }
7757
7758        // Connect `replay_streams` to all operators that consume the original stream.
7759        for (original_stream, replay_stream) in replay_streams.into_iter() {
7760            replay_sources
7761                .entry(replay_stream.local_node_id())
7762                .or_insert_with(|| {
7763                    self.circuit
7764                        .add_replay_edges(original_stream, replay_stream.as_ref());
7765                    replay_stream.stream_id()
7766                });
7767        }
7768
7769        Ok(participate_in_backfill_new)
7770    }
7771
7772    /// Returns `true` if all replay sources have completed their replay.
7773    pub fn is_replay_complete(&self) -> bool {
7774        let Some(replay_info) = self.replay_info.as_ref() else {
7775            return true;
7776        };
7777
7778        // Bootstrapping is finished when all replay sources have completed their replay and the
7779        // transaction has been committed.
7780
7781        let all_complete = replay_info.replay_sources.keys().all(|node_id| {
7782            self.circuit
7783                .map_local_node_mut(*node_id, &mut |node| node.is_replay_complete())
7784        });
7785
7786        all_complete && self.is_commit_complete()
7787    }
7788
7789    /// Finalize the replay phase of the circuit.
7790    ///
7791    /// * Notify all replay sources to go back to normal operation.
7792    /// * Delete all replay streams.
7793    /// * Prepare the scheduler to run the full circuit.
7794    pub fn complete_replay(&mut self) -> Result<(), DbspError> {
7795        // info!("Replay complete");
7796
7797        let Some(replay_info) = self.replay_info.take() else {
7798            return Ok(());
7799        };
7800
7801        // End replay mode.
7802        for (node_id, stream_id) in replay_info.replay_sources.iter() {
7803            self.circuit
7804                .map_local_node_mut(*node_id, &mut |node| node.end_replay())?;
7805            self.circuit.edges_mut().delete_stream(*stream_id);
7806        }
7807
7808        // Prepare the scheduler to run the full circuit.
7809        self.executor.prepare(&self.circuit, None)?;
7810
7811        // if Runtime::worker_index() == 0 {
7812        //     self.circuit.to_dot_file(
7813        //         |_node| Some(crate::circuit::dot::DotNodeAttributes::new()),
7814        //         |edge| {
7815        //             let style = if edge.is_dependency() {
7816        //                 Some("dotted".to_string())
7817        //             } else {
7818        //                 None
7819        //             };
7820        //             let label = if let Some(stream) = &edge.stream {
7821        //                 Some(format!("consumers: {}", stream.num_consumers()))
7822        //             } else {
7823        //                 None
7824        //             };
7825        //             Some(
7826        //                 crate::circuit::dot::DotEdgeAttributes::new(edge.stream_id())
7827        //                     .with_style(style)
7828        //                     .with_label(label),
7829        //             )
7830        //         },
7831        //         "final.dot",
7832        //     );
7833        //     info!("CircuitHandle::restore: final circuit is written to final.dot");
7834        // }
7835
7836        Ok(())
7837    }
7838
7839    pub fn fingerprint(&self) -> u64 {
7840        let mut fip = Fingerprinter::default();
7841        let _ = self.circuit.map_nodes_recursive(&mut |node: &dyn Node| {
7842            node.fingerprint(&mut fip);
7843            Ok(())
7844        });
7845        fip.finish()
7846    }
7847
7848    /// Attach a scheduler event handler to the circuit.
7849    ///
7850    /// This method is identical to
7851    /// [`RootCircuit::register_scheduler_event_handler`], but it can be used at
7852    /// runtime, after the circuit has been fully constructed.
7853    ///
7854    /// Use [`RootCircuit::register_scheduler_event_handler`],
7855    /// [`RootCircuit::unregister_scheduler_event_handler`], to manipulate
7856    /// handlers during circuit construction.
7857    pub fn register_scheduler_event_handler<F>(&self, name: &str, handler: F)
7858    where
7859        F: FnMut(&SchedulerEvent<'_>) + 'static,
7860    {
7861        self.circuit.register_scheduler_event_handler(name, handler);
7862    }
7863
7864    /// Remove a scheduler event handler.
7865    ///
7866    /// This method is identical to
7867    /// [`RootCircuit::unregister_scheduler_event_handler`], but it can be used
7868    /// at runtime, after the circuit has been fully constructed.
7869    pub fn unregister_scheduler_event_handler(&self, name: &str) -> bool {
7870        self.circuit.unregister_scheduler_event_handler(name)
7871    }
7872
7873    /// Export circuit in LIR format.
7874    pub fn lir(&self) -> LirCircuit {
7875        (&self.circuit as &dyn CircuitBase).to_lir()
7876    }
7877
7878    pub fn set_auto_rebalance(&self, enable: bool) -> Result<(), DbspError> {
7879        self.circuit.set_auto_rebalance(enable)
7880    }
7881
7882    pub fn set_balancer_hint_by_global_id(
7883        &self,
7884        global_node_id: &GlobalNodeId,
7885        hint: BalancerHint,
7886    ) -> Result<(), DbspError> {
7887        self.circuit
7888            .set_balancer_hint_by_global_id(global_node_id, hint)
7889    }
7890
7891    pub fn set_balancer_hint(
7892        &self,
7893        persistent_id: &str,
7894        hint: BalancerHint,
7895    ) -> Result<(), DbspError> {
7896        self.circuit.set_balancer_hint(persistent_id, hint)
7897    }
7898
7899    pub fn get_current_balancer_policies(&self) -> BTreeMap<GlobalNodeId, PartitioningPolicy> {
7900        self.circuit
7901            .get_current_balancer_policies()
7902            .into_iter()
7903            .map(|(node_id, policy)| (GlobalNodeId::root().child(node_id), policy))
7904            .collect()
7905    }
7906
7907    pub fn get_current_balancer_policy(
7908        &self,
7909        persistent_id: &str,
7910    ) -> Result<PartitioningPolicy, DbspError> {
7911        self.circuit.get_current_balancer_policy(persistent_id)
7912    }
7913
7914    pub fn rebalance(&self) {
7915        self.circuit.rebalance()
7916    }
7917
7918    pub fn start_compaction(&self) {
7919        self.circuit.start_compaction()
7920    }
7921
7922    pub fn is_compaction_complete(&self) -> bool {
7923        self.circuit.is_compaction_complete()
7924    }
7925}
7926
7927/// Real time and CPU time.
7928#[derive(Copy, Clone, Debug, Default, SizeOf)]
7929pub struct ElapsedTime {
7930    /// Real time running a task.
7931    pub real: Duration,
7932
7933    /// CPU time running a task.
7934    ///
7935    /// In the ordinary course, if `cpu` is much less than `real`, then it
7936    /// indicates that the task blocked its thread, e.g. for synchronous I/O,
7937    /// without yielding to the tokio scheduler.
7938    pub cpu: Duration,
7939}
7940
7941impl AddAssign for ElapsedTime {
7942    fn add_assign(&mut self, rhs: Self) {
7943        self.real += rhs.real;
7944        self.cpu += rhs.cpu;
7945    }
7946}
7947
7948pin_project! {
7949    /// An async task that measures its runtime.
7950    ///
7951    /// This uses the same wrapper technique as [tokio_metrics::Instrumented] or
7952    /// [tracing::Instrument]: by implementing [Future] manually, it wraps each
7953    /// poll with elapsed real and CPU time measurement.  When the future
7954    /// eventually completes, it outputs the wrapped future's output value plus
7955    /// the [ElapsedTime].
7956    ///
7957    /// [tokio_metrics::Instrumented]: https://docs.rs/tokio-metrics/latest/tokio_metrics/struct.Instrumented.html
7958    /// [tracing::Instrument]: https://docs.rs/tracing/latest/tracing/trait.Instrument.html
7959    #[derive(Debug)]
7960    pub struct Timed<T> {
7961        // The task being instrumented
7962        #[pin]
7963        task: T,
7964
7965        // Elapsed time running this task.
7966        elapsed: ElapsedTime,
7967    }
7968}
7969
7970impl<T> Timed<T> {
7971    fn new(task: T) -> Self {
7972        Self {
7973            task,
7974            elapsed: ElapsedTime::default(),
7975        }
7976    }
7977}
7978
7979/// Amount of time elapsed running a thread.
7980pub struct ThreadCpuTime(pub Duration);
7981
7982impl ThreadCpuTime {
7983    /// Returns the current time elapsed running the current thread.
7984    pub fn now() -> Self {
7985        let nanos = clock_gettime(ClockId::CLOCK_THREAD_CPUTIME_ID)
7986            .unwrap()
7987            .num_nanoseconds();
7988        Self(Duration::from_nanos(nanos.max(0).cast_unsigned()))
7989    }
7990
7991    /// Returns the time elapsed running the current thread since this
7992    /// `ThreadCpuTime`.
7993    ///
7994    /// This only makes sense if this `ThreadCpuTime` was for the currently
7995    /// running thread.
7996    ///
7997    /// Returns zero if the current time is earlier than self.
7998    pub fn elapsed(&self) -> Duration {
7999        Self::now().0.saturating_sub(self.0)
8000    }
8001}
8002
8003impl<T> Future for Timed<T>
8004where
8005    T: Future,
8006{
8007    type Output = (T::Output, ElapsedTime);
8008
8009    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
8010        let this = self.project();
8011        let start = Instant::now();
8012        let start_cputime = ThreadCpuTime::now();
8013        let ret = this.task.poll(cx);
8014        this.elapsed.real += start.elapsed();
8015        this.elapsed.cpu += start_cputime.elapsed();
8016        ret.map(|value| (value, take(&mut *this.elapsed)))
8017    }
8018}
8019
8020#[cfg(test)]
8021mod tests {
8022    use crate::{
8023        Circuit, Error as DbspError, RootCircuit,
8024        circuit::schedule::{DynamicScheduler, Scheduler},
8025        monitor::TraceMonitor,
8026        operator::{Generator, Z1},
8027    };
8028    use anyhow::anyhow;
8029    use std::{cell::RefCell, ops::Deref, rc::Rc, vec::Vec};
8030
8031    #[test]
8032    fn sum_circuit_dynamic() {
8033        sum_circuit::<DynamicScheduler>();
8034    }
8035    // Compute the sum of numbers from 0 to 99.
8036    fn sum_circuit<S>()
8037    where
8038        S: Scheduler + 'static,
8039    {
8040        let actual_output: Rc<RefCell<Vec<isize>>> = Rc::new(RefCell::new(Vec::with_capacity(100)));
8041        let actual_output_clone = actual_output.clone();
8042        let circuit = RootCircuit::build_with_scheduler::<_, _, S>(|circuit| {
8043            TraceMonitor::new_panic_on_error().attach(circuit, "monitor");
8044            let mut n: isize = 0;
8045            let source = circuit.add_source(Generator::new(move || {
8046                let result = n;
8047                n += 1;
8048                result
8049            }));
8050            let integrator = source.integrate();
8051            integrator.inspect(|n| println!("{}", n));
8052            integrator.inspect(move |n| actual_output_clone.borrow_mut().push(*n));
8053            Ok(())
8054        })
8055        .unwrap()
8056        .0;
8057
8058        for _ in 0..100 {
8059            circuit.transaction().unwrap();
8060        }
8061
8062        let mut sum = 0;
8063        let mut expected_output: Vec<isize> = Vec::with_capacity(100);
8064        for i in 0..100 {
8065            sum += i;
8066            expected_output.push(sum);
8067        }
8068        assert_eq!(&expected_output, actual_output.borrow().deref());
8069    }
8070
8071    #[test]
8072    fn recursive_sum_circuit_dynamic() {
8073        recursive_sum_circuit::<DynamicScheduler>()
8074    }
8075
8076    fn recursive_sum_circuit<S>()
8077    where
8078        S: Scheduler + 'static,
8079    {
8080        let actual_output: Rc<RefCell<Vec<usize>>> = Rc::new(RefCell::new(Vec::with_capacity(100)));
8081        let actual_output_clone = actual_output.clone();
8082
8083        let circuit = RootCircuit::build_with_scheduler::<_, _, S>(|circuit| {
8084            TraceMonitor::new_panic_on_error().attach(circuit, "monitor");
8085
8086            let mut n: usize = 0;
8087            let source = circuit.add_source(Generator::new(move || {
8088                let result = n;
8089                n += 1;
8090                result
8091            }));
8092            let (z1_output, z1_feedback) = circuit.add_feedback(Z1::new(0));
8093            let plus = source
8094                .apply2(&z1_output, |n1: &usize, n2: &usize| *n1 + *n2)
8095                .inspect(move |n| actual_output_clone.borrow_mut().push(*n));
8096            z1_feedback.connect(&plus);
8097            Ok(())
8098        })
8099        .unwrap()
8100        .0;
8101
8102        for _ in 0..100 {
8103            circuit.transaction().unwrap();
8104        }
8105
8106        let mut sum = 0;
8107        let mut expected_output: Vec<usize> = Vec::with_capacity(100);
8108        for i in 0..100 {
8109            sum += i;
8110            expected_output.push(sum);
8111        }
8112        assert_eq!(&expected_output, actual_output.borrow().deref());
8113    }
8114
8115    #[test]
8116    fn factorial_dynamic() {
8117        factorial::<DynamicScheduler>();
8118    }
8119
8120    // Nested circuit.  The circuit contains a source node that counts up from
8121    // 1. For each `n` output by the source node, the nested circuit computes
8122    // factorial(n) using a `NestedSource` operator that counts from n down to
8123    // `1` and a multiplier that multiplies the next count by the product
8124    // computed so far (stored in z-1).
8125    fn factorial<S>()
8126    where
8127        S: Scheduler + 'static,
8128    {
8129        let actual_output: Rc<RefCell<Vec<usize>>> = Rc::new(RefCell::new(Vec::with_capacity(100)));
8130        let actual_output_clone = actual_output.clone();
8131
8132        let circuit = RootCircuit::build_with_scheduler::<_, _, S>(|circuit| {
8133            TraceMonitor::new_panic_on_error().attach(circuit, "monitor");
8134
8135            let mut n: usize = 0;
8136            let source = circuit.add_source(Generator::new(move || {
8137                n += 1;
8138                n
8139            }));
8140            let fact = circuit
8141                .iterate_with_condition_and_scheduler::<_, _, S>(|child| {
8142                    let mut counter = 0;
8143                    let countdown = source.delta0(child).apply_mut(move |parent_val| {
8144                        if *parent_val > 0 {
8145                            counter = *parent_val;
8146                        };
8147                        let res = counter;
8148                        counter -= 1;
8149                        res
8150                    });
8151                    let (z1_output, z1_feedback) = child.add_feedback_with_export(Z1::new(1));
8152                    let mul = countdown.apply2(&z1_output.local, |n1: &usize, n2: &usize| n1 * n2);
8153                    z1_feedback.connect(&mul);
8154                    Ok((countdown.condition(|n| *n <= 1), z1_output.export))
8155                })
8156                .unwrap();
8157            fact.inspect(move |n| actual_output_clone.borrow_mut().push(*n));
8158            Ok(())
8159        })
8160        .unwrap()
8161        .0;
8162
8163        for _ in 1..10 {
8164            circuit.transaction().unwrap();
8165        }
8166
8167        let mut expected_output: Vec<usize> = Vec::with_capacity(10);
8168        for i in 1..10 {
8169            expected_output.push(my_factorial(i));
8170        }
8171        assert_eq!(&expected_output, actual_output.borrow().deref());
8172    }
8173
8174    fn my_factorial(n: usize) -> usize {
8175        if n == 1 { 1 } else { n * my_factorial(n - 1) }
8176    }
8177
8178    /// Verify that operators added across multiple `open_region`/`close_region`
8179    /// calls on the same [`RegionName`] all land in the same region tree node.
8180    #[test]
8181    fn open_close_region() {
8182        const REGION: &str = "my_region";
8183
8184        let monitor = TraceMonitor::new_panic_on_error();
8185        let region: Rc<RefCell<Option<super::RegionName>>> = Rc::new(RefCell::new(None));
8186
8187        let _circuit = RootCircuit::build({
8188            let monitor = monitor.clone();
8189            let region = region.clone();
8190            move |circuit| {
8191                monitor.attach(circuit, "monitor");
8192
8193                let r = circuit.create_region_name(REGION, 0);
8194
8195                circuit.open_region(r.clone());
8196                let source1 = circuit.add_source(Generator::new(|| 1_i32));
8197                circuit.close_region(r.clone());
8198
8199                circuit.open_region(r.clone());
8200                let source2 = circuit.add_source(Generator::new(|| 2_i32));
8201                circuit.close_region(r.clone());
8202
8203                circuit.open_region(r.clone());
8204                source1
8205                    .apply2(&source2, |a: &i32, b: &i32| a + b)
8206                    .inspect(|_| {});
8207                circuit.close_region(r.clone());
8208
8209                *region.borrow_mut() = Some(r);
8210                Ok(())
8211            }
8212        })
8213        .unwrap()
8214        .0;
8215
8216        // source1, source2, apply2, inspect — all in the single "my_region" node.
8217        let region = region.borrow();
8218        assert_eq!(
8219            monitor.count_nodes_in_region(region.as_ref().unwrap()),
8220            Some(4)
8221        );
8222    }
8223
8224    /// Verify that two separate `create_region` calls with the same name
8225    /// produce independent region nodes: operators added via each handle
8226    /// land in different nodes, not the same one.
8227    #[test]
8228    fn separate_create_region_same_name() {
8229        const REGION: &str = "my_region";
8230
8231        let monitor = TraceMonitor::new_panic_on_error();
8232        // RegionNames are created inside the build closure; smuggle them out via Rc<RefCell>.
8233        let r1: Rc<RefCell<Option<super::RegionName>>> = Rc::new(RefCell::new(None));
8234        let r2: Rc<RefCell<Option<super::RegionName>>> = Rc::new(RefCell::new(None));
8235
8236        let _circuit = RootCircuit::build({
8237            let monitor = monitor.clone();
8238            let r1 = r1.clone();
8239            let r2 = r2.clone();
8240            move |circuit| {
8241                monitor.attach(circuit, "monitor");
8242
8243                let region1 = circuit.create_region_name(REGION, 0);
8244                let region2 = circuit.create_region_name(REGION, 1);
8245
8246                circuit.open_region(region1.clone());
8247                circuit.add_source(Generator::new(|| 1_i32));
8248                circuit.close_region(region1.clone());
8249
8250                circuit.open_region(region2.clone());
8251                circuit.add_source(Generator::new(|| 2_i32));
8252                circuit.close_region(region2.clone());
8253
8254                *r1.borrow_mut() = Some(region1);
8255                *r2.borrow_mut() = Some(region2);
8256                Ok(())
8257            }
8258        })
8259        .unwrap()
8260        .0;
8261
8262        let r1 = r1.borrow();
8263        let r2 = r2.borrow();
8264        // Two distinct region nodes, each containing one operator.
8265        assert_eq!(monitor.count_regions_with_name(REGION), 2);
8266        assert_eq!(monitor.count_nodes_in_region(r1.as_ref().unwrap()), Some(1));
8267        assert_eq!(monitor.count_nodes_in_region(r2.as_ref().unwrap()), Some(1));
8268    }
8269
8270    #[test]
8271    fn init_circuit_constructor_error() {
8272        match RootCircuit::build(|_circuit| Err::<(), _>(anyhow!("constructor failed"))) {
8273            Err(DbspError::Constructor(msg)) => assert_eq!(msg.to_string(), "constructor failed"),
8274            _ => panic!(),
8275        }
8276    }
8277}