Skip to main content

dora_node_api/
lib.rs

1//! This crate enables you to create nodes for the [Dora] dataflow framework.
2//!
3//! [Dora]: https://dora-rs.ai/
4//!
5//! ## The Dora Framework
6//!
7//! Dora is a dataflow frame work that models applications as a directed graph, with nodes
8//! representing operations and edges representing data transfer.
9//! The layout of the dataflow graph is defined through a YAML file in Dora.
10//! For details, see our [Dataflow Specification](https://dora-rs.ai/docs/api/dataflow-config/)
11//! chapter.
12//!
13//! Dora nodes are typically spawned by the Dora framework, instead of spawning them manually.
14//! If you want to spawn a node manually, define it as a [_dynamic_ node](#dynamic-nodes).
15//!
16//! ## Normal Usage
17//!
18//! In order to connect your executable to Dora, you need to initialize a [`DoraNode`].
19//! For standard nodes, the recommended initialization function is [`init_from_env`][`DoraNode::init_from_env`].
20//! This function will return two values, a [`DoraNode`] instance and an [`EventStream`]:
21//!
22//! ```no_run
23//! use dora_node_api::DoraNode;
24//!
25//! let (mut node, mut events) = DoraNode::init_from_env()?;
26//! # Ok::<(), eyre::Report>(())
27//! ```
28//!
29//! You can use the `node` instance to send outputs and retrieve information about the node and
30//! the dataflow. The `events` stream yields the inputs that the node defines in the dataflow
31//! YAML file and other incoming events.
32//!
33//! ### Sending Outputs
34//!
35//! The [`DoraNode`] instance enables you to send outputs in different formats.
36//! For best performance, use the [Arrow](https://arrow.apache.org/docs/index.html) data format
37//! and one of the output functions that utilizes shared memory.
38//!
39//! ### Receiving Events
40//!
41//! The [`EventStream`] is an [`AsyncIterator`][std::async_iter::AsyncIterator] that yields the incoming [`Event`]s.
42//!
43//! Nodes should iterate over this event stream and react to events that they are interested in.
44//! Typically, the most important event type is [`Event::Input`].
45//! You don't need to handle all events, it's fine to ignore events that are not relevant to your node.
46//!
47//! The event stream will close itself after a [`Event::Stop`] was received.
48//! A manual `break` on [`Event::Stop`] is typically not needed.
49//! _(You probably do need to use a manual `break` on stop events when using the
50//! [`StreamExt::merge`][`futures_concurrency::stream::StreamExt::merge`] implementation on
51//! [`EventStream`] to combine the stream with an external one.)_
52//!
53//! Once the event stream finished, nodes should exit.
54//! Note that Dora kills nodes that don't exit quickly after a [`Event::Stop`] of type
55//! [`StopCause::Manual`] was received.
56//!
57//!
58//!
59//! ## Dynamic Nodes
60//!
61//! <div class="warning">
62//!
63//! Dynamic nodes have certain [limitations](#limitations). Use with care.
64//!
65//! </div>
66//!
67//! Nodes can be defined as `dynamic` by setting their `path` attribute to `dynamic` in the
68//! dataflow YAML file. Dynamic nodes are not spawned by the Dora framework and need to be started
69//! manually.
70//!
71//! Dynamic nodes cannot use the [`DoraNode::init_from_env`] function for initialization.
72//! Instead, they can be initialized through the [`DoraNode::init_from_node_id`] function.
73//!
74//! ### Limitations
75//!
76//! - Dynamic nodes **don't work with `dora run`**.
77//! - As dynamic nodes are identified by their node ID, this **ID must be unique**
78//!   across all running dataflows.
79//! - For distributed dataflows, nodes need to be manually spawned on the correct machine.
80//!
81//!
82//! ## Node Integration Testing
83//!
84//! Dora provides built-in support for integration testing of nodes. See the [integration_testing]
85//! module for details.
86
87#![warn(missing_docs)]
88
89pub use arrow;
90pub use dora_arrow_convert::*;
91pub use dora_core::{self, uhlc};
92pub use dora_message::{
93    DataflowId,
94    metadata::{
95        self, FIN, FLUSH, GOAL_ID, GOAL_STATUS, GOAL_STATUS_ABORTED, GOAL_STATUS_CANCELED,
96        GOAL_STATUS_SUCCEEDED, Metadata, MetadataParameters, Parameter, REQUEST_ID, SEGMENT_ID,
97        SEQ, SESSION_ID, get_bool_param, get_integer_param, get_string_param,
98    },
99};
100use dora_message::{
101    common::Timestamped,
102    daemon_to_node::{DaemonCommunication, DaemonReply},
103    node_to_daemon::DaemonRequest,
104};
105pub use event_stream::{
106    Event, EventScheduler, EventStream, StopCause, TryRecvError,
107    input_tracker::{InputState, InputTracker},
108    merged,
109};
110pub use flume;
111pub use flume::Receiver;
112pub use futures;
113#[cfg(feature = "tracing")]
114pub use node::init_tracing;
115pub use node::{
116    DataSample, DoraNode, DoraNodeBuilder, StreamSegment, ZERO_COPY_THRESHOLD, arrow_utils,
117};
118pub use uuid;
119
120pub use serde_json;
121use tokio::sync::oneshot;
122
123mod daemon_connection;
124mod error;
125mod event_stream;
126pub mod integration_testing;
127mod node;
128
129pub use error::{NodeError, NodeResult, PatternError};
130
131/// Backward-compatible alias so code using the old `DoraNode` name still compiles.
132/// Backward-compatible alias for [`Event`].
133pub use Event as DoraEvent;
134
135#[derive(Debug)]
136enum DaemonCommunicationWrapper {
137    Standard(DaemonCommunication),
138    Testing {
139        channel:
140            tokio::sync::mpsc::Sender<(Timestamped<DaemonRequest>, oneshot::Sender<DaemonReply>)>,
141    },
142}
143
144impl From<DaemonCommunication> for DaemonCommunicationWrapper {
145    fn from(value: DaemonCommunication) -> Self {
146        DaemonCommunicationWrapper::Standard(value)
147    }
148}