Plan the parallelism
A job is too slow and the obvious fix is more workers or more partitions. But parallelism has ceilings set by the data and the plan, and past them extra workers cost money and change nothing. How many workers can actually help, and how should work be handed to them?
You will end with a worker count justified by its ceiling, a task size, and a policy for scheduling, speculation and scaling.
Work through it
Compute the ceiling before adding workers
Take the largest partition's share of the work, f. The speed-up can never exceed 1 ÷ f. If the largest partition holds 5% of the work: stop buying cores at 20.
Find the pipeline breakers
Operators that must see all their input before producing output (sorts, hash-table builds, aggregations) split the plan into stages, and nothing overlaps across one. Count the stages to bound pipeline parallelism; the slowest stage bounds throughput.
Check whether the expensive operator can be split
If it is not decomposable (exact distinct count, exact median, a global sort without sampling): splitting it across workers is not available. Your options are an approximation whose results can be merged, or a merge tree.
Diagnose the long tail before fixing it
Plot each task's duration against its input bytes, and classify: imbalance, a straggler, or skew. The wrong fix costs capacity and changes nothing. See Handle skew and elasticity.
Choose static or dynamic scheduling
The execution model decides first: if stages stream into each other, assignment is static whether you like it or not. If stage outputs are written down, adapt each stage to what the previous one produced; the benefit grows with the depth of the plan.
Size tasks from the dispatch cost
Minimum task duration = dispatch cost ÷ the overhead you accept: 10 ms ÷ 1% = 1 second. Number of tasks = total work ÷ that duration. Check you get at least 10 tasks per worker so work can rebalance. Both bounds are tighter than intuition.
Enable speculation only when it can help
Running a duplicate of a slow task helps only if the cluster has spare capacity, tasks have no side effects and are deterministic, and the detector judges by estimated time remaining, not time elapsed.
Test elasticity before building it
Adding machines mid-run pays only when remaining work ÷ current capacity is much larger than the time to acquire a machine. It usually fails within one query and passes across a whole workload, so scale between queries.