Lab 6.1 — Trace a Hive SQL Query to the Generated Tez DAG
Lab type: Reproduce & Trace
Estimated time: 150 min
Tez modules: tez-dag, tez-runtime-library, tez-api
Hive classes (verify in your Hive checkout): SemanticAnalyzer, TezCompiler, TezWork, DagUtils, TezTask
Background
When you run
SELECT dept, COUNT(*) FROM employees GROUP BY dept;
on Hive with hive.execution.engine=tez, Hive compiles the SQL into a physical
plan of MapWork/ReduceWork units, wraps them in a TezWork graph, and hands
that to DagUtils, which builds a real Tez DAG — vertices named Map 1,
Reducer 2, edges of type SCATTER_GATHER, and a ShuffleVertexManager on the
reduce vertex. TezTask submits it via TezClient.submitDAG and the Tez AM
(DAGImpl) runs it.
In this lab you will reproduce that translation on your own machine, read the
EXPLAIN plan, map every plan vertex onto a runtime Tez vertex of the same
name, and confirm the topology two independent ways (the Hive summary and the
Tez .dot graph). You come out able to trace any Hive query to its DAG.
Why This Lab Matters for Contributors
Almost every Tez performance or correctness bug arrives described in Hive terms:
"my GROUP BY spawns 1009 reducers," "the join vertex hangs." To fix it you
must translate that Hive vocabulary into Tez runtime objects — vertices, edges,
vertex managers — because that is where the Tez code you would patch actually
lives. This lab builds the translation reflex. Without it, you cannot even read
the bug report; with it, you can point at the exact VertexImpl or
ShuffleVertexManager behind the symptom.
Prerequisites
- Level 6 index read; you can state the Hive/Tez seam.
-
Your Tez checkout builds (
mvn -q -DskipTests installsucceeds). - Docker installed or a Hadoop + Hive tarball you can run locally.
-
Comfort with
EXPLAINoutput from Lab H1. - You have read Lab H2: Inspect the DAG or are ready to.
Note: Hive is not in your Tez checkout. Everywhere this lab names a Hive class, a
grep/findcommand follows so you confirm it against your Hive version. Never cite a Hive symbol you have not seen in your own tree.
Step-by-Step Tasks
Step 1 — Stand up a local Hive-on-Tez
You need the smallest thing that compiles SQL to a Tez DAG and runs it. Two recipes; pick one and pin the versions you actually use — adapt every version string below to what you install.
Recipe A — Docker (fastest). The Apache Hive project publishes a single-container image that boots HiveServer2 with an embedded Derby metastore. Check current tags on Docker Hub / the Hive site and substitute the tag you find; the shape is:
# Adapt the tag to a real published Hive 4.x tag you verify on Docker Hub.
docker run -d --name hive4 \
-p 10000:10000 -p 10002:10002 \
--env SERVICE_NAME=hiveserver2 \
apache/hive:4.0.1
# Wait for HiveServer2, then connect with the bundled beeline:
docker exec -it hive4 beeline -u 'jdbc:hive2://localhost:10000/'
Recipe B — Tarballs (full control). If you want to see the Tez jars and configs on disk, install a matched trio and set them up by hand:
# Versions are EXAMPLES — verify current releases and adapt all three.
HADOOP=3.3.6 ; HIVE=4.0.1 ; TEZ=0.10.4
# 1) unpack hadoop, hive, tez tarballs
# 2) put tez's jars in HDFS (or file:///) and point tez.lib.uris at them
# 3) in hive-site.xml set:
# hive.execution.engine = tez
# hive.tez.container.size = 1024 (adapt to your RAM)
# 4) start a local metastore (Derby is fine for a lab) and HiveServer2
Whichever you choose, record the exact Hive and Tez versions — Hive×Tez compatibility is version-sensitive, and your notes are worthless without them:
-- inside beeline
SET hive.execution.engine; -- must print: tez
Expected: hive.execution.engine=tez. If it says mr or spark, set it:
SET hive.execution.engine=tez;. Write the Hive and Tez versions into your lab
notes now.
Step 2 — Create a tiny, deterministic dataset
Small and deterministic so the counters are predictable:
CREATE TABLE employees (id INT, dept STRING) STORED AS ORC;
INSERT INTO employees VALUES
(1,'eng'),(2,'eng'),(3,'eng'),
(4,'sales'),(5,'sales'),
(6,'ops');
Six rows, three distinct dept values (eng=3, sales=2, ops=1). Keep this
in your notes — you will predict counters against it in Step 6.
Step 3 — Read the EXPLAIN plan and extract the vertices
EXPLAIN SELECT dept, COUNT(*) FROM employees GROUP BY dept;
Find the Stage: Stage-1 / Tez block. For a single-GROUP BY query you expect
two vertices and one edge:
| Plan vertex | Operators inside | Meaning |
|---|---|---|
Map 1 | TableScan → Select → GroupBy → ReduceOutputOperator | read employees, partial aggregate by dept |
Reducer 2 | GroupBy → Select → FileSink | final aggregate, write result |
and the edge line Reducer 2 <- Map 1 (SIMPLE_EDGE). In Hive's EXPLAIN,
SIMPLE_EDGE is the shuffle edge — it becomes Tez SCATTER_GATHER. Copy the
whole Stage-1 block into your notes; it is the left-hand column of your
mapping.
Note: Hive names map vertices
Map Nand reduce verticesReducer N. Those exact strings become the Tez vertex names. Hold onto them.
Step 4 — Turn on the Hive-side DAG summary
Hive can print a per-vertex runtime summary after the query. Enable it and run:
SET hive.tez.exec.print.summary=true;
SELECT dept, COUNT(*) FROM employees GROUP BY dept;
After the result rows, Hive prints a summary table listing each vertex (Map 1,
Reducer 2), its task count, and timing. This is your first independent
confirmation of the topology: the vertex names here must match the EXPLAIN
plan. Save the summary block.
If your build labels the setting differently, grep the Hive conf for it:
grep -rn "print.summary" $(find . -name HiveConf.java)in your Hive checkout.
Step 5 — Find the same vertices on the Tez side
Now cross the seam. The vertices Hive named are, at runtime, VertexImpl
objects inside the Tez AM (DAGImpl). You can see the names two ways.
(a) The Tez .dot graph. Tez writes a Graphviz file of the submitted DAG
when debug artifacts are enabled. Confirm the switch and the writer in your
checkout:
# The config flag (default false):
grep -rn "generate.debug.artifacts" \
tez-api/src/main/java/org/apache/tez/dag/api/TezConfiguration.java
# The code that writes "<dagId>.dot":
grep -rn "\.dot\|Generating DAG graphviz file" \
tez-dag/src/main/java/org/apache/tez/Utils.java
Enable it for your query and rerun:
SET tez.generate.debug.artifacts=true;
SELECT dept, COUNT(*) FROM employees GROUP BY dept;
A <dagId>.dot file appears in the AM log directory. Its node names are Map 1
and Reducer 2; each edge is labelled with dataMovement=SCATTER_GATHER and
schedulingType=.... Render it:
dot -Tpng <dagId>.dot -o dag.png # or paste the .dot into any Graphviz viewer
The edge label text is built in tez-dag's org.apache.tez.Utils — it emits
dataMovement= and schedulingType= for each edge. That string is your proof
that the Hive SIMPLE_EDGE became a Tez SCATTER_GATHER edge.
(b) The Tez UI / AM logs. If you have the Tez UI or ATS wired up, the DAG
view shows the same two named vertices. Otherwise grep the AM syslog for the
vertex names (you will do this properly in Lab 6.2):
yarn logs -applicationId <appId> -log_files syslog | grep -E "Map 1|Reducer 2"
Step 6 — Build the correlation table
Now write the artifact this whole lab exists to produce: a table that maps every
EXPLAIN plan vertex to its Tez runtime object and predicts its counters.
| Hive plan (EXPLAIN) | Tez runtime object | Edge in / out | Predicted counters (6-row input) |
|---|---|---|---|
Map 1 (TS→SEL→GBY→RS) | VertexImpl "Map 1", MapTezProcessor | out: SCATTER_GATHER → Reducer 2 | INPUT_RECORDS_PROCESSED = 6; OUTPUT_RECORDS = 3 (post partial-GBY) |
Reducer 2 (GBY→SEL→FS) | VertexImpl "Reducer 2", ReduceTezProcessor | in: SCATTER_GATHER from Map 1 | REDUCE_INPUT_GROUPS = 3; OUTPUT_RECORDS = 3 |
The counter names are real Tez TaskCounter enum members — verify:
grep -nE "INPUT_RECORDS_PROCESSED|OUTPUT_RECORDS|REDUCE_INPUT_GROUPS" \
tez-api/src/main/java/org/apache/tez/common/counters/TaskCounter.java
Now run with the summary on and fill in the actual numbers. The partial
aggregate in Map 1 (map-side GROUP BY) is why Map 1 OUTPUT_RECORDS is 3,
not 6 — you should be able to explain that from the operator tree.
Step 7 — Attribute each DAG element to the Hive class that built it
The final discrimination: for each piece of the DAG, name the Hive class that constructed it. This is what makes you able to route a bug to the right project.
| DAG element | Built by (Hive class, verify) | Reproduce the grep |
|---|---|---|
Vertex "Map 1" / "Reducer 2" | DagUtils.createVertex | grep -n "createVertex|Vertex.create" $(find . -name DagUtils.java) |
The SCATTER_GATHER edge | DagUtils.createEdge | grep -n "createEdge|Edge.create" $(find . -name DagUtils.java) |
ShuffleVertexManager on Reducer 2 | DagUtils (attaches the plugin descriptor) | grep -n "ShuffleVertexManager" $(find . -name DagUtils.java) |
The submitted DAG | TezTask.execute → TezClient.submitDAG | grep -n "submitDAG" $(find . -name TezTask.java) |
| Everything after submit | Tez DAGImpl (not Hive) | grep -rn "class DAGImpl" tez-dag/src/main/java |
Run each grep in your Hive checkout (top four) and Tez checkout (last). The
point lands when you can say: "Reducer 2's ShuffleVertexManager was attached
by Hive's DagUtils but is executed by Tez's runtime-library — so a
reducer-count bug could be in either project, and I know how to tell which."
Deliverables
-
Recorded Hive and Tez versions and the working
SET hive.execution.engine=tez. -
The
EXPLAINStage-1 / Tezblock for theGROUP BYquery, saved. -
The
hive.tez.exec.print.summary=trueoutput, saved. -
The rendered Tez
.dotgraph (dag.png) or its raw text, showingdataMovement=SCATTER_GATHER. - The completed correlation table (Step 6) with predicted and actual counters, and your one-line explanation of the partial-aggregate count.
- The attribution table (Step 7) with every grep run against your own checkouts.
Troubleshooting
| Symptom | Likely cause | Where to look |
|---|---|---|
hive.execution.engine is mr | Tez not configured | SET hive.execution.engine=tez;; check tez.lib.uris |
No .dot file appears | Debug artifacts off, or wrong log dir | SET tez.generate.debug.artifacts=true; then look in the AM log dir; confirm flag with the grep in Step 5 |
EXPLAIN shows a Map-only plan (no Reducer) | Query got map-side-only optimized | Use the GROUP BY given; check hive.map.aggr didn't collapse it unexpectedly |
| More vertices than expected | Extra optimization stage (e.g. auto-reduce) | Record the actual topology; note which extra vertex and why |
| Summary setting not recognized | Version drift in the conf key | grep your Hive HiveConf.java for print.summary and use that key |
yarn logs empty | Log aggregation off, or wrong appId | Enable aggregation; get the appId from the Hive console line |
Stretch Goals
- Add
ORDER BYand watch a vertex appear. RunEXPLAINonSELECT dept, COUNT(*) FROM employees GROUP BY dept ORDER BY dept;. You now get a third vertex (Reducer 3) with parallelism 1 — Hive forces a single reducer for total order. Confirm parallelism 1 in the.dotgraph and explain, from Lab H1, why. - Provoke auto-parallelism. With a larger table and
hive.tez.auto.reducer.parallelism=true, the reduce vertex's task count is set at runtime byShuffleVertexManager, not at compile time. Compare theEXPLAINreducer count against the summary's actual task count and reconcile them via the plugin. - Diff two Hive versions. If you can run two Hive versions against the same
Tez,
EXPLAINthe same query on each and diff the plans. Any difference in vertex/edge shape is a compatibility surface — exactly the kind of thing that blocks a Tez release.
Validation
Answer these from your artifacts, not from memory:
- In the
GROUP BYquery, which Hive class turnsSIMPLE_EDGEinto a TezSCATTER_GATHERedge, and which Tez class executes that edge's shuffle? - Why is
Map 1 OUTPUT_RECORDS= 3 and not 6 for the six-row input? Name the operator responsible. - Where did the string
Reducer 2originate — Hive or Tez — and where do you see it on each side? - What config flag makes Tez write the
.dotfile, what is its default, and which Tez class writes the file? - If the query produced 1009 reducers unexpectedly, name the two projects the bug could live in and the one class in each you would open first.
- Which
TaskCountermember tells you the number of distinctdeptgroups, and on which vertex do you read it? - Adding
ORDER BYcreated a vertex with parallelism 1 — what forces that, and is it a Hive decision or a Tez decision?
When you can answer all seven and hand over both tables, proceed to Lab 6.2 — Debug a Failed Hive-on-Tez Query, or go deep with Lab H2: Inspect the DAG.