What is the optimal way to read from multiple Kafka topics and write to different sinks using Spark Structured Streaming?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
The optimal pattern in Spark Structured Streaming is usually to read the relevant Kafka topics through one Kafka source, parse once, and then branch the resulting stream into separate output queries or route inside foreachBatch. The right choice depends on whether the sinks are independent or need coordinated batch-level routing logic.
Read Multiple Topics Through One Kafka Source
Spark can subscribe to several topics in one source by listing them or using a pattern. That is usually cleaner than starting one Spark application per topic unless the pipelines are completely unrelated.
That gives you one ingestion point, one offset-tracking story, and one place to handle shared parsing and enrichment.
Split the Stream by Topic or Record Type
After parsing, branch into filtered DataFrames for each output path.
If the same topic carries multiple event types, filter on a decoded field instead of the Kafka topic name.
Spark does not let one writeStream send to multiple sinks by itself, so each independent sink becomes its own streaming query:
Use a different checkpoint location per sink query.
Use foreachBatch When Routing Logic Is More Complex
If several sinks need batch-aware logic or one sink depends on another branch's computation, foreachBatch can be cleaner.
This approach gives you full batch DataFrame control, but you are now responsible for sink-side idempotency and failure handling more explicitly.
Choose the Pattern Based on Sink Independence
A useful rule is:
- use multiple streaming queries when each sink is operationally independent
- use
foreachBatchwhen one batch needs coordinated routing logic or shared batch computation
Independent queries are often easier to monitor because each sink has its own streaming query status and checkpoint. foreachBatch is more flexible, but it concentrates more responsibility in your code.
Common Pitfalls
The biggest mistake is thinking one writeStream can fan out to many sinks automatically. In practice, you either start multiple output queries from shared transformations or use foreachBatch to route manually.
Another common issue is reparsing the same Kafka payload in several branches. Parse once near the source, then reuse the structured columns.
People also reuse the same checkpoint path for multiple queries. That breaks correctness because each query needs its own state and progress tracking.
Finally, do not overstate guarantees. Spark checkpointing and Kafka offsets help with fault tolerance, but end-to-end delivery behavior still depends on the semantics of each sink.
Summary
- Read multiple Kafka topics through one Kafka source when the pipelines share parsing or infrastructure.
- Parse once, then branch by topic or event type.
- Use separate
writeStreamqueries for independent sinks. - Use
foreachBatchwhen you need custom routing or batch-level control. - Keep checkpoint locations separate and think carefully about sink-specific delivery guarantees.

