Lab 2.1: Navigate the Repository Structure
Background
Apache Tez is a Maven multi-module project — the root pom.xml lists seventeen modules, and the
build order between them is not alphabetical; it is a dependency graph. A contributor who cannot say,
from memory, which module owns a class and what that module is allowed to depend on will edit the
wrong layer, break a public API by accident, or spend twenty minutes hunting for a file a committer
finds in five seconds.
This lab is a guided tour. You will read the real root pom.xml, walk every module, derive the
inter-module dependency arrows from the module POMs, learn to locate any class three different ways,
and read the .github, .asf.yaml, and dev-support files that govern how your future PRs are
built and checked. You will not change any code — but the muscle memory you build here makes every
later lab fast.
Why This Lab Matters for Contributors
- "Where does this live?" is the question you answer dozens of times per PR. Seconds instead of minutes compounds.
- Editing the right layer matters: a change in
tez-apiis a public API change with compatibility implications; the same logic intez-runtime-libraryis internal. Reviewers hold them to different bars. - Reading a module POM's
<dependency>block tells you what you may call from where — and why some "obvious" imports are architecturally forbidden. - The
.github/.asf.yaml/dev-supportfiles are the contract your PR is judged against. Knowing them up front saves a red CI run.
Prerequisites
-
Lab 1.1 complete — you can run
mvnagainst a Tez checkout. -
A clone of
apache/tez(your fork is fine; fork setup is Lab 2.2). -
git,grep/rg, andfindon your PATH.
Step-by-Step Tasks
Step 1: Read the Real Module List
Do not trust any hand-drawn tree — read the source of truth. The <modules> block in the root POM is
the authoritative, ordered list:
cd /path/to/tez
grep -n "<module>" pom.xml
You should see seventeen modules. The order is the coarse build order. Here is what each one is, its key packages, and when you would touch it:
| Module | Role | Key packages / classes | When you'd touch it |
|---|---|---|---|
hadoop-shim | Version-neutral interface over Hadoop APIs that differ across Hadoop releases. | org.apache.tez.hadoop.shim | Rarely — only when adapting to a Hadoop API that changed between versions. |
tez-api | The public contract. Everything app developers compile against. Highest stability bar. | org.apache.tez.dag.api (DAG, Vertex, Edge, TezConfiguration), ...dag.api.client (DAGClient), org.apache.tez.client (TezClient), org.apache.tez.runtime.api (Processor, Input, Output), org.apache.tez.common (TezUtils) | Adding/changing public API, config keys, or Javadoc. Compat-sensitive. |
tez-build-tools | Build-time resources: the checkstyle config and suppressions live here and are consumed as a plugin dependency. | checkstyle/checkstyle.xml, checkstyle/suppressions.xml | Changing a style rule (rare, needs consensus). |
tez-common | Utilities shared across modules that are not public API. Home of the ID record classes. | org.apache.tez.dag.records (TezDAGID, TezVertexID, TezTaskID, TezTaskAttemptID, TezID), org.apache.tez.common (AsyncDispatcher, Preconditions), org.apache.tez.util | Fixing shared helpers or ID parsing (this is Lab 2.3's territory). |
tez-runtime-library | The I/O library — the inputs, outputs, and shuffle/sort that run inside task containers. | ...runtime.library.input/.output, ...common.shuffle (ShuffleManager, Fetcher), ...common.sort, ...common.writers | Data movement, shuffle, sort. Levels 5–7. |
tez-runtime-internals | The runtime engine internals that host processors and drive task execution; not public. | org.apache.tez.runtime (LogicalIOProcessorRuntimeTask), org.apache.tez.runtime.common | Task execution internals. |
tez-mapreduce | MapReduce compatibility layer — run MR jobs on Tez unchanged. | org.apache.tez.mapreduce.input (MRInput), ...output (MROutput), mapper/reducer wrappers | MR-on-Tez behavior. |
tez-examples | Reference DAGs used in docs and integration tests. | OrderedWordCount, HashJoinExample, JoinDataGen, FilterLinesByWord | Learning; adding a teaching example. |
tez-tests | End-to-end integration suite against MiniTezCluster (in-process Tez + YARN + HDFS). Slow. | org.apache.tez.test | Adding end-to-end coverage. |
tez-dag | The Application Master — the largest, most complex module. DAG/Vertex/Task/TaskAttempt state machines run here. | org.apache.tez.dag.app (DAGAppMaster), ...app.dag.impl (DAGImpl, VertexImpl, TaskImpl, TaskAttemptImpl), ...app.rm, ...history | AM behavior, scheduling, recovery. Levels 3–4. |
tez-ext-service-tests | Tests for external/pluggable services (test-only module). | src/test/... | External service plugin testing. |
tez-ui | The web UI (Ember.js). Built via Maven front-end plugins. | tez-ui/src/main/webapp | UI changes; skip with -Pnoui. |
tez-plugins | Aggregator POM for optional history/timeline plugins (see below). | submodules | ATS/timeline history integration. |
tez-tools | Standalone analysis and debugging tools. | analyzers (CSVResult), swimlanes, counter-diff, tez-javadoc-tools, tez-log-split, tez-tfile-parser | Job analysis tooling (Lab 2.4 reads one). |
hadoop-shim-impls | Concrete implementations of hadoop-shim for specific Hadoop lines. | hadoop-shim-2.7, hadoop-shim-2.8 | Matching a specific Hadoop version. |
tez-dist | Assembles the .tar.gz distribution. No production code. | src/main/assembly | Packaging/release. |
docs | The Apache Tez website (mvn site -pl docs). | src/site/markdown | Documentation and the website. |
Note:
tez-pluginsis an aggregator whose active submodules depend on a profile. On the defaulthadoop28profile they aretez-protobuf-history-plugin,tez-yarn-timeline-history,tez-yarn-timeline-history-with-acls,tez-yarn-timeline-cache-plugin,tez-yarn-timeline-history-with-fs,tez-history-parser, andtez-aux-services. Verify withsed -n '/hadoop28/,/\/profile/p' tez-plugins/pom.xml.
Warning: The class you are looking for may not be in the module its name suggests. The ID classes (
TezTaskAttemptIDand friends) are in packageorg.apache.tez.dag.recordsbut live intez-common, nottez-dag. AndTezUtilsis in packageorg.apache.tez.commonbut lives intez-api, nottez-common. Package ≠ module. Always verify withfind.
Step 2: Derive the Dependency Direction
Modules declare their inter-dependencies explicitly, and Maven uses those to order the build. Extract the tez-to-tez edges yourself instead of trusting a diagram:
for m in tez-api tez-common tez-runtime-internals tez-runtime-library \
tez-mapreduce tez-dag tez-examples tez-tests; do
echo "== $m depends on =="
grep -A2 "<groupId>org.apache.tez" "$m/pom.xml" \
| grep "artifactId" | grep -v "$m<" | sort -u
done
The arrows this reveals (read "→" as "depends on"):
hadoop-shim ── (base; no tez deps)
tez-api ── (near-base; only build-time tez-javadoc-tools)
tez-common → tez-api, hadoop-shim
tez-runtime-internals → tez-api, tez-common, hadoop-shim
tez-runtime-library → tez-api, tez-common, tez-runtime-internals
tez-mapreduce → tez-api, tez-common, tez-runtime-library, tez-runtime-internals, hadoop-shim
tez-dag → tez-api, tez-common, tez-runtime-library, tez-runtime-internals, hadoop-shim
tez-examples → tez-api, tez-common, tez-runtime-library, tez-mapreduce
tez-tests → tez-api, tez-common, tez-runtime-library, tez-mapreduce, tez-examples, tez-dag
Two rules fall out of this, and they explain a lot of review feedback:
tez-apiis at the bottom. Everything depends on it; it depends on almost nothing. That is why it is the public API and why changes there are scrutinised — you cannot break it without breaking everyone above.- The arrows point one way.
tez-commonmay usetez-api, buttez-apimay not usetez-common. If a reviewer says "this helper belongs intez-common, nottez-api," it is because putting it intez-apiwould either be an unwanted public API or create a cycle.
Tip: To see the fully resolved graph including external jars:
mvn dependency:tree -pl tez-dag | grep "org.apache.tez".
Step 3: Walk tez-dag — the Application Master
This is where you will spend most of Levels 3–4. Get its shape:
ls tez-dag/src/main/java/org/apache/tez/dag/app
find tez-dag/src/main/java -name "*Impl.java" -path "*dag/impl*"
The state machines are the heart of it. Confirm DAGImpl is built on Hadoop's StateMachineFactory:
grep -n "StateMachineFactory" tez-dag/src/main/java/org/apache/tez/dag/app/dag/impl/DAGImpl.java | head
grep -c "addTransition" tez-dag/src/main/java/org/apache/tez/dag/app/dag/impl/VertexImpl.java
The addTransition count is the number of state-machine edges in VertexImpl — a rough measure of
why that class is the one people fear. You will read these in Level 3.
Step 4: Walk tez-runtime-library — I/O and Shuffle
The data-plane module. Shuffle fetch and external sort live here:
find tez-runtime-library/src/main/java -name "*Input*.java" -path "*/library/*" | grep -v test
find tez-runtime-library/src/main/java -name "Fetcher.java"
find tez-runtime-library/src/main/java -name "ShuffleManager.java"
Note the shuffle infrastructure is under .../common/shuffle. When a JIRA is filed against "shuffle
retry" or "fetch failure," this is the module and package.
Step 5: Locate Any Class — Three Ways
The core skill, fastest first:
# 1. By file name — when you know the class:
find . -name "VertexImpl.java" -not -path "*/target/*"
# 2. By declaration — when you want the exact definition site:
grep -rn "class DAGAppMaster" --include=*.java . | grep -v target
# 3. By behavior/constant — when you only know what it does:
grep -rln "TEZ_LOCAL_MODE" --include=*.java . | grep -v target
Practice until reflex. Find each and note the module it resolves in:
for c in TezClient TezConfiguration DAGAppMaster VertexImpl \
ShuffleManager Fetcher MRInput TezTaskAttemptID TezUtils; do
echo "== $c =="
find . -name "$c.java" -path "*/main/*" -not -path "*/target/*"
done
Confirm the two traps from Step 1: TezTaskAttemptID resolves under tez-common
(.../dag/records/), and TezUtils under tez-api (.../common/) — package names lie about module
location, find does not.
Step 6: Read .asf.yaml — the Contribution Model as Config
.asf.yaml is not documentation about the process; it is the process, enforced by ASF infra.
Read it:
cat .asf.yaml
What to extract, and why it matters for your PRs:
| Section | Line to find | Consequence for you |
|---|---|---|
features | issues: false | You cannot file bugs on GitHub; use Apache JIRA (TEZ-XXXX). |
enabled_merge_buttons | squash: true (others false) | Your PR is squash-merged; the squash subject is what lands in history. |
notifications | pullrequests: issues@…, jira_options: link | PRs are mailed to issues@ and auto-linked to the JIRA in the title. |
rulesets | restrict_force_push, restrict_deletion on ~DEFAULT_BRANCH, release/*, rel/* | master is protected. You branch on your fork and open a PR. |
Step 7: Read .github/ and dev-support/
ls -R .github
cat .github/workflows/build.yml
ls dev-support dev-support/spotless
head -60 dev-support/tez-personality.sh
Note three things:
.githubcontains onlyworkflows/build.yml. There is nopull_request_template.mdand no issue templates — because issues are on JIRA. Do not expect a checklist to be auto-filled into your PR; write the body yourself.build.ymlrunsmvn clean install -DskipTestsacross a Java[21, 25]×[ubuntu-latest, macos-latest]matrix on every push and PR tomaster. That is a compile gate, not a test gate.dev-support/tez-personality.shis the Yetus personality — it decides which precommit checks fire for which files. Readpersonality_file_filter: a changed.java,.proto, orpom.xmltriggersjavac,spotbugs,checkstyle, andjavadoc; a.shorJenkinsfiletriggersshellcheck; a.mdor.txttriggerscodespell.dev-support/spotless/license.javais the exact ASF header spotless will enforce on any new file you add.
The tests themselves run under Jenkins via the Jenkinsfile (Yetus rel/0.15.1 in Docker). Skim it
to see that it diffs origin/master...HEAD, runs test-patch.sh, and posts a GitHub comment with
emoji votes — that comment is the precommit result you will respond to in Lab
2.2.
Deliverables
-
A module table you filled in yourself from
grep -n "<module>" pom.xml— all seventeen, each with role and key package. - The dependency-arrow list from Step 2, produced by your own loop over the module POMs.
-
The resolved file path (from memory, then verified) for:
DAGAppMaster,VertexImpl,ShuffleManager,Fetcher,TezTaskAttemptID,TezUtils. -
Two sentences explaining why
tez-apisits at the bottom of the dependency graph and what that implies for changes to it. -
A note of the four
.asf.yamlsettings from Step 6 and what each one forces you to do. -
Confirmation, in your own words, that
.githubhas no PR template and what that means for how you write a PR body.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
find returns both a main and test copy of a class | Same class name in two source sets | Constrain: add -path "*/main/*" or -path "*/test/*". |
grep -rn "class X" returns nothing | Class is nested, generic, or named differently | Try grep -rn "X" --include=*.java -l to find mentioning files, then open one. |
find is slow and noisy | It is walking target/ build output | Always append -not -path "*/target/*". |
A module you expected in <modules> is missing | It is behind a profile (tez-plugins) | Read the profile blocks: sed -n '/profiles/,/\/profiles/p' tez-plugins/pom.xml. |
mvn dependency:tree fails on a module | Artifacts not installed locally | Run mvn install -DskipTests -Pnoui from the root once, then retry. |
Stretch Goals
- Draw the real dependency DAG. From the Step 2 output, render a graph (by hand or with
mvn dependency:tree) and verify it is acyclic. Find the longest path fromhadoop-shimtotez-tests. - Find every
.proto. Runfind . -name "*.proto" -not -path "*/target/*" | sort. For each, name its module and one message it defines. Protobuf changes are wire-format changes — note which module owns the DAG plan protos. - Map the checkstyle gate to a rule. Open
tez-build-tools/src/main/resources/checkstyle/checkstyle.xml, find theLineLengthmodule, and record themax. Then find one file that would violate it and confirm withmvn checkstyle:check -pl <its-module>. - Trace a config key to its owner. Pick
TEZ_AM_RESOURCE_MEMORY_MB(or any key), find its declaration inTezConfiguration(tez-api), and then find a reader of it intez-dag. You have just crossed the public-API → internal boundary the dependency graph predicts.
Validation / Self-check
You are done when you can answer these without notes:
- How many modules does the root
pom.xmllist, and which single module is the public API surface? - In which direction may dependencies point between
tez-apiandtez-common, and how do you verify it from a POM? - Which module physically contains
TezTaskAttemptID, and why is its module different from what its package name suggests? - Give the three ways to locate a class; which do you use when you know only the behavior?
- What does
.asf.yaml'senabled_merge_buttonstell you about how your commits will land, and what doesfeatures: issues: falsetell you about where bugs are filed? - Which file decides which Yetus precommit checks run for a changed
.javafile, and what are those checks?