Source: apache_tez ยท
apache_tez.mdยท updated 2026-05-29 ยท ๐ secret gistSynced verbatim from gist.github.com/bl9.
Apache Tez: A Deep Dive Into Architecture, Internals & Source Code
Table of Contents
- What Is Apache Tez?
- Why Tez Exists โ The MapReduce Problem
- Architecture Overview
- Core Concepts
- Source Code Structure
- Deep Dive: Key Modules
- The DAG Execution Lifecycle
- The Shuffle Pipeline
- Runtime Reconfiguration
- How Hive and Pig Use Tez
- Tez on YARN โ The Full Picture
- Reading the Source: A Guided Path
- Simpler Open Source Alternatives
- References
1. What Is Apache Tez?
Apache Tez is a distributed data-processing execution engine built on top of Apache Hadoop YARN. It generalizes the rigid two-phase MapReduce model into an arbitrary directed acyclic graph (DAG) of tasks, enabling higher-level frameworks like Hive and Pig to express complex query plans as a single, optimized execution graph rather than a chain of independent MapReduce jobs.
Tez is not an end-user framework. You don't typically write Tez applications directly (though you can). Instead, Tez acts as the execution backend for tools like:
- Apache Hive (SQL on Hadoop)
- Apache Pig (data flow scripting)
- Cascading (Java data pipelines)
Key Stats
| Metric | Value |
|---|---|
| Language | Java |
| Lines of Code | ~225,000 |
| Contributors | 109+ |
| First Commit | April 2013 |
| License | Apache 2.0 |
| Repository | github.com/apache/tez |
2. Why Tez Exists โ The MapReduce Problem
The Pain Points of MapReduce
Classic MapReduce forces every computation into a rigid two-phase pattern:
Input โ Map โ Shuffle/Sort โ Reduce โ Output (HDFS)
For a complex Hive query that involves multiple joins and aggregations, MapReduce had to chain several independent jobs:
MR Job 1: Scan + Filter โ write to HDFS
MR Job 2: Join โ read from HDFS, write to HDFS
MR Job 3: Aggregate โ read from HDFS, write to HDFS
MR Job 4: Final sort + output โ read from HDFS, write to HDFS
Each boundary between jobs meant:
- Materializing intermediate data to HDFS (disk I/O + replication overhead)
- Launching new YARN containers (JVM startup latency)
- No pipelining between stages (the next job can't start until the previous one fully finishes writing)
What Tez Changes
Tez lets you express the same computation as a single DAG:
Scan+Filter โโ
โโโ Join โโ Aggregate โโ Sort โโ Output
Scan+Filter โโ
Intermediate data flows through in-memory edges (or local disk, never HDFS) between vertices in the same DAG. Containers are reused across vertices. The entire plan runs as one YARN application, not four.
Performance Impact
For Hive queries, switching from MapReduce to Tez typically yields:
- 2โ5x faster for simple queries (fewer job launches, no HDFS writes between stages)
- 10โ100x faster for complex multi-stage queries (eliminates cascading HDFS I/O)
- Near-interactive latency for many queries that previously took minutes
3. Architecture Overview
Tez has a clean three-layer architecture:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CLIENT APPLICATION โ
โ (Hive, Pig, or custom Tez app) โ
โ โ
โ Uses DAG API to build: Vertices + Edges โ
โ Submits DAG to the Tez ApplicationMaster โ
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Submit DAG
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ TEZ APPLICATION MASTER (AM) โ
โ (runs as a YARN ApplicationMaster) โ
โ โ
โ โข DAG state machine (DAGImpl) โ
โ โข Vertex state machine (VertexImpl) โ
โ โข Task state machine (TaskImpl) โ
โ โข TaskAttempt state machine (TaskAttemptImpl) โ
โ โข Container management & reuse โ
โ โข Speculative execution โ
โ โข Runtime reconfiguration โ
โ โข Scheduling & data locality โ
โโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Launch tasks in containers
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ TASK RUNTIME (per container) โ
โ โ
โ โข Input (reads data: HDFS, shuffle, etc.) โ
โ โข Processor (user logic: map, reduce, join) โ
โ โข Output (writes data: shuffle, HDFS, etc.) โ
โ โ
โ Tasks run the Input-Processor-Output pipeline โ
โ within YARN containers on cluster nodes โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Two Core Components (Per the Tez README)
At its heart, Tez has just two components:
-
The data-processing pipeline engine โ where you plug in Input, Processor, and Output implementations to perform arbitrary data processing within each task.
-
The DAG-based application master โ where you compose those tasks into a DAG and the master handles scheduling, fault tolerance, and resource negotiation with YARN.
4. Core Concepts
DAG (Directed Acyclic Graph)
The top-level execution unit. A DAG contains vertices connected by edges. It maps to a single YARN application.
DAG dag = DAG.create("my-query");
dag.addVertex(scanVertex);
dag.addVertex(joinVertex);
dag.addEdge(Edge.create(scanVertex, joinVertex, edgeConfig));
Vertex
A logical stage in the pipeline. Each vertex has:
- A parallelism (number of tasks)
- A Processor class (the computation logic)
- Zero or more Inputs and Outputs
A vertex with parallelism=100 runs 100 tasks, each executing the same Processor on different data partitions.
Edge
Defines how data flows between two vertices. An edge specifies:
- Data movement type: ONE_TO_ONE, SCATTER_GATHER, BROADCAST
- Scheduling type: SEQUENTIAL (downstream waits) or CONCURRENT
- Data source type: PERSISTED (written to disk) or EPHEMERAL (pipelined)
- Edge manager plugin: controls routing of data between producer and consumer tasks
Task
A single unit of work within a vertex. Runs in a YARN container with an Input โ Processor โ Output pipeline.
Input / Processor / Output
The pluggable components of each task:
- Input: reads key-value pairs (from HDFS, from shuffle, from another source)
- Processor: transforms data (the user's computation logic)
- Output: writes key-value pairs (to shuffle, to HDFS, to another sink)
This is the fundamental abstraction. MapReduce hardcodes the inputs and outputs; Tez makes them pluggable.
5. Source Code Structure
The repository (github.com/apache/tez) is organized into these Maven modules:
tez/
โโโ tez-api/ # Public-facing API (DAG, Vertex, Edge, configs)
โโโ tez-common/ # Shared utilities, counters, security
โโโ tez-dag/ # โ
THE CORE: ApplicationMaster, state machines
โโโ tez-runtime-internals/ # โ
Task runtime framework (Input/Processor/Output lifecycle)
โโโ tez-runtime-library/ # โ
Built-in I/O implementations (shuffle, sort, merge)
โโโ tez-mapreduce/ # MapReduce compatibility layer
โโโ tez-examples/ # WordCount, OrderedWordCount examples
โโโ tez-plugins/ # History logging, YARN plugin
โโโ tez-tests/ # Integration tests
โโโ tez-ui/ # Web UI (Ember.js)
โโโ tez-dist/ # Distribution packaging
Complexity by Module
| Module | Approx. LOC | Complexity | What It Does |
|---|---|---|---|
tez-api | ~20k | Medium | User-facing API: DAG, Vertex, Edge builders, configuration |
tez-dag | ~70k | Very High | ApplicationMaster: DAG/Vertex/Task state machines, scheduling, container mgmt |
tez-runtime-internals | ~15k | High | Task-level runtime: manages the I/P/O pipeline lifecycle inside containers |
tez-runtime-library | ~30k | Very High | Shuffle, sort, merge, partitioning โ the data movement engine |
tez-mapreduce | ~15k | Medium | Wraps MR Mapper/Reducer to run as Tez Processors |
tez-common | ~5k | Low | Utilities, counters |
tez-examples | ~2k | Low | Best starting point for reading the code |
tez-plugins | ~10k | Medium | History event logging, YARN task communicator |
tez-ui | ~20k | Medium | Web interface (JavaScript/Ember.js) |
6. Deep Dive: Key Modules
6.1 tez-api โ The Public API
This is the module that Hive, Pig, and custom applications interact with. The key classes:
DAG.java
The builder for a DAG. Lets you:
- Create a named DAG
- Add vertices and edges
- Set DAG-level configuration and credentials
- Attach security tokens
DAG dag = DAG.create("word-count");
Vertex tokenizer = Vertex.create("Tokenizer", ProcessorDescriptor.create(
TokenProcessor.class.getName()), numTasks);
Vertex summation = Vertex.create("Summation", ProcessorDescriptor.create(
SumProcessor.class.getName()), numTasks);
dag.addVertex(tokenizer)
.addVertex(summation)
.addEdge(Edge.create(tokenizer, summation, edgeConfig));
Vertex.java
Defines a processing stage. Configuration includes:
- Processor class
- Parallelism (task count)
- Data sources (inputs that don't come from other vertices)
- Environment settings (JVM opts, environment variables)
- Vertex manager plugin (for dynamic parallelism)
Edge.java
Connects two vertices. The EdgeProperty specifies:
EdgeProperty.create(
DataMovementType.SCATTER_GATHER, // How data is routed
DataSourceType.PERSISTED, // Disk or in-memory
SchedulingType.SEQUENTIAL, // When downstream starts
outputDescriptor, // Output class on producer side
inputDescriptor // Input class on consumer side
);
Data movement types โ the critical routing strategies:
| Type | Description | Use Case |
|---|---|---|
ONE_TO_ONE | Task i in source โ Task i in dest | Pipeline stages with same parallelism |
SCATTER_GATHER | All source tasks โ all dest tasks (with partitioning) | Shuffle/sort (like MR's shuffle) |
BROADCAST | Every source task sends to every dest task | Broadcast joins (small table to all reducers) |
CUSTOM | User-defined routing via EdgeManagerPlugin | Specialized data routing |
TezClient.java
The client-side entry point. Creates a YARN application, uploads the DAG plan, and submits it to the Tez ApplicationMaster.
6.2 tez-dag โ The ApplicationMaster (The Hard Core)
This is where most of the complexity lives. The AM runs as a YARN ApplicationMaster and manages the entire DAG execution.
State Machines
The AM is built around four nested state machines, each implemented with Hadoop's StateMachineFactory:
DAGImpl (DAG lifecycle)
โโโ VertexImpl (per-vertex lifecycle)
โโโ TaskImpl (per-task lifecycle)
โโโ TaskAttemptImpl (per-attempt lifecycle)
DAGImpl.java โ DAG State Machine
States: NEW โ INITED โ RUNNING โ COMMITTING โ SUCCEEDED / FAILED / KILLED / ERROR
Key transitions:
| Event | From State | To State | What Happens |
|---|---|---|---|
| DAG_INIT | NEW | INITED | Parse DAG plan, create VertexImpl objects |
| DAG_START | INITED | RUNNING | Start root vertices (those with no inputs from other vertices) |
| VERTEX_COMPLETED | RUNNING | RUNNING | Check if downstream vertices can start |
| ALL_VERTICES_DONE | RUNNING | COMMITTING | All vertices succeeded, commit outputs |
| DAG_COMPLETED | COMMITTING | SUCCEEDED | Final commit done |
| INTERNAL_ERROR | any | ERROR | Unrecoverable failure |
This state machine orchestrates the entire execution. When a vertex completes, the DAG checks which downstream vertices have all their input-vertices completed and starts them.
Tip: You can generate a visual Graphviz diagram of this state machine:
mvn compile -Pvisualize \
-Dtez.dag.state.classes=org.apache.tez.dag.app.dag.impl.DAGImpl \
-DskipTests=true
This outputs Tez.gv which you can render with dot -Tpng Tez.gv -o dag-states.png.
VertexImpl.java โ Vertex State Machine
States: NEW โ INITIALIZING โ INITED โ RUNNING โ COMMITTING โ SUCCEEDED / FAILED / KILLED
This is the most complex state machine. A vertex:
- Initializes its tasks based on input splits or configured parallelism
- Waits for the VertexManager to signal readiness
- Schedules tasks on available containers
- Tracks task completion, handles failures and retries
- Commits output when all tasks succeed
Key complexity here: vertex reconfiguration. At runtime, a vertex can change its parallelism based on actual data sizes (see Section 9).
TaskImpl.java and TaskAttemptImpl.java
Task manages one logical unit of work. TaskAttempt tracks a single execution attempt of that task (there may be multiple attempts due to failures or speculative execution).
TaskAttempt handles:
- Container assignment
- Launch on a NodeManager
- Status updates and heartbeats
- Output commit
- Failure handling (retry logic)
Container Management
The AM doesn't just schedule tasks โ it manages container reuse. Key classes:
TaskSchedulerManagerโ coordinates container requests with YARNContainerLauncherManagerโ launches tasks in containersTaskCommunicatorManagerโ heartbeat protocol with running tasks
Container reuse is a critical optimization. When a task finishes, the AM can reassign the same container to the next task in the DAG without releasing it back to YARN and requesting a new one. This avoids the JVM startup penalty that made chained MapReduce jobs slow.
6.3 tez-runtime-internals โ The Task Runtime
This module manages the lifecycle of a single task running inside a YARN container.
LogicalIOProcessorRuntimeTask.java
The main class that runs inside each container. It:
- Initializes the configured Inputs
- Initializes the Processor
- Initializes the configured Outputs
- Calls
processor.run(inputs, outputs)โ this is where user code executes - Handles cleanup and status reporting
The Input-Processor-Output Model
โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโ
โ Input 1 โโโโโโโ โโโโโโโ Output 1 โ
โโโโโโโโโโโโค โ Processor โ โโโโโโโโโโโโโค
โ Input 2 โโโโโโโ โโโโโโโ Output 2 โ
โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโ
Each component is pluggable:
- Inputs implement
LogicalInputโ they know how to read from a data source - Processors implement
LogicalIOProcessorโ they transform data - Outputs implement
LogicalOutputโ they know how to write to a destination
The task runtime wires them together based on the DAG plan. This is the key abstraction that makes Tez flexible โ MapReduce hardcodes the wiring; Tez lets you plug in any combination.
6.4 tez-runtime-library โ Built-in I/O Implementations
This is the other highly complex module. It provides the actual data movement implementations.
Key Classes
| Class | Purpose |
|---|---|
OrderedPartitionedKVOutput | The "map output" โ partitions, sorts, and optionally combines data for a scatter-gather edge |
OrderedGroupedKVInput | The "reduce input" โ fetches shuffled data, merges sorted streams |
UnorderedKVOutput | Unpartitioned output (for broadcast edges) |
UnorderedKVInput | Reads broadcast data |
ShuffleManager | Coordinates fetching shuffle data from upstream tasks |
MergeManager | Manages the merge of sorted runs (in-memory and on-disk) |
IFile / IFileOutputStream | The intermediate file format for shuffle data |
ExternalSorter | Sorts data in memory, spills to disk when memory is exhausted |
The Shuffle Pipeline (Scatter-Gather)
This is the most performance-critical code path. When a producer vertex sends data to a consumer vertex via a SCATTER_GATHER edge:
Producer side (OrderedPartitionedKVOutput):
Records from Processor
โ Partition (by key, using configured Partitioner)
โ Sort (in-memory sort buffer)
โ Optional combine (reduce locally before shipping)
โ Spill to disk when buffer full
โ Merge spills into a single sorted, partitioned file
Consumer side (OrderedGroupedKVInput):
Fetch partition data from all producer tasks (HTTP)
โ Merge sorted streams (k-way merge)
โ Present to Processor as a sorted key-value stream
This is conceptually the same as MapReduce's shuffle, but with two critical improvements:
- No HDFS materialization โ intermediate data goes to local disk or stays in memory
- Pipelining โ consumers can start fetching before all producers finish (configurable via slow-start)
7. The DAG Execution Lifecycle
Here's what happens when Hive submits a query via Tez:
Step 1: DAG Construction (Client Side)
Hive's query optimizer produces a physical plan. The Tez execution engine (inside Hive) translates this into a Tez DAG:
Hive Query: SELECT dept, COUNT(*) FROM employees GROUP BY dept
Tez DAG:
Vertex "Map 1" (parallelism=10) โ reads employees table from HDFS
โ
โ SCATTER_GATHER edge (partition by dept)
โผ
Vertex "Reducer 2" (parallelism=4) โ aggregates counts, writes to HDFS
Step 2: DAG Submission
The TezClient submits the DAG:
- Serializes the DAG plan as a Protocol Buffer message
- Uploads the plan + JARs + configuration to HDFS
- Submits a YARN application (or reuses an existing Tez session)
Step 3: ApplicationMaster Initialization
The Tez AM starts on a cluster node:
DAGAppMaster.main()โ entry point- Registers with YARN ResourceManager
- Deserializes the DAG plan
- Creates
DAGImpl, which createsVertexImplobjects for each vertex
Step 4: Vertex Scheduling
The DAG starts root vertices (those with no upstream dependencies):
VertexImpldetermines parallelism (from input splits or configured value)VertexImplcreatesTaskImplobjects- Each task requests a YARN container (with data locality preferences)
- The
TaskSchedulerManagernegotiates containers from the ResourceManager
Step 5: Task Execution
For each scheduled task:
- The AM launches the task in an allocated container
TezTaskRunnerstarts in the container's JVM- The runtime initializes Input โ Processor โ Output
Processor.run()executes (this is where the actual computation happens)- The task reports status back to the AM via heartbeats
Step 6: Inter-Vertex Data Flow
When "Map 1" tasks produce output:
OrderedPartitionedKVOutputwrites sorted, partitioned data to local disk- The AM notifies "Reducer 2" that data is available
- "Reducer 2" tasks fetch their partitions from "Map 1" tasks via HTTP
ShuffleManager+MergeManagerhandle the fetch and merge
Step 7: Completion
- All tasks in the final vertex complete
- Output is committed (to HDFS or wherever configured)
- The DAG transitions to SUCCEEDED
- The AM reports completion to the client
- Containers are released (or held for session reuse)
8. The Shuffle Pipeline
The shuffle is the most performance-sensitive code in Tez. Here's how data moves through it in detail.
Producer Side
The flow in OrderedPartitionedKVOutput:
1. Processor writes key-value pairs
2. Each record is:
a. Partitioned โ which downstream task gets it
b. Serialized โ converted to bytes
c. Written to a circular in-memory buffer (sort buffer)
3. When buffer reaches threshold (default 80%):
a. Sort the buffer by (partition, key)
b. Optionally run combiner on each partition's data
c. Spill sorted data to local disk as an IFile
4. After all records:
a. Merge all spill files into a single output file
b. Create an index file mapping partition โ offset in the output file
c. Register the output with the AM
Key configuration:
| Property | Default | Description |
|---|---|---|
tez.runtime.io.sort.mb | 100 MB | Size of the in-memory sort buffer |
tez.runtime.sort.spill.percent | 0.8 | Buffer threshold that triggers a spill |
tez.runtime.combine.min.spills | 3 | Minimum spills before combiner runs |
Consumer Side
The flow in OrderedGroupedKVInput:
1. ShuffleManager determines which source tasks to fetch from
2. For each source task:
a. HTTP GET to fetch this consumer's partition from the source's output
b. If small enough โ keep in memory
c. If too large โ write to local disk
3. MergeManager performs k-way merge:
a. In-memory merge when memory segments exceed threshold
b. On-disk merge when disk segments exceed threshold
c. Final merge produces a single sorted stream
4. Processor reads merged sorted key-value pairs
The merge uses a priority queue (min-heap) over sorted segments โ the same algorithm as MapReduce, but with better memory management and configurable thresholds.
9. Runtime Reconfiguration
One of Tez's most powerful features โ and a major source of code complexity.
The Problem
When building the DAG, you often don't know the right parallelism for downstream vertices. Example: if "Map 1" produces 10 GB of shuffle data, you might want 100 reducers. If it produces 100 MB, you might want 2.
MapReduce forces you to guess at job submission time. Get it wrong, and you either waste resources (too many reducers) or create a bottleneck (too few).
How Tez Solves It
VertexManager plugins can dynamically reconfigure a vertex at runtime based on actual data from upstream:
public class ShuffleVertexManager extends VertexManagerPlugin {
@Override
public void onVertexManagerEventReceived(VertexManagerEvent event) {
// Receive actual output size from upstream tasks
actualOutputSize += event.getOutputSize();
}
@Override
public void onVertexStateUpdated(VertexStateUpdate update) {
// When enough upstream tasks have reported:
int newParallelism = actualOutputSize / desiredTaskInputSize;
getContext().reconfigureVertex(newParallelism, ...);
}
}
ShuffleVertexManager (built-in) is the most important VertexManager. It:
- Collects output-size statistics from completed upstream tasks
- Estimates the total output size
- Calculates the optimal parallelism for the downstream vertex
- Reconfigures the vertex (changes task count and routing) before tasks launch
This means a Hive query doesn't need SET mapreduce.job.reduces=100 โ Tez figures it out automatically.
Key Source Files
ShuffleVertexManager.javaintez-runtime-libraryโ the auto-parallelism logicVertexImpl.javaโ handles the reconfigure event, re-creates tasksVertexManager.javaintez-apiโ the plugin interface
10. How Hive and Pig Use Tez
Hive on Tez
When you run a Hive query with hive.execution.engine=tez:
- Hive compiles the HiveQL into an operator tree (Scan โ Filter โ Join โ Aggregate โ File)
- Hive's optimizer (Calcite-based) optimizes the plan
TezCompilertranslates the operator tree into a Tez DAG:- Each "work" unit (a group of operators that run together) becomes a Vertex
- Dependencies between work units become Edges
- Shuffle boundaries (GROUP BY, JOIN, DISTRIBUTE BY) become SCATTER_GATHER edges
- Broadcast joins become BROADCAST edges
TezSessionStatesubmits the DAG to a running Tez session (or starts a new one)- Tez executes the DAG on YARN
- Hive reads the output from HDFS
Example: Multi-Stage Query
SELECT d.name, COUNT(*)
FROM employees e
JOIN departments d ON e.dept_id = d.id
WHERE e.salary > 50000
GROUP BY d.name
ORDER BY COUNT(*) DESC;
As MapReduce (3 separate jobs):
Job 1: Scan employees, filter salary > 50000, scan departments โ Join โ HDFS
Job 2: Read join output โ Group by dept name, count โ HDFS
Job 3: Read aggregated output โ Sort by count DESC โ HDFS
As Tez (1 DAG, 4 vertices):
Vertex "Map 1" (scan employees + filter) โโโ
โโโ Vertex "Join" โโ Vertex "GroupBy" โโ Vertex "Sort"
Vertex "Map 2" (scan departments) โโโโโโโโโโ
No HDFS writes between stages. Containers reused. Dynamic parallelism at each boundary.
Pig on Tez
Pig's integration is similar. Pig's TezCompiler converts Pig's logical plan into a Tez DAG. Each Pig operator (LOAD, FILTER, GROUP, FOREACH, STORE) maps to Tez vertices and edges.
11. Tez on YARN โ The Full Picture
Session Mode vs. Non-Session Mode
Non-session mode: Each DAG submission creates a new YARN application. The AM starts, runs the DAG, and exits. Simple but has launch overhead.
Session mode: A long-lived Tez AM stays running between DAG submissions. Multiple DAGs can be submitted to the same session sequentially. This is what Hive uses for interactive queries โ the first query pays the AM startup cost, but subsequent queries start immediately.
TezClient (session mode)
โ DAG 1: submitted, runs, completes
โ DAG 2: submitted immediately (no AM restart), runs, completes
โ DAG 3: ...
โ session.stop() โ AM releases all containers and exits
Container Reuse
Within a session (and even within a single DAG), Tez reuses containers:
- Task A finishes in container C on node N
- The AM checks if any pending task prefers node N (data locality)
- If yes: reassign container C to that task โ no YARN negotiation needed
- The container's JVM runs the new task's Input/Processor/Output
This is managed by AMContainerMap and the TaskSchedulerManager in tez-dag.
Speculative Execution
Tez supports speculative execution: if a task is running significantly slower than its peers, the AM launches a duplicate attempt on a different node. Whichever finishes first wins; the other is killed.
Controlled by: tez.am.speculation.enabled=true
12. Reading the Source: A Guided Path
If you want to understand how Tez works by reading the code, follow this sequence:
Level 1: The API (1โ2 hours)
Start with the examples and the public API to understand the programming model.
-
tez-examples/WordCount.java(~200 lines) โ A complete Tez application. Shows how to define vertices, edges, processors, and submit a DAG. This is the "Hello World" of Tez. -
tez-examples/OrderedWordCount.javaโ Adds a second vertex for sorting. Shows a multi-vertex DAG with a SCATTER_GATHER edge. -
tez-api/DAG.javaโ Read the builder methods. Clean API. -
tez-api/Vertex.javaโ How vertices are configured. -
tez-api/Edge.java+EdgeProperty.javaโ How edges are defined. Pay attention toDataMovementType.
Level 2: The Task Runtime (2โ4 hours)
Understand what happens inside each task.
-
tez-runtime-internals/LogicalIOProcessorRuntimeTask.javaโ The task entry point. Follows the I/P/O lifecycle clearly. -
tez-api/Processor.javainterface โ Simple: justrun(Map<String, LogicalInput>, Map<String, LogicalOutput>). -
tez-runtime-library/OrderedPartitionedKVOutput.javaโ The producer side of shuffle. Follow fromwrite()through sort and spill. -
tez-runtime-library/OrderedGroupedKVInput.javaโ The consumer side. Follow from initialization through fetch and merge.
Level 3: The ApplicationMaster (4โ8 hours)
The hardest part. Read the state machines.
-
Generate the state diagram first:
mvn compile -Pvisualize \ -Dtez.dag.state.classes=org.apache.tez.dag.app.dag.impl.DAGImpl \ -DskipTests=trueRender the
.gvfile and keep it open as a reference. -
tez-dag/DAGImpl.javaโ Focus on theStateMachineFactoryat the top of the file. Each.addTransition()call defines one edge in the state diagram. Read the transition handlers to understand what happens at each step. -
tez-dag/VertexImpl.javaโ The most complex file. Focus on:- The state machine definition
handleInitEvent()โ how a vertex initializesscheduleTasks()โ how tasks are scheduledreconfigureVertex()โ runtime parallelism changes
-
tez-dag/TaskImpl.javaโ Simpler. Focus on attempt management. -
tez-dag/TaskAttemptImpl.javaโ Focus on launch, completion, and failure handling.
Level 4: Container & Scheduling (Advanced)
-
tez-dag/TaskSchedulerManager.javaโ How the AM interacts with YARN for containers. -
tez-dag/AMContainerMap.javaโ Container reuse logic. -
tez-runtime-library/ShuffleVertexManager.javaโ Auto-parallelism. TheonVertexManagerEventReceived()andreconfigureVertex()methods are where the magic happens.
13. Simpler Open Source Alternatives
If the Tez codebase feels overwhelming, these projects implement the same core ideas in much less code. Study them first to build intuition, then come back to Tez.
For Understanding DAG Execution Logic
Luigi (Python, by Spotify)
Repository: github.com/spotify/luigi Size: ~15,000 lines of Python Best for: Understanding DAG dependency resolution and task scheduling
Luigi implements the core job of Tez's DAG engine โ scheduling tasks in dependency order, handling retries, tracking state โ in readable Python. The key files:
scheduler.pyโ the central scheduler that resolves dependenciesworker.pyโ the worker that pulls and executes taskstask.pyโ the Task base class withrequires()andrun()
The requires() pattern maps directly to Tez's Edge concept โ each task declares its upstream dependencies.
Dask (Python)
Repository: github.com/dask/dask
Size: ~100k total, but local.py is ~400 lines
Best for: Understanding the minimal DAG execution algorithm
Dask's dask/local.py contains a complete single-machine DAG executor in under 400 lines. It's the clearest possible implementation of the core algorithm:
- Build a DAG of function calls
- Identify tasks with no dependencies
- Execute them
- Remove completed tasks from the graph
- Repeat until empty
For the distributed version, distributed/scheduler.py adds work-stealing and data locality โ concepts directly relevant to Tez's TaskSchedulerManager.
Prefect (Python)
Repository: github.com/PrefectHQ/prefect Size: ~50,000 lines Best for: Modern task/flow model with clean state management
Prefect's Task and Flow abstractions map almost 1:1 to Tez's Processor and DAG. Their state machine is simpler and better documented than Tez's.
For Understanding Distributed Execution
Ray (Python / C++)
Repository: github.com/ray-project/ray Best for: Understanding distributed task execution without YARN complexity
Ray's task model (remote functions, object store, scheduling) is conceptually the closest modern equivalent to Tez. Their architecture whitepaper and documentation are excellent. The key insight Ray shares with Tez: tasks produce objects, and downstream tasks consume those objects โ the system handles transfer.
Spark Core (Scala)
Repository: github.com/apache/spark Best for: Comparing Tez's approach to the main competing engine
Spark's DAGScheduler plays the same role as Tez's DAGImpl + VertexImpl. Comparing the two is illuminating:
| Concept | Tez | Spark |
|---|---|---|
| Execution unit | DAG | Job |
| Stage | Vertex | Stage |
| Task | Task | Task |
| Shuffle | OrderedPartitionedKVOutput | ShuffleMapTask |
| Dynamic parallelism | ShuffleVertexManager | Adaptive Query Execution |
| Container reuse | AM-managed | Executor model (always reused) |
For Understanding the Shuffle Pipeline
There's no great "simple" version of a distributed shuffle, because it's inherently complex. The most readable reference implementations:
- Spark's
SortShuffleWriterandExternalSorterโ same concepts as Tez'sOrderedPartitionedKVOutput, but in Scala with better comments - Hadoop MapReduce's
MapOutputBufferโ the original implementation that Tez's shuffle is based on (inhadoop-mapreduce-client-core)
Summary Comparison Table
| Project | Language | Lines of Code | What It Teaches | Learning Time |
|---|---|---|---|---|
Dask local.py | Python | ~400 | Minimal DAG executor algorithm | 1 hour |
| Luigi | Python | ~15k | Dependency resolution, scheduling, retry | 1 day |
| Prefect core | Python | ~50k | Task/flow model, modern state management | 1โ2 days |
| Tez examples | Java | ~2k | Tez API surface, I/P/O model | 2 hours |
| Ray core | Python/C++ | Large | Distributed object-based task execution | 2โ3 days |
| Spark DAGScheduler | Scala | ~5k | Stage-based DAG execution (Tez competitor) | 1 day |
Tez tez-dag | Java | ~70k | Production DAG AM with full state machines | 1 week+ |
Tez tez-runtime-library | Java | ~30k | Production shuffle/sort/merge pipeline | 1 week+ |
Recommended Learning Path
- Read Dask
local.pyโ understand the core algorithm (1 hour) - Read Luigi's
scheduler.pyโ add dependency resolution and retry (half day) - Read Tez
tez-examples/WordCount.javaโ see how Tez exposes the DAG API (1 hour) - Read Tez
tez-api/DAG.java+Vertex.java+Edge.javaโ the user-facing API (2 hours) - Generate Tez's state diagram โ visualize the AM's state machine (30 min)
- Read Tez
DAGImpl.javawith the state diagram open โ the AM core (half day) - Read Tez
OrderedPartitionedKVOutput.javaโ the shuffle producer (half day)
14. References
Official Resources
- Apache Tez Website: https://tez.apache.org
- GitHub Repository: https://github.com/apache/tez
- Tez Design Documents: https://cwiki.apache.org/confluence/display/TEZ
- How to Contribute: https://cwiki.apache.org/confluence/display/TEZ/How+to+Contribute+to+Tez
Key Papers & Talks
- "Apache Tez: Accelerating Hadoop Query Processing" โ Bikas Saha, Arun Murthy (Hortonworks, 2013)
- "Hive + Tez: A Performance Deep Dive" โ Jitendra Pandey, Gopal V
- InfoQ Article: "What is Apache Tez?" โ https://www.infoq.com/articles/apache-tez-saha-murthy/
Source Code Reading Guides
- Tez source reading notes by @oza: https://gist.github.com/oza/470e961ff10b60778772
- BUILDING.txt in the repo: Module structure and build instructions
Simpler Alternatives (GitHub)
- Luigi: https://github.com/spotify/luigi
- Dask: https://github.com/dask/dask
- Prefect: https://github.com/PrefectHQ/prefect
- pydags: https://github.com/DavidTorpey/pydags
- simple-dag: https://github.com/leokster/simple_dag
- Dagu: https://github.com/dagucloud/dagu
- Ray: https://github.com/ray-project/ray