Amazon EMR: The Internals Behind Petabyte-Scale Processing

Amazon EMR: The Internals Behind Petabyte-Scale Processing

A deep, engineer-grade walkthrough of how Amazon EMR actually works under the hood — cluster topology, scheduler internals, storage consistency, failure recovery, and the design decisions that separate a cluster that survives Black Friday from one that falls over at 2 a.m.

Most engineers meet Amazon EMR the same way: they launch a cluster, run a Spark job, and move on. That’s fine for a weekend project. It is not fine when your cluster is processing three terabytes of clickstream data an hour, your Spot task nodes are getting reclaimed mid-shuffle, and finance is asking why the bill tripled. This tutorial skips the “what is a cluster” basics entirely. Instead, it goes straight into the machinery that experienced architects actually argue about: instance fleets versus instance groups, how the YARN ResourceManager negotiates containers, why EMRFS needed a consistent view of S3 before S3 itself became strongly consistent, and how to design clusters that fail gracefully instead of catastrophically. If you already know that EMR stands for Elastic MapReduce and that it runs Hadoop and Spark, you are exactly the reader this was written for.

1Advanced Core Concepts I — Cluster Topology & Storage

Before touching scheduling or performance tuning, you need a precise mental model of how an EMR cluster is actually assembled and how it talks to storage. This is where most “intermediate” understanding quietly runs out.

Instance Fleets vs. Instance Groups: Two Different Provisioning Engines

EMR gives you two entirely separate mechanisms for describing the machines in a cluster, and they are not interchangeable feature flags — they are different provisioning engines with different failure semantics. An instance group is a single, uniform pool: one instance type, one purchasing option (On-Demand or Spot), scaled up or down as a block. An instance fleet is a target-capacity specification: you tell EMR “I need 200 units of core capacity” and hand it a list of up to fifteen eligible instance types across multiple Availability Zones, and EMR’s provisioning engine decides which combination to launch and continuously rebalances as Spot capacity shifts.

The practical difference shows up during a Spot interruption event. With instance groups, if your chosen instance type dries up in that Availability Zone, your scale-up request simply fails — there is no fallback. With instance fleets, EMR automatically falls back to the next eligible type in your list, in a different AZ if necessary, without you writing any orchestration logic. This is why almost every production-grade EMR deployment built after 2018 uses instance fleets for anything touching Spot.

Analogy

An instance group is like booking a single caterer for an event and hoping they show up. An instance fleet is like telling an event agency “I need enough food for 200 guests” and letting them pull from five different caterers, substituting on the fly if one cancels. You care about total capacity, not which specific caterer delivers it.

EMRFS and the Consistency Problem Nobody Talks About Anymore

EMRFS (EMR File System) is EMR’s implementation of the Hadoop file system interface over Amazon S3. Internally, it translates HDFS-style file operations into S3 API calls. For years, EMRFS shipped with an optional consistent view feature backed by a DynamoDB metadata table, because S3 originally offered only eventual consistency for overwrite-PUTs and list-after-write operations. A Spark job could write output files and then immediately list that prefix and miss some of the objects it had just written — a silent correctness bug that was brutal to debug. Since December 2020, S3 itself became strongly read-after-write consistent, which made EMRFS consistent view largely unnecessary for new clusters, but the concept still matters: it explains why so much legacy EMR tooling references DynamoDB tables, and it is the reason committer design (covered in Chapter 6) became such a serious engineering topic in the Hadoop ecosystem.

Custom AMIs and Bootstrap Actions: Two Ways to Customize a Fleet

A custom AMI bakes your OS packages, security agents, and libraries into the machine image itself, so nodes boot ready to work — this is the preferred pattern for large fleets because it removes per-node installation time and eliminates a class of “it worked on nine nodes but not the tenth” bugs. A bootstrap action is a script that runs on every node during cluster provisioning, before Hadoop daemons start, and is better suited to lightweight, frequently-changing configuration rather than heavyweight software installation. Mixing the two is common: a custom AMI for the base environment, bootstrap actions for cluster-specific parameters that change per job.

Provisioning

Instance Fleets

Target-capacity based, multi-type, multi-AZ, automatic Spot fallback. Default choice for production.

Provisioning

Instance Groups

Single type, single purchasing option per group. Simpler, but no automatic substitution on Spot loss.

