The execution lifecycle end to end: application, jobs, stages, and tasks; the DAG scheduler and shuffle boundaries.
⏱ 4 min readTopics chapter readerLevel · Execution
01 · Overview
Start here
⏱ 4 min · Topic 1 of 5
Chapter 2 named the parts: the driver, the executors and the cluster manager. Chapter 3 showed jobs, stages and tasks in the UI.
This chapter puts them in one order. It follows what happens between the moment you call an action and the moment you get the result.
It also answers a practical question: how many tasks can run at the same time?
02 · Levels
Application, job, stage, task
⏱ 5 min · Topic 2 of 5
Spark work has four levels, each one inside the one above:
Level
What it is
How many
Application
Your whole program, with one SparkSession.
One per program.
Job
The work for one action, such as show().
At least one per action.
Stage
A part of a job with no shuffle inside.
A new stage after each shuffle.
Task
The work of one stage on one partition.
One per partition.
The DAG scheduler, a part of the driver, cuts each job into stages. It cuts wherever rows must move between tasks, which is a shuffle (chapter 5).
The steps between two shuffles join into one stage. So count the shuffles to guess the stages, not the steps. filter and select add no stage, but groupBy does.
Take the revenue job: filter, then groupBy. It has one shuffle, so two stages. In chapter 3, show() even started 2 jobs, because AQE ran the first stage as its own job.
Interview question
“How many stages will this job have?” One more than the number of shuffles. Then check in the UI: with AQE, some stages appear as skipped or as separate jobs.
03 · Slots
How tasks get onto executors
⏱ 6 min · Topic 3 of 5
The task scheduler, another part of the driver, sends each task to an executor. One executor core runs one task at a time. A core is often called a task slot.
In the chapter 2 model, 2 executors have 2 cores each, so 4 slots. The revenue job has 3 partitions, so 3 tasks. Three slots work, and one stays idle.
When a stage has more tasks than slots, the tasks run in waves. A wave is one round in which every slot runs one task:
local[2] gives 2 slots. repartition(6) cuts the orders into 6 partitions, so a stage of count() runs 6 tasks. On 2 slots, that is 3 waves.
On a real cluster the arithmetic is the same. 10 executors with 4 cores have 40 slots. A stage with 200 tasks runs in 200 / 40 = 5 waves.
The number of partitions decides how busy the cluster is:
Partitions
What happens
Fix
Far fewer than slots
Most cores wait, and the stage is slow.
repartition to more partitions.
About 2 to 4 times the slots
Every core stays busy.
A good default.
Far more than slots
Many tiny tasks, and the start-up of each task adds up.
coalesce to fewer partitions.
Watch out
A big cluster does not help a stage with 3 partitions. Only 3 tasks run. If a stage is slow and the cores are idle, check the partition count first.
04 · End to end
From action to result
⏱ 5 min · Topic 4 of 5
Here is the whole path, in order:
Your transformations only describe the work. Nothing runs yet.
You call an action, such as show() or count().
Catalyst, the optimizer, turns your steps into a better plan (chapter 13).
The DAG scheduler cuts the plan into stages at each shuffle.
The task scheduler sends one task per partition to the free slots.
The executors run the tasks, and write and read shuffle data between stages.
The last stage ends, and the result goes to the driver.
If one task fails, Spark runs only that task again, on any free slot. It rebuilds the input from lineage or from shuffle files (chapter 4). The rest of the job keeps its work.
On a cluster, Spark tries a task up to 4 times by default (spark.task.maxFailures). After the last failure, the job fails.
Check yourself: a stage has 12 tasks and the cluster has 4 slots. How many waves run? (Answer: 3.)
05 · Next Chapter
Next Chapter
You now know how a job becomes tasks, and how tasks fill the slots of a cluster.
⏱ 3 min · Topic 5 of 5
Next chapter
Partitioning & Parallelism
You now know how a job becomes tasks, and how tasks fill the slots of a cluster.
Next, the partition itself: repartition or coalesce, how big a partition should be, and how Spark skips partitions it does not need.