Executor and Memory Sizing
Cores and memory per executor, memory overhead, the unified memory split between execution and storage, and what each job requests against what it uses.
Spark Health Check
Last reviewed by Tyler Eastridge
Spark performance tuning means fitting memory, partitions, shuffle and join strategy to your data and cluster. A Spark health check is an engineer-led review of how your jobs really run: executor and memory sizing, shuffle and spill, partition skew, adaptive query execution, file and partition layout, and what the cluster costs for the work it does. You get a written report, a ranked tuning list and the evidence behind each finding, on Spark running on YARN, Kubernetes, EMR, Dataproc, Databricks or standalone. It is delivered by AceMQ with partner Spark specialists.
Is This You?
Executors die with out-of-memory errors and the standing fix has been to raise executor memory again
One task in a stage runs for an hour while the other 199 finish in a minute
Nightly jobs creep towards the deadline for the reports that depend on them
Stage detail in the Spark UI shows gigabytes of shuffle spill to disk
Nobody can say how many cores and how much memory each job asks for, or how much it uses
A job that worked for two years fails after a lookup table grew past the broadcast threshold
Cloud spend on Spark clusters rises while the data volume does not
Outcomes
Concrete state changes, not deliverable counts. This is what actually differs about your Apache Spark estate when the engagement closes.
Before
Executor and memory settings were copied from a blog post or a previous job.
Requested cores and memory compared with peak usage per job from event logs, with the over-provisioned and under-provisioned jobs listed.
Before
Slow stages are blamed on the cluster.
Stage-level evidence of where the time goes: skewed keys, spill, a full shuffle that was not needed, or too many small tasks. Each finding names the fix.
Before
Adaptive query execution is assumed to be handling it.
A check of which AQE behaviours are on, which thresholds suit your data, and where a hint or a hand-set partition count overrides them.
Before
The bill goes up and nobody owns it.
Idle and oversized compute identified per job and per cluster, ranked by effort against saving, with the changes safe to make first.
Scope
Cores and memory per executor, memory overhead, the unified memory split between execution and storage, and what each job requests against what it uses.
Shuffle read and write volume and spill per stage, partition counts, skewed join and aggregation keys, and unnecessary repartitions.
Whether AQE is on, how it coalesces and splits shuffle partitions on your data, the skew-join thresholds, and which hints or settings override it.
Input partition sizing, small-file patterns, table partitioning, and the effect of file counts on task counts and scheduling overhead.
Idle executors, dynamic allocation, cluster shape and autoscaling, and jobs better served by a smaller or different engine.
Spark release against the project's maintenance policy, platform-specific settings on YARN, Kubernetes and Databricks, and what blocks an upgrade.
The Engagement
Every phase has a defined duration and a concrete artifact handed over at the end of it. You always know what stage you're in and what you've received.
We agree which clusters, jobs and platforms are in scope, the Spark and platform versions, the deadline each job has to meet, and what your engineers can export. Delivery is remote and guided by your engineers over screen share. We ask first for the Spark and Hadoop versions or distribution, where it runs, cores per node, and the current support window.
You receive
A senior engineer and, where the estate needs it, a partner Spark specialist work through the six areas against your event logs and run history, with your team in the room where a decision needs context. The duration depends on the number of jobs, the focus areas you choose and what turns up.
You receive
The report, ranked by what costs or fails first. Every finding carries the change, the effort, and the risk of leaving it. We say which changes are configuration, which need code, and which depend on the data.
You receive
A walkthrough with the engineer who did the work, then a sequenced plan your team can run or hand to us. Remediation is scoped separately, so the report is useful whoever makes the changes.
You receive
Spark records most of what a review needs. Event logs and the history server hold stage, task and executor detail for finished applications, and the SQL tab shows the physical plan. A finding should trace to a number your engineers can open, not to anyone's recollection of the job.
| Area | Evidence we read | What it tells us |
|---|---|---|
| Executor and memory sizing | Executor tab, peak memory metrics, spark.executor.* and spark.memory.* settings, container or pod limits | Requested versus used memory and cores, and whether failures are heap, overhead or container limits. |
| Shuffle, spill and skew | Stage detail: shuffle read and write size, spill to memory and disk, task duration and input size distribution | Which few stages carry the runtime, and whether one key or one partition holds the data. |
| Adaptive query execution | SQL tab plans, AQE settings, join hints in code | Whether the plan changed at runtime, and where a hint or fixed partition count prevents it. |
| Partitioning and file layout | Input partition counts, file counts and sizes, spark.sql.shuffle.partitions, repartition and coalesce calls | Too many tiny tasks or too few large ones, and avoidable full shuffles. |
| Cost and cluster use | Scheduler history, cluster definitions, executor add and remove events, idle time | Idle executors, oversized clusters, and jobs that wait on a few long tasks. |
| Versions and platform fit | Spark and Hadoop versions, platform release notes, deprecated settings in use | Where an upgrade removes a workaround, and what would break. |
Many tuning settings are inherited from an older cluster or a tutorial. The review compares what is set with what Spark documents as the default, and with what the job needs. These are the ones that matter most, from the Spark documentation checked on 31 October 2026.
| Setting | Spark default | What we look for |
|---|---|---|
| spark.sql.shuffle.partitions | 200 | A fixed count that no longer matches the data: too many tiny tasks on small stages, or huge partitions that spill on large ones. |
| spark.sql.adaptive.enabled | true (default since Spark 3.2.0) | AQE switched off by an old setting, or defeated by hard-coded partition counts and hints. |
| spark.sql.adaptive.advisoryPartitionSizeInBytes | 64 MB | Whether the target suits your data. It applies when AQE coalesces small shuffle partitions or splits skewed ones. |
| spark.sql.adaptive.coalescePartitions.parallelismFirst | true | With the default, AQE ignores the 64 MB target and only respects a 1 MB minimum. The Spark documentation recommends false on a busy cluster. |
| spark.sql.adaptive.skewJoin thresholds | Enabled; a partition is skewed above 5 times the median and 256 MB | Skew that falls just under the thresholds, and skew in aggregations, which skew-join handling does not cover. |
| spark.sql.autoBroadcastJoinThreshold | 10 MB | A threshold raised years ago, and BROADCAST hints in code, which Spark honours even when statistics are above the threshold. |
| spark.sql.files.maxPartitionBytes | 128 MB | Input partition sizes that give too many tasks over small files, or too few over large ones. |
| spark.executor.memoryOverhead | 10% of executor memory, minimum 384 MiB (40% for Kubernetes non-JVM jobs) | Executors killed by the container limit rather than the JVM heap, which is common with PySpark and native libraries. |
| spark.memory.fraction | 0.6 of heap minus 300 MB | A changed value, since the Spark documentation recommends leaving it at the default. |
| spark.dynamicAllocation.enabled | false | Idle executors held for the whole job, and whether shuffle tracking, an external shuffle service or decommissioning is configured if it is turned on. |
| spark.local.dir | /tmp | Shuffle and spill files on a slow or small disk. |
A common finding
Raising executor memory hides a skewed key. The job fails later, costs more per run, and the stage still waits on its slowest task. Skew and spill evidence comes before any change to memory settings.
An executor needs memory for the JVM heap, for off-heap and native overhead, and for the work in each of its task slots. When a stage spills, it ran out of execution memory and wrote intermediate data to disk. When a container is killed, the overhead was too small.
Skew is the usual reason one task runs for an hour while the rest finish in a minute. A join or group on a column where a null placeholder or default value holds a large share of the rows sends all of them to one partition. Adaptive query execution can split skewed partitions in a sort-merge join at runtime, which fixes many cases without code changes.
It does not fix all of them. Skew in an aggregation is not handled by skew-join splitting. A hard-coded repartition can remove the chance for AQE to coalesce. A broadcast hint forces a broadcast even when the table has grown, so a job that worked for two years fails when a lookup table outgrows the driver's memory. We read the physical plan to see what Spark actually did, then choose between enabling or tuning AQE, salting a hot key, removing a null-key path, or deleting a hint.
Spark cost is mostly idle and oversized compute. Dynamic allocation is off by default and, when turned on, needs shuffle tracking, an external shuffle service or decommissioning so executors can be released without losing shuffle data, and executors holding cached data are never removed by default. A cluster sized for one heavy job at 2 a.m. can sit idle for the rest of the day. For Databricks spend measured by DBU, the cost-optimization page goes further.
Version matters for tuning because newer releases remove workarounds. As of 31 October 2026 the Apache Spark project lists 4.3.0 as released on 30 October 2026. Its versioning policy maintains non-LTS branches with bug fixes for 6 months and LTS branches for 18 months, and the 3.5 line has an extended period of security fixes only that ends in November 2027. The health check records which line you run, what your platform supports, and what stands between you and a supported release.
Typical engagements
Typical engagement scenarios: what the work covers and what it delivers. They describe the service, not past results.
Global Payments Processor
Right-sizing Databricks compute by moving scheduled work off all-purpose clusters and tightening autoscaling, instance selection, and idle timeouts.
Healthcare Analytics Provider
Migrating off the legacy Hive metastore to Unity Catalog with external location mapping, table upgrades, and a grant model that survives audit.
Digital Retail Platform
Fixing Delta tables where streaming writes and over-partitioning have produced millions of tiny files, stalling reads and vacuum operations.
Insurance Services Group
Named-engineer support for production Databricks job failures — driver OOM, spot reclamation, library conflicts, and workflow retry storms.
Telecommunications Operator
Fixing nightly jobs where a handful of skewed keys concentrate data onto a few executors and drive repeated out-of-memory failures.
Industrial Automation Manufacturer
Resolving jobs that fail after a dimension table grows past the broadcast threshold and the optimizer keeps trying to broadcast it anyway.
National Retail Bank
Reducing shuffle write volume and disk spill on nightly batch jobs so the processing window fits inside the reporting deadline.
Media Streaming Provider
Profiling a Spark estate to find over-provisioned jobs, redundant pipelines, and workloads better served by something other than Spark.
FAQ
Executor and memory sizing, shuffle and spill, partition skew, adaptive query execution, file and partition layout, cluster cost and Spark version. It starts from your event logs and Spark configuration and ends in a written, ranked tuning list with the evidence for each item.
Find out which kind they are first. A Java heap error and a container killed for exceeding its memory limit have different causes, and both are often a symptom of a skewed partition. Check the stage's task input sizes before raising executor memory, which delays the failure and raises cost.
Yes. It has been enabled by default since Apache Spark 3.2.0. It coalesces small shuffle partitions, switches join strategy at runtime and splits skewed partitions in sort-merge joins, but a hard-coded partition count or a broadcast hint can limit what it does.
Spark on YARN, Kubernetes, EMR, Dataproc, Databricks and standalone clusters. Delivery is remote and guided by your engineers, who export event logs and configuration. We do not need standing access to your clusters.
One to four weeks from kickoff to walkthrough, depending on the number of jobs, the focus areas and what we find. The scope is agreed on the first day, and you get a written quote within 1 hour of asking.
Let's talk
Tell us which Spark version and platform you run and which jobs hurt. We will scope a health check and reply to your quote request within 1 hour.
Send us a message and we'll follow up within 1 hour — or book a free 30-min consultation directly.
305-204-2607info@acemq.comPrefer to talk now? Call us directly or use the consultation tab to find a time that works.