Thirty-six seconds, eighteen state changes
Riya submits the item-revenue job again, but this time Kabir has a script that asks the ResourceManager every 0.2 seconds how the job is doing, and a second window that follows the ResourceManager's own log. The job takes 36 seconds. In that time the application changes state 8 times, its first attempt changes state 10 times, and the container that runs the ApplicationMaster goes through 5 states on worker2. Nobody typed anything after pressing Enter.
Sir, the job had already been accepted 0.1 seconds after I pressed Enter. Then why did it sit for five seconds before running?
Because “accepted” only means the ResourceManager found room for one container: the one that will run your job's manager, the ApplicationMaster. The NodeManager on worker2 then had to fetch your jar, start a JVM and let the ApplicationMaster register itself. That took the five seconds. Only a registered ApplicationMaster can ask for more containers.
And the ResourceManager writes all of it into its log. Every line says “State change from X to Y on event = Z”. You can follow your job like a parcel.
Today we follow one application from the first RPC to the last log file: the client, the ResourceManager, the ApplicationMaster, the containers, and what happens when we kill it or ask for something impossible.
The YARN application lifecycle
A YARN application is a unit of work submitted to the ResourceManager, such as a MapReduce job or a Spark program. Its lifecycle begins when a client obtains a new application ID and submits an ApplicationSubmissionContext containing the queue, the resources of the ApplicationMaster and a ContainerLaunchContext (command, environment and local resources such as the job jar staged in HDFS). The ResourceManager saves the application to its state store (NEW → NEW_SAVING → SUBMITTED), the scheduler admits it to a queue (ACCEPTED) and allocates a container for the ApplicationMaster; a NodeManager localizes the resources and launches it. After the ApplicationMaster registers, the application is RUNNING: the ApplicationMaster requests further containers through periodic allocate heartbeats, launches its tasks on NodeManagers, monitors them, and finally unregisters with a final status. Each run of an ApplicationMaster is an application attempt; if one fails, the ResourceManager can start a new attempt, up to yarn.resourcemanager.am.max-attempts (2 by default). The application ends in FINISHED, FAILED or KILLED, and the ResourceManager records its aggregate resource usage in MB-seconds and vcore-seconds.
Your job first gets a manager (the ApplicationMaster) in one container. The manager introduces itself to YARN, asks for workers (more containers), runs the work, reports the result and leaves. YARN writes down every step and how much memory was used for how long.
Words you'll meet in this lesson
application_1792643020992_0136.appattempt_…_0136_000001.…_01_000001.From our lab (Hadoop 3.5.0, ItemRevenue on 140 MB)
| Polling the RM | +0.1 s ACCEPTED (1 container, 512 MB) · +5.0 s RUNNING · +6.0 s 2 containers (1,024 MB) · +35.6 s FINISHED, final SUCCEEDED |
|---|---|
| RM log | NEW → NEW_SAVING → SUBMITTED → ACCEPTED all at 06:25:43; attempt LAUNCHED → RUNNING on REGISTERED at 06:25:48; FINISHED at 06:26:25 |
| AM container (worker2) | NEW → LOCALIZING → SCHEDULED → RUNNING (06:25:43) … EXITED_WITH_SUCCESS → DONE (06:26:25) |
| Usage | Aggregate Resource Allocation: 33,224 MB-seconds, 64 vcore-seconds |
| Kill | KILLED at +16.9 s: “Application … was killed by user hadoop at 172.22.0.2” |
| Impossible request | AM of 3,072 MB on 2,048 MB nodes → InvalidResourceRequestException before anything is created |
Architecture at a glance
Nine steps take one application from your terminal to its result. The numbers match the steps in Part 4.
The lifecycle at three levels
Beginner A wedding caterer
- You (the client) call the catering company's office (the ResourceManager) and book an event. The office writes it in the register first, so it isn't lost if the phone line drops.
- The office sends one event manager (the ApplicationMaster) to the venue. Nothing is cooked until the manager arrives and reports in.
- The manager asks the office for cooks and waiters (containers) as needed, tells them what to do, and lets them go when their part is done.
- At the end the manager reports “done” and the office sends you the bill: people × hours, just like MB-seconds.
Intermediate Who talks to whom
- Client → RM (ApplicationClientProtocol):
getNewApplication, thensubmitApplication. Before submitting, the MapReduce client uploads the jar,job.xmland split list to.stagingin HDFS (Lesson 8.6). - RM → NM: launches the AM container. The NM localizes the staged files (LOCALIZING), then starts the JVM (RUNNING).
- AM → RM (ApplicationMasterProtocol):
registerApplicationMaster, thenallocateabout every second with container requests that name preferred hosts and racks (data locality), and finallyfinishApplicationMaster. - AM → NM (ContainerManagementProtocol):
startContainersfor each task. Map and reduce tasks then report progress to the AM over the “umbilical” protocol, not to the RM.
Advanced Attempts, recovery and the state store
- An application can have several attempts. If the AM container dies, the RM starts attempt
_000002elsewhere, up toyarn.resourcemanager.am.max-attempts(default 2; MapReduce also readsmapreduce.am.max-attempts). The MapReduce AM can recover finished tasks from the job history of the previous attempt. - The _SAVING states (NEW_SAVING, ALLOCATED_SAVING, FINAL_SAVING) are writes to the RM state store, so a restarted or failed-over RM (RM HA with ZooKeeper) still knows every application.
- ACCEPTED can last a long time on a busy cluster: the queue or its AM limit (
maximum-am-resource-percent) may not allow another ApplicationMaster yet (Lesson 11.3). - The RM's state (FINISHED) and the app's final status (SUCCEEDED or FAILED) are different things: a MapReduce job whose tasks failed still ends as FINISHED with final status FAILED.
Exam tip: For “Explain the lifecycle of a YARN application”, draw client, RM, NM with the AM and NMs with containers, and number the steps: new ID, submit, AM container allocated, AM launched, AM registers, allocate requests, containers launched, AM unregisters. Name the states NEW, SUBMITTED, ACCEPTED, RUNNING, FINISHED/FAILED/KILLED.
The three state machines
YARN tracks the same job at three levels. Each has its own states, and each appears in a different log.
| Object | Kept by | States in our run (in order) | Where you see it |
|---|---|---|---|
| Application (RMApp) | ResourceManager | NEW, NEW_SAVING, SUBMITTED, ACCEPTED, RUNNING, FINAL_SAVING, FINISHING, FINISHED | RM log, REST /ws/v1/cluster/apps, yarn application -status |
| Attempt (RMAppAttempt) | ResourceManager | NEW, SUBMITTED, SCHEDULED, ALLOCATED_SAVING, ALLOCATED, LAUNCHED, RUNNING, FINAL_SAVING, FINISHING, FINISHED | RM log, yarn applicationattempt -list |
| Container | NodeManager | NEW, LOCALIZING, SCHEDULED, RUNNING, EXITED_WITH_SUCCESS, DONE | NM log, yarn container -list |
How an application can end
FINISHED · SUCCEEDED
The AM unregistered and said all went well. Our run: +35.6 s.
normalFINISHED · FAILED
The AM unregistered but its job failed, e.g. a task failed 4 times (Lesson 11.5).
job failureFAILED
The AM itself died on every allowed attempt, so YARN gave up.
AM failureKILLED
A user or admin ran yarn application -kill, or a timeout or preemption policy removed it. Our run: +16.9 s.
Animated example: results day at a university board
The same states, on a different job: a board computes 12 lakh students' results overnight. Times are from our lab run (06:25:43 → 06:26:25); only the story around them is invented.
- Submit: the application goes NEW → NEW_SAVING → SUBMITTED → ACCEPTED in the same second; the state store write comes first.
- AM container: the first attempt is scheduled and a container is allocated and launched on a NodeManager.
- Register: five seconds later the ApplicationMaster JVM is up and registers; attempt and application both become RUNNING.
- Work: for 30 seconds the RM log is quiet about this app. The AM asks for containers and watches its tasks itself.
- Unregister: the AM reports success; the RM saves the final status (FINAL_SAVING) and waits for the AM to exit (FINISHING).
- Done: the AM container exits with success and the application is FINISHED. Board results are ready long before morning.
yarn application -list -appStates ALL shows every application the RM still remembers, with its state and final status. yarn applicationattempt -list <appId> and yarn container -list <attemptId> zoom in one level at a time.
Following one application on the lab cluster
We use the Module 8 lab (HDFS + YARN, three workers) and the 140 MB orders_x30.csv. app_states.py polls the RM REST API every 0.2 s; docker logs shows the RM and NM logs. Both are in “Run it on your PC”.
Steps 1–3: the client asks for an ID, stages files, submits
By the time our first poll arrived, the application had already passed NEW, NEW_SAVING and SUBMITTED. ACCEPTED with 1 container and 512 MB means the scheduler has already reserved the ApplicationMaster's container.hadoop jar mr.jar ItemRevenue /user/hadoop/data/orders_x30.csv /out/lc1 & python3 app_states.py application_1792643020992_0136submitted application_1792643020992_0136 + 0.1 s state=ACCEPTED final=UNDEFINED containers=1 allocatedMB=512Step 4: the ResourceManager records and schedules
Every “_SAVING” state is a write to the RM's state store, so a restarted RM can recover. The scheduler found room for the AM in the same second.docker logs lab8-resourcemanager-1 | grep application_1792643020992_013606:25:43 RMAppImpl - application_…_0136 State change from NEW to NEW_SAVING on event = START 06:25:43 RMAppImpl - application_…_0136 State change from NEW_SAVING to SUBMITTED on event = APP_NEW_SAVED 06:25:43 RMAppImpl - application_…_0136 State change from SUBMITTED to ACCEPTED on event = APP_ACCEPTED 06:25:43 RMAppAttemptImpl - appattempt_…_0136_000001 State change from SCHEDULED to ALLOCATED_SAVING on event = CONTAINER_ALLOCATED 06:25:43 RMAppAttemptImpl - appattempt_…_0136_000001 State change from ALLOCATED to LAUNCHED on event = LAUNCHEDStep 5: the NodeManager localizes and launches the AM
LOCALIZING copies the job jar anddocker logs lab8-worker2-1 | grep container_1792643020992_0136_01_00000106:25:43 ContainerImpl - Container container_…_0136_01_000001 transitioned from NEW to LOCALIZING 06:25:43 ContainerImpl - Container container_…_0136_01_000001 transitioned from LOCALIZING to SCHEDULED 06:25:43 ContainerImpl - Container container_…_0136_01_000001 transitioned from SCHEDULED to RUNNINGjob.xmlfrom the HDFS staging folder to the worker's disk. Container_000001of attempt_01is always the ApplicationMaster.Step 6: the ApplicationMaster registers
The five seconds between LAUNCHED and REGISTERED are the MRAppMaster JVM starting and reading the job configuration. Only a registered AM may send06:25:48 appattempt_…_000001 State change from LAUNCHED to RUNNING on event = REGISTERED 06:25:48 application_…_0136 State change from ACCEPTED to RUNNING on event = ATTEMPT_REGISTERED + 5.0 s state=RUNNING final=UNDEFINED containers=1 allocatedMB=512allocaterequests.Steps 7–8: the AM gets containers and runs tasks
One second after registering, the AM held a second container: the single map task (one 140 MB split, Lesson 8.2). When the map finished at +27 s its container was released, and a reduce container arrived at +28 s. The RM logged no application state changes in this phase: tasks are the AM's business.+ 6.0 s state=RUNNING final=UNDEFINED containers=2 allocatedMB=1024 + 27.1 s state=RUNNING final=UNDEFINED containers=1 allocatedMB=512 + 28.1 s state=RUNNING final=UNDEFINED containers=2 allocatedMB=1024Step 9: unregister, clean up, send the bill
The AM unregistered at 06:26:19 (FINAL_SAVING, FINISHING); its container exited at 06:26:25 (EXITED_WITH_SUCCESS, DONE) and the app became FINISHED. 33,224 MB-seconds over about 36 s means roughly 920 MB held on average: the AM all the time, plus one task container most of the time.yarn application -status application_1792643020992_0136+ 35.6 s state=FINISHED final=SUCCEEDED containers=1 allocatedMB=512 Application-Type : MAPREDUCE Queue : root.default State : FINISHED Final-State : SUCCEEDED AM Host : worker2 Aggregate Resource Allocation : 33224 MB-seconds, 64 vcore-seconds
Two ways it goes differently
# kill the second run 15 s after submitting it
yarn application -kill application_1792643020992_0137
# ask for a 3 GB ApplicationMaster on 2 GB NodeManagers
hadoop jar mr.jar ItemRevenue -D yarn.app.mapreduce.am.resource.mb=3072 …A kill releases every container at once and records who killed it and from where. An impossible request is rejected at submission, so no application ever reaches ACCEPTED.
An application stuck in ACCEPTED is not “almost running”. It means no container has been found for its ApplicationMaster yet: the queue is full, the queue's AM share (maximum-am-resource-percent) is used up, or no node is big enough. Check the queue in the RM UI before resubmitting, or you will just add another waiting application.
From Enter to FINISHED
Six steps of our 36-second run: what the polling script printed, and what the daemons were doing.
- The client submits the job and prints the application ID.
- 0.1 s later the application is ACCEPTED and holds one 512 MB container.
- At 5 s it becomes RUNNING, still with only that one container.
- At 6 s it holds two containers: 1,024 MB.
- At 27 s one container is released, at 28 s a new one appears.
- At 35.6 s it is FINISHED and SUCCEEDED; YARN reports the usage.
- The client gets an application ID, uploads the jar and configuration to HDFS and submits.
- The RM saves the application, accepts it into the queue and places the AM container on worker2, which starts localizing.
- The ApplicationMaster JVM starts and registers with the RM.
- On its next allocate heartbeat the AM receives a container on worker1 and starts the map task there.
- The map container is released; a reduce container is started on another worker.
- The AM unregisters with SUCCEEDED and exits; the NodeManagers aggregate the logs into HDFS.
The lifecycle at work
1. Chargeback in a shared bank cluster
A bank's platform team reads every finished application's MB-seconds and vcore-seconds from the RM and bills each department monthly for its share of the cluster.
2. Alerting on stuck jobs at a telecom operator
The nightly billing pipeline alerts if any application stays ACCEPTED for more than 10 minutes, a sign that its queue is full or an AM can't be placed.
3. Surviving a lost node during a long ETL
An e-commerce company's 3-hour ETL lost the node running its ApplicationMaster. YARN started attempt 2, and the MapReduce AM recovered the completed tasks instead of starting from zero.
4. Spark and other engines on the same YARN
A Spark job in cluster mode follows the same lifecycle: its driver runs inside the ApplicationMaster container and its executors are ordinary YARN containers. Hive on Tez and Flink do the same.
5. Queue-time dashboards for a research cluster
A university HPC/Hadoop team plots “time from submit to RUNNING” per queue each day. Rising queue time tells them when students' assignment deadlines need a bigger share.
Simplified; organisations differ in tools and policies.
Simulate the application and attempt state machines
A small model of YARN's RMApp and RMAppAttempt state machines. Each event moves an object to its next state, exactly as the lines in the ResourceManager log say. Run it and compare with our real log.
APP = {("NEW", "START"): "NEW_SAVING", ("NEW_SAVING", "APP_NEW_SAVED"): "SUBMITTED",
("SUBMITTED", "APP_ACCEPTED"): "ACCEPTED", ("ACCEPTED", "ATTEMPT_REGISTERED"): "RUNNING",
("RUNNING", "ATTEMPT_UNREGISTERED"): "FINAL_SAVING", ("FINAL_SAVING", "APP_UPDATE_SAVED"): "FINISHING",
("FINISHING", "ATTEMPT_FINISHED"): "FINISHED"}
ATTEMPT = {("NEW", "START"): "SUBMITTED", ("SUBMITTED", "ATTEMPT_ADDED"): "SCHEDULED",
("SCHEDULED", "CONTAINER_ALLOCATED"): "ALLOCATED_SAVING", ("ALLOCATED_SAVING", "ATTEMPT_NEW_SAVED"): "ALLOCATED",
("ALLOCATED", "LAUNCHED"): "LAUNCHED", ("LAUNCHED", "REGISTERED"): "RUNNING",
("RUNNING", "UNREGISTERED"): "FINAL_SAVING", ("FINAL_SAVING", "ATTEMPT_UPDATE_SAVED"): "FINISHING",
("FINISHING", "CONTAINER_FINISHED"): "FINISHED"}
state = {"app": "NEW", "attempt": "NEW"}
def fire(obj, event, time):
table = APP if obj == "app" else ATTEMPT
new = table[(state[obj], event)] # an event that isn't allowed here raises KeyError
print(f"{time} {obj:7} {state[obj]:>16} -> {new:16} on {event}")
state[obj] = new
for t, obj, ev in [("06:25:43", "app", "START"), ("06:25:43", "app", "APP_NEW_SAVED"), ("06:25:43", "app", "APP_ACCEPTED"),
("06:25:43", "attempt", "START"), ("06:25:43", "attempt", "ATTEMPT_ADDED"),
("06:25:43", "attempt", "CONTAINER_ALLOCATED"), ("06:25:43", "attempt", "ATTEMPT_NEW_SAVED"),
("06:25:43", "attempt", "LAUNCHED"), ("06:25:48", "attempt", "REGISTERED"),
("06:25:48", "app", "ATTEMPT_REGISTERED"), ("06:26:19", "attempt", "UNREGISTERED"),
("06:26:19", "attempt", "ATTEMPT_UPDATE_SAVED"), ("06:26:19", "app", "ATTEMPT_UNREGISTERED"),
("06:26:19", "app", "APP_UPDATE_SAVED"), ("06:26:25", "attempt", "CONTAINER_FINISHED"),
("06:26:25", "app", "ATTEMPT_FINISHED")]:
fire(obj, ev, t)new = table[(state[obj], event)] # an event that isn't allowed here raises KeyErrorA state machine only accepts certain events in each state. YARN logs “Can't handle this event at current state” in that case.("06:25:43", "attempt", "LAUNCHED"), ("06:25:48", "attempt", "REGISTERED"),The 5-second gap: the MRAppMaster JVM starting up on worker2.("06:25:48", "app", "ATTEMPT_REGISTERED"), ("06:26:19", "attempt", "UNREGISTERED"),Between these two lines the RM logs nothing about the app: the AM runs its own tasks.
Run it on your PC: watch, kill and break an application
Use the Module 8 lab (Lesson 8.1). Copy app_states.py and lifecycle_lab.sh into the namenode container (docker cp … lab8-namenode-1:/tmp/mr/) and run the script there. It runs ItemRevenue on orders_x30.csv while polling the ResourceManager, kills a second run, and asks for an impossible container. The RM and NM logs are read from your PC with docker logs.
Poll the ResourceManager
# app_states.py: submit-and-watch. Polls the ResourceManager REST API every 0.2 s and prints every
# change of application state, plus how many containers the app holds. Usage: python3 app_states.py <app_id>
import json, sys, time, urllib.request
RM = "http://resourcemanager:8088/ws/v1/cluster/apps/"
app, t0, last = sys.argv[1], time.time(), None
while True:
try:
a = json.load(urllib.request.urlopen(RM + app))["app"]
except Exception:
time.sleep(0.2); continue
now = (a["state"], a.get("finalStatus"), a.get("runningContainers", 0), a.get("allocatedMB", 0))
if now != last:
print(f"+{time.time() - t0:5.1f} s state={now[0]:10} final={now[1]:9} containers={max(now[2], 0)} allocatedMB={max(now[3], 0)}")
last = now
if a["state"] in ("FINISHED", "FAILED", "KILLED"):
print("diagnostics:", (a.get("diagnostics") or "-")[:150])
break
time.sleep(0.2)
# Used by lifecycle_lab.sh: python3 /tmp/mr/app_states.py application_<id>Real output of the first part of lifecycle_lab.sh. The NEW, NEW_SAVING and SUBMITTED states pass in under 0.1 s, so the poller first sees ACCEPTED; the RM log below has them all.
Watch, kill, break
# lifecycle_lab.sh: watch a YARN application move through its states, then kill one
cd /tmp/mr
run_and_watch() { # output dir, extra args
hdfs dfs -rm -r -f -skipTrash $1 > /dev/null 2>&1
hadoop jar mr.jar ItemRevenue $2 /user/hadoop/data/orders_x30.csv $1 > /tmp/lc.log 2>&1 &
until app=$(grep -o "application_[0-9_]*" /tmp/lc.log | head -1) && [ -n "$app" ]; do sleep 0.1; done
echo "submitted $app"
python3 /tmp/mr/app_states.py $app
}
echo "== 1. a normal job"
run_and_watch /out/lc1 ""
wait
echo "-- the same app as YARN reports it afterwards"
yarn application -status $(grep -o "application_[0-9_]*" /tmp/lc.log | head -1) 2>/dev/null | grep -E "Application-Type|Queue|State|Final-State|Start-Time|Finish-Time|Progress|AM Host|Aggregate Resource" | sed 's/^\s*//'
echo "== 2. kill a job while it runs"
hdfs dfs -rm -r -f -skipTrash /out/lc2 > /dev/null 2>&1
hadoop jar mr.jar ItemRevenue /user/hadoop/data/orders_x30.csv /out/lc2 > /tmp/lc2.log 2>&1 &
until app=$(grep -o "application_[0-9_]*" /tmp/lc2.log | head -1) && [ -n "$app" ]; do sleep 0.1; done
python3 /tmp/mr/app_states.py $app &
sleep 15; yarn application -kill $app 2>&1 | grep -v INFO
wait
grep -E "Job .* failed|killed|KILLED" /tmp/lc2.log | sed -E 's/^[0-9-]+ [0-9:]+ INFO //' | head -2
echo "== 3. ask for an AM bigger than any node (3 GB on 2 GB NodeManagers)"
hadoop jar mr.jar ItemRevenue -D yarn.app.mapreduce.am.resource.mb=3072 /user/hadoop/data/canteen_orders.csv /out/lc3 2>&1 | grep -E "Exception|Invalid" | head -2 | cut -c1-250
# Run: docker exec lab8-namenode-1 bash /tmp/mr/lifecycle_lab.shReal output. The InvalidResourceRequestException line is cut at 250 characters by the script; the full message also names the requested and the maximum resources.
Every state change in the daemon logs
# In the mr-lab folder, after lifecycle_lab.sh (use your own application id):
app=application_1792643020992_0136; id=${app#application_}
docker logs lab8-resourcemanager-1 2>&1 | grep "$app\|appattempt_$id" | grep "State change"
docker logs lab8-worker2-1 2>&1 | grep "container_${id}_01_000001 transitioned"
# worker2 is the AM host shown by yarn application -statusReal log lines, with the date and log level trimmed for width. Your AM may run on another worker; check “AM Host” first.
A container's life on the NodeManager
The NodeManager keeps its own small state machine for each container. Follow the ApplicationMaster's container _000001 through the five transitions we saw in the worker2 log.
Worked examples: real problems, solved and traced in memory
Four lifecycle problems from different organisations: billing, waiting, retries and kills.
Bill a department for its jobs
Problem statement
A bank charges departments ₹5 per GB-hour of YARN memory (an illustrative internal rate). The risk team ran 40,000 jobs last month, each about like ours: 33,224 MB-seconds. What is the bill?
40,000 × 33,224 MB-s · ₹5 per GB-hour
1,328,960,000 MB-s = 360.50 GB-hours -> Rs 1,803
Approach
- Total MB-seconds = jobs × MB-s per job.
- Convert: GB-hours = MB-s / 1024 / 3600.
- Multiply by the rate.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Where did our 33,224 MB-seconds go?
Problem statement
Our app started at 1792650343192 ms and finished at 1792650379385 ms. The AM held 512 MB all the time. How many MB-seconds were the task containers, and how many seconds of 512 MB containers is that?
start/finish in ms · total 33,224 MB-s · AM 512 MB
elapsed 36.2 s, AM 18,531 MB-s, tasks 14,693 MB-s = 28.7 s of a 512 MB container
Approach
- Elapsed = finish − start.
- AM share = 512 × elapsed.
- Task share = total − AM share; divide by 512.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Will the sale-day ETL survive an AM crash?
Problem statement
An e-commerce ETL runs 3 hours. The node running its ApplicationMaster has a 5% chance of failing during that time; a new attempt lands on another node with the same 5% chance. With max-attempts = 1 vs 2, what is the chance the application FAILS because of AM crashes?
p(AM node fails) = 0.05 per attempt
max-attempts=1: fails 5.00% of the time max-attempts=2: fails 0.25% of the time
Approach
- With 1 attempt, failure probability = p.
- With 2 attempts, both must fail: p × p.
- Compare.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Kill or wait?
Problem statement
At 02:00 a telecom's billing job is stuck: RUNNING for 3 h with 4 of 400 maps left, each retrying. The next job in the queue must finish by 06:00 and needs 3.5 h on the full queue. The stuck job holds 30% of the queue. Should on-call kill it now, or wait until 03:00?
deadline 06:00 · next job 3.5 h at 100%, longer with less
kill now, start 02:00 at 100% done at 05:30 OK start now beside it at 70% done at 07:00 LATE wait, start 03:00 at 100% done at 06:30 LATE
Approach
- Runtime of the next job scales with the share it gets.
- Compute finish time if started at 02:00 with 70% vs at 03:00 with 100%.
- Compare with the deadline.
Solution: run it live
What happens in memory, step by step
Recorded from a real Python run: press Next to execute one line at a time. Watch variables change in each frame, and lists and dictionaries (numbered #1, #2 …) fill up as objects.
Check yourself
Quick revision
Lesson 11.2 in one screen
- Client:
getNewApplication→ stage jar/conf in HDFS →submitApplication(with the AM's ContainerLaunchContext). - App states: NEW → NEW_SAVING → SUBMITTED → ACCEPTED → RUNNING → FINAL_SAVING → FINISHING → FINISHED (or FAILED, KILLED).
- Attempt states: … SCHEDULED → ALLOCATED → LAUNCHED → RUNNING (on REGISTERED) …; container: NEW → LOCALIZING → SCHEDULED → RUNNING → EXITED_WITH_SUCCESS → DONE.
- Lab: ACCEPTED +0.1 s, RUNNING +5.0 s (AM JVM start), FINISHED +35.6 s; 33,224 MB-seconds, 64 vcore-seconds.
- The AM registers, sends allocate heartbeats with locality preferences, and launches tasks with startContainers; tasks report to the AM.
- An application can have several attempts (
yarn.resourcemanager.am.max-attempts, default 2). - Kill:
yarn application -killfrees all containers at once; too-big requests fail with InvalidResourceRequestException.
Interview and viva questions
What are the steps when a YARN application is submitted?
The client asks the ResourceManager for a new application ID, uploads the application's files (for MapReduce the jar, job.xml and splits) to a staging directory in HDFS, and submits an ApplicationSubmissionContext. The ResourceManager saves it, the scheduler admits it to a queue and allocates a container for the ApplicationMaster, and a NodeManager localizes the files and launches it. The ApplicationMaster registers, requests further containers through allocate calls, launches its tasks on NodeManagers, and unregisters with a final status when the work is done.
What is the difference between an application and an application attempt in YARN?
An application is the whole submission. An attempt is one run of its ApplicationMaster. If the ApplicationMaster fails, the ResourceManager can start a new attempt with a new AM container, up to yarn.resourcemanager.am.max-attempts (2 by default). Attempt IDs look like appattempt_<cluster timestamp>_<app number>_000001.
Why is my YARN application stuck in ACCEPTED?
ACCEPTED means the application is queued but its ApplicationMaster container has not been started. Common causes: the queue is at capacity, the queue's limit for ApplicationMasters (maximum-am-resource-percent) is reached, no NodeManager has enough free memory, or NodeManagers are unhealthy or lost. Check the queue and node pages in the ResourceManager UI and the application's diagnostics.
What is the difference between state and final status of a YARN application?
State is the ResourceManager's lifecycle state (for example RUNNING or FINISHED). Final status is the outcome reported by the application: SUCCEEDED, FAILED, KILLED or UNDEFINED while running. An application can be in state FINISHED with final status FAILED, for example a MapReduce job whose tasks failed.
What are MB-seconds in YARN?
The aggregate resource allocation of an application: for every container, its memory in MB multiplied by the seconds it was allocated, summed. vcore-seconds are the same for virtual cores. They appear in yarn application -status and the REST API and are used for chargeback and capacity planning.
Practice problems
Lifecycle exercises built on the real states and numbers from our lab.
Count the state changes
The RM log showed 7 application transitions and 10 attempt transitions; the NM log 5 for the AM container. How many in total?
7 + 10 + 5
22 state changes for one 36-second job
Show solution and explanation
print(7 + 10 + 5, "state changes for one 36-second job")How it works: 22 logged transitions, all without anyone typing a command after submit.
Time to register
The attempt was LAUNCHED at 06:25:43 and RUNNING at 06:25:48. How long did the AM take to start?
two timestamps
5 s
Show solution and explanation
from datetime import datetime
f = "%H:%M:%S"
print((datetime.strptime("06:25:48", f) - datetime.strptime("06:25:43", f)).seconds, "s")How it works: 5 s: mostly JVM start-up and reading the job configuration.
Average memory held
33,224 MB-seconds over 36.2 seconds. What was the average memory held?
33,224 MB-s · 36.2 s
918 MB on average
Show solution and explanation
print(round(33224 / 36.2), "MB on average")How it works: About 918 MB: the 512 MB AM plus one 512 MB task container most of the time.
Parse a state line
Extract object, from-state, to-state and event from a real RM log line.
one log line
('application_1792643020992_0136', 'ACCEPTED', 'RUNNING', 'ATTEMPT_REGISTERED')Show solution and explanation
import re
line = "06:25:48 RMAppImpl:921 - application_1792643020992_0136 State change from ACCEPTED to RUNNING on event = ATTEMPT_REGISTERED"
m = re.search(r"(\S+) State change from (\S+) to (\S+) on event = (\S+)", line)
print(m.groups())How it works: The four parts you need to build a timeline of any application from the RM log.
Queue wait report
Given (startedTime, launchTime) in ms for three apps, print each app's wait for its AM in seconds.
3 apps from the RM REST API
0136 0.1 s 0137 0.1 s 0138 60.5 s
Show solution and explanation
apps = {"0136": (1792650343192, 1792650343300), "0137": (1792650380000, 1792650380050), "0138": (1792650400000, 1792650460500)}
for a, (s, l) in apps.items():
print(a, f"{(l - s) / 1000:.1f} s")How it works: The first two started at once; the third waited a minute in ACCEPTED (illustrative times).
Allowed transitions
Using a transition table, check whether ACCEPTED → FINISHED is allowed directly.
small table
False
Show solution and explanation
allowed = {"ACCEPTED": {"RUNNING", "FAILED", "KILLED", "FINAL_SAVING"}, "RUNNING": {"FINAL_SAVING", "KILLED", "FAILED"}}
print("FINISHED" in allowed["ACCEPTED"])How it works: False: an app must pass through RUNNING (or fail/be killed). In YARN an ACCEPTED app can also be killed.
Cost of idle ApplicationMasters
A team submits 3,000 tiny jobs a day; each holds a 1,536 MB AM for 40 s. How many GB-hours a day are spent on AMs alone, and at ₹0.05 per GB-hour what does that cost per month?
3,000 × 1,536 MB × 40 s
50 GB-hours a day, Rs 75 a month
Show solution and explanation
gb_h = 3000 * 1536 * 40 / 1024 / 3600
print(f"{gb_h:.0f} GB-hours a day, Rs {gb_h * 0.05 * 30:,.0f} a month")How it works: About 50 GB-hours a day, ₹75 a month (illustrative), and the AMs also block queue slots. Batching jobs or uber mode for tiny jobs helps.
Rebuild a timeline
From a list of (time, object, from, to) log entries, print how long each state lasted for the application.
app transitions from the RM log
NEW 0 s NEW_SAVING 0 s SUBMITTED 0 s ACCEPTED 5 s RUNNING 31 s FINAL_SAVING 0 s FINISHING 6 s
Show solution and explanation
from datetime import datetime
log = [("06:25:43", "NEW"), ("06:25:43", "NEW_SAVING"), ("06:25:43", "SUBMITTED"), ("06:25:43", "ACCEPTED"),
("06:25:48", "RUNNING"), ("06:26:19", "FINAL_SAVING"), ("06:26:19", "FINISHING"), ("06:26:25", "FINISHED")]
t = lambda s: datetime.strptime(s, "%H:%M:%S")
for (a, s1), (b, _) in zip(log, log[1:]):
print(f"{s1:12} {(t(b) - t(a)).seconds:3d} s")How it works: ACCEPTED 5 s, RUNNING 31 s, FINISHING 6 s (waiting for the AM container to exit). The log has 1-second resolution, so the fast states show 0.
Real-world scenario questions (asked in company interviews)
The YARN application lifecycle is asked in Hadoop, Spark and platform-engineering interviews.
“What happens when you run hadoop jar?”
The classic “walk me through it” question.
The interviewer is checking: the roles of client, RM, AM and NM.
Show a strong answer
- The client gets an application ID, uploads the jar, configuration and splits to HDFS staging, and submits to the ResourceManager.
- The RM allocates a container for the MRAppMaster; a NodeManager localizes the files and launches it; the AM registers.
- The AM requests containers (preferring nodes with the data), launches map and reduce tasks via NodeManagers, tracks them, then unregisters; logs are aggregated.
Jobs sit in ACCEPTED for 30 minutes every morning
The cluster dashboard shows 40% free memory at the same time.
The interviewer is checking: AM limits and queue limits, not just total capacity.
Show a strong answer
- Free memory may belong to other queues; the submitting queue can be at its capacity or user limit.
- The queue's maximum-am-resource-percent may be reached by many small jobs, so no new AM can start.
- Check queue usage in the RM scheduler page, raise the AM share or queue capacity, or batch small jobs.
“The node running the ApplicationMaster died. Is the job lost?”
A follow-up on fault tolerance.
The interviewer is checking: application attempts and recovery.
Show a strong answer
- The RM notices the AM container is gone and starts a new attempt on another node, up to yarn.resourcemanager.am.max-attempts (default 2).
- The MapReduce AM can recover completed tasks from the previous attempt's history; other frameworks may restart the work.
- With RM HA and the state store, even an RM failover keeps the application.
Design chargeback for a shared Hadoop cluster
Five departments share one cluster and finance wants a monthly split.
The interviewer is checking: knowing where YARN usage data comes from.
Show a strong answer
- Each finished application has memorySeconds and vcoreSeconds in the RM REST API (and the timeline/history servers).
- Collect them per queue or user daily, convert to GB-hours and vcore-hours, and price both.
- Reserve capacity costs separately; idle reserved capacity is a policy choice, not just usage.