Lab 1.3: Run a Simple Tez DAG Locally
Background
Apache Tez supports a local mode that runs an entire DAG — Application Master, vertices, and tasks — inside a single JVM, with no YARN cluster and no HDFS. This is the primary environment for rapid development and for most of the integration tests you will read later. Being able to run a DAG locally, watch its lifecycle in the logs, and reason about what each line means is the bridge between "I built Tez" (Lab 1.1) and "I understand what Tez does."
You will run a real shipped example — OrderedWordCount from the tez-examples module — in
local mode, then read its log output as the DAG, its vertices, and its tasks move through their
state machines. OrderedWordCount is the canonical Tez example: it tokenizes text, sums word
counts, and emits words ordered by frequency, using the full public DAG API (DAG, Vertex,
Edge, MRInput/MROutput, and an ordered partitioned shuffle edge).
This lab depends on a clean build (Lab 1.1). The mechanics of local mode are dissected further in the local-mode deep-dive; the DAG structure you run here is the subject of the DAG-model deep-dive.
Why This Lab Matters for Contributors
- Local mode is how you verify a behavior change without standing up a cluster — your daily loop.
- Every integration test in
tez-testsis built on the same local/mini-cluster infrastructure. - Reading a real DAG's construction gives concrete meaning to the state-machine code you study in Level 4 — the log lines here are state transitions.
- Local-mode-only bugs and DAG-API usability issues are a real, accessible contribution surface.
Prerequisites
-
Lab 1.1 complete, including Step 7 (the
tez-disttarballs). -
The JDK/
protocprerequisites from Lab 1.1 still on yourPATH. -
You know your build's project version string (
mvn help:evaluate -Dexpression=project.version -q -DforceStdout). -
~1 GBfree disk for input/output scratch under/tmp.
Understanding Tez Local Mode
Local mode is switched on by a single configuration key. Read the real definition and its default:
grep -n "TEZ_LOCAL_MODE\b\|local.mode.without.network\|OPTIMIZE_LOCAL_FETCH" \
tez-api/src/main/java/org/apache/tez/dag/api/TezConfiguration.java
You will find the verified keys (do not hard-code the string values; read them):
| Constant | Property string | Default | Effect |
|---|---|---|---|
TezConfiguration.TEZ_LOCAL_MODE | tez.local.mode | false | Run tasks as threads in the client JVM — no YARN, no containers. |
TezConfiguration.TEZ_LOCAL_MODE_WITHOUT_NETWORK | tez.local.mode.without.network | false | Skip the IPC server; LocalClient calls the AM directly. |
TezRuntimeConfiguration.TEZ_RUNTIME_OPTIMIZE_LOCAL_FETCH | tez.runtime.optimize.local.fetch | true | Read shuffle data straight off local disk instead of over the network. |
Crucially, you rarely set these by hand for a shipped example. TezExampleBase — the base class of
every example in tez-examples — accepts a -local command-line flag that does the wiring for
you. Confirm it in the source:
grep -n "LOCAL_MODE\|TEZ_LOCAL_MODE\|OPTIMIZE_LOCAL_FETCH\|disableSplitGrouping" \
tez-examples/src/main/java/org/apache/tez/examples/TezExampleBase.java
When you pass -local, TezExampleBase sets TEZ_LOCAL_MODE=true, turns on
TEZ_RUNTIME_OPTIMIZE_LOCAL_FETCH, and disables split grouping — exactly what a single-JVM run
needs. That is the supported, real recipe; use it rather than hand-rolling -Dtez.local.mode=true.
Anatomy of OrderedWordCount
Read the example before running it:
sed -n '1,120p' tez-examples/src/main/java/org/apache/tez/examples/OrderedWordCount.java
grep -n "Vertex.create\|Edge.create\|addVertex\|addEdge\|newBuilder" \
tez-examples/src/main/java/org/apache/tez/examples/OrderedWordCount.java
The DAG has three vertices (the names are string constants — Tokenizer and Summation come from
WordCount, Sorter is defined in OrderedWordCount):
[Tokenizer] MRInput(TextInputFormat) → emits (word, 1)
│ OrderedPartitionedKV edge (key=Text, value=IntWritable, HashPartitioner)
▼
[Summation] sums per word → writes (count, word) ← count is the KEY
│ OrderedPartitionedKV edge (key=IntWritable, value=Text, HashPartitioner)
▼
[Sorter] 1 task; the ordered edge already sorted by count → MROutput(TextOutputFormat)
flowchart TD
A["Tokenizer<br/>TokenProcessor<br/>MRInput → (word, 1)"]
B["Summation<br/>SumProcessor<br/>sum → (count, word)"]
C["Sorter<br/>NoOpSorter (1 task)<br/>→ MROutput"]
A -->|"OrderedPartitioned KV<br/>(Text, IntWritable)"| B
B -->|"OrderedPartitioned KV<br/>(IntWritable, Text)"| C
The key insight: the sort happens on the edge, not in a processor. SumProcessor writes the
count as the key; the ordered partitioned edge into Sorter sorts and groups by that count key,
so NoOpSorter (a SimpleMRProcessor) only forwards the already-ordered data to MROutput. This
is the essence of the Tez model — behavior is expressed through edge properties and I/O
configuration, not just processor code. You will build the same shape from scratch, with integers
instead of words, in Lab 1.4.
Step-by-Step Tasks
Step 1: Prepare Sample Input
mkdir -p /tmp/tez-lab/input
cat > /tmp/tez-lab/input/words.txt << 'EOF'
the quick brown fox jumps over the lazy dog
the dog barked at the fox
quick brown dog
EOF
rm -rf /tmp/tez-lab/output # MROutput refuses to overwrite an existing dir
Step 2: Build the Examples and Assemble a Runnable Classpath
The cleanest, self-contained way to run from a source build is the exploded full distribution you produced in Lab 1.1 Step 7 — it contains every Tez jar plus the bundled Hadoop and third-party jars, so no separate Hadoop install is needed.
# Ensure examples + the dist are built:
mvn package -DskipTests -Pnoui -pl tez-examples -am -q
mvn package -DskipTests -Pnoui -pl tez-dist -am -q
# The assembly emits a 'dir' format alongside the tarball:
VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
export TEZ_DIST="tez-dist/target/tez-${VERSION}"
ls "$TEZ_DIST" # tez-*.jar at the root; third-party + hadoop jars under lib/
The distribution layout is defined by tez-dist/src/main/assembly/tez-dist.xml: the Tez module
jars land at the root (/) and their runtime dependencies under /lib. So a classpath of
"$TEZ_DIST/*:$TEZ_DIST/lib/*" is everything you need.
Note: If you use the minimal tarball instead, Hadoop jars are excluded by design (see
tez-dist-minimal.xml), so you must append$(hadoop classpath)from a real Hadoop install. The full dist avoids that.
Step 3: Run OrderedWordCount in Local Mode
The -local flag is the supported switch (Step "Understanding Tez Local Mode"). Options come
before the positional arguments <input> <output> [numPartitions]:
java -cp "$TEZ_DIST/*:$TEZ_DIST/lib/*" \
org.apache.tez.examples.OrderedWordCount \
-local -counter \
/tmp/tez-lab/input \
/tmp/tez-lab/output \
1
-counter also prints DAG counters at the end (it maps to the COUNTER_LOG option in
TezExampleBase). You can equally launch through the examples driver — the tez-examples jar's
manifest main class is org.apache.tez.examples.ExampleDriver, which registers orderedwordcount
among other examples:
grep -n "addClass" tez-examples/src/main/java/org/apache/tez/examples/ExampleDriver.java
java -cp "$TEZ_DIST/*:$TEZ_DIST/lib/*" \
org.apache.tez.examples.ExampleDriver orderedwordcount \
-local /tmp/tez-lab/input /tmp/tez-lab/output 1
Tip: If a run fails because the output dir exists,
rm -rf /tmp/tez-lab/outputand retry. ThisFileAlreadyExistsExceptionis Hadoop'sMROutputrefusing to clobber — the same behavior you'd hit on a cluster.
Step 4: Verify the Output
cat /tmp/tez-lab/output/part-*
Words appear ordered by count (the exact tie-break order among equal counts depends on the sort):
1 jumps
1 over
1 lazy
1 barked
1 at
2 quick
2 brown
2 fox
3 dog
4 the
Step 5: Read the Execution Log — Map Lines to State Transitions
This is the real payoff. During the run, Tez logs the lifecycle of the DAG, each vertex, and each task. The log-line formats are stable and worth memorizing; confirm the exact strings in source so you can grep them in any future run:
grep -n "Running DAG:" tez-dag/src/main/java/org/apache/tez/dag/app/DAGAppMaster.java
grep -n "transitioned from" \
tez-dag/src/main/java/org/apache/tez/dag/app/dag/impl/DAGImpl.java \
tez-dag/src/main/java/org/apache/tez/dag/app/dag/impl/VertexImpl.java
grep -n "DAG completed, dagId=" tez-dag/src/main/java/org/apache/tez/dag/app/DAGAppMaster.java
The verified formats you will see in the console:
| Source (module/class) | Log format | What it means |
|---|---|---|
tez-dag DAGAppMaster | Running DAG: <dagName> | The AM accepted the DAG plan and is starting it. |
tez-dag DAGImpl | <dagId> transitioned from <A> to <B> due to event <E> | A DAG-level state-machine transition (e.g. NEW → INITED, RUNNING → SUCCEEDED). |
tez-dag VertexImpl | <vertexId> [<name>] transitioned from <A> to <B> due to event <E> | A vertex transition — note the [Tokenizer] / [Summation] / [Sorter] identifier. |
tez-dag DAGAppMaster | DAG completed, dagId=<id>, dagState=<state> | Terminal: the DAG finished; dagState is SUCCEEDED/FAILED/KILLED. |
tez-api TezClient | Submitting DAG ... | The client handed the DAG to the AM. |
For each vertex transition line, name the state pair. For example a Tokenizer line moving
NEW → INITIALIZING → INITED → RUNNING → SUCCEEDED is the exact path you will trace in the
vertex-lifecycle deep-dive. Write down the sequence you
observe for one vertex — you will reconcile it against the state machine in Level 4.
Step 6: Turn Up the Volume (Optional)
To see every transition clearly, raise the log level for the impl package and re-run:
java -cp "$TEZ_DIST/*:$TEZ_DIST/lib/*" \
-Dtez.root.logger=INFO,console \
org.apache.tez.examples.OrderedWordCount \
-local /tmp/tez-lab/input /tmp/tez-lab/output2 1 2>&1 | grep "transitioned from"
Count how many distinct vertex/DAG transitions a three-vertex DAG produces. It is more than you expect — which is why the state machines need 100+ tests (Lab 1.2).
Deliverables
-
OrderedWordCount -localruns to completion and writes apart-*file. - The output is ordered by count, and you can explain why the edge, not a processor, sorts it.
-
You located the four real log-line formats (
Running DAG, DAG transition, vertex transition,DAG completed) in thetez-dagsource withgrep. - You wrote down the full state sequence for one vertex from the log.
-
You can state which config key enables local mode and which flag on
TezExampleBasesets it.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
FileAlreadyExistsException: Output directory ... already exists | MROutput will not overwrite. | rm -rf /tmp/tez-lab/output before each run (or use a fresh output path). |
NoClassDefFoundError / ClassNotFoundException for a Hadoop or Tez class | Classpath missing jars — usually you used the minimal dist or a single module jar. | Use the full exploded dist: "$TEZ_DIST/*:$TEZ_DIST/lib/*". With the minimal dist, add $(hadoop classpath). |
$TEZ_DIST is empty / does not exist | You didn't build the dir format, or the version glob is wrong. | Re-run mvn package -DskipTests -Pnoui -pl tez-dist -am; re-evaluate VERSION via mvn help:evaluate. |
| Run contacts YARN / tries to connect to a ResourceManager | You omitted -local, so it ran in distributed mode. | Pass -local (before the positional args). Confirm the log says it is running in local mode. |
Permission denied / cannot write output | Output path not writable, or fs.defaultFS unexpectedly points at HDFS. | Write under /tmp; optionally force local FS with a generic option: add -Dfs.defaultFS=file:/// (before -local). |
DAG ends FAILED with a task exception | A processor threw; the AM logs the failing task attempt. | Grep the log for ERROR and for transitioned from ... to FAILED; the stack trace names the class/method. Common causes: bad input path (FileNotFoundException), classpath gaps. |
| Output words not sorted the way you expect for equal counts | Tie-break among equal keys is not specified by the example. | Correct behavior — only the count ordering is guaranteed; see the edge config in the source. |
UnknownHostException or slow start on a laptop | Reverse-DNS / hostname resolution during AM startup. | Harmless in local mode; add -Dtez.local.mode.without.network=true to skip IPC entirely. |
Stretch Goals
-
Watch the transitions live. Re-run piping through
grep "transitioned from"and reconstruct the DAG-level path (NEW → INITED → RUNNING → SUCCEEDED). Confirm it against dag-model. -
Change parallelism. Run with
numPartitions = 2(the third positional arg). Observe twopart-*files and twoSummationtask transitions in the log. Reason about which vertex's parallelism that argument actually controls (readcreateDAG). -
Try another shipped example in local mode.
WordCount,SimpleSessionExample,HashJoinExample, andJoinDataGen/JoinValidateare all registered inExampleDriver. Run:java -cp "$TEZ_DIST/*:$TEZ_DIST/lib/*" org.apache.tez.examples.ExampleDriverwith no args to list them, then run
wordcount -localand compare its log toorderedwordcount. -
Preview an added vertex. Before you build one from scratch in Lab 1.4, sketch (on paper) how you would insert a filter vertex between
SummationandSorterthat drops words with count < 2: which two edges change, and what processor code the new vertex needs. Lab 1.4 makes you actually write it. -
(Cluster, optional/awareness) On a real pseudo-distributed YARN, local mode is not used. Instead you upload the dist tarball to HDFS and set
tez.lib.uris:grep -n "tez.lib.uris\|use.cluster.hadoop-libs\|mapreduce_shuffle" \ docs/src/site/markdown/install.mdSet
tez.lib.uristo the HDFS path oftez-<version>.tar.gz, ensure YARN's NodeManageraux-servicesincludes the shuffle handler (mapreduce_shuffle), and drop-local. This is the real deployment path you will care about only from Level 5 onward.
Validation / Self-check
You are done when you can answer these without notes:
- Which single configuration key enables local mode, what is its default, and which
TezExampleBasecommand-line flag turns it on for a shipped example? - In
OrderedWordCount, what does the sorting — a processor or an edge — and how do you know from the code? - What is the exact log-line format that reports a vertex state transition, and what does the
bracketed
[...]part contain? - Why does the second run fail if you don't delete the output directory, and where does that behavior come from?
- What is the difference between the full and minimal dist for the purpose of running an example locally, and what do you have to add if you use the minimal one?
- Trace the DAG-level state path you observed from the logs (
NEW → … → SUCCEEDED). - On a real cluster you would drop
-local— what two things must be configured instead (tez.lib.urisand the NodeManager shuffleaux-service)?
Where to go next: you have now run someone else's DAG and watched it live. In Lab 1.4 — Number Pipeline DAG you build the same three-vertex shape from scratch, in code you own, and assert the result. Deepen the mechanics with the local-mode, vertex-lifecycle, and dag-model deep-dives, then carry the skill into Level 2.