Stream Processing and Event Streaming Questions
Building on event-streaming platforms: Kafka and message queues, event sourcing, partitioning, consumer groups, exactly-once vs at-least-once delivery, and windowing. Covers handling late and out-of-order events, watermarks, and stateful stream operators. The core skill for real-time data engineering.
Compare Avro, Protocol Buffers, and JSON as serialization formats for streaming events. Discuss payload size, parsing speed, and how well each supports schema enforcement and evolution.
Sample Answer
Direct answer
Avro and Protocol Buffers are both compact, schema-driven binary formats well suited to high-throughput streaming, with Avro's schema-registry integration especially mature in the Kafka ecosystem, while JSON is human-readable and requires no schema-registry infrastructure at all, at the cost of much larger payloads, slower parsing, and no built-in schema enforcement.
Structured elaboration
Avro embeds enough information (via a schema known to both producer and consumer, resolved through a schema registry) to serialize very compactly with no field names repeated per record, and has first-class, mature tooling for schema evolution and compatibility checking in the streaming ecosystem specifically. Protocol Buffers achieve similarly compact payloads via field numbering rather than a full schema lookup, and are often preferred by teams already using them for gRPC service contracts, giving one shared schema definition style across both streaming and RPC. JSON needs no schema registry, no code generation, and is trivially human-readable for debugging, but every record repeats its field names as text, inflating payload size significantly, and has no built-in mechanism to detect a breaking schema change before it happens.
On parsing speed specifically: JSON parsing is the slowest of the three because a decoder has to tokenize raw text, infer types on the fly, and match string field names against the target structure for every single record. Avro and Protobuf are both binary formats and parse substantially faster since there's no text tokenization or type inference involved, but they aren't identical to each other either: Protobuf typically decodes a bit faster than Avro in practice because its generated (code-genned) bindings give direct, compiled field access by tag number, whereas Avro's decoding commonly goes through a schema-resolution step at read time (matching the writer's schema against the reader's), which is very fast but still an extra layer of indirection compared to Protobuf's generated getters.
Worked example
For a very high-throughput, cost-sensitive pipeline (billions of events per day where payload size and per-record decode cost directly affect network, storage, and CPU cost), Avro or Protobuf's compactness and faster parsing pay for themselves quickly; for a lower-volume internal debugging or prototyping topic where engineers frequently want to eyeball raw messages without a deserialization tool, JSON's readability is worth the size and parsing-speed cost. A team already using Protobuf for its gRPC services often standardizes on Protobuf for its Kafka topics too, avoiding the cost of maintaining two separate schema ecosystems, and getting Protobuf's typically faster decode path as a side benefit.
Trade-offs and pitfalls
JSON's lack of enforced schema is the most common source of "silent breaking change" incidents: a producer field rename or type change ships with no build-time or registry-level warning, and consumers discover the break only in production. Avro's registry-based approach requires the extra infrastructure and operational dependency of running (or using a managed) schema registry, which is a real cost some smaller teams underestimate before committing to it. On parsing speed, it's easy to overweight the Protobuf-versus-Avro difference: it's real but usually much smaller than either format's gap versus JSON, so choosing between Avro and Protobuf should still be driven mainly by ecosystem fit (schema registry maturity, existing gRPC usage) rather than a marginal decode-speed edge.
A small number of heavy keys are producing most of a topic's traffic, creating hot partitions and outage-like symptoms. Describe how you'd detect this and remediate it without breaking the per-key ordering guarantee downstream consumers rely on.
Sample Answer
Direct answer
Detecting and remediating a hot partition without breaking ordering means either splitting the hot key into several sub-keys that route to different partitions (salting), or, if that's not possible without breaking a real business ordering requirement for that key, isolating the hot key onto its own dedicated partition rather than force-spreading it.
Structured elaboration
Detection is straightforward: monitor per-partition throughput and identify partitions receiving disproportionately more traffic than the average, then trace that imbalance back to specific keys mapping to those partitions. Remediation has two genuinely different shapes depending on whether the hot key's events actually need strict relative ordering: if they don't (each event for that key can be processed independently, in any order, without breaking correctness), salting the key into sub-keys spreads its load across multiple partitions cleanly. If the key's events DO need strict ordering (a common case for high-volume, high-value keys, like a major customer's order stream), salting isn't safe, since it would let events for that same key be processed in parallel, out of order, across sub-keys, so the remediation instead is to give that specific hot key its own dedicated, appropriately-provisioned partition, isolating its load rather than attempting to spread it.
Worked example
A topic where 10% of partitions receive 90% of traffic due to a handful of very large, heavy-user keys: for keys where per-key ordering genuinely doesn't matter (say, individual page-view events with no cross-event dependency), salting each hot key into several sub-keys spreads that key's traffic across multiple partitions immediately. For a hot key representing a major customer's order stream, where per-order-key ordering genuinely matters for correctness, the fix instead routes that specific customer's key to a dedicated partition sized and provisioned for its actual (much higher) throughput, leaving the rest of the topic's more evenly-distributed keys on the regular partition scheme.
Trade-offs and pitfalls
Salting a key that genuinely needs ordering silently breaks correctness while looking, from a load-distribution metric alone, like a successful fix; the ordering requirement has to be checked first, not assumed away because the throughput problem is otherwise straightforward to solve. Dedicating a partition to one hot key is a manual, ongoing operational overhead (someone has to notice when a NEW key becomes hot and repeat the process) rather than a fully automatic, self-scaling solution.
Design the operational controls for a multi-tenant streaming platform (shared Kafka cluster and stream-processing jobs) used by many teams: resource quotas, access controls, network isolation, and how you'd prevent one noisy tenant from degrading others.
Sample Answer
Direct answer
Operating a multi-tenant streaming platform means enforcing per-tenant resource quotas, isolating access via authentication and authorization, and building in guardrails so one tenant's misbehaving workload can't degrade everyone else's throughput or latency.
Structured elaboration
Resource quotas (throughput quotas on produce/fetch requests, storage quotas per topic or tenant) are the primary lever against noisy neighbors: without them, one tenant pushing an unexpected volume spike can consume shared broker bandwidth or CPU that every other tenant depends on. Access controls (ACLs scoping which principals can produce or consume from which topics, network-level isolation for genuinely sensitive tenants) prevent one team from accidentally (or maliciously) reading or writing another's data. For stateful stream-processing jobs specifically (Flink or Kafka Streams jobs run on shared compute, often on Kubernetes), the same isolation problem shows up as noisy-neighbor CPU/memory contention between jobs on the same cluster, addressed with resource requests/limits per job, dedicated node pools for the largest tenants, and safe rolling-upgrade paths that don't require taking the whole shared cluster down.
Worked example
Concretely, a shared cluster serving five teams would set a per-tenant produce-byte-rate quota (so no single tenant's traffic spike starves the others), ACLs scoping each team's service accounts to only their own topic prefix, and, for the Kubernetes-hosted stream-processing jobs layer, per-job resource requests/limits plus pod anti-affinity rules so two large jobs don't land on the same node and compete for the same CPU. A safe upgrade path (rolling one node at a time, verifying job health before proceeding) avoids a platform-wide outage when applying a change that affects every tenant's jobs.
Trade-offs and pitfalls
Too-strict quotas can throttle a legitimate tenant during an expected traffic spike (a marketing campaign, a seasonal peak) unless quotas are reviewed and adjusted proactively rather than reactively. The common operational mistake is treating multi-tenancy as purely an access-control problem and skipping resource quotas entirely, which leaves the platform vulnerable to exactly the noisy-neighbor scenario multi-tenancy is supposed to guard against; ACLs stop the wrong people from touching the wrong data, but they don't stop the right person's workload from starving everyone else.
How would you diagnose and respond to network partitions and split-brain-like symptoms in a streaming ecosystem where brokers, a controller/coordination layer, and downstream stream processors all disagree about cluster state?
Sample Answer
Direct answer
Network partitions and split-brain-like symptoms in a streaming ecosystem show up as brokers, their controller/coordination layer, and downstream processors disagreeing about who's the leader for a partition or which consumer owns which work; the fix is always to trust the coordination layer's quorum decision and force everything else (leader assignment, consumer offsets) to converge to it, never to let two sides keep operating independently.
Structured elaboration
A network partition can split a broker cluster into two groups that can each see a majority (or, worse, neither sees a majority) of the coordination service. If the coordination layer correctly demotes a minority-side broker (a broker that can no longer reach quorum steps down as leader for its partitions), the system self-heals once connectivity is restored: the minority side simply catches up as a follower. The dangerous case is a controller or coordination-layer bug (or misconfiguration like unclean leader election enabled) that lets a minority-side broker keep acting as leader, accepting writes that the majority side never sees, which produces genuinely divergent histories for the same partition, a real split-brain.
Worked example
Concretely, your triage sequence: first check the coordination layer's own health and quorum status (is it itself partitioned, or reporting a clean picture); then check for any partition whose reported leader differs between what different brokers believe versus what the coordination layer says, which is the direct signature of a stale or diverged leader; then check consumer groups for members that appear registered from two different broker views simultaneously, an equivalent split-brain at the consumer-group level. Once found, the resolution is to force the ecosystem to converge on the coordination layer's authoritative view: fence off (or restart) any broker or processor still operating on a stale view, and, if unclean leader election was involved, treat the affected partitions' recent history as suspect and reconcile against a known-good replica.
Trade-offs and pitfalls
Disabling unclean leader election trades availability for consistency: if the only in-sync replica is unreachable, the partition simply goes unavailable rather than electing a stale replica that could silently lose recently acknowledged writes. Teams under availability pressure sometimes enable unclean leader election broadly to avoid outages, without recognizing they've traded a temporary availability problem for a permanent, silent data-consistency problem that's much harder to detect after the fact.
A client needs effectively-exactly-once writes into a system of record (for example a ledger or data warehouse) whose sink does not support distributed transactions. Given the available techniques (Kafka transactions with a two-phase-commit sink, idempotent upserts with dedup keys, outbox-plus-CDC, an external coordinator), which would you actually implement for a high-throughput case, and why?
Sample Answer
Direct answer
Given a sink that doesn't support distributed transactions, the most broadly practical choice is idempotent upserts keyed by a deterministic ID derived from the source offset, because it needs no special protocol support from the sink and degrades gracefully; reach for Kafka transactions with a two-phase-commit-capable sink, outbox-plus-CDC, or a fully external coordinator only when the sink genuinely can't express an idempotent write (a pure append-only log with no upsert semantics) or the write itself has side effects that can't be made idempotent.
Structured elaboration
Each option has a distinct failure mode to weigh. Kafka transactions (the producer's transactional.id, initTransactions/beginTransaction/commitTransaction API, paired with consumers reading at isolation.level=read_committed) give exactly-once atomicity across multiple Kafka topics and partitions, and can be extended end-to-end only if the sink itself participates in a two-phase-commit protocol (pre-commit, then a durable commit marker written once the transaction coordinator confirms); this works well when the sink is Kafka itself or a connector explicitly built for it, but most warehouses and ledgers don't expose that kind of commit protocol, and even when they do, the extra coordinator round-trip per transaction adds real latency that hurts at high throughput. Idempotent upserts (keyed by, say, partition + offset) are simple and need no sink cooperation beyond supporting an upsert, but require the write itself to be naturally expressible as "set this row to this value" rather than an unconditional append or an external side effect (charging a card, calling a third-party API) that can't be replayed harmlessly. Outbox-plus-CDC (writing the intended change to a local transactional table, then relaying it via change-data-capture) buys atomicity between a primary write and a downstream event at the cost of an extra moving part (the CDC relay) and added latency. A fully external coordinator (building your own two-phase-commit-like protocol across the source and sink, distinct from Kafka's own built-in transactions) gives a similarly strong guarantee to the Kafka-transactions option but without even Kafka's native support, making it the most operationally complex to build and maintain correctly; it's rarely justified unless neither of the simpler options fits.
Worked example
For a high-throughput case writing aggregated metrics into a data warehouse table: an idempotent upsert keyed by (window_start, dimension_key) is almost always sufficient, since re-computing and re-writing the same aggregate for the same window and key after a recovery produces an identical row, with no need for the warehouse to understand Kafka transactions or a two-phase-commit protocol at all. This is the implementation you'd actually ship for that case; Kafka transactions with a 2PC-capable sink would add a coordinator round-trip to every commit for a guarantee the idempotent upsert already provides more cheaply here, and building a fully external coordinator would add even more operational surface for no additional benefit in this scenario.
Trade-offs and pitfalls
The idempotent-upsert approach breaks down the moment the operation has an external side effect that can't be expressed as a deterministic upsert (sending an email, calling a payment gateway); those cases genuinely need either Kafka transactions paired with a sink that can honor a two-phase commit, a fully external coordinator, or an idempotency-key mechanism enforced at the external system's own API boundary, which the streaming pipeline alone can't manufacture. Choosing the heaviest option (Kafka transactions with a 2PC sink, or a hand-built external coordinator) by default, out of caution, adds real operational and latency cost that's usually unjustified when a simpler idempotent write would have been sufficient for the actual side effect involved.
Unlock Full Question Bank
Get access to all 34 Stream Processing and Event Streaming interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.