Stream-stream joins — order meets shipment
You will build: a pipeline that joins orders with their shipments inside a 5-minute window — the primitive behind order↔payment matching, alert↔ack correlation and supply-chain gap detection.
Prerequisites
- Comfortable with Windows & aggregations.
1 · Scaffold an app with a streaming stage
pulse new fulfillment --source webhook --stage streaming:match
2 · Declare the join
stages:
- name: match
engine: streaming
operators:
- type: join
leftTopic: fulfillment.in
rightTopic: shipments
on: orderId
window: "5m"
joinType: inner # inner · left · right · full
The join pairs every left event with right events sharing the same on key, as long as both arrive within the window. You don't wire the second topic anywhere else — declaring the join auto-subscribes the stage to rightTopic.
3 · Deploy and feed both sides
pulse secret set PULSE_WEBHOOK_SECRET dev-secret # the scaffolded webhook source requires it
pulse deploy .
pulse events publish --topic fulfillment.in --value '{"orderId":"o-42","item":"drone","amount":249}'
pulse events publish --topic shipments --value '{"orderId":"o-42","carrier":"dhl","tracking":"T-981"}'
4 · Watch the pair come out
pulse events tail --topic fulfillment.match.out
One joined event for o-42, carrying fields from both sides — the order and its shipment, correlated in flight.
5 · Make the misses the signal
joinType: left
With a left join, an order that never meets a shipment inside the window still emits — with the right side empty. Chain a rule-based stage on "shipment missing" and you've built shipped-but-never-scanned detection: the gaps are the alerts.
What just happened
You correlated two independent event flows declaratively — no consumer groups, no state store code, no reconciliation batch. The window bounds memory and defines lateness; the join type decides whether matches, misses or both flow downstream.
Troubleshooting
No joined events appear
Check the on field name exists in both payloads with identical values, and that both events were sent within the window. pulse events tail --topic shipments confirms the right side actually landed.
Deploy rejects the operator
leftTopic, rightTopic and window are required; joinType must be inner | left | right | full. The validator names the offending field.