⚡ Inside Apache Spark's Distributed Execution Model
One driver plans the job and splits each stage into tasks; a ring of executors runs those tasks in parallel. Tune the cluster below and watch tasks stream out, and — when a shuffle is due — watch every executor exchange data before the next stage can begin.
Cluster
Live metrics
p = parallel fraction of the stage (task execution).
(1−p) = serial fraction (driver planning + shuffle barrier).
As N → ∞, Speedup → 1/(1−p) — the Amdahl's-law ceiling.
How Spark's Execution Model Works
A running Spark application has exactly one driver — it builds the logical and physical plan, breaks it into stages at every shuffle boundary, and splits each stage into one task per partition. Tasks are shipped to executors, independent JVM processes spread across the cluster, which run them in parallel and report back. Within a stage tasks never talk to each other, so Spark pipelines narrow transformations (map, filter) freely — but a shuffle (groupBy, join on non-co-partitioned data) forces every task in the upstream stage to finish before any downstream task can start, a hard synchronisation point. Because only the per-partition task work parallelises across executors while planning and shuffle barriers stay serial, the achievable speedup follows Amdahl's law: Speedup(N) = 1 / ((1−p) + p/N), where p is the parallel fraction — which is exactly why adding executors helps a lot at first and then flattens out, especially once shuffles dominate the job.