Common flow patterns
This page collects graph shapes that come up often when building real pipelines.
Multiple producers (multi-source ingestion)
A flow can ingest from several independent sources and fan them into shared downstream processing — for example, several cameras feeding one detector pool:
from videoflow.core import Flow
from videoflow.processors import JoinerProcessor
from videoflow.consumers import CommandlineConsumer
def build_flow():
cam_a = VideoUrlReader('rtsp://.../a', name='cam-a')
cam_b = VideoUrlReader('rtsp://.../b', name='cam-b')
detector_a = ObjectDetector(name='det-a', nb_tasks=2)(cam_a)
detector_b = ObjectDetector(name='det-b', nb_tasks=2)(cam_b)
merged = JoinerProcessor(name='merged')(detector_a, detector_b)
out = CommandlineConsumer(name='out')(merged)
return Flow([out])
You list only the consumers; both producers are discovered automatically.
Fan-out to parallel branches
A single node’s output can feed several children. Each child gets its own copy of every message (broadcast), and the branches run independently:
frame = FrameReader(name='frame')
boxes = ObjectDetector(name='boxes')(frame)
faces = FaceDetector(name='faces')(frame) # same frame, different branch
overlay = JoinerProcessor(name='overlay')(frame, boxes, faces)
Contrast this with nb_tasks: distinct children each receive all messages
(fan-out/broadcast), whereas replicas of the same node compete for messages
(load balancing).
Joins
A processor with several parents is a join. It waits for one message from each
parent that originated from the same upstream event, then runs process() with
all of them. The classic use is re-attaching a derived result to the frame it came
from, so a downstream annotator can draw on the original image:
frame = FrameReader(name='frame')
detector = ObjectDetector(name='detector', nb_tasks=4)(frame)
annotator = BoundingBoxAnnotator(name='annotator')(frame, detector)
Note
A join runs single (nb_tasks=1) by default; the nodes around it can still be
replicated (here detector runs with nb_tasks=4). To scale the join
itself, give it partition_by='trace_id' so both halves of an event land on
the same replica. See Scaling and node allocation.
Reducing broker hops
Every edge in the graph is a network hop through the broker. For a chain of cheap, sequential CPU steps, that overhead can dominate the actual work. Two ways to reduce it:
Do more per node. Fold several trivial transformations into one processor’s
process()method instead of chaining several one-line processors.Choose the right mode. In Batch versus realtime mode, realtime mode avoids buffering entirely and keeps only the latest message per edge, which is cheaper for high-rate live sources.
Note
Earlier single-machine versions offered a TaskModuleNode to fuse a subgraph
into one operating-system process. Fusing nodes into a single container in
the distributed engine is not supported yet; for now, express the fused logic as
a single processor as described above.