Storage

EMRFS

Hadoop-compatible interface translating file operations into S3 API calls, with pluggable committers.

Customization

Custom AMI

Pre-baked machine image; fastest, most consistent way to standardize a large fleet.

Customization

Bootstrap Actions

Per-boot scripts for lightweight, job-specific configuration changes.

i
Field Note

Teams that standardize on custom AMIs typically cut cluster startup time by 30–50% compared to bootstrap-action-heavy clusters, because there is nothing left to install at boot beyond final configuration.

2Advanced Core Concepts II — Processing Engines at Scale

EMR is not one engine, it is a hosting platform for several. Choosing correctly — and understanding how each engine behaves once you’re past 100 nodes — is an architectural decision, not a syntax preference.

Spark’s Dynamic Allocation Under Real Load

Spark on EMR can request executors dynamically through the External Shuffle Service, which decouples shuffle data from the executor process so that an executor can be reclaimed by YARN without losing shuffle output other tasks still need to read. At scale, the interesting failure mode is executor thrashing: if your idle timeout is too aggressive, Spark releases executors it needs seconds later and pays the container allocation cost repeatedly. Advanced tuning here means setting `spark.dynamicAllocation.executorIdleTimeout` and cachedExecutorIdleTimeout deliberately based on stage duration patterns, not defaults.

Hive LLAP and Why It Exists

Standard Hive-on-Tez launches a fresh container per query fragment, which is fine for batch ETL but far too slow for interactive, sub-second BI queries. Hive LLAP (Live Long and Process) keeps a persistent daemon pool warm on cluster nodes, caching columnar data in memory across queries and skipping container startup entirely. The trade-off is that LLAP daemons permanently reserve memory and vCPU on core nodes, so a cluster mixing heavy batch ETL and LLAP interactive workloads needs careful capacity partitioning or it starves one workload to feed the other.

Presto / Trino Coordinator-Worker Architecture on EMR

Unlike Spark and Hive, Presto (and its fork Trino) never writes intermediate shuffle data to disk by default — it streams data in-memory between stages through a coordinator that plans the query and workers that execute fragments in parallel. This makes it extremely fast for interactive federated queries across S3, Hive Metastore, and JDBC sources in a single query, but also means a single query with a poor join order can exhaust worker memory cluster-wide, because there is no automatic spill-to-disk safety net in older Presto versions the way Spark has.

HBase on EMR: The Stateful Outlier

HBase is unusual among EMR workloads because it is stateful and long-running rather than job-based, storing its data (HFiles) directly in S3 via HBoss or on EMRFS, with a WAL for durability. Running HBase on a transient EMR cluster is an anti-pattern discussed further in Chapter 11 — HBase wants a stable, long-lived cluster, which pulls against EMR’s cost advantage of ephemeral, per-job clusters.

Batch

Spark

In-memory DAG execution, dynamic allocation, best general-purpose engine for ETL and ML pipelines.

Warehouse

Hive / LLAP

SQL-on-Hadoop; LLAP mode trades reserved memory for sub-second interactive latency.

Interactive

Presto / Trino

In-memory federated SQL across multiple sources; extremely fast, memory-sensitive.

Stateful

HBase

Wide-column store; needs a stable long-lived cluster rather than a transient one.

3Internal Working

This is the layer EMR sits on top of: YARN’s resource negotiation and EMR’s own control plane orchestrating it.

Every EMR cluster running Hadoop-family engines is, underneath, a YARN cluster. The ResourceManager runs on the master node and owns a global view of cluster capacity, split into queues by the Capacity Scheduler. When a job is submitted, YARN launches an ApplicationMaster — a per-job coordinator container — which then negotiates additional containers from the ResourceManager based on the job’s actual resource demand. Each worker node runs a NodeManager that reports available memory and vCPU upward and enforces the container resource limits the ResourceManager hands down, killing containers that exceed their memory allocation.

Sitting above YARN, the EMR control plane — which runs outside your cluster, in AWS’s own account — is responsible for cluster provisioning, step submission, health monitoring, and Auto Scaling / Managed Scaling decisions. This separation matters: even if your master node’s YARN ResourceManager becomes unresponsive, the EMR control plane can still detect the unhealthy state and, in multi-master configurations, promote a standby master. The EMR control plane does not run your jobs; it manages the infrastructure your jobs run on.

