The Spark Deep Dive: Intermediate
Spark is the single most interrogated technology in data engineering interviews. The pattern is consistent: explain the architecture, debug a slow job, optimize a join, size the cluster, handle skew. Five moves. If you can execute all five cleanly, you pass the technical gauntlet. If you fumble the shuffle explanation or blank on broadcast thresholds, the loop ends early.
What you will be able to do
Explain Spark Architecture
Draw the Spark execution model from driver to task
The Driver-Executor Model
Spark's driver-executor model: the driver builds the DAG and schedules tasks; executors run them in parallel (5-core rule); a wide transformation forces a shuffle exchange (the stage boundary) before the result is written.
| Component | Role | Key Resource |
|---|---|---|
| Driver | Plans, schedules, coordinates | Memory for broadcast vars + result collection |
| Executor | Runs tasks, caches data | Cores × memory per executor |
| Task | Unit of work on one partition | 1 core slot, partition-sized memory |
Stages and Shuffle Boundaries
- map, filter, select, withColumn
- Each output partition depends on ONE input partition
- Pipelined within a stage - no data movement
- Essentially free at scale
- groupBy, join, repartition, distinct
- Output partitions depend on ALL input partitions
- Trigger a shuffle - data crosses the network
- Dominant cost in most Spark jobs
- ▸Target partition size: ~128MB. Below 64MB you have too many small tasks. Above 256MB you risk OOM and poor parallelism.
- ▸For a 100GB dataset: 100GB / 128MB ≈ 800 partitions is a reasonable starting point.
This Job Is Slow
Debug a slow Spark job using Spark UI metrics
The Debugging Sequence
Reading the Spark UI
| Spark UI Metric | Healthy | Investigate |
|---|---|---|
| Max task time / Median | < 3x | > 10x (skew) |
| GC time / Task time | < 5% | > 10% (memory) |
| Shuffle spill (disk) | 0 bytes | > 0 (memory pressure) |
| Shuffle write | Proportional to data | > 10x input (cartesian?) |
| Peak exec memory | < 60% of alloc | > 80% (OOM risk) |
Common Slow-Job Patterns
- ▸Shuffle explosion: A join that produces a cartesian product due to duplicate keys
- ▸Data skew: One partition has 10B rows, others have 10K - one task takes 1000x longer
- ▸Small files: Reading 100K small Parquet files instead of 1K properly-sized files
- ▸Collect to driver: df.collect() or toPandas() pulling billions of rows to a single machine
- ▸UDF bottleneck: Python UDFs serialize/deserialize every row between JVM and Python
Optimize This Join
Choose the right join strategy for a given data shape
Join Strategies in Spark
| Strategy | When | Cost |
|---|---|---|
| Broadcast Hash Join | One side < 10MB (default) | No shuffle. Fastest. |
| Sort-Merge Join | Both sides large, keys sortable | Shuffle + sort both sides |
| Shuffle Hash Join | One side much smaller (but > broadcast) | Shuffle both, build hash on small side |
| Cartesian / BNL | No join keys (cross join) | O(n×m). Almost always a mistake. |
Bucketed Joins
Join Reordering
How Do You Size the Cluster?
Size a Spark cluster for a given workload
The 5-Core Rule
| Node Spec | Executors per Node | Cores per Executor | Memory per Executor |
|---|---|---|---|
| 16 cores, 64GB | 3 | 5 | ~19GB (after OS + overhead) |
| 32 cores, 128GB | 6 | 5 | ~19GB |
| 8 cores, 32GB | 1 | 5 (waste 2) | ~28GB |
| 4 cores, 16GB | 1 | 4 (suboptimal) | ~14GB |
Memory Breakdown
- ▸spark.executor.memory = heap size (e.g., 19g)
- ▸spark.executor.memoryOverhead = off-heap + container padding (default max(384MB, 10% of heap))
- ▸spark.memory.fraction = 0.6 (unified memory share of heap)
- ▸spark.memory.storageFraction = 0.5 (initial storage share of unified memory, but boundary is soft)
Sizing for the Workload
What About Data Skew?
Detect and resolve data skew in Spark joins
Detecting Skew
Salting: The Standard Fix
Other Skew Strategies
- Filter out the skewed key(s)
- Process them separately (broadcast the small side for just that key)
- Union the results back together
- Best when only 1-3 keys are skewed
- repartition(N, 'key') before the join
- Redistributes data more evenly
- Adds a shuffle but can help with moderate skew
- Not effective when one key dominates
> The interviewer says: "this Spark job joins a 500GB fact table to a 2GB dimension table. It used to take 20 minutes and now it takes 3 hours. Debug it out loud, then tell me how you would size the cluster for it." This is the whole gauntlet in one prompt.
explode the small side across all N salt values, and join on the composite key. Filter null join keys out first since they all collapse into one partition.spark.sql.autoBroadcastJoinThreshold of 10MB, so force broadcast(dim_df) to remove the shuffle entirely and verify BroadcastHashJoin in explain(), or bucket both sides on the join key if this same join runs daily.The technical gauntlet every pipeline interview hits
- Category
- Pipeline Architecture
- Difficulty
- intermediate
- Duration
- 35 minutes
- Challenges
- 0 hands-on challenges
Topics covered: Explain Spark Architecture, This Job Is Slow, Optimize This Join, How Do You Size the Cluster?, What About Data Skew?
Lesson Sections
- Explain Spark Architecture (concepts: paSparkExecutionModel)
"Walk me through how Spark executes a query." This is the opener. If your answer is vague, the interviewer downgrades your level. If it is precise, they trust your debugging answers later. The Driver-Executor Model Spark runs one driver process and N executor processes. The driver parses your code into a logical plan, optimizes it via Catalyst, converts it to a physical plan, and splits that plan into stages. Each stage contains tasks - one task per partition. The driver sends tasks to executo
- This Job Is Slow (concepts: paSparkUiDiagnosis)
"This Spark job used to take 20 minutes. Now it takes 3 hours. What do you do?" This is the most common Spark question across all companies. The interviewer is testing your debugging methodology, not a single trick. The Debugging Sequence Check shuffle metrics FIRST. 80% of slow Spark jobs are shuffle-bound. Open the Spark UI → Stages tab → sort by shuffle write. If one stage writes 500GB of shuffle data while others write 5GB, you found the problem. Do not start with code review - start with
- Optimize This Join (concepts: paBroadcastJoin)
"You're joining a 500GB fact table with a 2GB dimension table and it's slow. How do you fix it?" The answer they want: broadcast the dimension table. But the follow-ups go deeper. Join Strategies in Spark The broadcast threshold is controlled by spark.sql.autoBroadcastJoinThreshold, default 10MB. If one side of the join is below this threshold, Spark ships the entire table to every executor, eliminating the shuffle entirely. For dimension tables up to ~1-2GB, you can force a broadcast even above
- How Do You Size the Cluster? (concepts: paSparkExecutionModel)
"You need to process 2TB of data daily. How do you size your Spark cluster?" This tests whether you understand memory, cores, and executors as interacting constraints rather than independent knobs. The 5-Core Rule Use 5 cores per executor. This is the well-tested sweet spot. More than 5 cores causes excessive GC pressure and HDFS throughput bottlenecks (each core opens concurrent connections). Fewer than 5 underutilizes memory. On a node with 16 cores, run 3 executors (5 cores each, 1 core reser
- What About Data Skew? (concepts: paDataSkew)
"Your join is fast for most keys but one key takes 10x longer. What is happening and how do you fix it?" Data skew is the single most common root cause of slow Spark jobs in production. Every interviewer expects you to handle it. Detecting Skew In the Spark UI, skew shows up as one task running dramatically longer than others in the same stage. The Tasks tab shows the min, median, and max task duration. If max is 50x the median, one partition holds massively more data than the others. Check the