The weekly report that took 22 hours
Every Monday, TiffinGo's analytics team copies the whole week of app logs, about 10 TB spread over 40 servers, to one big reporting server. Then a script counts orders per city. The copying alone takes most of the day. Kabir proudly tells Arjun Sir he has found the problem: “We need a faster reporting server!”
Sir, the reporting server is old. A new one with 64 cores would finish the counting much faster.
How long does the counting take now, Kabir?
About an hour. The copying takes… twenty-one hours.
So a faster CPU saves you part of one hour, and the twenty-one hours stay. What is actually slow?
Moving 10 TB over the network!
Right. Now think. The data is already on 40 servers, and each of those servers has a CPU sitting idle. Your counting program is maybe 50 kilobytes. What if you sent the program to the data, and each server counted its own part?
Then only the counts come back, a few kilobytes each. That's… the whole idea of Hadoop, isn't it?
It's the most important idea in this whole module: move the code, not the data. Hadoop calls it data locality.
Data locality: move the code, not the data
Data locality is the principle of running a computation on, or as close as possible to, the node where its input data is stored, instead of transferring the data across the network to the computation. In Hadoop, the scheduler uses the block locations reported by HDFS to place each task on a node that holds a replica of its input block (node-local or data-local); if none is free, on another node in the same rack (rack-local); and only as a last resort elsewhere (off-rack). This minimises network traffic, which is usually the scarcest resource in a cluster.
Data is huge and the network is slow, but programs are tiny. So Hadoop sends the program to the machines that already have the data, and each machine works on what is on its own disks. Only small results travel over the network.
Coming from Big Data Fundamentals? Its lesson Distributed computing basics introduced “move the program, not the data” as a general idea. Here you will see exactly how Hadoop does it: block locations, node-local and rack-local tasks, and the counters that prove it.
Words you'll meet in this lesson
Why it matters: the numbers
| A typical MapReduce job jar | tens of KB to a few MB |
|---|---|
| Input of a typical job | GB to TB |
| Moving 10 TB over 1 Gbps | about 23 hours (10 × 1,024 × 8 ÷ 1 Gbps ≈ 81,920 s) |
| Moving 10 TB over 10 Gbps | about 2.3 hours, if nothing else uses the link |
| 40 nodes each reading 256 GB locally at 1 GB/s (many disks) | about 4–5 minutes, all in parallel |
| Job counters that show locality | Data-local map tasks, Rack-local map tasks (and other local tasks) |
Times are back-of-the-envelope estimates. Real networks are shared, and disks and protocols add overhead.
Locality at three levels
Beginner The census enumerator
- India's census does not bring every family to one office to be counted.
- It sends enumerators (the “program”) to every area. Each one counts the families there (the “data”).
- Only the totals travel back to the district office, then the state, then Delhi.
- Moving people would take forever; moving a few counters and some forms is easy. Hadoop works the same way.
Intermediate How Hadoop places tasks near the data
- When a job starts, it asks the NameNode for the locations of every input block (each block has 3 replicas, so 3 candidate nodes).
- The ApplicationMaster asks YARN for containers on those nodes. If one of them has a free slot, the map task runs there: node-local.
- If all three are busy, YARN may wait briefly (delay scheduling), then try a node in the same rack, then anywhere.
- After the job, the counters show how well it went, for example
Data-local map tasks=118andRack-local map tasks=10. - Three replicas help locality too: three places a task could run locally instead of one.
Advanced Where locality breaks down, and the modern view
- Shuffle can't be local: map outputs must travel to the reducers. Locality saves the input read, not the shuffle. Combiners (Module 9) shrink what is shuffled.
- Small files: thousands of tiny blocks mean thousands of tiny tasks; scheduling overhead dominates (Module 7).
- Hot spots: if everyone reads the same popular file, its 3 holders are overloaded. Raise replication for such files.
- Cloud object storage: with data in S3/GCS/ADLS and compute on separate VMs, there is no node-local option. It works because cloud data-centre networks are very fast and storage is built for parallel reads. Many teams now accept this trade: cheaper, elastic compute in exchange for less locality.
- Spark uses the same idea through “preferred locations” for each partition, and also caches data in memory on the executors.
Exam tip: “Explain data locality in Hadoop” is a favourite question. Give the definition, the three levels (node-local → rack-local → off-rack), why it matters (network is the bottleneck; code is small, data is big), and how HDFS block locations and replication make it possible.
Three levels of locality
| Level | Where the task runs | Path of the input data | Network distance | Cost |
|---|---|---|---|---|
| Node-local | On a node holding a replica | Local disk → task | 0 | Cheapest |
| Rack-local | Another node, same rack | Disk → ToR switch → task | 2 | Low |
| Off-rack | A node in another rack | Disk → ToR → core → ToR → task | 4 | Highest: uses the shared uplinks |
Ways systems put compute near data
Co-located storage and compute
Every worker runs both a DataNode and a NodeManager, so the CPU sits next to the disks. Classic on-premises Hadoop.
Hadoop defaultLocality-aware scheduling
The scheduler is told where each block is and prefers those nodes, with delay scheduling to wait for a local slot.
YARN, SparkPush filters down
Ask the storage to drop rows and columns early (for example Parquet column pruning), so less data is read and moved at all.
Read lessCombine before sending
Sum or count locally first, then send only the partial results. The MapReduce combiner does this.
Send lessDisaggregated (cloud)
Storage and compute are separate services. Locality is traded for elasticity, helped by very fast networks.
Modern cloudYou can check locality for any finished MapReduce job: open it in the JobHistory web UI or run mapred job -status <job-id> and look at the job counters. If most map tasks are not data-local, look for busy nodes, a misconfigured rack script, or too few replicas.
How Hadoop sends one map task to its data
The job asks: where is the input?
The client computes the input splits (normally one per block) and records the nodes that hold each one. This list travels with the job.block_locations = { # what the NameNode answers "blk_1": ["dn1", "dn4", "dn5"], "blk_2": ["dn2", "dn4", "dn6"], } print(block_locations["blk_1"])['dn1', 'dn4', 'dn5']The ApplicationMaster asks for containers on those nodes
For the task of
YARN resource requests can name specific hosts and racks. The scheduler tries to satisfy the most local one first.blk_1, it asks YARN: “a container on dn1, dn4 or dn5, or failing that, on /rack1 or /rack2.”Only the program travels
The job jar (say 50 KB) and the configuration are copied to the chosen node, and the task JVM starts there.
Hadoop puts job files in HDFS and each NodeManager downloads them once per job, shared by all tasks on that node. Even with 1,000 tasks, this is tiny compared with the input data.The task reads its block from local disk
The map task opens
With short-circuit local reads enabled, the task reads the block file straight from disk without going through the DataNode's network socket, which is faster still.blk_1. Because dn1 itself holds a replica, the HDFS client reads it from dn1's disk.Only small results leave the node
The map task writes its output (for example counts per city) to dn1's local disk. Reducers fetch only these small outputs.
128 MB of logs might become a few KB of counts. That is the shuffle: the only big network step of a MapReduce job, and much smaller than moving the input.
Same report, two ways: what Kabir sees, and what crosses the network
First the old way (copy all data to one server), then the Hadoop way (send the program to the data). Left: Kabir's terminal. Right: what moves across the cluster.
- Old way: Kabir's script copies the week's 10 TB of logs from 40 servers to one reporting server. The estimate says over 21 hours.
- Next morning the copy finishes and counting takes under an hour. About 22 hours in total, almost all of it moving data.
- Hadoop way: the logs already sit in HDFS on the 40 nodes. Kabir submits a small job jar. 10 TB in 128 MB blocks gives 81,920 input splits.
- Thousands of map tasks run at the same time across the cluster, each on its own block.
- The counters show that about 97% of map tasks were data-local and the rest rack-local. The job takes 41 minutes.
- Same data, same servers. Moving the code instead of the data turned a day into minutes. (Times are illustrative.)
- The old way pulls every node's data through the rack switches and the core switch into one server. All links fill up.
- For hours the network is the bottleneck. The report server's CPU mostly waits, and other jobs suffer too.
- The Hadoop way: the job jar (tens of KB) is sent to the nodes that hold the blocks. That is all that crosses the network at the start.
- Each node's map tasks read the blocks on its own disks, all nodes at once. No input data crosses the core switch.
- When a node's slots are busy, a task may run on a neighbour in the same rack (rack-local). Data crosses only the rack switch.
- The only big network step is the shuffle of map outputs, which are tiny here (counts per city).
Try it yourself: locality on, locality off
Run the same job twice: once with Hadoop's normal locality-aware scheduling, and once with locality switched off, so tasks go to any free node and must pull their block over the network (red packets).
Where moving the code wins
1. Log analysis
Web and app servers write billions of log lines a day into HDFS. Counting errors, sessions or orders per city runs where the logs are stored; only the totals move.
2. Scanning transactions for fraud rules
A bank re-checks months of card and UPI transactions against new fraud rules. Each node filters its own blocks and sends back only the suspicious rows, a tiny fraction of the data.
3. Columnar formats: move less from disk too
Hive and Spark queries on Parquet or ORC files read only the columns a query needs and skip blocks whose min/max values can't match. Locality cuts network; columnar formats cut disk reads (Module 10).
4. Cloud: when locality is given up on purpose
Many companies keep data in cloud object storage and spin up compute clusters per job. There is no node-local read, but the cloud network is fast and the cluster can be deleted afterwards. Knowing locality helps you understand that trade-off.
5. Tuning a slow job
When a job is slow, one of the first things an engineer checks is the locality counters. A sudden drop in data-local tasks often points to a full cluster, a broken rack script, or data written with replication 1.
Examples are simplified; figures are illustrative.
Move the data or move the code? Do the maths
Compare the time to bring all the data to one machine with the time to send a small program to every node and let each read its own share in parallel. Then try the challenges, including a tiny locality-aware scheduler.
data_tb = 10
nodes = 40
network_gbps = 10 # link into the single reporting server
local_read_gb_s = 1.0 # each node, many disks in parallel
jar_kb = 50
move_data_h = data_tb * 1024 * 8 / network_gbps / 3600
move_code_s = jar_kb * 8 / 1024 / 1024 / network_gbps
local_min = data_tb * 1024 / nodes / local_read_gb_s / 60
print(f"Move the data : {move_data_h:.1f} hours of transfer")
print(f"Move the code : {move_code_s:.5f} s to send the jar")
print(f"Then each node reads its share in {local_min:.1f} minutes")move_data_h = data_tb * 1024 * 8 / network_gbps / 3600TB → GB (× 1024), bytes → bits (× 8), ÷ link speed in Gbit/s = seconds, ÷ 3600 = hours.move_code_s = jar_kb * 8 / 1024 / 1024 / network_gbpsThe same formula for a 50 KB jar: KB → Gbit is ÷ 1024 ÷ 1024 after × 8.local_min = data_tb * 1024 / nodes / local_read_gb_s / 60Each node reads only its own 1/40th of the data, and all nodes read at the same time.
A locality-aware scheduler, one line at a time
Watch the scheduler give each task to a free node that holds its block, and fall back to any free node when none does. The free list shrinks as slots are used.
Worked examples: real problems, solved and traced in memory
Three locality problems from real cluster work: a transfer estimate, a scheduling decision, and a job report built from task placements. Each runs live and is traced in memory.
Copying 2 TB to a laptop for analysis
Problem statement
An analyst wants to copy 2 TB of HDFS data to her laptop over the office Wi-Fi (about 200 Mbps in practice) to analyse it in Python. How many hours will the copy take? Compare with a 1 Gbps wired link.
2 TB · 200 Mbps vs 1000 Mbps
Wi-Fi : 23.3 hours Wired 1 Gbps : 4.7 hours
Approach
- Convert TB to megabits: × 1024 × 1024 × 8.
- Divide by the link speed in Mbps to get seconds.
- Print hours for both links.
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.
Place map tasks with locality first
Problem statement
Four map tasks are waiting. Each node has a number of free container slots. Give each task a slot on a node that holds its block if possible, otherwise any node with a free slot, and label each placement.
M1 [dn1, dn3] · M2 [dn1, dn2] · M3 [dn1] · M4 [dn4] · free slots dn1 2, dn2 1, dn3 1
M1 -> dn1 node-local M2 -> dn1 node-local M3 -> dn2 remote M4 -> dn3 remote
Approach
- For each task, list holders that still have a free slot.
- Use the first one (node-local), or any node with slots (remote).
- Reduce that node's free slots by one.
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.
Turn task placements into locality counters and network cost
Problem statement
After a job, each map task's placement is known (node, rack or off) and every input block is 128 MB. Produce the job counters, the share of data-local tasks, and how many GB of input crossed the network (rack-local and off-rack only).
[node, node, rack, node, off, node, rack, node, node, off] · 128 MB per block
Data-local map tasks = 6 Rack-local map tasks = 2 Off-rack map tasks = 2 Data-local share: 60%, input over network: 0.5 GB
Approach
- Count each kind with a dictionary.
- Data-local share = node ÷ total.
- Network GB = (rack + off) × 128 ÷ 1024.
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 1.5 in one screen
- Move the code, not the data: jars are KB, data is TB, the network is the bottleneck.
- Data locality: run each task on a node that holds a replica of its input block.
- Levels: node-local (distance 0) → rack-local (2) → off-rack (4).
- The job gets block locations from the NameNode; YARN tries to place containers on those hosts.
- Delay scheduling: wait briefly for a local slot before going remote.
- Short-circuit reads: a local task reads the block file straight from disk.
- Check counters:
Data-local map tasks,Rack-local map tasks. - The shuffle can't be local; combiners shrink it.
- Cloud object storage gives up locality for elasticity, relying on fast networks.
Interview and viva questions
What is data locality and why is it important in Hadoop?
Data locality means running computation on the node where the data is stored. In Hadoop, map tasks are scheduled on DataNodes that hold a replica of their input split. It matters because network bandwidth, especially between racks, is much more limited than the combined disk bandwidth of the cluster. Moving a small program to many nodes avoids moving terabytes of data.
What are the levels of data locality?
Node-local (data-local): the task runs on a node with a replica. Rack-local: the task runs on another node in the same rack, reading over the rack switch. Off-rack: the task runs in a different rack, reading through the core network. The scheduler prefers them in that order.
How does the scheduler know where the data is?
When a job is submitted, the input format computes splits and asks the NameNode for the block locations of each split. The ApplicationMaster includes these hosts and racks in its container requests to the ResourceManager, which tries to allocate containers on those nodes.
What can cause poor data locality?
A busy or full cluster (no free slots on the right nodes), very small files or uneven block placement, replication factor 1, a wrong or missing rack topology script, newly added empty nodes before running the balancer, or reading data that is not in HDFS at all (for example cloud object storage).
Does Spark use data locality?
Yes. Spark records preferred locations for each partition (for HDFS input, the block locations) and its scheduler waits a short time (spark.locality.wait) for a slot at the best locality level: process-local, node-local, rack-local, then any.
Practice problems
Transfer estimates and small scheduling programs. The business problems are the kind of questions asked when a team argues “just copy the data”.
Gigabits to hours
How many hours does it take to move 5 TB over a 10 Gbps link?
5 TB · 10 Gbps
1.14 hours
Show solution and explanation
print(round(5 * 1024 * 8 / 10 / 3600, 2), "hours")How it works: 5 TB = 40,960 Gbit; ÷ 10 Gbit/s = 4,096 s ≈ 1.14 hours, and that is with the link to yourself.
Jar vs data
A job jar is 200 KB and the input is 4 TB. How many times bigger is the data?
200 KB · 4 TB
21,474,836 times bigger
Show solution and explanation
data_kb = 4 * 1024 * 1024 * 1024
print(f"{data_kb / 200:,.0f} times bigger")How it works: About 2 crore times. Sending the program to every node, even 1,000 nodes, is still tiny.
Classify placements
A task's block is on dn2 (rack r1). Classify running the task on dn2, dn3 (r1) and dn7 (r2).
block on dn2 · rack {dn2: r1, dn3: r1, dn7: r2}dn2 node-local dn3 rack-local dn7 off-rack
Show solution and explanation
rack = {"dn2": "r1", "dn3": "r1", "dn7": "r2"}
for n in ["dn2", "dn3", "dn7"]:
kind = "node-local" if n == "dn2" else "rack-local" if rack[n] == rack["dn2"] else "off-rack"
print(n, kind)How it works: Same node, same rack, other rack: the three levels of locality.
Closest replica to read
A client on dn6 must read a block whose replicas are on dn1, dn5 and dn9. Using Hadoop's distance (0, 2, 4), print the replica it should read.
client dn6 · replicas dn1, dn5, dn9 · racks r1: dn1-dn4, r2: dn5-dn8, r3: dn9
dn5
Show solution and explanation
rack = {"dn1": "r1", "dn5": "r2", "dn6": "r2", "dn9": "r3"}
def dist(a, b):
return 0 if a == b else 2 if rack[a] == rack[b] else 4
reps = ["dn1", "dn5", "dn9"]
print(min(reps, key=lambda r: dist("dn6", r)))How it works: HDFS clients sort replicas by network distance and read the closest: dn5, in the same rack.
Data-local percentage
A job ran 1,250 map tasks: 1,180 data-local and 70 rack-local. Print the data-local percentage and whether it meets a 90% target.
1250 tasks · 1180 data-local
94.4% data-local; target met: True
Show solution and explanation
total, local = 1250, 1180
pct = local / total * 100
print(f"{pct:.1f}% data-local; target met: {pct >= 90}")How it works: 94.4%: healthy. Track this per job over time; a sudden drop is an early warning.
Network saved by locality
A job reads 2,000 blocks of 128 MB. With locality, 3% of tasks are remote; without it, 85% are. How many GB of input cross the network in each case?
2000 × 128 MB · 3% vs 85% remote
with locality : 7.5 GB over the network without : 212.5 GB over the network
Show solution and explanation
blocks, mb = 2000, 128
for label, remote in [("with locality", 0.03), ("without", 0.85)]:
print(f"{label:14}: {blocks * remote * mb / 1024:.1f} GB over the network")How it works: 7.5 GB vs 212.5 GB. Locality keeps the shared uplinks free for the shuffle and for other jobs.
Copy to a warehouse or query in place?
A team wants to copy 30 TB from the Hadoop cluster to a separate analytics server every night over a 10 Gbps link, for a report that needs only 2 GB of aggregated results. Estimate both transfers and print a recommendation.
30 TB vs 2 GB · 10 Gbps
Copy all data : 6.8 hours every night Copy the result : 1.6 seconds Recommendation : aggregate on the cluster, move only the result
Show solution and explanation
link = 10 # Gbps
full = 30 * 1024 * 8 / link / 3600
agg = 2 * 8 / link
print(f"Copy all data : {full:.1f} hours every night")
print(f"Copy the result : {agg:.1f} seconds")
print("Recommendation : aggregate on the cluster, move only the result")How it works: Nearly 7 hours of saturated network every night versus 2 seconds. Push the computation to where the data lives.
Should the hot file get more replicas?
A small lookup file (one block) is read by every one of 600 map tasks. With 3 replicas, each holder serves 200 tasks. How many replicas are needed so no node serves more than 20 tasks?
600 readers · at most 20 per replica
Replicas needed: 30
Show solution and explanation
import math
readers, limit = 600, 20
print("Replicas needed:", math.ceil(readers / limit))How it works: 30 replicas. Hadoop does this for job files: mapreduce.client.submit.file.replication defaults to 10, and the distributed cache spreads small side files to the nodes that need them.
Real-world scenario questions (asked in company interviews)
Locality questions that interviewers use to separate people who have memorised definitions from people who understand clusters.
“Let's copy the data to the BI server every night”
The BI team asks for the full 15 TB clickstream table to be copied from Hadoop to their reporting database every night, so their dashboards can query it. The dashboards show daily totals per city and product.
The interviewer is checking: do you apply “move the code, not the data” to a real request?
Show a strong answer
- 15 TB a night over a 10 Gbps link is over 3 hours of a saturated network, and the BI database must store it all.
- The dashboards need aggregates. Compute them on the cluster (Hive/Spark job) and export only the summary tables, probably a few GB.
- If analysts need ad-hoc detail, let them query the cluster directly with Hive, Impala or Spark SQL, or a query engine like Trino that reads the data in place.
Avoid: agreeing to copy everything “because the BI tool needs it”.
Data-local tasks dropped from 95% to 55%
Last month, jobs on the cluster were 95% data-local. This week they are 55% and the core switch uplinks are saturated. Nothing in the job code changed.
The interviewer is checking: can you list concrete causes of poor locality and how to verify each?
Show a strong answer
- New nodes added without balancing: data sits on old nodes, but free slots are on new ones. Run the balancer.
- Rack script changed or broken: check
hdfs dfsadmin -printTopology. - Cluster busier: more concurrent jobs mean fewer free slots on the right nodes; review queue capacities and delay-scheduling settings.
- Data written with replication 1 (to save space) leaves only one local candidate per block. Check with
hdfs fsck. - Input moved to object storage or another cluster, where node-local reads are impossible.
Avoid: “Buy a faster core switch” before finding the cause.
“If locality is so important, why does everyone use S3?”
During a migration discussion, a senior engineer argues against moving from on-prem HDFS to cloud object storage: “We'll lose data locality and everything will be slow.”
The interviewer is checking: do you understand that locality is a trade-off, not a law?
Show a strong answer
- True: reads become network reads. But cloud data-centre networks are very fast and object stores serve many parallel reads well.
- In return: compute and storage scale separately, clusters can be created per job and deleted, and storage is cheap and highly durable.
- Mitigations: columnar formats (read less), partitioning and predicate pushdown, caching layers, and right-sized instances with high network bandwidth.
- Benchmark the key jobs on both before deciding.
Avoid: treating either side as always right. Measure the workload.
Explain data locality to a non-technical manager
Your manager, who is not technical, asks why the Hadoop cluster can process 10 TB in under an hour when copying 10 TB to one server takes a whole day.
The interviewer is checking: can you explain a core idea simply and correctly?
Show a strong answer
“Imagine a census. Bringing every family to one office would take forever. Instead, we send counters to every area, they count locally at the same time, and only the totals travel back. Our cluster does the same: the data already lives on 40 machines, so we send the small counting program to each machine, all 40 work in parallel on their own part, and only small results come back.”
Avoid: jargon such as “node-local container allocation” with a non-technical audience.