When the shell is not enough
The canteen app's backend, written in Java, must save every order batch into HDFS and read daily totals back. Riya's first version calls hdfs dfs -put from Java with Runtime.exec(). It works, but each call starts a new JVM: 1.8 seconds per batch, and errors arrive as text she has to parse. Arjun shows her that every hdfs dfs command is itself a small Java program built on one class, FileSystem, and her backend can use that class directly.
Everything you did with the shell, you can do from code: fs.create() is -put, fs.open() is -cat, fs.rename() is -mv. The JVM starts once, connections are reused, and errors are real Java exceptions.
Is it different from writing a normal file?
Almost the same streams you know from Java. The difference is what happens underneath: a create asks the NameNode, the bytes go through the write pipeline from Lesson 6.1, and open reads from the nearest DataNode as in Lesson 6.2. Spark and Hive use this same class, which is why they can read HDFS and S3 with the same code.
The Hadoop FileSystem API
The Hadoop FileSystem API (org.apache.hadoop.fs) is the Java interface through which all Hadoop clients access storage. FileSystem.get(Configuration) returns an implementation chosen by the URI scheme of fs.defaultFS or of a Path; for hdfs:// it is DistributedFileSystem, which uses a DFSClient to call the NameNode over RPC and to stream data to and from DataNodes. Files are created with create() (returning an FSDataOutputStream that supports hflush() and hsync()) and read with open() (returning a seekable FSDataInputStream). Metadata is returned as FileStatus and block placement as BlockLocation; namespace operations include mkdirs, listStatus, rename and delete.
A Java library that lets your program open, write, list, rename and delete HDFS files, the way the shell does, but inside your own application.
Words you'll meet in this lesson
fs.defaultFS says where HDFS is.FileSystem.get(conf).create() returns; hflush() makes data visible to readers.open() returns; supports seek(offset).API facts worth remembering
| FileSystem.get(conf) | Returns a cached, shared instance per scheme, authority and user; closing it closes it for the whole JVM |
|---|---|
| rename() | Returns false on many failures instead of throwing: always check it |
| delete(path, true) | Recursive and permanent: the API does not use the trash |
| create(path, true) | true = overwrite an existing file |
| Visibility | Other readers see written data after hflush() or close() |
| Classpath | hadoop classpath prints the jars to compile and run with |
The API at three levels
Beginner Like Java file handling you already know
Pathis likejava.io.File,open()/create()give streams.- You wrap them in a
BufferedReaderor write bytes, as usual. - Always close streams (try-with-resources).
Intermediate Everyday use
- Upload:
fs.copyFromLocalFile(src, dst), orcreate()and write. - Check before reading:
fs.exists(p),fs.getFileStatus(p).getLen(). - Walk a tree:
fs.listFiles(dir, true)(recursive iterator, includes block locations). - Publish results: write into a temporary folder, then one
fs.rename().
Advanced What library and framework authors care about
getFileBlockLocationsis how MapReduce and Spark place tasks near data.hflush()vshsync(): visible to readers vs also forced to disk on DataNodes.- Seek + read lets a reader start in the middle of a file: that is how input splits work.
- Use
FileSystem.newInstance()if your code must close its own instance without affecting others. - On a secure cluster, the same code needs a Kerberos login (
UserGroupInformation).
Exam tip: “Write a Java program to read a file from HDFS.” Steps: create a Configuration; FileSystem fs = FileSystem.get(conf); FSDataInputStream in = fs.open(new Path(...)); read lines with a BufferedReader (or IOUtils.copyBytes(in, System.out, 4096, false)); close the stream. Mention that open() gets block locations from the NameNode and reads from DataNodes.
API methods, shell commands and what happens underneath
| FileSystem method | Shell equivalent | Talks to |
|---|---|---|
mkdirs(p) | -mkdir -p | NameNode |
create(p, overwrite) + write + close | -put | NameNode, then DataNode pipeline |
append(p) | -appendToFile | NameNode, then DataNodes |
open(p) + read | -cat, -get | NameNode (locations), then DataNodes |
getFileStatus(p) | -stat, -ls one file | NameNode |
listStatus(dir), listFiles(dir, recursive) | -ls, -ls -R | NameNode |
getFileBlockLocations(st, 0, len) | fsck -blocks -locations | NameNode |
rename(src, dst) | -mv | NameNode |
delete(p, recursive) | -rm -r -skipTrash | NameNode (blocks freed later) |
setReplication(p, n) | -setrep | NameNode |
Ways to reach HDFS from a program
Java FileSystem API
Native and fastest; what Hadoop's own tools use. This lesson.
Java, ScalaWebHDFS (REST)
HTTP + JSON on the NameNode port 9870, redirecting to DataNodes on 9864. Any language, even curl.
Any languagelibhdfs / pyarrow
A C library over the Java client; Python's pyarrow.fs.HadoopFileSystem uses it.
Through a framework
Spark, Hive, Flink read hdfs:// paths with this API inside; you rarely see it.
When you change hdfs:// to s3a://bucket/ in a path, the same code talks to cloud storage, if the connector jar and credentials are configured. That portability is the main reason the API is an abstract FileSystem class.
Two real programs on the lab cluster
Both Java files are in “Run it on your PC” below. We compiled and ran them inside the namenode container, where Java and the Hadoop jars are already installed.
Compile and package
javac -cp $(hadoop classpath) HdfsBasics.java ItemCount.java jar cf hdfs-demo.jar *.class hadoop jar hdfs-demo.jar HdfsBasicsConnected to: hdfs://namenodehadoop classpathlists every Hadoop jar.hadoop jarruns the class with that classpath and the cluster's configuration, sonew Configuration()findsfs.defaultFS=hdfs://namenodeandFileSystem.get()returns a DistributedFileSystem.Write and read back
try (FSDataOutputStream out = fs.create(file, true)) { out.write("order_id,item,amount\n".getBytes(StandardCharsets.UTF_8)); out.write("3001,samosa,18\n3002,chai,15\n".getBytes(StandardCharsets.UTF_8)); } try (FSDataInputStream in = fs.open(file); BufferedReader r = ...) { while ((line = r.readLine()) != null) System.out.println(" read: " + line); }read: order_id,item,amount read: 3001,samosa,18 read: 3002,chai,15create= NameNode create + addBlock; the bytes went through a 3-DataNode pipeline; closing the stream completed the file.openfetched block locations and streamed from a DataNode.Metadata and block locations
The hosts are Docker container IDs: datanode-1, datanode-2 and datanode-3.FileStatus st = fs.getFileStatus(file); for (BlockLocation b : fs.getFileBlockLocations(bs, 0, bs.getLen())) ...orders.csv 49 bytes replication 3 block size 128 MB owner hadoop block at offset 0 length 134,217,728 on ab3b1bedf98a, 46201bb62e68, 264eb9c0f8e2 block at offset 134,217,728 length 6,542,542 on ab3b1bedf98a, 46201bb62e68, 264eb9c0f8e2orders_x30.csv(140,760,270 bytes) has a full 128 MB block plus a 6.5 MB tail block. A job scheduler uses exactly this list to run tasks next to the data.List, rename, delete
Both return booleans. A rename to a missing parent folder returnsSystem.out.println("rename: " + fs.rename(file, renamed)); System.out.println("exists after rename: " + fs.exists(file)); System.out.println("delete: " + fs.delete(dir, true));ls: hdfs://namenode/user/hadoop/api_demo/orders.csv rename: true exists after rename: false delete: truefalsewithout an exception, so check it.delete(dir, true)removed the folder and the file permanently: the API skips the trash.Count items in one file
300,000 orders (4.5 MB) streamed through one client in 1.3 seconds, including connecting. The program reads the file block after block likehadoop jar hdfs-demo.jar ItemCount /user/hadoop/data/canteen_orders.csvchai 50,170 dosa 49,948 idli 49,984 poha 49,891 samosa 50,021 vada pav 49,986 took 1327 ms in one JVM-catdoes.Same program, 90× the data, and a bug
403 MB took 5.9 s: about 68 MB/s through one client. A 4 TB file would take about 17 hours this way, which is why MapReduce (Module 8) runs one reader per block in parallel. And “item 89”:hadoop jar hdfs-demo.jar ItemCount /user/hadoop/data/orders_x90.csvchai 4,515,300 dosa 4,495,320 idli 4,498,560 item 89 poha 4,490,190 samosa 4,501,890 vada pav 4,498,740 took 5890 ms in one JVMorders_x90.csvwas made by concatenating the file 90 times, so 89 header lines sit inside the data. HDFS stores bytes; cleaning data is the program's job.The same file over HTTP: WebHDFS
The NameNode answers metadata itself but never sends file data: for OPEN it redirects (HTTP 307) to a DataNode (datanode-2, port 9864). The same split as the Java client, in plain HTTP.curl -s "http://namenode:9870/webhdfs/v1/user/hadoop/canteen/raw/day1.csv?op=GETFILESTATUS&user.name=hadoop" curl -s -i "http://namenode:9870/webhdfs/v1/user/hadoop/canteen/raw/day1.csv?op=OPEN&user.name=hadoop" | grep -iE "^HTTP|^Location"{"FileStatus":{"accessTime":1792579946689,"blockSize":134217728,"childrenNum":0,"fileId":16425,"group":"supergroup","length":47,"modificationTime":1792575484677,"owner":"hadoop","pathSuffix":"","permission":"640","replication":3,"storagePolicy":0,"type":"FILE"}} HTTP/1.1 307 Temporary Redirect Location: http://46201bb62e68:9864/webhdfs/v1/user/hadoop/canteen/raw/day1.csv?op=OPEN&user.name=hadoop&namenoderpcaddress=namenode:8020&offset=0
Don't call fs.close() on the instance from FileSystem.get() in a long-running server: it is shared, and other parts of the application will start failing with “Filesystem closed”. Close streams, keep the FileSystem open (or use FileSystem.newInstance()). Our short demo programs close it on exit, which is fine.
Six API calls, and who answers each
The calls from HdfsBasics, in order.
FileSystem.getreads the configuration and connects.createreturns an output stream.- Writing and closing stores the file.
openreturns an input stream.- Reading prints the three lines.
- Rename and delete both return true.
- The configuration's
fs.defaultFSpicks DistributedFileSystem, which wraps a DFSClient. createis one RPC to the NameNode: the file exists, still empty and open for writing.write+close: addBlock, the DataNode pipeline (Lesson 6.1), then complete.openasks the NameNode where the blocks are (Lesson 6.2).- Reading streams bytes directly from a DataNode.
renameanddeletetouch only the NameNode.
The API at work
1. Ingestion services
Services that collect events (from Kafka, logs or apps) keep an HDFS file open, write records and call hflush() regularly so readers see recent data, then roll to a new file every hour.
2. Moving to the cloud
Teams migrating from on-premise Hadoop to cloud object stores often change only paths and configuration: Spark and Hive jobs keep working because they talk to the FileSystem abstraction.
3. Non-Java tools
Dashboards and Python scripts read result files through WebHDFS or HttpFS (a gateway that also hides the DataNodes), with no Hadoop installation on the client.
4. Housekeeping tools
Admins write small Java tools that walk the namespace once with listFiles(dir, true) to find small files, old data or wrong replication, far faster than thousands of shell calls.
Simplified; organisations differ in tools and policies.
The FileSystem API, modelled in Python
The Java API can't run in your browser, so this Python class copies its shape: create, open, get_file_status, get_file_block_locations, rename and delete, with a NameNode namespace and 128 MB blocks placed on DataNodes. Run it, then try the tasks.
BLOCK = 128 * 1024 * 1024
class FileSystem:
def __init__(self, datanodes):
self.ns = {} # path -> (length, replication, blocks)
self.dns = datanodes
self.next_dn = 0
def create(self, path, data_len, replication=3):
blocks = []
for off in range(0, data_len, BLOCK):
hosts = [self.dns[(self.next_dn + i) % len(self.dns)] for i in range(replication)]
self.next_dn += 1
blocks.append((off, min(BLOCK, data_len - off), hosts))
self.ns[path] = (data_len, replication, blocks)
def get_file_status(self, path):
length, rep, blocks = self.ns[path]
return {"path": path, "len": length, "replication": rep, "blocksize": BLOCK}
def get_file_block_locations(self, path):
return self.ns[path][2]
def rename(self, src, dst): # returns False instead of raising, like Java
if src not in self.ns or dst in self.ns:
return False
self.ns[dst] = self.ns.pop(src)
return True
def delete(self, path):
return self.ns.pop(path, None) is not None
fs = FileSystem(["datanode-1", "datanode-2", "datanode-3"])
fs.create("/user/hadoop/data/orders_x30.csv", 140_760_270)
print(fs.get_file_status("/user/hadoop/data/orders_x30.csv"))
for off, length, hosts in fs.get_file_block_locations("/user/hadoop/data/orders_x30.csv"):
print(f" block at offset {off:,} length {length:,} on {', '.join(hosts)}")
print("rename:", fs.rename("/user/hadoop/data/orders_x30.csv", "/user/hadoop/data/old.csv"))
print("rename again:", fs.rename("/user/hadoop/data/orders_x30.csv", "/user/hadoop/data/old.csv"))for off in range(0, data_len, BLOCK):The file is cut into 128 MB blocks; the last one is shorter. The real file gave 134,217,728 + 6,542,542 bytes.def rename(self, src, dst): # returns False instead of raising, like JavaJava's fs.rename() also returns a boolean. The real program printed rename: true. Code that ignores a false silently loses track of files.def get_file_block_locations(self, path):Only metadata: the NameNode knows the hosts of every block. Schedulers use this to run tasks next to the data.
Run it on your PC: Java against HDFS
Save these files in the hdfs-lab folder. The namenode container already has Java and the Hadoop jars, so we copy the files in, compile there with javac -cp $(hadoop classpath), package a jar and run it with hadoop jar. The exact commands are at the end of the first file.
Write, read, inspect, rename, delete
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class HdfsBasics {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration(); // reads core-site.xml, hdfs-site.xml
try (FileSystem fs = FileSystem.get(conf)) {
System.out.println("Connected to: " + fs.getUri());
Path dir = new Path("/user/hadoop/api_demo");
fs.mkdirs(dir);
// write a small file
Path file = new Path(dir, "orders.csv");
try (FSDataOutputStream out = fs.create(file, true)) { // true = overwrite
out.write("order_id,item,amount\n".getBytes(StandardCharsets.UTF_8));
out.write("3001,samosa,18\n3002,chai,15\n".getBytes(StandardCharsets.UTF_8));
}
// read it back
try (FSDataInputStream in = fs.open(file);
BufferedReader r = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
String line;
while ((line = r.readLine()) != null) {
System.out.println(" read: " + line);
}
}
// metadata, as the NameNode stores it
FileStatus st = fs.getFileStatus(file);
System.out.printf("%s %d bytes replication %d block size %d MB owner %s%n",
st.getPath().getName(), st.getLen(), st.getReplication(),
st.getBlockSize() / (1024 * 1024), st.getOwner());
// where are the blocks of a big file?
Path big = new Path("/user/hadoop/data/orders_x30.csv");
FileStatus bs = fs.getFileStatus(big);
for (BlockLocation b : fs.getFileBlockLocations(bs, 0, bs.getLen())) {
System.out.printf(" block at offset %,d length %,d on %s%n",
b.getOffset(), b.getLength(), String.join(", ", b.getHosts()));
}
// list, rename, delete
for (FileStatus s : fs.listStatus(dir)) {
System.out.println(" ls: " + s.getPath());
}
Path renamed = new Path(dir, "orders_old.csv");
System.out.println("rename: " + fs.rename(file, renamed));
System.out.println("exists after rename: " + fs.exists(file));
System.out.println("delete: " + fs.delete(dir, true)); // true = recursive
}
}
}
// In the hdfs-lab folder:
// docker cp HdfsBasics.java lab5-namenode-1:/tmp/
// docker cp ItemCount.java lab5-namenode-1:/tmp/
// docker exec -it lab5-namenode-1 bash
// Then inside the container:
// cd /tmp
// javac -cp $(hadoop classpath) HdfsBasics.java ItemCount.java
// jar cf hdfs-demo.jar *.class
// hadoop jar hdfs-demo.jar HdfsBasicsReal output. The hosts are the container IDs of datanode-1, -2 and -3 (yours will differ). orders_x30.csv is canteen_orders.csv repeated 30 times; if you don't have it: for i in $(seq 30); do cat canteen_orders.csv; done | hdfs dfs -put - /user/hadoop/data/orders_x30.csv
Count orders per item, streaming one file
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.TreeMap;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
/** Count orders per item by streaming one HDFS file through a single client. */
public class ItemCount {
public static void main(String[] args) throws Exception {
Path path = new Path(args[0]);
Map<String, Integer> counts = new TreeMap<>();
long start = System.currentTimeMillis();
try (FileSystem fs = FileSystem.get(new Configuration());
FSDataInputStream in = fs.open(path);
BufferedReader r = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
r.readLine(); // skip the header
String line;
while ((line = r.readLine()) != null) {
String item = line.split(",")[1];
counts.merge(item, 1, Integer::sum);
}
}
counts.forEach((item, n) -> System.out.printf("%-9s %,d%n", item, n));
System.out.println("took " + (System.currentTimeMillis() - start) + " ms in one JVM");
}
}
// Inside the namenode container, in /tmp:
// hadoop jar hdfs-demo.jar ItemCount /user/hadoop/data/canteen_orders.csv
// hadoop jar hdfs-demo.jar ItemCount /user/hadoop/data/orders_x90.csvReal output for the 4.5 MB and the 403 MB file. “item 89” comes from header lines inside the concatenated file: a data bug, not an HDFS one.
The same file over HTTP (WebHDFS)
#!/usr/bin/env bash
# webhdfs.sh: the same file system over plain HTTP (REST). No Java, no Hadoop client needed.
# Run inside the namenode container: bash webhdfs.sh
NN=http://namenode:9870/webhdfs/v1
F=/user/hadoop/canteen/raw/day1.csv
echo "--- GETFILESTATUS (metadata, answered by the NameNode)"
curl -s "$NN$F?op=GETFILESTATUS&user.name=hadoop"; echo
echo "--- OPEN without following redirects"
curl -s -i "$NN$F?op=OPEN&user.name=hadoop" | grep -iE "^HTTP|^Location"
echo "--- OPEN, following the redirect to the DataNode (-L)"
curl -s -L "$NN$F?op=OPEN&user.name=hadoop"
# In the hdfs-lab folder:
# docker cp webhdfs.sh lab5-namenode-1:/tmp/
# docker exec lab5-namenode-1 bash /tmp/webhdfs.shReal output. The NameNode answers GETFILESTATUS itself and redirects OPEN (HTTP 307) to a DataNode on port 9864.
Reading a file block by block, as open() does
A simplified reader: ask for block locations once, then read each block from its first (nearest) host until the file's length is reached.
Worked examples: real problems, solved and traced in memory
Three API questions with numbers: what one JVM saves over shell calls, how fast one client can scan, and how many readers you need. Each runs live and is traced in memory.
API instead of shell calls
Problem statement
A backend saves 2,000 small order batches a day. Through hdfs dfs -put each call costs about 1.8 s of JVM start-up; through the API in a running service, about 0.02 s each. How much time is saved per day?
2,000 calls · 1.8 s vs 0.02 s
shell 60 min, API 40 s, saved 59 min per day
Approach
- Shell total = calls × 1.8 s.
- API total = calls × 0.02 s.
- Difference in minutes.
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.
How long does one reader take?
Problem statement
ItemCount read 403 MB in 5.89 s. At that speed, how long would one client take for 1 TB, and for 4 TB?
402.7 MB in 5.89 s
1 TB: 4.3 hours at 68 MB/s 4 TB: 17.0 hours at 68 MB/s
Approach
- Speed = MB ÷ seconds.
- Time = size ÷ speed.
- Convert to hours.
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.
How many parallel readers for a 1-hour target?
Problem statement
A 4 TB file has 128 MB blocks. If each reader handles whole blocks at 68 MB/s, how many parallel readers are needed to finish within one hour, and how many blocks does each read?
4 TB · 128 MB blocks · 68 MB/s · 1 hour
32,768 blocks, 18 readers, about 1821 blocks each
Approach
- Blocks = size ÷ 128 MB.
- One reader reads 68 × 3600 MB per hour.
- Readers = ceil(size ÷ per-reader capacity); blocks each = blocks ÷ readers.
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 6.5 in one screen
Configuration→FileSystem.get(conf)→DistributedFileSystemforhdfs://.- Write:
create(path, overwrite)→write→close(orhflushto make data visible). - Read:
open(path)→ seekable stream; reads from DataNodes. - Metadata:
getFileStatus,listStatus,listFiles(dir, true),getFileBlockLocations. renameanddeletereturn booleans;deleteskips the trash.- Lab: 300,000 orders in 1.3 s; 403 MB in 5.9 s (≈68 MB/s) through one client.
- WebHDFS: REST on port 9870, OPEN redirects (307) to a DataNode on 9864.
- Run with
javac -cp $(hadoop classpath)andhadoop jar app.jar Class.
Interview and viva questions
How do you read a file from HDFS in Java?
Create a Configuration, get a FileSystem with FileSystem.get(conf), call fs.open(new Path("/path")) to get an FSDataInputStream, wrap it in a BufferedReader (or copy it with IOUtils.copyBytes), and close the stream. The client gets block locations from the NameNode and reads the bytes directly from DataNodes.
How do you write a file to HDFS in Java?
Call fs.create(path, overwrite) to get an FSDataOutputStream, write bytes, and close it. Closing completes the file on the NameNode. Use hflush() to make written data visible to readers before closing, and hsync() to also force it to disk on the DataNodes.
What is the difference between FileSystem.get() and FileSystem.newInstance()?
get() returns a cached instance shared across the JVM for the same scheme, authority and user; closing it affects every user of that instance. newInstance() always returns a new instance that you own and may close.
What is WebHDFS?
A REST API for HDFS served by the NameNode (port 9870 in Hadoop 3) and DataNodes (port 9864). Metadata operations are answered by the NameNode; for reading and writing data it redirects the client to a DataNode. It lets any language use HDFS over HTTP.
How does Spark know where HDFS blocks are?
Through the same FileSystem API: getFileBlockLocations (or listFiles with locations) returns the hosts of each block, and the scheduler prefers to run a task on one of those hosts.
Can the same Java code access S3?
Yes, if the S3A connector is on the classpath and configured: FileSystem.get(URI.create("s3a://bucket/"), conf) returns an S3AFileSystem with the same methods. Some semantics differ (for example rename is a copy on object stores, not an atomic metadata change).
Practice problems
API exercises using the real outputs from the lab programs.
Bytes in the demo file
HdfsBasics wrote the header and two order lines (with newline characters). Confirm the 49 bytes it reported.
"order_id,item,amount\n3001,samosa,18\n3002,chai,15\n"
49 bytes
Show solution and explanation
data = "order_id,item,amount\n" + "3001,samosa,18\n3002,chai,15\n"
print(len(data.encode("utf-8")), "bytes")How it works: 49 bytes: 21 + 15 + 13, exactly the length getFileStatus reported.
Check the total
Add the six ItemCount results for canteen_orders.csv.
50,170 · 49,948 · 49,984 · 49,891 · 50,021 · 49,986
300000
Show solution and explanation
print(sum([50170, 49948, 49984, 49891, 50021, 49986]))How it works: 300,000 orders: every data line counted once, the header skipped.
Block lengths
orders_x30.csv is 140,760,270 bytes. Compute its block lengths with 128 MB blocks.
140,760,270 bytes
[134217728, 6542542]
Show solution and explanation
size, block = 140_760_270, 128 * 1024 * 1024
print([min(block, size - off) for off in range(0, size, block)])How it works: [134217728, 6542542]: the two BlockLocations printed by the real program.
Ratio for x90
orders_x90.csv is the 300,000-order file 90 times. Check that each item's count is exactly 90× and explain “item 89”.
x1 and x90 counts
True header lines inside: 89
Show solution and explanation
x1 = {"chai": 50170, "dosa": 49948, "idli": 49984, "poha": 49891, "samosa": 50021, "vada pav": 49986}
x90 = {"chai": 4515300, "dosa": 4495320, "idli": 4498560, "poha": 4490190, "samosa": 4501890, "vada pav": 4498740}
print(all(x90[k] == 90 * x1[k] for k in x1), "header lines inside:", 90 - 1)How it works: True: every count is exactly 90×. The first header was skipped, the other 89 copies were counted as an item called “item”.
Parse WebHDFS JSON
From the real GETFILESTATUS reply, print length, replication, permission and block size in MB.
{"FileStatus":{…}}47 3 640 128 MB
Show solution and explanation
import json
reply = '{"FileStatus":{"accessTime":1792579946689,"blockSize":134217728,"childrenNum":0,"fileId":16425,"group":"supergroup","length":47,"modificationTime":1792575484677,"owner":"hadoop","pathSuffix":"","permission":"640","replication":3,"storagePolicy":0,"type":"FILE"}}'
st = json.loads(reply)["FileStatus"]
print(st["length"], st["replication"], st["permission"], st["blockSize"] // 2**20, "MB")How it works: 47 bytes, replication 3, permission 640 (set with chmod in Lesson 6.3), 128 MB blocks.
Where does the redirect go?
Extract host, port, offset and NameNode address from the real 307 Location header.
Location: http://46201bb62e68:9864/…
46201bb62e68 9864 0 namenode:8020
Show solution and explanation
from urllib.parse import urlparse, parse_qs
loc = "http://46201bb62e68:9864/webhdfs/v1/user/hadoop/canteen/raw/day1.csv?op=OPEN&user.name=hadoop&namenoderpcaddress=namenode:8020&offset=0"
u = urlparse(loc); q = parse_qs(u.query)
print(u.hostname, u.port, q["offset"][0], q["namenoderpcaddress"][0])How it works: DataNode 46201bb62e68 (datanode-2) on port 9864, offset 0, and the NameNode RPC address the DataNode should use.
Service vs shell for an app
An app team pays Rs 12 per vCPU-hour. Their shell-based uploader uses 1 vCPU fully for 60 minutes a day (mostly JVM start-ups); an API-based version needs 2 minutes. What is the yearly saving, and what else improves?
60 vs 2 min/day · Rs 12/vCPU-hour
Rs 4,234 per year
Show solution and explanation
rate = 12
saved_hours = (60 - 2) / 60 * 365
print(f"Rs {saved_hours * rate:,.0f} per year")How it works: About Rs 4,234 a year in compute (illustrative), but the bigger wins are proper exceptions instead of parsing text, and uploads that finish in milliseconds.
Locality-aware task plan
Given block locations (like getFileBlockLocations returns) and 3 worker nodes, assign each block to a worker that holds a replica, keeping the load as even as possible. Print the plan and how many blocks are local.
6 blocks · hosts per block
{'dn-1': [(0, 'local'), (4, 'local')], 'dn-2': [(1, 'local'), (3, 'local')], 'dn-3': [(2, 'local'), (5, 'local')]}
local: 6 of 6Show solution and explanation
locs = {0: ["dn-1", "dn-2", "dn-3"], 1: ["dn-1", "dn-2", "dn-3"], 2: ["dn-1", "dn-3", "dn-4"],
3: ["dn-2", "dn-4", "dn-5"], 4: ["dn-1", "dn-5", "dn-2"], 5: ["dn-4", "dn-5", "dn-3"]}
workers = {"dn-1": [], "dn-2": [], "dn-3": []}
for b, hosts in locs.items():
local = [w for w in workers if w in hosts]
pick = min(local or workers, key=lambda w: len(workers[w]))
workers[pick].append((b, "local" if pick in hosts else "remote"))
print(workers)
print("local:", sum(t == "local" for v in workers.values() for _, t in v), "of", len(locs))How it works: Every block finds a worker with a local replica here, two blocks each. This is the core idea of MapReduce scheduling (Module 8): move the computation to the data.
Real-world scenario questions (asked in company interviews)
API questions check whether you can go beyond the shell and understand what client code really does.
“Write code to copy a local file to HDFS”
A whiteboard coding question.
The interviewer is checking: Configuration, FileSystem, Path, closing resources.
Show a strong answer
Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(conf);fs.copyFromLocalFile(new Path("orders.csv"), new Path("/user/hadoop/raw/"));, orfs.create()plusIOUtils.copyBytes()from a local input stream.- Close streams with try-with-resources; mention that the bytes go through the DataNode write pipeline.
A web service fails with “java.io.IOException: Filesystem closed”
It happens randomly, after some requests finish.
The interviewer is checking: the FileSystem cache.
Show a strong answer
- Some code calls
fs.close()on an instance fromFileSystem.get(), which is cached and shared by all threads. - Fix: never close the shared instance (close only streams), or use
FileSystem.newInstance()where code must own and close its instance.
A Python dashboard must read results from HDFS
No Hadoop installation on the dashboard servers, and a firewall in between.
The interviewer is checking: WebHDFS vs HttpFS.
Show a strong answer
- WebHDFS works over HTTP, but OPEN redirects to DataNodes on port 9864, so every DataNode must be reachable.
- Through a firewall, use HttpFS: a gateway that streams the data itself, so only one host and port are opened.
- Keep results small (aggregated by Hive or Spark) so the dashboard never pulls raw data.
“My Java program takes 17 hours to scan 4 TB. Why?”
A single program reads a big file from start to end.
The interviewer is checking: single client vs parallel processing.
Show a strong answer
- One client reads one stream: in our lab about 68 MB/s, so 4 TB ≈ 17 hours.
- HDFS can serve many readers at once. Split the work by blocks (getFileBlockLocations) and run readers on the nodes that hold them: that is MapReduce or Spark.