Data Pipeline Scalability and Performance Questions
Diagnosing and fixing throughput, cost, and capacity problems in data pipelines: batch ETL/ELT jobs and streaming ingestion that must handle growing data volume within cost and SLA constraints. Covers bottleneck diagnosis (I/O-bound versus CPU-bound stages, profiling a slow pipeline); partition and key design as a load-distribution DECISION for a processing job (choosing keys, avoiding and mitigating hot partitions); recognizing when a transformation forces an expensive shuffle or join and choosing a mitigation strategy at the decision level, not engine-internal tuning; capacity planning anchored in real data volume (cluster, storage, and network sizing from an event rate and payload profile); and throughput techniques including incremental processing, result caching and materialization, storage-format and compaction choices for throughput, and cost-efficient scaling (autoscaling policy, spot/reserved capacity trade-offs). Also covers multi-tenant resource governance for shared data-processing infrastructure (quotas, isolation, throughput enforcement). Distinct from distributed-data-processing-with-spark-and-hadoop (engine-internal execution mechanics of a specific framework), data-reliability-and-fault-tolerance (correctness and consistency guarantees under failure, e.g. exactly-once semantics and checkpointing), workflow-orchestration-and-scheduling (DAG/dependency coordination), and general application or service performance tuning (web request latency, generic distributed-systems architecture with no data-pipeline framing).
Present a prioritized plan to reduce monthly compute costs for nightly Spark jobs by 40% while minimizing impact on job completion times. List your candidate optimizations, expected savings per action, how you'd validate savings safely, and a recommended rollout order.
Sample Answer
Direct answer. Reducing data-processing compute cost by a large percentage without breaking SLAs works best as a prioritized set of levers applied roughly in order of expected-savings-per-effort, validated incrementally rather than applied all at once, so a cost regression can be attributed to a specific change and rolled back cleanly if it risks the SLA.
Structured elaboration.
- Candidate levers, roughly ordered by typical impact-to-effort ratio. (a) Right-sizing: many clusters/instances are provisioned generously and rarely revisited -- auditing actual utilization against provisioned capacity often finds low-risk, immediate savings. (b) Partitioning: repartitioning or adding partition pruning so jobs scan only the data they actually need (by date, by key) cuts both compute time and data volume moved -- often a large win when the current layout forces full-table or full-partition scans. (c) Caching: caching a reused expensive intermediate result (a join output, an aggregation) across jobs that currently recompute it repeatedly removes duplicated compute entirely, at the cost of a cache-invalidation policy to keep it correct. (d) Storage lifecycle and format: moving cold data to cheaper tiers and adopting better compression/columnar formats reduces ongoing storage cost with minimal processing-logic risk. (e) Spot instances for tolerant workloads (instances reclaimable with short notice at a steep discount, safe for checkpointed batch work). (f) Job consolidation: multiple small, similar jobs run separately often share fixed per-job overhead that consolidation eliminates. (g) Scheduling: shifting non-time-critical batch work to off-peak windows where spot/reserved pricing is more favorable. (h) Data-transfer cost: at large scale, cross-region or cross-service data movement can be a surprisingly large cost line; auditing and reducing unnecessary transfer often yields savings nobody had been tracking.
- Quick wins vs. longer-term investment. Right-sizing and storage-tier lifecycle policies are typically quick, low-risk wins (config/policy changes, not code rewrites); job consolidation and format migration are medium-effort; deeper architectural changes (moving compute location, redesigning a pipeline's core processing logic) are longer-term investments reserved for when the quick/medium wins are exhausted and cost pressure remains.
- Measuring improvements and attribution. Roll out changes incrementally (one lever, or a small batch of independent levers, at a time) and measure BOTH cost and SLA-relevant metrics (latency, completion time, error rate) before and after each change, so a regression can be attributed to the specific change that caused it rather than lost in a bundle of simultaneous changes.
- Cost attribution to teams/pipelines. Tag resources and cost line items by owning team/pipeline BEFORE starting the optimization effort, so savings (and any regressions) can be measured and communicated at the granularity stakeholders actually care about, and so future cost growth can be caught early by the team closest to the cause.
- Validating savings safely. For any change carrying correctness or SLA risk (job consolidation, format changes, moving compute location), validate against a comparable data volume in a non-production environment first, and roll out to production with a defined rollback plan and monitoring window before declaring the change permanent.
- Rollout order. Quick wins first (fastest validated savings, builds momentum and buys margin for riskier changes later), then medium-effort changes, with the highest-risk architectural changes last and only if cost targets are not yet met by the earlier tiers.
Worked example. A team facing pressure to cut cost by 40% without breaking SLAs finds via a resource audit that (a) several clusters are provisioned at roughly 2x their peak observed utilization (right-sizing alone plausibly recovers a meaningful fraction of the target), (b) 60% of data older than 30 days sits on hot storage despite being queried less than once a month (moving it to a cheaper cold storage tier recovers further savings at near-zero risk), and (c) three separate small nightly jobs process overlapping data with duplicated per-job overhead (consolidating them removes that duplication), and (d) two of those same jobs re-run an identical expensive join against a slowly-changing dimension on every invocation, a strong caching candidate once the jobs are consolidated. Applying right-sizing, partition pruning, and storage tiering first (lowest risk, fast to validate) captures a large share of the 40% target within the first rollout wave; job consolidation, validated carefully against output correctness given its higher implementation risk, closes most of the remaining gap; if a residual gap remains after these, spot-instance adoption for the most preemption-tolerant remaining workloads is the next lever, reserving any deeper architectural change as a longer-term follow-up rather than a first-wave requirement.
Trade-offs & pitfalls. The temptation under cost pressure is to apply the most aggressive lever (often spot instances or an architectural rewrite) first because it sounds impactful, when right-sizing and storage-lifecycle changes are frequently both cheaper to implement AND lower-risk -- sequence by risk-adjusted impact, not by how dramatic a change sounds. For an ML-specific variant of this problem (cutting cost for a training pipeline), REPRODUCIBILITY is an added constraint the general levers above do not automatically respect: sampling or aggressive caching applied to training data must be validated not to silently change model outputs run-to-run, which is a correctness risk beyond the usual SLA/latency risk this framework otherwise covers.
Design an autoscaling policy for Spark Structured Streaming jobs running on Kubernetes. Define which metrics you would use (e.g., input lag, CPU, shuffle write rate), threshold values or heuristics, cooldown windows, scaling granularity, and safeguards such as max pods and prewarming. Explain how you would avoid oscillation and protect stateful jobs during scale operations.
Sample Answer
Direct answer. An autoscaling policy for a stateful streaming job needs metrics that reflect ACTUAL backlog and resource pressure (input lag and CPU/shuffle-write-rate, not just raw event rate), thresholds with cooldown windows to avoid flapping, and explicit safeguards for the state-redistribution cost that makes scaling a STATEFUL job fundamentally more disruptive than scaling a stateless one.
Two terms this answer leans on throughout. "State" means a running total or aggregate the job keeps per key (e.g., a running sum or session window per user) that lives in the job's memory across many incoming events, rather than being read fresh from storage each time -- this is why scaling a stateful job is harder than scaling a stateless one: the state has to move too. "Shuffle" means redistributing records across machines over the network so that records sharing the same key land on the same worker (needed for a per-key aggregation or join); it is expensive because it moves data across the network and forces a synchronization point, unlike work a single machine can do on its own local data.
Structured elaboration.
- Metrics to scale on. Input/consumer lag (the most direct signal that the job is falling behind, distinct from CPU which can look fine even while lag grows if the bottleneck is elsewhere), CPU utilization (a secondary signal for resource pressure not yet manifesting as lag), and shuffle-write rate (specific to jobs with a shuffle-heavy stage, since a job can be lag-healthy but shuffle-bound in a way that predicts an imminent problem).
- Thresholds and cooldowns. Scale up when lag exceeds a threshold sustained for a short window (avoiding reaction to a single noisy spike); scale down only after a LONGER sustained period of low lag/CPU (asymmetric cooldowns: scale up fast to protect the SLA, scale down conservatively to avoid oscillation from scaling down right before the next natural traffic increase).
- Scaling granularity. For a stateful job, the unit of scaling matters: adding whole task managers/pods (coarser -- a task manager is a worker process in a Flink-style cluster that runs one or more parallel slots of the job) is simpler and less disruptive to already-running state than fine-grained per-slot scaling, which may require MORE frequent state redistribution for marginal gains.
- Safeguards. A maximum-pod/instance cap (bounding worst-case cost from a runaway scaling loop), pre-warming (starting new instances slightly ahead of when they will be needed, if load is predictable, to hide their startup latency from the scaling decision's effective reaction time), and explicit protection for stateful jobs specifically: a state-redistribution BUDGET (how much state can be rebalanced per scaling event) so a single scale-up does not trigger a rebalance so large it causes its own latency spike, defeating the purpose of scaling up in the first place.
- Avoiding oscillation. Combine the cooldown windows above with hysteresis (different thresholds for scaling up vs. scaling down, rather than one threshold crossed in both directions) so the system does not scale up, immediately see the resulting lower-per-instance load, scale back down, and repeat.
- Predictable-spike handling. For KNOWN traffic patterns (e.g., a predictable morning spike), scheduled/predictive pre-scaling ahead of the expected spike avoids the reactive-scaling lag entirely for that case, reserving reactive autoscaling as the fallback for genuinely unpredictable load.
Worked example. A stateful streaming job with per-key aggregation state scales from 10 to 15 task managers when consumer lag exceeds 30 seconds for 3 consecutive minutes. Because scaling a stateful job means redistributing existing key-state across the new task-manager count, the policy caps this specific scaling event to a bounded redistribution (e.g., limiting how many keys move at once, or using an incremental-checkpoint-based rescale rather than a full state reload) so the job does not incur a multi-minute pause exactly when it is already under lag pressure. Scale-down triggers only after lag stays under 5 seconds for 20 minutes (a much longer, asymmetric window), preventing an immediate scale-back-down the moment the scale-up relieves pressure.
Trade-offs & pitfalls. The most common mistake in autoscaling a STATEFUL job specifically is applying a stateless-job autoscaling policy (fast, symmetric thresholds, fine-grained scaling) without accounting for state-redistribution cost, which can make scaling events themselves a source of latency spikes rather than a relief from them -- the redistribution cost must be an explicit input to the policy's design, not an afterthought discovered in production. Predictable/scheduled scaling reduces reaction lag for known patterns but requires the underlying traffic pattern to genuinely BE predictable; applying scheduled pre-scaling to load that turns out to be irregular wastes cost during the scheduled windows without protecting against the actual unpredictable spikes.
Design a benchmark to compare ingesting Parquet (columnar) versus Avro (row) into an analytical data warehouse. Specify dataset characteristics (schema, cardinality, nullability), ingestion metrics (MB/s, CPU), query metrics (p50/p95 query latency for typical analytic queries), compression codecs, schema-evolution scenarios, and overall cost measurement strategy.
Sample Answer
Direct answer. Benchmarking Parquet against Avro for ingestion into an analytical warehouse requires controlling for dataset characteristics that change the answer (schema width, cardinality, nullability) and measuring BOTH ingestion cost and query cost, because the two formats trade off in opposite directions on those two axes and a benchmark that only measures one will recommend the wrong format for the actual workload.
Structured elaboration.
- Dataset characteristics to specify up front, since they materially change the result: schema width (a wide table with 100 columns shows Parquet's columnar-scan advantage much more dramatically than a 5-column table), cardinality per column (low-cardinality columns compress far better in columnar layout, so a benchmark should report per-column cardinality, not just row count), and nullability (sparse/mostly-null columns compress very differently in row vs. columnar layout).
- Ingestion metrics. MB/s written and CPU utilization during the write path -- Avro's row-oriented write is typically faster here for streaming/small-batch writes, so this metric is where Avro is expected to look better, and the benchmark should confirm by how much.
- Query metrics. p50/p95 query latency for a representative set of TYPICAL analytic queries (not a single query type) -- critically, include both narrow-column queries (2-3 of many columns, where Parquet's advantage is largest) and wide queries (most/all columns, where the gap should narrow or reverse).
- Compression codec as a controlled variable. Test at least two codecs (e.g., Snappy for speed, a higher-ratio codec for storage cost) for each format, since codec choice can swing results as much as the format choice itself, and conflating the two in reported results makes the benchmark's conclusion ambiguous about which variable actually drove the outcome.
- Schema-evolution scenarios. Run a dedicated sub-benchmark, not just a static-schema one: add a nullable column mid-dataset, remove/rename a column, and widen a numeric type, then measure (a) whether ingestion of NEW data requires touching already-written files for either format, (b) whether a query spanning old and new files still returns correct results without manual intervention, and (c) any added query latency from reconciling schema versions across files. This is where the two formats differ mechanically: Avro resolves reader/writer schema per record at read time, so old and new records coexist naturally in the same file or stream; Parquet's per-file schema means a query spanning files written before and after the change relies on the query engine's schema-merge support, which the benchmark should time separately from a same-schema query of equivalent size.
- Overall cost measurement. Combine storage cost (compressed size x storage price) and compute cost (query time x compute price) into a single comparable number for the workload's actual read:write ratio, since a format that ingests slower but queries much faster is a net win for a read-heavy workload and a net loss for a write-heavy one.
Worked example. A 50-column clickstream schema with high-cardinality user/session IDs and mostly-populated columns, benchmarked at 3 query widths (2-column, 10-column, all-50-column) and both codecs: expect Parquet+Snappy to show the largest p95 latency advantage on the 2-column query (potentially an order of magnitude, since it can skip reading 48 of 50 columns' bytes from disk), a narrower advantage at 10 columns, and Avro potentially competitive or better at the full-50-column query, where columnar's selective-read advantage disappears and Avro's simpler row reconstruction may win. Reporting all three widths, rather than picking one, is what makes the benchmark actually decision-useful for a team whose real query mix is a blend.
Trade-offs & pitfalls. The most common benchmarking mistake here is running a single query shape (often a narrow, columnar-favoring query, since that is the 'expected' Parquet-wins case) and generalizing the result to the whole workload -- a benchmark's value is in matching the TEST query mix to the REAL query mix, not in confirming a prior assumption about which format is 'obviously' better.
Your Kafka + Flink consumers report sustained high CPU and falling throughput. Provide an investigation checklist to determine whether the CPU is due to (a) inefficient user code, (b) expensive deserialization, (c) GC, (d) network/IO waits, or (e) partition hotspots. For each item list the commands, logs, or metrics you would collect.
Sample Answer
Direct answer. When Kafka+Flink consumers show sustained high CPU with falling throughput, the fastest path to a diagnosis is a structured elimination across the five usual suspects -- inefficient user code, expensive deserialization, garbage collection, network/I/O waits, and partition hotspots -- because each has a distinct, cheap-to-check signature, and guessing which one it is wastes far more time than checking all five in a fixed order.
Structured elaboration.
| Suspect | What to check | Commands / metrics | Distinguishing signal |
|---|---|---|---|
| (a) Inefficient user code | CPU flame graph / profiler on a consumer instance | async-profiler, JFR (Java Flight Recorder), or py-spy for Python consumers | Flame graph shows time concentrated in application code (a specific transform function), not framework internals |
| (b) Expensive deserialization | Format and schema-registry overhead | Compare CPU time in the deserializer class specifically (visible in the same flame graph); check message format (JSON vs Avro/Protobuf) | Deserializer/parser frames dominate the profile even though the user's business logic per record is trivial |
| (c) GC | JVM GC logs / pause metrics | jstat -gcutil, GC log analysis, or the streaming framework's exposed GC-pause metric | Frequent long GC pauses correlated with the throughput dips; CPU usage includes a large GC-thread share |
| (d) Network / I/O waits | Thread dump or wait-state sampling | jstack (look for threads BLOCKED/WAITING on socket reads), NIC utilization, downstream-sink latency | High CPU number is misleading here -- check whether it is genuine compute or busy-waiting/polling; downstream sink latency correlates with the slowdown |
| (e) Partition hotspots | Per-partition consumer-lag and per-partition throughput | Kafka consumer-group lag broken down by partition | One or a few partitions show much higher lag/throughput than the others while total partition count and consumer count look adequate on paper |
The order matters: (a) and (b) are visible in the same flame graph in one profiling pass, so check them together first; (c) and (d) each have one specific, cheap metric to confirm or rule out; (e) is the odd one out because CPU-per-instance can look uniform even when the real problem is skew, so it needs a per-partition breakdown rather than a per-instance one.
Worked example. A flame graph shows 60% of sampled stack traces inside the Avro deserializer, with the actual per-record business logic taking under 5%. GC logs show pause times under 50ms, well within budget. This points squarely at (b): deserialization overhead, not inefficient logic or GC. The fix (evaluate a faster deserializer, cache schema lookups instead of resolving per record, or reduce schema complexity/nesting) targets the actual bottleneck instead of the team's first instinct, which is usually to add more consumer instances -- that would scale the deserialization cost linearly with cost while leaving the underlying inefficiency in place.
Trade-offs & pitfalls. "High CPU" alone under-determines the cause -- CPU time spent in GC, in deserialization, and in genuine business logic all look identical on a bare CPU-utilization dashboard, which is why the flame graph (which attributes CPU time to specific code paths) is the tool that actually resolves the ambiguity, not the utilization percentage itself. A common mistake is treating (d) network/I/O wait as a CPU problem because the process shows high CPU from busy-polling on a socket; a thread-state dump distinguishes genuine compute from spin-waiting. Partition hotspots (e) are the easiest to miss because they hide behind a healthy-looking AVERAGE across instances -- always check the per-partition distribution, not just the aggregate.
Explain backpressure in streaming systems. Describe at least three distinct mechanisms frameworks or architectures use to implement it, when to apply each, and how backpressure impacts upstream latency, throughput, and system stability.
Sample Answer
Direct answer. Backpressure is the mechanism by which a slow downstream consumer signals an upstream producer to slow down, rather than letting unbounded data pile up in memory until something crashes; without it, a fast producer and a slow consumer inevitably lead to either out-of-memory failures or silent data loss when internal buffers overflow.
Structured elaboration. Three broad mechanism families, each trading off differently between safety, throughput, and complexity:
- Credit-based / token flow control. The consumer grants the producer a fixed "credit" (a number of messages or bytes it is allowed to send before waiting for more credit). This is precise and prevents overflow by construction, at the cost of a round-trip for credit renewal that can add latency if credit windows are too small. Used when strict boundedness matters (e.g., service-to-service streaming over gRPC).
- Bounded buffers with a drop policy. A fixed-size buffer absorbs short bursts; once full, the system applies an explicit policy (drop the newest message, drop the oldest, or block the producer). Simple to implement and reason about, but a drop policy means acknowledging that SOME data loss is an accepted trade-off under sustained overload -- appropriate for lossy telemetry, wrong for anything requiring durability guarantees.
- Upstream rate-limiting / throttling. The producer (or an intermediary) is told directly to slow its send rate, either via an explicit signal from the consumer (consumer-lag-driven throttling) or a pre-agreed rate cap. This avoids buffering pressure entirely by preventing the mismatch from occurring, at the cost of the producer needing somewhere of its own to hold back data it is not yet allowed to send (which just moves the buffering problem one hop further upstream unless the ORIGINAL data source can itself be throttled).
When to apply each. Credit-based flow control fits systems where every message matters and a controlled protocol exists between the two parties (internal service-to-service streams). Bounded buffers with drop fit lossy, high-volume telemetry where losing occasional data points under extreme load is acceptable. Upstream rate-limiting fits systems where the true data SOURCE can itself be slowed (e.g., a producer reading from a file or database can pause its own read rate) rather than an external event stream that cannot be paused (e.g., live sensor data, which must go into a buffer or be dropped, since the source itself cannot be told to wait).
Worked example. A stream processor consuming from a message broker falls behind during a traffic spike: with credit-based flow control, the processor stops issuing new credit once its internal queue reaches a watermark, and the broker (acting as the intermediary) naturally stops delivering further messages to that consumer until credit is renewed, propagating the slowdown back toward the original producer without the processor's own memory ever exceeding the credit-bounded queue size.
Impact on latency, throughput, and stability. All three mechanisms trade the same two things for the same third: they ADD latency (a credit round-trip, or a producer deliberately holding data instead of sending it immediately) and CAP throughput at whatever the slowest downstream stage can sustain, not at what the fastest upstream stage could otherwise produce -- in exchange for system STABILITY. Without backpressure, a persistent producer/consumer speed mismatch does not stay a local latency problem: an unbounded internal queue grows until the process OOMs, and the crash that follows is usually worse than the original slowdown, since a crashed consumer typically triggers a rebalance and a thundering herd of reconnecting or retrying clients, spreading the instability to parts of the system that were never actually overloaded. Backpressure converts an unbounded-growth failure mode into a bounded, visible slowdown, which is a stability win even when it costs real latency and throughput to get there.
Trade-offs & pitfalls. Backpressure affects the WHOLE chain, not just the immediate producer-consumer pair -- if propagated correctly, a slow sink several hops downstream should eventually cause the ORIGINAL source to slow down too; a common bug is implementing backpressure only at the last hop (consumer to its immediate broker) while every upstream hop keeps producing at full rate, simply moving the overflow point one stage earlier rather than resolving it. Backpressure that is too aggressive (over-eager throttling) sacrifices throughput unnecessarily during transient, self-resolving spikes; tuning the threshold/watermark that triggers backpressure against real traffic patterns matters as much as choosing the mechanism itself.
Unlock Full Question Bank
Get access to all 16 Data Pipeline Scalability and Performance interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.