flowchart TB
    subgraph AWS_ControlPlane["EMR Control Plane (AWS-managed)"]
        CP["Cluster Orchestrator
Provisioning + Health Checks + Managed Scaling"] end subgraph VPC["Customer VPC"] subgraph Master["Master Node"] RM["YARN ResourceManager"] NN["HDFS NameNode"] end subgraph Core["Core Nodes"] NM1["NodeManager"] DN1["HDFS DataNode"] end subgraph Task["Task Nodes (Spot/On-Demand)"] NM2["NodeManager"] end end S3["Amazon S3
via EMRFS"] CP -->|"Provision + Monitor"| Master CP -->|"Scale In/Out"| Core CP -->|"Scale In/Out"| Task RM |"Container Negotiation"| NM1 RM |"Container Negotiation"| NM2 NN |"Block Metadata"| DN1 NM1 -->|"Read/Write"| S3 NM2 -->|"Read/Write"| S3

Fig 1. EMR control plane, YARN internals, and EMRFS data path

Notice the arrow direction into S3 — this is deliberate. Task nodes typically hold no HDFS data blocks at all (they are compute-only), while core nodes hold both compute and HDFS storage. This asymmetry is exactly why task nodes are the safest place to run Spot instances: losing one mid-job costs you recomputation of in-flight tasks, but never data loss, because durable output ultimately lands in S3, not on ephemeral task-node disks.

4Data Flow & Lifecycle

Following one job from submission to committed output reveals where most production incidents actually occur.

flowchart LR
    A["Step Submitted
(EMR Step API)"] --> B["ApplicationMaster
launched by YARN"] B --> C["Containers Allocated
across Core + Task nodes"] C --> D["Task Execution
+ Shuffle"] D --> E{"Shuffle Spill
needed?"} E -->|Yes| F["Spill to local disk
then merge"] E -->|No| G["In-memory shuffle"] F --> H["Output Committer"] G --> H H --> I["Staged writes
to S3 via EMRFS"] I --> J["Atomic Commit / Rename"] J --> K["Step marked COMPLETED"]

Fig 2. Lifecycle of a single EMR step from submission to durable commit

The most under-appreciated stage is the output committer. Traditional Hadoop commit algorithms rename staged output files from a temporary directory into the final directory — an operation that is a fast, atomic metadata change on HDFS but is neither fast nor atomic on S3, because S3 “renames” are actually a copy-then-delete of the underlying object. At scale, this made job commit time balloon and occasionally produced partial output if a job failed mid-rename. EMR’s S3-optimized committer solves this by writing directly to the final S3 location using S3’s multipart upload API and only finalizing (completing) the multipart upload at commit time, which is genuinely atomic from S3’s perspective — either the whole object appears, or none of it does.

1

Step Submission

Client calls the EMR Step API; the ApplicationMaster is launched by YARN.

2

Resource Negotiation

ApplicationMaster requests containers; ResourceManager allocates them across NodeManagers.

3

Shuffle

Intermediate data is exchanged between stages, spilling to local disk if it exceeds memory.

4

Commit

The output committer finalizes data in S3, ideally via multipart upload completion rather than rename.

5

Teardown

On a transient cluster, EMR terminates the fleet once all steps report COMPLETED.

5Advantages, Disadvantages & Trade-offs

Advantages

  • Decouples compute from storage entirely when using S3, so clusters can be transient and cost-proportional to actual usage.
  • Supports multiple mature open-source engines (Spark, Hive, Presto, Flink, HBase) on one managed control plane.
  • Instance fleets provide automated Spot diversification, often cutting compute cost 60–70% versus On-Demand-only clusters.
  • Deep native integration with IAM, Lake Formation, KMS, VPC, and CloudWatch reduces custom security tooling.

Disadvantages

  • Cluster provisioning still takes minutes, which is unacceptable for sub-second or highly bursty ad-hoc query patterns — EMR Serverless mitigates but doesn’t eliminate this.
  • Managing YARN queue capacity and multi-tenant clusters requires genuine distributed-systems expertise; misconfiguration causes silent resource starvation.
  • S3 as a filesystem introduces eventual-consistency-era legacy complexity (committers, EMRFS) that engineers must still understand even though S3 is now strongly consistent.
  • Cost visibility across shared long-running clusters is harder than with fully serverless, per-query billing models.

6Performance & Scalability

Scaling EMR well is less about adding nodes and more about removing bottlenecks that more nodes can’t fix.

EMR Managed Scaling

Managed Scaling replaced the older custom Auto Scaling rules by having EMR itself monitor YARN memory and pending container counts and decide scaling actions algorithmically, within a min/max unit boundary you define per instance fleet. It scales core and task capacity independently, and — critically — it scales down gracefully by decommissioning nodes only after their running containers complete, rather than killing work in progress.

The S3-Optimized Committer and Graviton

Beyond correctness (Chapter 4), the S3-optimized committer meaningfully improves throughput because it eliminates the final rename/copy pass across potentially millions of files. Separately, EMR’s support for Graviton (ARM-based) instances routinely delivers 15–30% better price-performance for Spark and Presto workloads that aren’t dependent on x86-only native libraries, making instance-type selection within a fleet a genuine performance lever, not just a cost one.

Adaptive Query Execution

Spark’s Adaptive Query Execution (AQE), broadly available on modern EMR releases, re-optimizes a query plan mid-execution using actual runtime statistics — dynamically coalescing small shuffle partitions, switching a sort-merge join to a broadcast join if one side turns out smaller than the optimizer initially estimated, and splitting skewed partitions automatically. This closes a long-standing gap where static query plans, generated before any data was read, were frequently wrong for skewed real-world datasets.

60–70%
TYPICAL COST REDUCTION
WITH SPOT FLEETS
15–30%
PRICE-PERFORMANCE GAIN
ON GRAVITON
15
MAX INSTANCE TYPES
PER FLEET SPEC
“At scale, the engine rarely fails because of raw throughput. It fails because one skewed partition, one slow committer, or one starved YARN queue quietly becomes the ceiling for the entire job.”

7High Availability & Reliability

EMR supports a multi-master mode with three master nodes running in different subnets within the same Availability Zone group, coordinated through Apache ZooKeeper for leader election. If the active master fails, a standby is promoted automatically, and YARN, HDFS NameNode, and Hive Metastore services fail over with it — turning what used to be a full cluster-down event into a brief, largely transparent failover.

Spot interruption handling on task nodes relies on the two-minute EC2 Spot interruption notice: EMR’s NodeManager decommissioning logic stops accepting new containers on that node immediately and gives running containers a grace period to finish or checkpoint before the instance is reclaimed. Because task nodes hold no HDFS blocks, the actual data-loss risk from a Spot interruption is close to zero — the cost is re-execution of the in-flight tasks that were running on that node, not corrupted output.

!
Reliability Trap

Running core nodes on Spot is a common mistake for HA-sensitive workloads: core nodes hold HDFS data blocks, so losing one can mean losing data blocks outright unless replication factor and rack awareness are configured to tolerate it. Reserve Spot for task nodes; keep core nodes On-Demand or Reserved for HDFS-heavy clusters.

8Security

EMR’s security model layers several independent controls rather than relying on one. Kerberos provides mutual authentication between cluster services and users, essential for multi-tenant clusters where you cannot trust the network perimeter alone. EMRFS with IAM roles per user or per group (via a credential provider configured in the security configuration) lets different users or applications on the same cluster assume different IAM roles when reading or writing S3 — critical when Finance and Marketing share a cluster but must never see each other’s raw data.

For fine-grained, column- and row-level access control beyond what IAM’s object-level permissions can express, EMR integrates with AWS Lake Formation, which centralizes and enforces table-, column-, and row-level grants across Spark, Hive, and Presto engines without duplicating policy logic in each engine separately. Encryption is likewise layered: at-rest encryption for EMRFS data uses S3-managed or KMS-managed keys, local disk encryption protects shuffle spill data, and in-transit encryption (TLS) protects the Hadoop RPC and shuffle network traffic — all bundled together as an EMR security configuration object that can be attached to any cluster.

DECISION · SEC-EMR-01Common Pattern
Context

A multi-tenant EMR cluster needs different teams to access different S3 prefixes with different permissions, without provisioning a separate cluster per team.

Decision

Attach a security configuration enabling per-user/per-group IAM roles for EMRFS, combined with Lake Formation grants for table-level governance, instead of relying on a single cluster-wide instance role.

Consequence

Higher initial setup complexity, but one shared cluster now safely serves multiple tenants, improving utilization and reducing the number of clusters to patch and monitor.

9Monitoring, Logging & Metrics

EMR publishes cluster-level metrics to CloudWatch automatically, and the metrics that actually predict trouble are rarely CPU utilization — they are YARN memory pressure indicators. YARNMemoryAvailablePercentage dropping steadily and ContainerPendingRatio climbing are the earliest reliable signals that a cluster is under-provisioned relative to its workload, well before jobs start failing outright. Application-level logs (stdout, stderr, container logs) are archived to a configured S3 log URI so they survive after a transient cluster terminates, which is non-negotiable for debugging failed jobs after the fact.

Spark’s own History Server, and EMR Studio’s notebook-based debugging UI, sit on top of these archived logs and event logs to reconstruct a completed job’s DAG, stage timings, and skew patterns after the cluster is gone — this is what makes transient, ephemeral clusters debuggable in production rather than a black box.

MetricWhat It SignalsAction Threshold
YARNMemoryAvailablePercentageCluster-wide memory headroomSustained <15% → scale out
ContainerPendingRatioQueued vs. running containersRising trend over 10+ min → capacity gap
HDFSUtilizationCore-node disk pressure>80% → add core capacity or offload to S3
IsIdleWhether cluster has pending work1 for extended period → terminate transient cluster

10Deployment & Cloud

EMR is no longer a single deployment shape. Choosing between them is one of the highest-leverage architectural decisions a team makes.

EMR on EC2 is the original model: you own the cluster’s instances directly, with full control over instance types, fleets, and bootstrap customization — best when workloads are large, steady, and benefit from fine-grained tuning. EMR on EKS runs Spark jobs as pods inside an existing Kubernetes cluster, which is the right choice when an organization has already standardized its platform team and tooling around Kubernetes and wants big data jobs to share that same operational model, node pools, and namespace-based multi-tenancy. EMR Serverless removes cluster and instance management entirely — you submit a job, EMR provisions and tears down workers automatically per job, billed per vCPU-second and GB-second actually consumed — ideal for spiky, unpredictable, or infrequent workloads where keeping any cluster warm is wasted spend.

When EMR on EC2 wins

Large, steady, cost-sensitive batch pipelines where Spot fleet tuning and custom AMIs deliver meaningful, compounding savings.

When EMR on EKS wins

Organizations already running Kubernetes as their platform standard, wanting unified observability and namespace isolation across all workloads, not just big data.

When EMR Serverless wins

Bursty or unpredictable workloads — nightly jobs, ad-hoc analyst queries — where cluster idle time would otherwise be pure waste.

11Design Patterns & Anti-patterns

The dominant pattern in modern EMR architectures is the transient, medallion-style lakehouse pipeline: raw data lands in a “bronze” S3 prefix, a transient EMR cluster spins up, transforms it into cleaned “silver” and aggregated “gold” tables (often in an open table format like Apache Iceberg or Hudi for ACID guarantees on top of S3), and terminates. No cluster runs when there is no job to run.

ANTI-PATTERN · AP-EMR-01Avoid
Pattern

A single, long-lived, monolithic EMR cluster serving every team’s every job indefinitely, “because shutting it down is a hassle.”

Why it fails

YARN queue contention grows silently as more jobs share it, a single misconfigured job can starve the whole cluster, and you pay for idle capacity around the clock instead of only during actual processing windows.

Better alternative

Purpose-built transient clusters per pipeline, or a shared cluster with strict Capacity Scheduler queue limits and Managed Scaling, sized to the workload calendar rather than provisioned once and forgotten.

A second common anti-pattern is the small-files problem: writing millions of tiny output files to S3 (common with over-partitioned Spark output) inflates listing time, driver memory for file metadata, and downstream query planning cost far more than the storage cost itself. Coalescing output partitions before the final write, or compacting small files as a background maintenance job, is standard practice on any production EMR pipeline writing to S3.

12Best Practices & Common Mistakes

Do: size core nodes for HDFS, not compute

Keep core node count tied to HDFS storage/replication needs; scale task nodes independently for pure compute demand.

Don’t: ignore shuffle partition count

Default Spark shuffle partitions (200) are frequently wrong at scale; tune relative to actual data volume and AQE settings.

Do: separate log and checkpoint S3 buckets

Keeps lifecycle policies, access controls, and cost allocation clean and independent from production data buckets.

Don’t: hardcode instance types

Hardcoding a single instance type defeats the purpose of instance fleets and reintroduces Spot fragility you were trying to avoid.

Do: tag every cluster

Cost allocation tags on clusters and instance fleets are the only reliable way to attribute EMR spend per team at scale.

Don’t: mix HBase with transient batch clusters

Stateful engines need stable long-lived infrastructure; forcing them onto ephemeral clusters breaks their durability model.

13Real-World & Industry Examples

Netflix — Large-Scale Data Platform

Netflix has publicly discussed running large-scale Spark and Presto workloads over S3-backed data lakes for analytics and recommendation pipelines, favoring transient, right-sized clusters over permanently running infrastructure to match its highly variable batch processing calendar.

Yelp — Streaming and Batch Convergence

Yelp has described using Spark on EMR-style infrastructure for large batch ETL feeding its search and ad-ranking systems, emphasizing cost control through Spot-heavy task fleets for non-time-critical batch stages.

Financial Services — Regulated, Multi-Tenant Clusters

Financial institutions commonly adopt the Kerberos-plus-Lake-Formation security pattern from Chapter 8 specifically because regulators require demonstrable, auditable, column-level access control across shared analytics infrastructure — not just object-level S3 permissions.

14Frequently Asked Questions

Q1Should every EMR cluster use instance fleets instead of instance groups?
Almost always yes for production, because of automatic Spot fallback across instance types and AZs. Instance groups remain useful mainly for simple, single-type, non-Spot dev/test clusters.
Q2Is EMRFS consistent view still necessary today?
No, not for correctness — S3 has been strongly read-after-write consistent since December 2020. The concept remains important to understand because it shaped committer design and legacy tooling still referencing it.
Q3Can core nodes safely run on Spot Instances?
It depends on your HDFS replication factor and tolerance for data-block loss. Most production guidance keeps core nodes On-Demand or Reserved and reserves Spot for task nodes, which hold no HDFS blocks.
Q4How does EMR Managed Scaling decide when to scale?
It continuously evaluates YARN memory availability and pending container counts against the min/max unit bounds you configure, scaling core and task capacity independently and decommissioning nodes gracefully after their work completes.
Q5When should I choose EMR Serverless over EMR on EC2?
Choose Serverless when workload timing is unpredictable or infrequent enough that keeping any cluster provisioned would waste money; choose EC2 when workloads are large, steady, and benefit from deep fleet and AMI tuning.
Q6Why does the S3-optimized committer matter for correctness, not just speed?
Traditional rename-based commit algorithms are not atomic on S3 and can leave partial output if a job fails mid-commit. The S3-optimized committer finalizes output via an atomic multipart upload completion instead.
Q7Is Hive LLAP compatible with running heavy batch ETL on the same cluster?
Only with careful capacity partitioning. LLAP daemons permanently reserve memory and vCPU, so unmanaged coexistence with batch ETL will starve one workload.
Q8What is the biggest hidden cost driver on EMR clusters writing to S3?
The small-files problem — excessive output partitioning inflates S3 listing time, driver metadata memory, and downstream query planning cost well beyond the raw storage price.

15Summary and Key Takeaways

Key Takeaways

  • Instance fleets, not groups, are the production default because they provide automatic multi-type, multi-AZ Spot fallback.
  • Task nodes are the safe place for Spot since they hold no HDFS blocks; core nodes need more caution due to data-locality risk.
  • The output committer determines correctness, not just speed — the S3-optimized committer’s atomic multipart commit avoids partial-output failures that plague rename-based commits on S3.
  • Choose your deployment model deliberately: EMR on EC2 for steady tunable workloads, EMR on EKS for Kubernetes-standardized platforms, EMR Serverless for bursty or unpredictable jobs.
  • Security is layered, not singular — Kerberos for authentication, per-user IAM roles for EMRFS, and Lake Formation for fine-grained governance work together, not as substitutes for each other.
  • Long-lived monolithic clusters are the most common anti-pattern in production EMR deployments, driving queue contention, blast-radius risk, and idle-capacity waste.
  • YARN memory pressure metrics predict trouble long before CPU metrics do — watch YARNMemoryAvailablePercentage and ContainerPendingRatio, not just utilization graphs.