Take a Break
5:00
Inhale…
Give your mind a break — no phone, no music, just idle time or a quick walk.
Distributed Resource Management, SQL-to-Hadoop Ingestion, and Streaming Event Collection
Prerequisite Knowledge
This lecture builds on the following concepts from earlier lectures. If any feel unfamiliar, review the linked notes before proceeding.
Previously Covered in This Subject
- MapReduce programming model, execution lifecycle, and the Combiner / Partitioner optimizations — covered in Lecture 4
- HDFS block splitting, replication, and rack awareness — covered in Lecture 3
- Apache Hive as a data warehousing layer over Hadoop — covered in Lecture 3
- Apache YARN and the Resource Manager / Node Manager roles — covered in Lecture 3
- Apache Sqoop and Apache Flume within the Hadoop ecosystem — covered in Lecture 3
5.1 Lecture Overview, Progress Tracking, and MapReduce/Hive Foundation
5.1.1 Big Data Curriculum Roadmap and Syllabus Positioning
Why does this lecture matter? Every big data system needs two things: a place to store petabytes of data, and a way to process it in parallel. We already built the storage layer (HDFS) and the processing layer (MapReduce). Now we answer: How do we share a 10,000-node cluster among dozens of teams? and How do we get data from our existing databases into Hadoop?
Enterprise big data architectures rely on a layered stack of storage engines, distributed execution frameworks, multi-tenant schedulers, and data ingestion pipelines. In the overall big data ecosystem maturity model, foundational infrastructure comprises two core capabilities:
- Infinite Distributed Storage — the ability to store files far larger than any single machine's disk. Provided by the Hadoop Distributed File System (HDFS), which splits large files into fixed-size block replicas (default 128 MB per block, three replicas each) distributed across master NameNodes and slave DataNodes.
- Infinite Parallel Processing — the ability to run computations across hundreds or thousands of machines simultaneously. Provided by the MapReduce (MR) programming paradigm, which executes data-localized map, shuffle, sort, and reduce operations across multi-node clusters.
Having established how HDFS handles block reads and writes and how MapReduce parallelizes key-value computations, the current curriculum focus shifts to two critical operational layers:
- Resource orchestration — YARN (Yet Another Resource Negotiator), the cluster operating system that allocates CPU, memory, and disk to competing jobs.
- Data ingestion — Sqoop (SQL to Hadoop) for bulk relational database imports, and Flume (streaming event ingestion) for continuous log collection.
These components bridge relational data stores and unstructured log streams with Hadoop storage and processing engines. Subsequent course modules progress to NoSQL database systems (such as HBase and Cassandra), followed by Apache Spark and distributed machine learning after the mid-semester evaluation. Although current class progression is officially one week behind the initial syllabus calendar, all core topics will be thoroughly covered.
5.1.2 Recap of MapReduce Execution Components and Coin-Counting Walkthrough
Think back to the classroom exercise. Imagine you are sitting in a lecture hall. The professor dumps a bag of mixed coins on your desk — aluminum, copper, silver, and gold. Your job: count how many of each type you have. Now imagine 30 other students doing the same with their own bags. That is MapReduce in physical form.
To contextualize YARN resource management, consider the physical mechanics of MapReduce execution established through the coin-counting classroom walkthrough. In that exercise, students were tasked with counting and tabulating mixed coin collections (aluminum, copper, silver, and gold coins) distributed across a classroom.
MapReduce Execution Pipeline — Component by Component
Each component in the pipeline corresponds to a physical step the students performed:
- RecordReader converts raw input splits (such as physical bags of coins or raw HDFS file blocks) into structured key-value pairs \((k_1, v_1)\), where the key represents a record offset and the value contains raw coin properties. In the classroom, this is the act of picking up a coin and looking at it.
- Mapper processes individual key-value pairs to emit intermediate key-value pairs \((k_2, v_2)\). In the coin example, each mapper inspects a coin and emits
(coin_type, 1)— for instance,(Aluminum, 1),(Copper, 1), or(Gold, 1). The mapper applies a function to each record independently, with no knowledge of other records.
- Combiner (Mini-Reducer) performs localized pre-aggregation directly on the mapper node prior to network transfer. A mapper holding five aluminum coins collapses five
(Aluminum, 1)pairs into a single intermediate summary(Aluminum, 5), drastically reducing inter-node network traffic. The combiner is an optional optimization — it runs the same logic as the reducer but only on the local mapper's output.
- Shuffle and Sort Phase collects intermediate output from all mappers, partitions records by key, routes identical keys to the same target reducer node, and sorts keys in ascending order. This is the most network-intensive phase of a MapReduce job. In the classroom analogy, imagine all students walking to the front of the room and sorting their aluminum coins into one pile, copper into another, and so on.
- Reducer aggregates intermediate values associated with each unique key to produce final output summaries \((k_3, v_3)\). Each reducer receives all values for a given key and produces the final answer — e.g.,
(Aluminum, 47),(Copper, 23), etc.
Choosing between single-reducer and multi-reducer topologies:
The choice depends on whether the computation requires global coordination:
- Single Reducer Topology — Required when a global aggregate or total ordering across all keys is necessary (such as identifying the overall top-selling product category or finding the global maximum coin count). All intermediate mapper outputs converge onto a single physical reducer node. This creates a bottleneck but guarantees a total order.
- Multi-Reducer Topology — Used when key groups can be processed completely independently (such as calculating separate coin subtotals for each metal type or computing department-wise sales). Each reducer independently handles a distinct partition of keys in parallel. The partitioner function (by default,
HashPartitioner) determines which reducer receives which key.
Scope: The combiner is not always safe to use. It works for commutative and associative operations like SUM, MAX, and MIN. It does not work for operations like computing a mean (average), because the average of averages is not the same as the average of all values. If you use a combiner with a non-associative operation, you will get wrong results.
5.1.3 Hive Architecture and SQL-to-MapReduce Query Translation
Why did Facebook build Hive? Their data analysts knew SQL, not Java. Hadoop only spoke Java MapReduce. Hive is the translator that lets SQL people talk to Hadoop.
Writing native MapReduce programs directly in Java requires managing low-level job configurations, custom serializable data types (Writable), and complex mapper-reducer classes. To democratize big data processing for data analysts familiar with relational database queries, enterprise engineering teams at Facebook developed Apache Hive.
Hive's Architectural Position
Hive sits on top of the Hadoop ecosystem as a data warehousing layer. It provides:
- HiveQL — a SQL-like declarative query language (heavily influenced by MySQL's dialect)
- Metastore — a central repository storing table schemas, column types, partition definitions, and HDFS location metadata
- Query Compiler — translates HiveQL into a Directed Acyclic Graph (DAG) of MapReduce jobs
- Execution Engine — submits the generated jobs to the cluster (MapReduce by default; Tez or Spark as alternatives)
Hive is not a traditional relational database. It does not support row-level updates or real-time queries. It is designed for batch analytics over large datasets stored in HDFS. A key architectural difference: traditional databases use schema on write (data is validated when loaded), while Hive uses schema on read (data is validated when queried). This makes Hive's load operation a fast file copy, but query-time parsing can be slower.
The compilation pipeline — from SQL to MapReduce:
When a user submits a HiveQL query, Hive compiles the SQL statement into a Directed Acyclic Graph (DAG) of underlying MapReduce jobs:
\[ \text{HiveQL Query} \longrightarrow \text{Query Parser / Compiler} \longrightarrow \text{Logical Plan} \longrightarrow \text{Physical MapReduce Job(s)} \]
The query engine parses the SQL syntax, converts relational operations into MapReduce mapper and reducer transformations, submits the generated job to the cluster, and returns the aggregated results to the user.
Worked Example: Translating a GROUP BY + ORDER BY query into MapReduce stages
Consider a SQL query designed to group and sort records by column:
SELECT department, SUM(sales) AS total_sales
FROM store_sales
GROUP BY department
ORDER BY total_sales DESC;
Hive translates this query into a three-stage MapReduce workflow:
Stage 1 — Grouping Mapper & Combiner: Mappers read rows from store_sales and extract (department, sales). Each mapper emits key-value pairs like (Electronics, 500), (Clothing, 200), (Electronics, 300). Local combiners pre-aggregate on the mapper node: (Electronics, [500, 300]) becomes (Electronics, 800) before the shuffle.
Stage 2 — Partitioned Reducers: Intermediate records shuffle to reducers partitioned by department. Each reducer computes the final SUM(sales) for its assigned department. After this stage, we have: (Electronics, 800), (Clothing, 200), (Grocery, 450), etc. — but they are in no particular order.
Stage 3 — Global Sorting Reducer: To execute the ORDER BY total_sales DESC clause across all departments, a single global reducer receives the department sub-totals, sorts them by total_sales in descending order, and writes the final ordered result to HDFS.
Sense-check: The three-stage structure makes sense because GROUP BY can be parallelized (Stage 2), but ORDER BY across all groups requires a single reducer to guarantee a total ordering (Stage 3). If the query only had GROUP BY without ORDER BY, Hive could skip Stage 3 entirely.
Exam note: Expect exam questions testing the explicit conversion of SQL queries (GROUP BY, JOIN, ORDER BY) into MapReduce mapper, shuffle, and reducer execution phases. Be prepared to draw the multi-stage diagram showing mapper output keys, combiner pre-aggregations, partitioned reducers, and a single global sorting reducer.
5.1.4 Student Questions and Answers
Q: If a MapReduce job requires computing department-wise sales totals alongside finding the single department with the absolute maximum sales, how many reducers should be configured?
A: Calculating department-wise sales sub-totals can be parallelized across multiple reducers (one per department or group of departments). However, identifying the single department with the global maximum sales requires a second convergence phase using a single reducer to compare the sub-totals from all departments. This is a two-phase design: multi-reducer for parallel aggregation, then a single reducer for the global comparison.
Why this seems confusing: A student might think "I can just use one reducer for everything." That works, but it sacrifices parallelism. The key insight is that finding a global maximum requires seeing all values at once, which forces a single-reducer bottleneck. Computing independent subtotals does not.
Q: Why did Facebook introduce Apache Hive when MapReduce was already available in Hadoop?
A: Native MapReduce requires writing low-level Java code, implementing custom key-value serialization (Writable interfaces), managing complex job configurations (JobConf), and compiling JAR files. Hive was created to provide a declarative SQL interface (HiveQL) that automatically translates SQL queries into optimized MapReduce jobs, allowing analysts to process data in HDFS without writing Java code.
The real-world trigger: At Facebook, data analysts outnumbered Java engineers by a wide margin. These analysts already knew SQL from working with MySQL and Oracle. Hive let them leverage existing skills against petabyte-scale Hadoop datasets. Today, Hive is used by most major Hadoop deployments as the primary SQL interface for batch analytics.
5.2 YARN Architecture, Component Decoupling, and Container Abstractions
5.2.1 Evolution from Hadoop 1.0 to Hadoop 2.0 and Module Decoupling
The bottleneck that forced a redesign. In Hadoop 1.0, one single process — the JobTracker — had to manage every task on every node in the cluster. Imagine a factory where one manager personally oversees every worker on every assembly line. At 4,000 workers, that manager collapses under the load. That is exactly what happened to Hadoop 1.0.
In Hadoop 1.0 architecture, cluster resource management and job lifecycle tracking were tightly coupled inside the MapReduce framework itself. A single master daemon called the JobTracker managed both cluster resource allocation and the execution monitoring of every individual map and reduce task across slave TaskTrackers.
This monolithic architecture suffered from severe structural limitations:
- Scalability Bottlenecks: A single JobTracker could not scale beyond approximately 4,000 nodes or 40,000 concurrent tasks because tracking individual task heartbeats (one per second per task) overwhelmed master memory and CPU resources. The reference text (Tom White, Hadoop: The Definitive Guide) confirms these exact limits.
- Single Point of Failure (SPOF): If the JobTracker crashed, all active jobs failed immediately. There was no mechanism for another daemon to take over, because the rapidly changing in-memory state (each task's status, progress counters, and intermediate results) was too complex to replicate.
- Inflexible Workload Support: Clusters could run only Java MapReduce jobs. Non-MapReduce applications — such as graph processing (e.g., Pregel-style algorithms), iterative machine learning (which needs to loop over data many times), real-time streaming (Storm, Samza), or native C/C++ executables — could not run directly on the cluster infrastructure. Every computation had to be shoehorned into the map-shuffle-reduce paradigm.
The YARN solution — separating "who gets resources" from "how work is done"
To resolve these architectural flaws, Hadoop 2.0 completely decoupled resource management from the MapReduce processing model by introducing YARN (Yet Another Resource Negotiator). The core idea is simple but powerful:
- Old model (Hadoop 1.0): The JobTracker handled both scheduling (matching tasks with machines) and monitoring (tracking progress, restarting failed tasks, maintaining counters). One process, two very different jobs.
- New model (YARN): These responsibilities are split into separate entities:
- The Resource Manager handles scheduling only (allocating containers to applications).
- A per-job Application Master handles monitoring only (tracking that specific job's tasks).
This "divide and conquer" approach made high availability tractable: provide HA for the Resource Manager, then provide HA for each application's Application Master independently.
YARN acts as a generic, multi-tenant cluster operating system that manages physical resources (CPU, memory, storage, network) independently of application frameworks. Under Hadoop 2.0 and 3.0 (which added YARN Timeline Service v2 for enhanced metrics logging), MapReduce became just one of many application engines running on top of YARN, alongside Apache Spark, Apache Tez, and custom C/C++/Python execution scripts.
Component mapping from Hadoop 1.0 to YARN:
| Hadoop 1.0 (MapReduce 1) | YARN (Hadoop 2.0+) |
|---|---|
| JobTracker | Resource Manager + Application Master + Timeline Server |
| TaskTracker | Node Manager |
| Slot (fixed map/reduce slots) | Container (flexible, fine-grained resources) |
Pitfall — Confusing APIs with implementations: The "old" and "new" MapReduce APIs are not the same thing as MapReduce 1 and MapReduce 2 implementations. The APIs are client-side features that determine how you write MapReduce programs. The implementations are different ways of running those programs. Both old and new APIs work on both MapReduce 1 and 2. Don't mix them up on the exam.
5.2.2 Core Master-Slave Components: Resource Manager and Node Manager
YARN adopts a hierarchical master-slave architecture that mirrors the storage layer topology of HDFS:
\[ \text{Storage Layer: NameNode (Master)} \longleftrightarrow \text{DataNode (Slave)} \] \[ \text{YARN Compute Layer: Resource Manager (Master)} \longleftrightarrow \text{Node Manager (Slave)} \]
The Resource Manager resides on the master node to control global resource distribution, while Node Managers run on slave nodes to monitor local node health and manage physical compute containers.
Resource Manager (RM) — The central master authority operating on the cluster master machine. The RM maintains complete visibility over total cluster capacity, active slave nodes, memory allocations, virtual CPU cores (VCores), and tenant submission queues. It handles client job submissions and enforces resource allocation policies without directly managing individual task execution.
The RM internally splits its work between two components:
- Application Manager — handles client connection handshakes, validates job submission parameters, negotiates the initial container for spawning the Application Master, and monitors AppMaster health (restarting it on a different node if its host machine fails).
- Scheduler — purely responsible for allocating resources across competing applications based on configured capacity or fairness constraints. The Scheduler performs no task monitoring, status tracking, or fault recovery — its CPU loops stay dedicated strictly to resource allocation.
Node Manager (NM) — A slave daemon running on every worker node in the cluster. The NM monitors local machine hardware metrics (CPU utilization, RAM consumption, disk health), transmits periodic heartbeat reports (by default, once per second) to the Resource Manager, and executes container lifecycle commands (launching, monitoring, and terminating processes). Each heartbeat carries information about running containers and available resources, making it a potential scheduling opportunity.
5.2.3 Container Abstraction and Linux Cgroups Integration
What is a container? Think of a container as a reserved parking spot on a busy street. The spot has a size (memory), a type of vehicle restriction (CPU cores), and a maximum stay (execution time). You can park different cars there at different times, but the spot itself has fixed boundaries.
A Container in YARN represents the fundamental, atomic unit of compute and memory resource allocation leased to an application. It encapsulates a specific slice of a physical slave machine's capacity that cannot be broken down further:
\[ \text{Container} = \{\text{VCores}, \text{RAM (MB)}, \text{Disk I/O Bounds}, \text{Network Bandwidth (MBps)}\} \]
A container binds specific quantities of CPU cores, memory, disk I/O, and network bandwidth into an isolated execution sandbox on a slave node.
Key characteristics of YARN containers:
- Resource Boundaries: Configured at the cluster level via Linux Control Groups (Cgroups). For example, a basic container definition might specify 1 VCore and 2 GB of RAM, or 4 VCores and 8 GB of RAM. Even if an application task requires only a fraction of CPU or memory, YARN allocates the full minimum container boundary.
- Dynamic Resource Enforcement: Modern YARN containers enforce multi-dimensional limits using Cgroups, restricting process memory usage, disk write rates, and network bandwidth consumption. This prevents one rogue process from starving its neighbors.
- Container Reusability: Containers are generic and reusable sandboxes. Once a container process completes a mapper task, the same container workspace can be reused to execute subsequent map or reduce operations, eliminating process initialization overhead. This is a significant improvement over Hadoop 1.0's fixed "slots," where a map slot sat idle while reduce tasks waited.
Pitfall — Atomic allocation wastes resources. A common misconception: if an application needs only 1 VCore and 512 MB of RAM, but the minimum container is 4 VCores and 2 GB, YARN allocates the full container. The unused 3 VCores and 1.5 GB of RAM sit idle inside that container — they cannot be given to another application. This is the trade-off for simplicity: YARN avoids the complexity of fractional resource allocation at the cost of some wasted capacity.
5.2.4 Container Launch Context and Application Master Architecture
Launching a process inside a YARN container requires an explicit specification bundle called the Container Launch Context (CLC). The CLC contains three categories of information:
- Environment Variables & Local Resources: Specifies environment paths, command-line parameters, required library dependencies, JAR files, and remote resource URIs needed by the application binary.
- Security Credentials & Tokens: Encapsulates authentication keys and security tokens issued by the Resource Manager, ensuring that only authorized application processes can execute within slave containers. This is a critical security boundary — without valid tokens, a container cannot communicate with other YARN services.
- Execution Commands: Contains the exact OS-level launch command (such as running a Java class
main()method, executing a compiled C++ binary, or invoking a Python script).
The Application Master — YARN's key architectural innovation
To prevent the master Resource Manager from becoming overloaded with task-level monitoring across thousands of containers (exactly the problem that killed the JobTracker), YARN delegates application lifecycle management to a per-application master process called the Application Master (AppMaster / AM).
Key design properties:
- Per-Job Instance: A dedicated AppMaster process is spawned inside a standard worker container on one of the slave nodes for each submitted job. If ten clients submit ten independent jobs, YARN launches ten separate AppMaster processes across the worker nodes. This is fundamentally different from the JobTracker, which managed all jobs centrally.
- AppMaster Responsibilities:
- Computes overall application resource requirements and requests necessary worker containers from the Resource Manager.
- Receives container leases (tokens) from the Resource Manager and contacts individual slave Node Managers to launch task containers.
- Tracks individual task progress, handles task retries upon failure, and orchestrates task convergence (such as coordinating mapper output shuffle to reducers).
- Returns real-time status updates directly to the client, completely bypassing the Resource Manager during runtime execution.
- Client communication shift: Once the RM spawns the AppMaster, the client talks directly to the AppMaster for progress updates, not to the RM. This keeps the RM lightweight and focused on resource allocation.
5.2.5 Student Questions and Answers
Q: Why is YARN described as a generic resource manager compared to Hadoop 1.0's JobTracker?
A: Hadoop 1.0's JobTracker was hardcoded strictly for Java MapReduce workflows. It understood map tasks and reduce tasks, but nothing else. YARN decouples resource allocation from the application execution model, allowing the cluster to run MapReduce, Spark, Tez, compiled C/C++ executables, Python scripts, or Ruby applications side by side on the same hardware. In YARN's view, they are all just "applications requesting containers."
Analogy: The JobTracker was like a restaurant that only served one dish. YARN is the building itself — it provides kitchen space, electricity, and water to any restaurant (application) that wants to set up inside.
Q: What happens if an application requires only 1 CPU core and 512 MB of RAM, but the cluster's minimum container configuration is 4 cores and 2 GB of RAM?
A: YARN allocates resources in atomic container units. The application will be granted a full container of 4 cores and 2 GB of RAM. The unneeded resources remain allocated to that container but underutilized, as YARN cannot divide resources below the configured container baseline.
Why this happens: YARN uses Linux Cgroups to enforce resource isolation. A Cgroup defines a fixed resource boundary. Splitting a Cgroup dynamically would break the isolation guarantees. The administrator's job is to choose container sizes that balance utilization against management overhead.
5.3 YARN Execution Flow, Security Handshake, and RM Web Console Administration
5.3.1 Detailed 10-Step YARN Job Lifecycle Walkthrough
Why this matters. The 10-step YARN job lifecycle is the single most important operational sequence in Hadoop 2.x compute. Every distributed engine that runs on YARN — MapReduce, Spark, Tez, and custom binaries — follows this exact protocol. If you can draw this flow from memory and explain why each step exists, you can diagnose any job submission failure in production.
When a client application submits a job to a YARN-enabled Hadoop cluster, the system executes a strict multi-stage protocol across the master Resource Manager, slave Node Managers, the Application Master, and task containers. Unlike a monolithic system where one daemon controls everything, YARN distributes responsibility so that no single component becomes a bottleneck.
The 10-step lifecycle — from submission to teardown
[Client] ---> (1. Submit Job) ---> [Resource Manager (RM)]
|
(2. Select Slave)
v
[App Master] <--- (4. Launch AM) <--- [Node Manager 1 (NM1)]
|
+---> (5. Status Updates) ---> [Client]
|
+---> (6. Request Containers) ---> [Resource Manager]
| |
| (7. Grant Leases)
v v
[Node Manager 2 & 3] <--- (8. CLC + Tokens) ---+
|
+---> (9. Launch Mappers/Reducers) ---> [Worker Containers]
|
+---> (10. Heartbeats & Teardown) ---> [Resource Manager]
Step 1 — Job Submission. The client application connects to the master node and submits the application job package to the Resource Manager. This package includes the JAR file (or application binary), configuration settings, and the initial Container Launch Context (CLC) details for the Application Master. The submission arrives at the RM's IPC port (port 8032). Internally, the RM's Application Manager component receives the submission, validates the requesting user's credentials (Kerberos principal or delegation token), checks queue ACLs to confirm the user is permitted to submit to the target queue, and registers the application in its metadata store.
Step 2 — Initial Scheduling. The Resource Manager's internal Scheduler component identifies an eligible, under-utilized slave node (for example, Node Manager 1, abbreviated NM1). The Scheduler considers current memory and VCore availability, node health status (reported via heartbeats), and configured scheduling policy (FIFO, Fair, or Capacity). The Application Manager records the application's initial state as ACCEPTED.
Step 3 — AppMaster Allocation Request. The Resource Manager issues a command to NM1 instructing it to allocate a standard container for the job's Application Master. This first container is special: it hosts the AM process that will manage the entire application lifecycle. The RM calculates the container's resource requirements from the CLC submitted in Step 1 — typically a modest allocation (for example, 1 VCore, 1–2 GB RAM) because the AM itself does minimal computation.
Step 4 — AppMaster Initialization. NM1 launches the container, initializes the Application Master process inside it, and registers the AppMaster with the Resource Manager. During registration, the AM and RM exchange a security handshake: the RM issues delegation tokens that the AM will use later when contacting other Node Managers. At this point, the application state transitions from ACCEPTED to RUNNING.
Step 5 — Context Delegation to Client. The Resource Manager returns the network address and container context of the newly spawned AppMaster directly to the client. From this moment forward, the client bypasses the Resource Manager and polls the AppMaster directly for job progress, logs, and completion status. This is a deliberate design choice: it keeps the RM free from thousands of per-second status polling requests.
Step 6 — Container Resource Request. The AppMaster evaluates the job's input splits (computed from the input data stored in HDFS), calculates the required number of worker containers, and submits resource requests to the Resource Manager. Each request specifies:
- Memory requirements (in MB) and VCores
- Data locality preferences (node-specific, rack-specific, or off-rack)
- Priority and deadline constraints
Locality preferences are critical for performance. A request for a node-local container means the AM wants the container on the exact machine that holds the HDFS data block. If that machine is unavailable, the AM relaxes to rack-local (same rack, different machine), and finally to off-rack (any available machine). Waiting briefly for a node-local allocation — a technique called delay scheduling — dramatically improves throughput by avoiding remote reads over the network.
Step 7 — Container Lease Granting. The Resource Manager's Scheduler evaluates cluster capacity against pending requests, allocates container leases on target slave nodes (for example, NM2 and NM3), and returns secure container lease tokens to the AppMaster. Each lease is a time-limited authorization: if the AM does not use the container within the lease period (typically several minutes), the RM reclaims it for other applications.
Step 8 — Worker Container Launch Handshake. The AppMaster contacts NM2 and NM3 directly, transmitting the Container Launch Context (CLC) along with the RM-issued security tokens. This is a critical security boundary: the NM will not launch a container unless the tokens are valid and were issued by the RM. The CLC contains environment variables, library dependencies, JAR files, and the exact OS-level command to execute (such as launching a Java main() method or a compiled C++ binary).
Step 9 — Task Execution. NM2 and NM3 validate the security tokens with the Resource Manager and launch the designated worker containers. Inside each container, the actual computation runs — mappers, reducers, Spark executors, or any other application logic. Each container operates within its resource boundaries enforced by Linux Cgroups (memory limits, CPU shares, disk I/O throttling). If a container exceeds its memory limit, the Linux kernel's OOM (Out of Memory) killer terminates the process, and the NM reports the failure back to the AM.
Step 10 — Heartbeat Monitoring and Job Teardown. Worker containers continuously send execution status heartbeats to the AppMaster (typically once per second per task). These heartbeats carry progress percentages, counter values, and health indicators. If a heartbeat is missing for a configurable timeout period, the AM declares the task failed and requests a replacement container from the RM. Upon successful task completion, the AM collects final outputs, releases all container leases back to the RM, notifies the client of job completion (with final counters and output locations), and terminates itself. The RM marks the application as FINISHED and reclaims all resources.
5.3.2 Internal Resource Manager Components: Application Manager vs Scheduler
The Resource Manager daemon splits its operational workload between two primary internal components. Understanding the boundary between them is essential: the Application Manager handles application lifecycle, while the Scheduler handles resource allocation. These two concerns must never mix, because combining them (as the JobTracker did) creates the scalability bottleneck that forced the Hadoop 2.0 redesign.
Application Manager (App Mgr). The Application Manager is the gateway through which all client applications enter the YARN cluster. Its responsibilities include:
- Client connection handshake: When a client submits a job via the RM IPC port (
8032), the Application Manager accepts the connection, authenticates the user (via Kerberos tickets or delegation tokens), and validates the submission parameters (valid queue name, sufficient resource request within queue limits, correct CLC format). - Initial container negotiation: The Application Manager negotiates with the Scheduler to obtain the first container on a slave node for the Application Master process. This negotiation includes specifying the AM's memory and VCore requirements and any placement preferences.
- AppMaster lifecycle monitoring: After the AM is launched, the Application Manager monitors its health via periodic registration heartbeats. If the AM's host machine fails (hardware crash, network partition, or OS panic), the Application Manager detects the missed heartbeat, marks the AM as lost, and requests a new container from the Scheduler to restart the AM on a different healthy node. This is a form of per-application high availability — each application's AM can fail and recover independently without affecting other applications on the cluster.
- Application state management: The Application Manager maintains the state machine for each application:
NEW → SUBMITTED → ACCEPTED → RUNNING → FINISHED(orFAILED/KILLED). This state is exposed via the RM Web UI on port8088.
Scheduler. The Scheduler is the pure resource allocation engine. Its sole job is to divide the cluster's total memory and VCore capacity among competing applications and tenant queues. Key design properties:
- No task monitoring: The Scheduler does not track individual task progress, restart failed tasks, or collect execution counters. Those responsibilities belong to the Application Master. This strict separation ensures the Scheduler's CPU loops remain dedicated to allocation decisions, allowing it to handle tens of thousands of concurrent container requests per second.
- Pluggable policies: The Scheduler supports swappable scheduling policies — FIFO (simple queue), Fair Scheduler (equal sharing with dynamic rebalancing), and Capacity Scheduler (hard SLA partitions). The administrator selects the policy at cluster configuration time, and the Scheduler enforces it.
- Delay scheduling optimization: When the Scheduler cannot satisfy a node-local request immediately (because the target node is busy), it can hold the request briefly (typically 1–2 seconds) rather than immediately assigning an off-rack container. This small delay dramatically increases the fraction of node-local allocations, reducing network I/O and improving overall job throughput. Studies show that even a 2-second delay can improve locality from 50% to over 90%.
- Capacity accounting: The Scheduler maintains real-time tallies of allocated and available resources per node, per rack, and per queue. Each NM heartbeat updates these tallies, making every heartbeat a scheduling opportunity.
Why the split matters for scalability. In Hadoop 1.0, the single JobTracker handled both lifecycle management and task scheduling, which meant a single CPU thread was responsible for everything. YARN's split allows the Scheduler to run in a tight, optimized loop while the Application Manager handles the slower, I/O-bound lifecycle operations. On a cluster with 10,000 nodes and 100,000 concurrent tasks (YARN's design target), this separation is not optional — it is a hard architectural requirement.
5.3.3 Asynchronous Execution, Multi-Task Convergence, and Job Configuration
In real-world big data workflows, tasks running inside containers execute asynchronously. Not all mappers start at the same time, and not all mappers finish at the same time — the duration of each task depends on the size and characteristics of its input split, the computational complexity of the logic, and the hardware speed of the host node.
The car manufacturing assembly line analogy. Imagine a car factory where engine fabrication, door stamping, and chassis welding are performed by independent stations on the assembly line. Each station operates at its own speed: engine casting takes 45 minutes, door stamping takes 12 minutes, and chassis welding takes 30 minutes. No station waits for another to start — they all begin as soon as their raw materials arrive. The finished parts converge at the final assembly station only when all required pieces are ready.
In YARN, the same principle applies. Each mapper starts as soon as its container is allocated and its input split is available. Mapper A might finish in 3 seconds (small split, simple logic), while Mapper B takes 47 seconds (large split, complex aggregation). The shuffle-and-sort phase cannot begin until all mappers have completed, because every reducer needs intermediate key-value pairs from every mapper. This convergence point is analogous to the final assembly station — it is the bottleneck that determines overall job completion time.
This is why straggler tasks (the slowest mappers) dominate job runtime. A single slow mapper on an overloaded node can delay the entire job, even if 99 other mappers finished in seconds. YARN addresses this with speculative execution: if a task is running significantly slower than the median, YARN launches a duplicate copy on another node and uses whichever finishes first.
Coordinating asynchronous tasks into a unified job outcome requires explicit convergence logic:
- Mapper-to-Reducer Convergence: Asynchronous mapper outputs must be collected, sorted, and shuffled before reducer execution begins. The shuffle phase transfers intermediate key-value pairs from mapper output directories (stored on local disk) across the network to the appropriate reducers. Each reducer pulls data from all mappers, sorts it by key, and groups values by key for the reduce function.
- Job Configuration (
JobConf): Written in Java or specified via configuration XML,JobConfdefines the exact orchestration parameters: - Number of mapper tasks and reducer tasks (
job.setNumReduceTasks(N)). - Custom Mapper and Reducer class bindings.
- Convergence rules: Specifying whether a single reducer is required (global aggregation) or multiple parallel reducers are permitted (partitioned aggregation).
How different engines request containers — MapReduce vs Spark.
Not all frameworks request containers the same way. The difference has major implications for resource utilization and scheduling:
- MapReduce — Two-Phase Container Request. MapReduce requests containers in two distinct waves. First, it requests all mapper containers upfront (one per input split). The AM waits for mappers to complete. Only after the shuffle phase begins does the AM request reduce containers in a second wave. This means the cluster must have capacity available at two different points in time. If the cluster is heavily loaded when reducers are needed, the job stalls waiting for free containers.
- Spark — Upfront Fixed Executor Request. Spark takes a different approach: the Spark driver (which runs inside the Application Master) requests a fixed number of executor containers upfront and holds them for the entire application session. Each executor can run many tasks sequentially or in parallel (via threads within the JVM). Spark does not release and re-acquire containers between stages — it keeps them warm. This design favors iterative algorithms (such as machine learning training loops) that benefit from cached intermediate data in executor memory.
- Failure Handling. Both frameworks handle failed tasks by requesting replacement containers from the RM. If a mapper fails, MapReduce requests a new container on a different node and re-runs the mapper on the same input split. Spark similarly re-launches failed stages. The key difference is that MapReduce's two-phase approach can fail at the transition boundary (mappers done but no reducers available), while Spark's upfront approach avoids this but holds idle resources during stage boundaries.
5.3.4 YARN Administration Ports and Resource Manager Web UI Console
Cluster administrators monitor YARN performance using command-line tools and web management consoles listening on standard HTTP and IPC ports:
| YARN Daemon / Service | Port Number | Description and Operational Function |
|---|---|---|
| Resource Manager Web UI | 8088 |
Main HTTP web console displaying cluster metrics, total/allocated memory, VCores usage, active nodes, running/completed applications, and queue allocations. This is the primary dashboard for cluster health monitoring. |
| Resource Manager IPC / Admin | 8032 |
Inter-Process Communication (IPC) port used by clients and command-line utilities (such as Sqoop) to submit jobs to the Resource Manager. This is the port that client applications connect to programmatically via Hadoop RPC. |
| Resource Tracker Port | 8030 |
Internal IPC port used by slave Node Managers to send heartbeats and resource availability reports to the Resource Manager. Every NM heartbeat arrives on this port. |
| Node Manager Web UI | 8042 |
HTTP web interface running on each slave node to display local container logs, node health metrics, and process execution details. |
Exam note: Memorize the following two ports — they are the most frequently tested in diagnostic and troubleshooting questions:
- Port
8088— Resource Manager Web UI (HTTP). This is where administrators view cluster status, running applications, and queue metrics in a browser. - Port
8032— Resource Manager IPC Submission Port. This is where client tools (Sqoop,hadoop jar, Spark submit) connect to submit jobs. If this port is unreachable, no job can be submitted.
Additional ports worth noting: 8030 (NM-to-RM heartbeat) and 8042 (NM Web UI on each worker node).
Using the RM Web UI (http://master:8088). The RM Web UI provides several critical views for administrators:
- Cluster Overview: Total memory, allocated memory, total VCores, allocated VCores, active nodes, decommissioned nodes, and lost nodes.
- Applications Tab: Lists all running, pending, finished, failed, and killed applications with their application ID, user name, queue name, start time, elapsed time, and final status.
- Scheduler Tab: Visualizes the queue hierarchy, per-queue resource allocation percentages, pending applications per queue, and active container counts.
- Node Status: Per-node breakdown of available memory, VCores, running containers, and health status.
5.3.5 Live Troubleshooting Scenario: YARN Connection Failures during Tool Execution
Example — Sqoop import fails with Connection refused on port 8032.
During live cluster operations, executing a client tool (such as Sqoop or a MapReduce job) may trigger a fatal connection exception:
ERROR tool.ImportTool: Import failed: java.net.ConnectException:
Call From master_host/127.0.0.1 to localhost:8032 failed on connection exception:
java.net.ConnectException: Connection refused
Root Cause Analysis and Step-by-Step Resolution:
- Diagnosis: The client application started successfully and initialized local execution. However, when it attempted to submit the underlying MapReduce job to the YARN Resource Manager IPC interface at
localhost:8032, the connection was refused. TheConnection refusederror (as opposed to a timeout) means no process is listening on that port at all — the RM daemon is not running.
- Checking System Daemons: Inspecting HDFS via
jpsshowsNameNodeandDataNodeprocesses are active, meaning file storage is online. However,ResourceManagerandNodeManagerdaemons are missing because YARN was not started. This is a classic operator error: starting only the storage layer without the compute layer.
- Remediation Command: Execute the YARN startup script from the Hadoop
sbindirectory:
start-yarn.sh
This script starts the Resource Manager on the master node and Node Managers on all configured slave nodes.
- Verification: Running
jpsnow confirmsResourceManageris listening on port8032and port8088, andNodeManageris active on worker nodes. Re-running the client tool completes the job submission successfully.
Key insight: HDFS and YARN are independent daemons that must be started separately. The start-all.sh convenience script starts both, but start-dfs.sh and start-yarn.sh are separate scripts that must each be invoked if you are starting services manually.
Pitfall — HDFS running ≠ YARN running.
This is one of the most common mistakes made by students and new cluster operators. Because HDFS (storage) and YARN (compute) share the same master node and the same Hadoop configuration directory, it is easy to assume that starting HDFS automatically starts YARN. It does not.
start-dfs.shstartsNameNode,DataNode,SecondaryNameNode, and (if configured)JournalNodes.start-yarn.shstartsResourceManagerandNodeManagers.start-all.shstarts both — but it is deprecated in newer Hadoop versions and is effectively just the two scripts above combined.
The symptom: Tools like Sqoop, Hive-on-MR, or hadoop jar connect to port 8032 to submit jobs. If the ResourceManager is not running, the connection is refused and the job fails immediately — even though HDFS reads and writes work perfectly. Students often spend significant debugging time checking HDFS health (which is fine) before realizing YARN was never started.
The fix: Always verify that both daemons are running before executing any compute workload:
jps # Should show ResourceManager and NodeManager in addition to NameNode/DataNode
5.3.6 Application Lifespan, Scalability, and Availability
YARN applications are not all short-lived batch jobs. The framework supports a spectrum of application lifespans:
- Short-lived applications: A single MapReduce job or Sqoop import that runs for seconds to minutes, then terminates and releases all containers.
- Per-session applications: A Spark shell or Tez session that starts a set of executors and reuses them across multiple interactive queries. The session may last hours, holding containers even between queries.
- Long-running applications: A shared proxy service (such as a Spark Streaming job or a HiveServer2 instance) that runs indefinitely and acts as a gateway for multiple users. These applications must handle their own internal multi-tenancy.
Scalability design targets. YARN was designed to manage clusters of up to 10,000 nodes and schedule 100,000 concurrent tasks. Achieving this requires the Scheduler to remain decoupled from task-level monitoring (handled by per-application AMs) and the NM heartbeat protocol to be lightweight (each heartbeat carries a compact summary of node state, not individual task details).
High availability — divide and conquer. YARN achieves availability through two independent mechanisms:
- Resource Manager HA: An active/standby RM pair with automatic failover. If the active RM crashes, the standby RM takes over within seconds using shared state stored in ZooKeeper.
- Application Master HA: Each application's AM can be restarted independently on a different node if its host fails. The AM checkpoint mechanism saves recovery metadata so the new AM instance can resume from the last checkpoint rather than restarting from scratch.
5.3.7 Student Questions and Answers
Q: Why does the client talk directly to the Application Master instead of routing all progress queries through the Resource Manager?
A: Routing all status queries through the Resource Manager would create a severe bottleneck on large clusters with thousands of running tasks. Consider a cluster running 1,000 applications, each with 100 tasks, each reporting progress once per second — that is 100,000 progress queries per second hitting a single daemon. By delegating client status polling directly to the per-application AppMaster, the Resource Manager stays lightweight and focused solely on resource allocation. Each AM handles only its own tasks' status queries, distributing the monitoring load across the cluster's worker nodes.
Scalability math: With \(N\) applications, each averaging \(T\) tasks, and each task reporting once per second, the centralized model would generate \(N \times T\) queries per second at the RM. In the decentralized model, each AM handles only \(T\) queries, and the RM handles only \(N\) registration heartbeats — a reduction factor of \(T\) (often 100–1,000x).
Q: If HDFS is running normally, why does a Sqoop import fail with a Connection refused error on port 8032?
A: HDFS handles file storage daemons (NameNode and DataNode), but Sqoop executes its import by launching a MapReduce job on YARN. Port 8032 is the Resource Manager IPC port used for job submission. If YARN daemons were not started via start-yarn.sh, the Resource Manager process is offline and no process is listening on port 8032, causing the client's TCP connection attempt to be refused by the operating system. HDFS and YARN are independent daemon groups that must be started separately — running one does not imply the other is running.
5.4 YARN Scheduling Algorithms, Multi-Tenancy Quotas, and Resource Allocation
Why Scheduling Matters: In a multi-tenant Hadoop cluster, dozens of teams submit jobs simultaneously — a data scientist running an interactive Spark shell, an ETL pipeline processing nightly batch loads, and an analyst querying a Hive warehouse — all competing for the same finite pool of memory and CPU cores. The scheduler is the single component that decides who gets what, when, and for how long. A poor scheduling policy leads to starvation (small jobs waiting hours), waste (cluster sitting idle because no single job can fill it), or chaos (one team consuming 100% of resources). Every production cluster administrator must understand scheduling trade-offs, and every exam on Big Data Analytics will test your ability to compare these policies, compute fair-share allocations, and identify when each scheduler is appropriate.
5.4.1 FIFO Scheduler Mechanics and Multi-Tenant Limitations
FIFO Scheduler is the simplest scheduling policy in YARN. Applications are placed into a single monolithic queue ordered strictly by arrival timestamp. The Resource Manager allocates all available cluster memory and VCores to the job at the head of the queue, and that job retains exclusive control of the entire cluster until it completes. Only then does the next job in line receive any resources.
Operational Logic:
- A job is submitted and appended to the tail of the single FIFO queue.
- The Resource Manager examines the job at the head of the queue.
- If the cluster has sufficient resources, the head job receives containers across all available nodes.
- The job monopolizes the cluster — no other job receives any resources until the head job finishes.
- Once the head job completes, the next job in the queue becomes the new head and the cycle repeats.
The "Small Job Blocked Behind Large Job" Problem:
Consider this real-world scenario that illustrates why FIFO is unsuitable for shared production clusters:
| Time | Event |
|---|---|
| 9:00 AM | A large 4-hour ETL batch job (Job A) is submitted. It receives 100% of cluster resources. |
| 9:05 AM | A VP submits a 10-second SQL query (Job B) for an executive dashboard. |
| 9:05 AM – 1:00 PM | Job B sits in the queue receiving zero resources. The VP's dashboard shows a spinning loader for 4 hours. |
| 1:00 PM | Job A finally completes. Job B now receives all resources and finishes in 10 seconds. |
The critical insight is that Job B required only 10 seconds of compute time, yet it experienced a 1,440x latency amplification (4 hours vs. 10 seconds) purely because of its position in the queue. In a production environment, this behavior is unacceptable — executive stakeholders expect sub-second SLA responses, not 4-hour waits.
Why FIFO Exists Despite Its Limitations:
- Simplicity: Zero configuration overhead — no weights, no queues, no policies to tune.
- Deterministic ordering: Jobs execute in exactly the order they arrive, making debugging and auditing straightforward.
- Single-user clusters: For a single data scientist running jobs sequentially on a personal development cluster, FIFO is perfectly adequate since there are no competing tenants.
- Historical context: FIFO was the default scheduler in early Hadoop 1.x (MapReduce v1) before multi-tenancy requirements drove the development of Fair and Capacity schedulers.
5.4.2 Fair Scheduler Mechanics, Dynamic Rebalancing, and Cloudera Defaults
Fair Scheduler (the default in Cloudera's Hadoop distribution) assigns resources dynamically so that all running applications receive an approximately equal share of cluster capacity over time. Unlike FIFO, it does not let a single job monopolize the cluster — instead, it continuously rebalances allocations as jobs arrive and depart.
Dynamic Rebalancing in Detail:
The Fair Scheduler operates on a principle of continuous reallocation. Here is how it works step by step:
- Single-job scenario: When only one job (Job A) is running, it receives 100% of the cluster — the Fair Scheduler does not artificially limit a lone job.
- Second job arrives: When Job B is submitted, the scheduler detects that two jobs now share the cluster. It identifies containers belonging to Job A that are running on TaskTrackers where Job B also needs slots, and preempts (kills) some of Job A's containers. Those freed resources are immediately reallocated to Job B. The result: each job now holds approximately 50% of cluster resources.
- Third job arrives: The process repeats. Resources are further divided so each job holds roughly 33.3% of the cluster.
- Job completes: When Job A finishes, its containers are released. The scheduler immediately redistributes those resources to the remaining jobs (B and C), bringing each up to approximately 50% again.
This dynamic rebalancing means that a short job submitted mid-way through a long-running batch will still complete in a reasonable timeframe — it doesn't have to wait for the long job to finish.
Idle Capacity Borrowing: When a department's queue has no active jobs (e.g., the Marketing team runs nothing on weekends), the Fair Scheduler temporarily lends Marketing's unused capacity to other active queues (e.g., Data Science). When Marketing submits a new job on Monday morning, the scheduler reclaims borrowed capacity from other queues, gradually returning Marketing to its configured fair share. This happens transparently — no manual intervention is required.
Fair Scheduler Policy Options: The Fair Scheduler supports multiple allocation policies that can be configured on a per-queue basis:
- Fair sharing (default): Each application gets an equal share of resources.
- FIFO within a queue: Jobs inside a single queue execute in arrival order, but different queues still share fairly across each other.
- Dominant Resource Fairness (DRF): When clusters have multiple resource dimensions (CPU + memory), DRF ensures fairness across both dimensions simultaneously (see Section 5.4.7).
Preemption Support: The Fair Scheduler can proactively kill containers belonging to queues that are consuming more than their fair share, freeing resources for starved queues. This is particularly important for ensuring SLA compliance — a low-priority batch job holding excess resources will have its containers killed in favor of a latency-sensitive interactive query that has fallen below its fair share.
5.4.3 Multi-Tenant Queue Hierarchies, Weights, and Quota Mathematical Formulations
Enterprise Hadoop clusters organize users into hierarchical queues that mirror corporate department structures. The Fair Scheduler distributes cluster capacity among these queues according to configured relative integer weights. A queue with weight 4 receives twice the resources of a queue with weight 2, and a queue with weight 0 receives nothing while others are active.
Mathematical Formulation of Queue Resource Percentages:
Let \(Q = \{q_1, q_2, \dots, q_K\}\) be the set of top-level tenant queues, where each queue \(q_k\) is assigned a configurable relative integer weight \(w_k > 0\). The percentage share of total cluster resources \(R(q_k)\) allocated to queue \(q_k\) is defined as:
\[ R(q_k) = \frac{w_k}{\sum_{j=1}^{K} w_j} \times 100\% \]
In words: the resource percentage of a specific queue equals its individual weight divided by the sum of all queue weights, multiplied by one hundred percent.
Worked Computation Example — Three Department Queues:
Consider a cluster with three departmental queues configured with the following relative weights:
| Queue | Symbol | Weight |
|---|---|---|
| Sales | \(q_1\) | \(w_1 = 3\) |
| Marketing | \(q_2\) | \(w_2 = 4\) |
| Data Science | \(q_3\) | \(w_3 = 13\) |
Step 1 — Calculate Total Weight Sum: \[ \sum_{j=1}^{3} w_j = w_1 + w_2 + w_3 = 3 + 4 + 13 = 20 \]
Step 2 — Compute Individual Queue Resource Allocations:
- Sales Queue Share:
\[ R(\text{Sales}) = \frac{w_1}{\sum w_j} \times 100\% = \frac{3}{20} \times 100\% = 15\% \]
- Marketing Queue Share:
\[ R(\text{Marketing}) = \frac{w_2}{\sum w_j} \times 100\% = \frac{4}{20} \times 100\% = 20\% \]
- Data Science Queue Share:
\[ R(\text{Data Science}) = \frac{w_3}{\sum w_j} \times 100\% = \frac{13}{20} \times 100\% = 65\% \]
Verification: \(15\% + 20\% + 65\% = 100\%\) ✓
If the cluster has 500 GB of memory and 200 VCores total:
- Sales receives: 75 GB memory, 30 VCores
- Marketing receives: 100 GB memory, 40 VCores
- Data Science receives: 325 GB memory, 130 VCores
Sub-Queue Hierarchy Breakdown:
Queues can contain nested sub-queues, forming a tree structure that mirrors organizational hierarchies. This is configured using parent-child relationships in the Fair Scheduler XML configuration.
Consider the Sales queue (15% total cluster share) split into two regional sub-queues:
| Sub-Queue | Weight | Parent Share | Total Cluster Share |
|---|---|---|---|
| North America Sales | \(w_{\text{NA}} = 30\) | 50% of Sales | 7.5% |
| Europe Sales | \(w_{\text{EU}} = 30\) | 50% of Sales | 7.5% |
Sub-queue share calculation: \[ R(\text{NA} \mid \text{Sales}) = \frac{w_{\text{NA}}}{w_{\text{NA}} + w_{\text{EU}}} = \frac{30}{30 + 30} = 50\% \quad \text{of Sales capacity} \] \[ R(\text{NA} \mid \text{cluster}) = 50\% \times 15\% = 7.5\% \]
\[ R(\text{EU} \mid \text{Sales}) = \frac{w_{\text{EU}}}{w_{\text{NA}} + w_{\text{EU}}} = \frac{30}{30 + 30} = 50\% \quad \text{of Sales capacity} \] \[ R(\text{EU} \mid \text{cluster}) = 50\% \times 15\% = 7.5\% \]
The hierarchy means sub-queue weights are always relative to their siblings within the same parent, and the parent's share is relative to its own siblings. This recursive structure allows arbitrarily deep organizational modeling.
5.4.4 Zero-Weight Queues and Best Effort Jobs
A queue configured with weight = 0.0 receives zero guaranteed cluster resources during normal operation. It will never receive containers while higher-priority queues (weight \(> 0\)) have active pending tasks. However, when all weighted queues are completely idle and cluster capacity sits unutilized, the Fair Scheduler allocates available containers to the zero-weight queue. This creates a best-effort execution tier.
Configuration Example:
Under the Data Science queue, a sub-queue named best_effort_jobs is configured with a weight of zero:
<queue name="best_effort_jobs">
<weight>0.0</weight>
</queue>
Operational Meaning of Zero Weight:
When weighted queues are busy, the zero-weight queue is starved — it literally receives 0% of the cluster. This is because: \[ R(\text{best\_effort}) = \frac{0}{\sum w_j} \times 100\% = 0\% \]
The zero-weight queue only receives resources when the denominator's active queues shrink to zero (i.e., all weighted queues become idle). At that point, the scheduler has surplus capacity with no competing demand, and the best-effort jobs fill the gap.
The CSS Flexbox Analogy:
The zero-weight pattern is directly analogous to CSS Flexbox's flex-grow property:
| YARN Fair Scheduler | CSS Flexbox |
|---|---|
Queue with weight = 4 |
Element with flex-grow: 4 |
Queue with weight = 0 |
Element with flex-grow: 0 |
| Unused cluster capacity | Remaining space in the flex container |
| Best-effort jobs run when others idle | flex-grow: 0 element receives no extra space unless others have none |
In CSS, an element with flex-grow: 0 does not expand to fill available space — it only takes its minimum content size. Similarly, a YARN queue with weight = 0 receives no share of the cluster's capacity. But just as a flex-grow: 0 element still occupies its intrinsic size when it is the only element in the container, a zero-weight YARN queue will receive the entire cluster when it is the only queue with active jobs.
This same design pattern appears in operating systems (background/low-priority process scheduling), cloud batch processing (AWS Batch job queue priorities), and Android XML layout weights (layout_weight = 0).
5.4.5 Resource Guarantees and Constraints: Min Resources and Max Resources
Critical Configuration — Min and Max Resource Bounds: Without explicit resource bounds, the Fair Scheduler's dynamic rebalancing and idle capacity borrowing can lead to dangerous scenarios. A single high-volume queue could temporarily consume 100% of cluster resources during off-peak hours, leaving no capacity for sudden incoming jobs from other departments. Administrators must configure minResources and maxResources to prevent both starvation and monopolization.
Minimum Resources (minResources):
- Configures a guaranteed floor of memory and VCores for a queue (e.g.,
minResources = 65%for Data Science). - Even under extreme cluster load, YARN guarantees this baseline capacity will be available when a job is submitted to that queue.
- If the Data Science queue has
minResources = 65%, then even if 10 other queues are fighting for resources, Data Science will always receive at least 65% of the cluster when it has active jobs. - Think of it as a service-level guarantee — the queue's contractual minimum.
Maximum Resources (maxResources):
- Configures a hard ceiling capping maximum resource consumption (e.g.,
maxResources = 85%for Data Science). - Even if all other tenant queues are completely idle, jobs in the Data Science queue cannot consume more than 85% of total cluster capacity.
- This reserves a 15% safety buffer for incoming interactive queries from other departments, ensuring they can launch immediately without waiting for the scheduler to rebalance.
- Think of it as a fairness constraint — no single queue can hog the entire cluster.
Why Both Are Needed:
| Scenario | Without Bounds | With minResources = 65%, maxResources = 85% |
|---|---|---|
| Data Science is active, all others busy | Might get less than 65% if other queues are aggressive | Guaranteed at least 65% |
| Data Science is the only active queue | Would consume 100% of the cluster | Capped at 85%, leaving 15% available for burst arrivals |
| A new high-priority queue arrives | Data Science might lose resources unpredictably | Data Science never drops below 65%, new queue gets capacity from the remaining 35% |
5.4.6 Capacity Scheduler Overview and Hortonworks Defaults
Capacity Scheduler (the default in Hortonworks distributions) is designed for large multi-tenant enterprise organizations requiring strict SLA guarantees with hard-partitioned queues. Unlike the Fair Scheduler's fluid rebalancing, the Capacity Scheduler provides guaranteed minimum capacities, hierarchical queue structures, queue elasticity for borrowing unused capacity, and support for preemption to reclaim allocated resources.
Key Properties of the Capacity Scheduler:
- Hard-Partitioned Queues with Guaranteed Capacity:
Cluster capacity is divided into sub-queues with strict minimum capacity guarantees. Each organization or department receives a dedicated, guaranteed fraction of the cluster. Unlike the Fair Scheduler where shares are fluid, Capacity Scheduler queues have hard minimums that other queues cannot encroach upon.
- Queue Elasticity (Borrowing Unused Capacity):
While each queue has a guaranteed minimum, unused capacity within a queue can be temporarily borrowed by other active queues. If the Engineering queue's guaranteed 30% is only 60% utilized (using 18% of total cluster), the remaining 12% can be borrowed by other queues that need it. When Engineering's load increases, it reclaims its full guaranteed capacity.
- Maximum Capacity Limits:
Each queue can be configured with a maximum-capacity parameter that caps how much borrowed capacity it can consume. This prevents any single queue from monopolizing the cluster even during idle periods.
- FIFO Within Each Queue:
While different queues share capacity via the Capacity Scheduler, jobs within a single queue execute in strict FIFO order. This provides predictable ordering within a department while allowing resource sharing across departments.
- Preemption:
The Capacity Scheduler supports preemption of containers when a queue needs to reclaim its guaranteed capacity. If Queue A's containers are running on resources borrowed from Queue B, and Queue B suddenly needs its guaranteed minimum back, the scheduler can kill Queue A's excess containers to free resources for Queue B.
- Hierarchical Queues:
Queues can be nested to any depth, mirroring corporate organizational structures (division → department → team).
Capacity Scheduler Configuration Example:
A typical enterprise configuration might define a two-level hierarchy:
root
├── prod (40% capacity guarantee)
└── dev (60% capacity guarantee, max-capacity: 75%)
├── eng (50% of dev = 30% of total)
└── science (50% of dev = 30% of total)
prodqueue: Guaranteed 40% of the cluster. Reserved for production ETL pipelines and SLA-critical workloads. Even if thedevqueue is very busy,prodalways gets at least 40%.devqueue: Guaranteed 60% of the cluster. Itsmax-capacityis set to 75%, meaning it can borrow up to 15% extra fromprod(whenprodis idle), but can never exceed 75% total. This ensuresprodalways retains at least 25% for burst production workloads.engsub-queue: Receives 50% of thedevqueue's capacity (which is 30% of the total cluster).sciencesub-queue: Receives the other 50% of thedevqueue's capacity (30% of the total cluster).
The max-capacity on dev is the key safety valve: without it, if prod has no running jobs, dev could consume 100% of the cluster, and a sudden production job would have to wait for the scheduler to reclaim resources. With max-capacity = 75%, at least 25% is always available for prod bursts.
5.4.7 Dominant Resource Fairness (DRF) — Multi-Dimensional Resource Fairness
Dominant Resource Fairness (DRF) extends fairness beyond a single resource dimension. When clusters have multiple resource types (CPU cores and memory), simply dividing "resources" equally is insufficient — one job might be memory-intensive while another is CPU-intensive. DRF ensures fairness by looking at each application's dominant resource — the resource dimension where the application consumes the largest fraction of the cluster's total supply.
Why Single-Dimension Fairness Fails:
Consider a cluster with 100 CPUs and 10 TB of memory. Two applications run:
- App A (e.g., a data loading job): Requests 2 CPUs and 300 GB memory per container.
- App B (e.g., a machine learning training job): Requests 6 CPUs and 100 GB memory per container.
If we only look at CPU fairness, App B uses 3x more CPU per container, so we might give App A more containers. If we only look at memory fairness, App A uses 3x more memory per container. DRF resolves this by computing each application's dominant share.
DRF Calculation:
For each application, compute the fraction of each resource type it consumes per container:
| Resource | App A per container | App B per container |
|---|---|---|
| CPU | \(\frac{2}{100} = 2\%\) | \(\frac{6}{100} = 6\%\) |
| Memory | \(\frac{300\text{ GB}}{10{,}000\text{ GB}} = 3\%\) | \(\frac{100\text{ GB}}{10{,}000\text{ GB}} = 1\%\) |
App A's dominant resource is memory (3%) — memory is the resource dimension where App A consumes the largest fraction of the cluster's total.
App B's dominant resource is CPU (6%) — CPU is the resource dimension where App B consumes the largest fraction of the cluster's total.
DRF aims to equalize each application's dominant share. If App A gets \(n_A\) containers and App B gets \(n_B\) containers:
- App A's dominant share: \(n_A \times 3\%\)
- App B's dominant share: \(n_B \times 6\%\)
Setting them equal: \(n_A \times 3\% = n_B \times 6\% \implies n_A = 2 \times n_B\)
Result: App A gets twice as many containers as App B. In terms of actual resource consumption:
- App A: many containers × (2 CPU, 300 GB each) — it's a memory-heavy workload that gets lots of containers because each one uses little CPU.
- App B: half as many containers × (6 CPU, 100 GB each) — it's a CPU-heavy workload that gets fewer containers because each one demands significant CPU.
This ensures neither application dominates the cluster on any single resource dimension, which is the hallmark of true multi-resource fairness.
5.4.8 Comparison of YARN Schedulers
| Feature | FIFO Scheduler | Fair Scheduler | Capacity Scheduler |
|---|---|---|---|
| Default in | Apache Hadoop (early versions) | Cloudera (CDH) | Hortonworks (HDP) |
| Queue model | Single queue | Multiple pools with weights | Hierarchical queues with capacity guarantees |
| Sharing model | No sharing — one job at a time | Dynamic equal sharing over time | Guaranteed minimum capacity per queue |
| Idle capacity borrowing | N/A | Yes — idle queues lend capacity automatically | Yes — via queue elasticity |
| Preemption | None | Yes — kills containers of over-resourced queues | Yes — reclaims guaranteed capacity |
| Resource guarantees | None (head-of-line blocking) | Fair share with min/max bounds | Hard minimum capacity + max capacity limits |
| Within-queue ordering | Strict FIFO | Fair sharing (or configurable per-queue) | FIFO within each queue |
| Multi-resource fairness | Not applicable | Supports DRF per-queue | FIFO only (single-dimension) |
| Best for | Single-user dev clusters | Multi-tenant clusters with variable workloads | Enterprise clusters with strict SLA requirements |
| Starvation risk | High — small jobs blocked behind large jobs | Low — dynamic rebalancing prevents starvation | Low — guaranteed minimums prevent starvation |
| Configuration complexity | Minimal (no config needed) | Moderate (weights, policies, preemption settings) | High (hierarchical queues, capacity percentages, max-capacity) |
| Elasticity | None | Full — resources flow to active jobs | Partial — up to max-capacity limits |
5.4.9 Student Questions and Answers
Q: If a queue is configured with weight = 0.0, does that mean jobs submitted to it will never run?
A: No. A weight of 0.0 means the queue has zero guaranteed resource allocation when other queues are busy. However, whenever higher-priority queues become idle, the Fair Scheduler allocates unutilized cluster capacity to the weight = 0.0 queue. In the CSS Flexbox analogy, this is like an element with flex-grow: 0 — it won't expand when competing with other flex items, but if it's the only element in the container, it receives all available space.
Q: What is the difference between minResources and maxResources in YARN queue configuration?
A: minResources specifies the minimum guaranteed resource baseline that YARN will reserve for a queue under heavy cluster load — this is the queue's floor. maxResources sets a strict upper ceiling capping how much total cluster capacity that queue can borrow, even when all other queues are completely idle — this is the queue's ceiling. Together, they create a bounded range [minResources, maxResources] within which the queue operates.
5.4.10 Key Takeaways
Exam note: Scheduling algorithms are heavily tested. Know the following:
- FIFO = single queue, head-of-line blocking, unsuitable for multi-tenancy.
- Fair Scheduler = dynamic equal sharing, idle capacity borrowing, preemption, supports DRF.
- Capacity Scheduler = guaranteed minimum capacities, queue elasticity, FIFO within each queue, max-capacity limits.
- DRF = equalizes each application's dominant resource share across multiple resource dimensions (CPU + memory).
- Weight formula: \[ R(q_k) = \frac{w_k}{\sum_{j=1}^{K} w_j} \times 100\% \]
- Zero-weight queues behave like CSS
flex-grow: 0— they only receive resources when no other queue needs them. - Be able to compute queue shares given weights, and identify which resource is an application's dominant resource given its per-container requests and the cluster's total capacity.
5.5 Sqoop Relational Data Ingestion Framework
The Problem Sqoop Solves: Enterprise data warehouses store mission-critical business data—customer profiles, transaction logs, inventory records—inside Relational Database Management Systems (RDBMS) such as MySQL, Oracle, PostgreSQL, and SQL Server. As organizations adopt Hadoop for large-scale analytics, they face a fundamental impedance mismatch: RDBMS data lives behind JDBC sockets in normalized tables, while Hadoop processes data as distributed files on HDFS. Moving data manually (via CSV exports, custom scripts, or hand-written MapReduce jobs) is error-prone, slow, and single-threaded. Apache Sqoop was created to bridge this gap with a single CLI command that parallelizes bulk data transfer between any JDBC-accessible RDBMS and HDFS, Hive, or HBase—automatically partitioning, distributing, and writing the data across the cluster.
5.5.1 Purpose, Architectural Overview, and Name Etymology
What Is Sqoop?
Apache Sqoop derives its name from SQL + Hoop (Hadoop)—a portmanteau reflecting its core mission: shuttling structured relational data into and out of the Hadoop ecosystem. It is an open-source tool originally developed by Cloudera and later promoted to an Apache top-level project.
Sqoop performs bi-directional bulk data transfer between RDBMS systems and Hadoop storage layers:
- Import: RDBMS → HDFS (or Hive, HBase)
- Export: HDFS → RDBMS
The bi-directional capability is critical: after a MapReduce or Spark job produces analytical results (e.g., aggregated sales by zip code), Sqoop can export those results back into the operational database so that downstream applications—dashboards, CRMs, reporting tools—can consume them.
Architectural Execution Paradigm:
Although Sqoop is invoked as a command-line interface (CLI) tool, it does not execute data transfers through a single client process. Instead, it follows a four-stage pipeline that compiles a user's CLI arguments into a full distributed MapReduce job:
\[ \text{Sqoop CLI Command} \;\longrightarrow\; \text{Java Code Generation} \;\longrightarrow\; \text{Compile JAR} \;\longrightarrow\; \text{MapReduce Job on YARN} \]
Stage 1 — CLI Parsing: Sqoop reads connection parameters (--connect, --username, --table, --num-mappers, etc.) and validates them.
Stage 2 — Java Code Generation: Sqoop connects to the target database via JDBC, retrieves the table metadata (column names, SQL types, primary key), and generates a table-specific Java class that implements the DBWritable interface. This interface provides two critical serialization methods:
readFields(ResultSet __dbResults)— populates Java fields from a JDBCResultSetrow (deserialization for import)write(PreparedStatement __dbStmt)— inserts Java fields into aPreparedStatement(serialization for export)
The generated class also implements Hadoop's Writable interface, allowing instances to be serialized across the network during MapReduce execution and stored in SequenceFiles.
Stage 3 — JAR Compilation: Sqoop compiles the generated Java source file into a JAR (.jar) artifact using the Java compiler bundled with the JDK.
Stage 4 — MapReduce Submission on YARN: Sqoop submits the compiled JAR as a Map-only MapReduce job to the YARN Resource Manager. Crucially, there are zero Reducer tasks by default—each mapper opens an independent JDBC socket connection to the database, reads a partition of rows defined by SQL WHERE clauses, and streams the data directly into HDFS block files.
Sqoop uses Hadoop's DataDrivenDBInputFormat as the InputFormat for the import job. This class is responsible for splitting the source table into partitions and assigning each partition to a mapper. The partitioning is based on a "splitting column" (typically the primary key) and uses the column's MIN and MAX values to construct bounding queries.
Sqoop Connectors:
Sqoop ships with built-in connectors for the most popular databases:
| Connector | Notes |
|---|---|
| MySQL | Supports both JDBC and direct mode (via mysqldump) |
| PostgreSQL | Supports both JDBC and direct mode (via pg_dump) |
| Oracle | Supports both JDBC and direct mode (via Oracle-specific tools) |
| Microsoft SQL Server | JDBC only |
| IBM DB2 | JDBC only |
| Netezza | Supports direct mode |
| Generic JDBC | Fallback connector for any JDBC-compliant database |
Third-party connectors (e.g., Teradata, Couchbase) extend Sqoop to NoSQL stores and enterprise data warehouses. The connector architecture is modular—each connector knows how to translate SQL metadata into Hadoop-compatible types and can optionally provide optimized bulk-transfer strategies.
5.5.2 Core Command Syntax and Parameter Reference
Sqoop Import Command Structure:
A standard Sqoop import command specifies database connection details, authentication credentials, the target table, HDFS destination, filtering criteria, and parallelism configuration:
sqoop import \
--connect jdbc:mysql://localhost:3306/retail_db \
--username root \
--password-file /user/hadoop/credentials/mysql.pwd \
--table customer_profiles \
--target-dir /user/hadoop/data/customer_profiles \
--where "signup_date >= '2023-01-01'" \
--num-mappers 4 \
--split-by customer_id
Note: Sqoop also supports sqoop-export, sqoop-codegen, sqoop-eval, sqoop-create-hive-table, sqoop-list-databases, and sqoop-list-tables as alternative tool commands.
Key Sqoop CLI Command Parameters:
| Parameter Name | Function and Configuration Meaning |
|---|---|
--connect |
JDBC connection string specifying database type, hostname, port number, and target database name (e.g., jdbc:mysql://localhost:3306/retail_db). For distributed clusters, always use the full hostname—localhost will cause mappers on other nodes to fail. |
--username / --password |
Database authentication credentials. In production, --password-file referencing a secure HDFS file is preferred over plaintext --password. Alternatively, -P prompts for the password at the terminal. |
--table |
Specifies the single relational database table to extract. Mutually exclusive with --query. |
--target-dir |
Specifies the HDFS destination directory where output files (e.g., part-m-00000, part-m-00001) will be written. Must not already exist unless --delete-target-dir or --append is used. |
--where |
Applies a SQL WHERE clause filter to import a subset of rows (e.g., filtering by date or status). The filter is pushed down into each mapper's query for efficiency. |
--num-mappers (-m) |
Defines the number of parallel MapReduce mapper tasks to launch. Default is 4. Setting to 1 forces sequential (single-threaded) import. |
--split-by |
Specifies the column used to partition data across parallel mappers. If omitted, Sqoop defaults to the table's primary key. Required if the table has no primary key and --num-mappers > 1. |
--query |
Allows a free-form SQL SELECT statement instead of importing a whole table. Must include the \$CONDITIONS marker in the WHERE clause for Sqoop to inject partition bounds. |
--hive-import |
Directly loads the imported data into a Hive table (auto-creates the table definition based on the source schema). Eliminates the need for a separate CREATE TABLE + LOAD DATA step in Hive. |
--direct |
Enables direct-mode import using database-specific bulk extraction tools (e.g., mysqldump for MySQL, pg_dump for PostgreSQL). Bypasses JDBC for higher throughput, but does not support all data types (e.g., CLOB/BLOB in MySQL). |
--as-textfile / --as-sequencefile / --as-avrodatafile / --as-parquetfile |
Controls the output serialization format. Default is --as-textfile. |
--null-string / --null-non-string |
Specifies the string representation for NULL values from the database (relevant for text-file imports where NULL and the string "null" could be confused). |
--driver |
Manually specifies the JDBC driver class name. Required only when Sqoop cannot auto-detect the driver from the connection string. |
5.5.3 Parallel Import Mechanics, Splitting Logic, and Primary Key Pitfalls
Understanding how Sqoop parallelizes data ingestion across --num-mappers N is critical for both performance tuning and preventing runtime job failures. Sqoop does not stream the entire table through a single socket—it divides the row range into equal slices and assigns each slice to an independent mapper.
Partition Splitting Algorithm — Worked Example:
Splitting a table with 1,000 rows across 4 mappers
Suppose you execute a Sqoop import with --num-mappers 4 and --split-by customer_id on a table called customer_profiles. Sqoop first issues a metadata query to determine the range of the splitting column:
\[ \text{Query: } \texttt{SELECT MIN(customer\_id), MAX(customer\_id) FROM customer\_profiles;} \]
Assume the database returns \(\text{MIN} = 1\) and \(\text{MAX} = 1000\).
Step 1 — Compute the range:
\[ \text{Range} = \text{MAX} - \text{MIN} + 1 = 1000 - 1 + 1 = 1000 \]
Step 2 — Compute the step size:
\[ \text{Step Size} = \left\lfloor \frac{\text{MAX} - \text{MIN} + 1}{\text{num-mappers}} \right\rfloor = \left\lfloor \frac{1000 - 1 + 1}{4} \right\rfloor = \left\lfloor \frac{1000}{4} \right\rfloor = 250 \]
Step 3 — Assign bounding queries to each mapper:
Sqoop constructs SQL WHERE clauses using half-open intervals \([\text{lower}, \text{upper})\) for all but the final mapper, which uses a closed interval:
| Mapper | SQL Query |
|---|---|
| Mapper 1 | SELECT * FROM customer_profiles WHERE customer_id >= 1 AND customer_id < 251 |
| Mapper 2 | SELECT * FROM customer_profiles WHERE customer_id >= 251 AND customer_id < 501 |
| Mapper 3 | SELECT * FROM customer_profiles WHERE customer_id >= 501 AND customer_id < 751 |
| Mapper 4 | SELECT * FROM customer_profiles WHERE customer_id >= 751 AND customer_id <= 1000 |
Each mapper opens its own JDBC connection, executes its assigned query, and writes its results to a separate HDFS file (part-m-00000 through part-m-00003).
Why the "+1" matters: Without the +1, a table with IDs from 1 to 4 and num-mappers=4 would yield a step size of \(\lfloor 3/4 \rfloor = 0\), causing all mappers to target the same row range and creating incorrect splits.
Primary Key Pitfall — The Most Common Sqoop Error:
If --split-by is not specified, Sqoop automatically selects the table's Primary Key as the splitting column. This works seamlessly for most tables. However, if the target table lacks a primary key and --split-by is also omitted, a multi-mapper import will fail immediately:
ERROR tool.ImportTool: Error during import: No primary key could be found
for table <table_name>. Please specify one with --split-by or use
--num-mappers 1.
Why this fails: Without a primary key (or an explicit --split-by column), Sqoop cannot determine MIN and MAX values, so it cannot compute the boundary ranges that partition work across mappers. Each mapper needs a unique SQL WHERE clause to avoid reading the same rows.
Resolution strategies:
- Best option: Explicitly specify a numeric, uniformly distributed column:
--split-by column_name - Fallback option: Force single-mapper import:
--num-mappers 1(or-m 1). This eliminates the need for splitting entirely, but sacrifices parallelism—use only for small tables.
Choosing a good splitting column: The ideal splitting column is numeric (integer or long), indexed, and uniformly distributed. A column with heavy skew (e.g., most IDs clustered in 1–100, with a few outliers at 1,000,000) will cause some mappers to process far more rows than others, resulting in load imbalance. Non-numeric columns (e.g., strings, UUIDs) can also cause issues because Sqoop's range-based splitting assumes numeric ordering.
General Step Size Formula:
\[ \text{Step Size} = \frac{\text{MAX} - \text{MIN} + 1}{\text{num-mappers}} \]
where:
- \(\text{MIN}\) = minimum value of the splitting column (retrieved via
SELECT MIN(col)) - \(\text{MAX}\) = maximum value of the splitting column (retrieved via
SELECT MAX(col)) - \(\text{num-mappers}\) = the value of the
--num-mappers(or-m) parameter
When the range is not evenly divisible by the number of mappers, the final mapper's range absorbs the remainder, ensuring no rows are lost.
5.5.4 Directory Management and Target Overwrite Rules
HDFS Output Directory Behavior:
When Sqoop writes imported data to HDFS, it creates output files named part-m-00000, part-m-00001, part-m-00002, etc., inside the specified --target-dir. Each file corresponds to one mapper's output. By default, Sqoop enforces strict directory safety:
Rule 1 — Directory Already Exists ⇒ Job Aborts: If the HDFS path specified by --target-dir already exists, Sqoop refuses to proceed and throws:
ERROR tool.ImportTool: Import directory /user/hadoop/data/customer_profiles already exists.
This prevents accidental data corruption from overwriting a previous import.
Rule 2 — --delete-target-dir ⇒ Delete and Recreate: Adding --delete-target-dir to the command instructs Sqoop to recursively delete the existing HDFS directory and all its contents before starting the import. This is useful for full-refresh imports where you want the latest snapshot of the source table.
sqoop import --connect jdbc:mysql://localhost:3306/retail_db \
--table orders --target-dir /user/hadoop/data/orders \
--delete-target-dir --num-mappers 4
Rule 3 — --append ⇒ Add Files to Existing Directory: Adding --append instructs Sqoop to import new data into the existing directory without deleting it. Sqoop automatically assigns incremented file names (e.g., part-m-00004, part-m-00005) to avoid overwriting existing files. This is useful for incremental ingestion patterns where new records are periodically appended.
sqoop import --connect jdbc:mysql://localhost:3306/retail_db \
--table orders --target-dir /user/hadoop/data/orders \
--append --num-mappers 4 --where "order_date > '2023-06-01'"
Important: --delete-target-dir and --append are mutually exclusive—using both in the same command will cause an error.
5.5.5 Output Data File Formats
Serialization Format Options:
Sqoop supports four output file formats when writing imported data to HDFS. The choice of format affects readability, compression, schema preservation, and downstream compatibility with Hive, Spark, and MapReduce.
| Format | Flag | Characteristics |
|---|---|---|
| Text Files | --as-textfile |
Default format. Plain-text, comma-delimited (or custom delimiter) records. Human-readable via hdfs dfs -cat. Supports all SQL types via string conversion. Limitations: no built-in schema, no native binary field support (e.g., VARBINARY), and ambiguity between SQL NULL and the string "null" (mitigated by --null-string / --null-non-string). |
| SequenceFiles | --as-sequencefile |
Binary key-value format native to Hadoop. Each imported row is stored as the "value" element using the generated Java class (which implements Writable). Supports block-level compression. Efficient for MapReduce splitting. Java-specific—not portable to Python or other non-JVM ecosystems. |
| Avro Data Files | --as-avrodatafile |
Compact binary format with embedded JSON schema (field names, types, nullability). Supports schema evolution (adding/removing fields across imports). Language-agnostic (readable by Java, Python, C, etc.). Ideal for long-term archival and cross-system data exchange. Cannot be directly loaded into Hive by Sqoop (requires manual CREATE TABLE with Avro serde). |
| Parquet Files | --as-parquetfile |
Columnar binary format optimized for analytical (OLAP) queries. Stores data by column rather than by row, enabling high compression ratios (similar values are adjacent) and fast column projection (only requested columns are read). Directly loadable into Hive by Sqoop. Preferred for Spark SQL and Hive workloads. |
Additional notes from the textbook:
- SequenceFiles are Java-specific, whereas Avro and Parquet files can be processed by a wide range of languages (Python, C++, R, etc.).
- When importing into SequenceFiles, downstream MapReduce jobs must use the generated Java class to deserialize records. Text-based imports can be consumed without generated code, but the generated class's
parse()methods simplify field extraction and type conversion. - Avro-based imports allow Sqoop to be used without any generated code integration—Generic Avro records can be processed directly using Avro's
GenericRecordAPI.
5.5.6 Live Demo Walkthrough: 220,000 Record MySQL Import
End-to-End Demonstration: Importing 220,000 Records from MySQL to HDFS
The following walkthrough demonstrates Sqoop's complete import lifecycle—from command invocation to YARN job execution to HDFS output verification.
Step 1 — Command Execution:
sqoop import \
--connect jdbc:mysql://localhost:3306/profile_db \
--username root \
--password root \
--table profiles \
--target-dir /user/hadoop/sqoop_demo \
--num-mappers 4
Key observations about this command:
- No
--split-byis specified, so Sqoop will use the primary key of theprofilestable as the splitting column. - No
--whereclause is specified, so the entire table is imported. - No format flag is specified, so output defaults to
--as-textfile(comma-delimited). - The
--num-mappers 4parameter requests four parallel mappers.
Step 2 — Internal Execution Sequence:
- Code Generation: Sqoop connects to
profile_dbvia JDBC, inspects theprofilestable metadata (column names, SQL types, primary key), and generates aprofiles.javasource file implementingDBWritable. - JAR Compilation: Sqoop compiles
profiles.javaintoprofiles.jar. - YARN Submission: Sqoop submits the JAR as a Map-only MapReduce job to the YARN Resource Manager (port
8032). - Splitting: Sqoop queries
SELECT MIN(primary_key), MAX(primary_key) FROM profilesand divides the range into 4 equal partitions. - Parallel Execution: 4 mapper tasks are launched, each opening its own JDBC connection, executing its
SELECT ... WHERE pk >= ? AND pk < ?query, and streaming rows into an HDFS output file.
Step 3 — Execution Logs (Abbreviated):
INFO tool.ImportTool: Beginning import...
INFO manager.SqlManager: Executing SQL statement: SELECT t.* FROM profiles AS t LIMIT 1
INFO manager.SqlManager: Executing SQL statement: SELECT t.* FROM profiles AS t WHERE 1=0
INFO orm.CompilationManager: Writing jar file: /tmp/sqoop-hadoop/compile/...
INFO mapreduce.ImportJobBase: Beginning import of profiles
INFO impl.YarnClientImpl: Submitted application application_...
INFO mapreduce.Job: Running job: job_...
INFO mapreduce.Job: map 0% reduce 0%
INFO mapreduce.Job: map 25% reduce 0%
INFO mapreduce.Job: map 50% reduce 0%
INFO mapreduce.Job: map 75% reduce 0%
INFO mapreduce.Job: map 100% reduce 0%
INFO mapreduce.Job: Job job_... completed successfully
INFO mapreduce.ImportJobBase: Retrieved 220000 records.
Step 4 — Output Verification:
hdfs dfs -ls /user/hadoop/sqoop_demo
Output confirms 4 mapper-generated files:
-rw-r--r-- 1 hadoop hadoop 18456789 /user/hadoop/sqoop_demo/part-m-00000
-rw-r--r-- 1 hadoop hadoop 18523456 /user/hadoop/sqoop_demo/part-m-00001
-rw-r--r-- 1 hadoop hadoop 18398123 /user/hadoop/sqoop_demo/part-m-00002
-rw-r--r-- 1 hadoop hadoop 18412567 /user/hadoop/sqoop_demo/part-m-00003
Each file contains approximately 55,000 records (220,000 / 4 = 55,000), confirming that the data was evenly distributed across the 4 mappers.
hdfs dfs -cat /user/hadoop/sqoop_demo/part-m-00000 | head -5
Sample output:
1,john_doe,john@example.com,2023-01-15,active
2,jane_smith,jane@example.com,2023-02-20,active
3,bob_jones,bob@example.com,2023-03-10,inactive
4,alice_wang,alice@example.com,2023-04-05,active
5,charlie_brown,charlie@example.com,2023-05-18,active
Total imported records: 220,000 rows written to HDFS across 4 part files.
Direct-Mode Import Alternative:
For MySQL specifically, the same import could be performed in direct mode:
sqoop import \
--connect jdbc:mysql://localhost:3306/profile_db \
--username root \
--password root \
--table profiles \
--target-dir /user/hadoop/sqoop_demo \
--direct \
--num-mappers 4
In direct mode, Sqoop bypasses JDBC and instead spawns instances of mysqldump for each mapper, reading data from its output stream. This is typically much faster for MySQL but cannot handle CLOB or BLOB columns, and metadata is still queried through JDBC.
Hive Integration Alternative:
To import directly into a Hive table (bypassing the manual CREATE TABLE + LOAD DATA steps):
sqoop import \
--connect jdbc:mysql://localhost:3306/profile_db \
--username root \
--password root \
--table profiles \
--hive-import \
--num-mappers 4
This single command imports the data into HDFS and then auto-creates a Hive table definition based on the source database schema, loading the data directly into Hive's warehouse directory. Note that Hive's type system is less rich than most SQL systems, so some type narrowing (e.g., DATE → STRING) may occur with a warning.
5.5.7 Sqoop Exports: HDFS Back to RDBMS
Sqoop is not limited to imports—it can also export data from HDFS back into a relational database. The export process mirrors the import architecture: a MapReduce job reads HDFS files (text or SequenceFile format), parses records using a generated Java class, and builds batch INSERT statements to write rows into the target table.
sqoop export \
--connect jdbc:mysql://localhost:3306/analytics_db \
--username root \
--password root \
--table sales_by_zip \
--export-dir /user/hive/warehouse/zip_profits \
--input-fields-terminated-by '\0001'
Key export considerations:
- The target table must already exist in the database—Sqoop does not auto-create it during export.
- Column order in the target table must match the order of fields in the HDFS files.
- For Hive-sourced data, the default field delimiter is Ctrl-A (
\0001), specified via--input-fields-terminated-by. - Exports are not atomic—parallel mappers commit independently, so partial results may be visible before all mappers complete. Use
--staging-tablefor transactional guarantees (data is written to a staging table first, then moved atomically to the destination). - Direct-mode exports are also supported for MySQL (via
mysqlimport) and PostgreSQL.
5.5.8 Student Questions and Answers
Q: Why does Sqoop use Map-only jobs instead of launching Reducers?
A: Sqoop's purpose is to extract data from an RDBMS and write it directly to HDFS blocks. Reducers are required for data aggregation, sorting, or joining—none of which are needed during a raw data transfer. Launching Reducer tasks would introduce unnecessary network shuffle overhead (all mapper output would need to be transferred across the network to reducer nodes). Map-only jobs allow each mapper to write directly to local HDFS blocks in parallel, maximizing I/O throughput.
Q: What happens if I attempt to run a multi-mapper Sqoop import on a database table that does not have a primary key?
A: Sqoop will throw an import exception and terminate immediately. Without a primary key to serve as the default splitting column, Sqoop cannot compute MIN and MAX split bounds, so it cannot divide the table into parallel slices. Resolution: either (1) specify --split-by <numeric_column_name> to designate an alternative splitting column, or (2) set --num-mappers 1 to force a sequential import that bypasses the splitting algorithm entirely.
Exam note: Be able to (1) list and explain the core CLI parameters (--connect, --table, --target-dir, --num-mappers, --split-by, --where, --hive-import, --direct), (2) compute the step size for a given MIN, MAX, and num-mappers using the formula \(\text{Step Size} = \frac{\text{MAX} - \text{MIN} + 1}{\text{num-mappers}}\), (3) trace which SELECT ... WHERE query each mapper executes, and (4) explain why a table without a primary key requires either --split-by or --num-mappers 1. Also know the four output formats (text, SequenceFile, Avro, Parquet) and the difference between --delete-target-dir and --append.
5.6 Flume: Unstructured Streaming Log Ingestion Framework
The Streaming Data Problem. Most enterprise data is not sitting neatly in relational databases waiting to be bulk-exported. Web servers generate continuous access logs, mobile apps emit event streams, IoT sensors produce telemetry, and social media platforms broadcast real-time feeds. This data is unstructured or semi-structured, arrives as a never-ending stream rather than in discrete batches, and is distributed across many machines. Apache Sqoop cannot address this problem — it is designed for structured batch extraction from RDBMS. Apache Flume was built specifically to solve the complementary challenge: reliably collecting, aggregating, and delivering high-volume streaming event data into Hadoop-based storage systems in near real time.
5.6.1 Purpose, Origin, and Comparison with Sqoop
What Flume Is and Why It Exists. Apache Flume is a distributed, reliable, and available system for efficiently collecting, aggregating, and moving large volumes of streaming log data into HDFS, HBase, or other storage systems. It was originally developed by Cloudera to handle the operational challenge of ingesting web server logs from hundreds or thousands of machines into a Hadoop cluster. After proving itself in production environments, it was donated to and accepted by the Apache Software Foundation as a top-level project.
Flume's core design philosophy is that data ingestion should be continuous (not batch), distributed (running on edge nodes near data sources), reliable (with transactional delivery guarantees), and configurable (assembled from pluggable components rather than requiring custom code).
Comparative Analysis: Sqoop vs Flume
| Operational Dimension | Apache Sqoop | Apache Flume |
|---|---|---|
| Data Source Type | Structured relational data (RDBMS: MySQL, Oracle, PostgreSQL, DB2). | Unstructured or semi-structured streaming data (web server logs, application events, syslog, social media feeds, sensor telemetry). |
| Ingestion Paradigm | Batch-oriented bulk data transfer (entire tables or query result sets). | Real-time continuous streaming event ingestion (events flow as they are generated). |
| Underlying Engine | MapReduce or YARN jobs that read from JDBC sources and write to HDFS. | Long-running independent Java agent processes that run continuously on edge and aggregator machines. |
| Directionality | Import (RDBMS → HDFS) and Export (HDFS → RDBMS). | Unidirectional: source → channel → sink (event producers to storage systems). |
| Data Model | Row-oriented: each record maps to a database row with a known schema. | Event-oriented: each record is a Flume Event consisting of a byte-array payload and optional string-keyed header attributes. |
| Delivery Guarantee | Exactly-once (MapReduce task semantics with speculative execution). | At-least-once (transactional delivery with possible duplicates; requires downstream deduplication). |
| Primary Destination | HDFS, Hive tables, or HBase (via import); RDBMS (via export). | HDFS, HBase, Solr, Elasticsearch, Kafka, or secondary Flume agents (via Avro RPC). |
| Typical Use Case | Nightly ETL extraction of a sales database into Hive for analytics. | Continuous ingestion of web server access logs from 500 edge servers into HDFS. |
Exam note: The fundamental Sqoop vs Flume distinction is structured batch vs unstructured streaming. Sqoop moves rows from tables; Flume moves events from log streams. Sqoop runs as a finite MapReduce job; Flume runs as a long-lived agent process. If the question mentions RDBMS, databases, tables, or bulk import/export, the answer is Sqoop. If it mentions logs, events, streams, real-time, or continuous ingestion, the answer is Flume.
5.6.2 Flume Agent Architecture: Source, Channel, and Sink
The Agent as a Building Block. A Flume deployment is composed of one or more independent JVM processes called Agents. Each agent is a self-contained event processing unit that runs three interconnected components — a Source, a Channel, and a Sink — forming a directed event flow:
[External Event Generator] ---> (Source) ---> [Channel] ---> (Sink) ---> [External Destination]
(web server, (HDFS, HBase,
application, Solr, another
sensor, etc.) Flume agent)
Agents are configured declaratively using a Java properties file (not written in code). The configuration specifies which component types to instantiate, their properties, and how they are wired together. Each agent runs as a single flume-ng process on a host machine.
The Flume Event
Every piece of data flowing through Flume is represented as a Flume Event object. An event has exactly two parts:
- Body: A byte array (
byte[]) containing the raw payload data. For a web server log line, this would be the UTF-8 encoded text of the log entry. For audio data, it would be the raw audio bytes. Flume treats the body as opaque — it does not parse or interpret the contents. - Headers: A
Map<String, String>of optional key-value string attributes. Headers carry metadata about the event (such as timestamps, hostnames, severity levels, or custom tags) and are used by interceptors and multiplexing selectors for routing decisions. Headers are not part of the payload and can be added, modified, or removed as the event flows through the agent.
Component 1: Source
The Source is the entry point of a Flume agent. It is responsible for receiving or actively pulling data from an external event generator, wrapping raw records into standardized Flume Event objects, and pushing those events into one or more connected Channels.
| Source Type | Mechanism | Typical Use Case | Reliability Note |
|---|---|---|---|
| Avro Source | Listens on a TCP port for events sent over Avro RPC by an Avro Sink on another Flume agent. | Agent-to-agent communication in multi-tier aggregation topologies. | Transactional; provides at-least-once delivery between agents. |
| Exec Source | Runs a Unix command (e.g., tail -F /var/log/syslog) and converts each line of stdout into an event. |
Monitoring continuously appended log files on a server. | Not reliable. If the agent restarts, events written between the last read and the crash may be lost or duplicated. The spooling directory source is preferred. |
| Spooling Directory Source | Monitors a designated directory for newly placed files. Reads each file line-by-line, creating one event per line. Renames completed files with a .COMPLETED suffix. |
Ingesting log files that are periodically rotated into the spool directory. | Reliable; only marks a file as completed after all its events are committed to the channel via transaction. |
| NetCat Source | Listens on a TCP or UDP port and converts each line of received text into an event. | Simple testing and debugging; receiving events from telnet or nc commands. |
Simple TCP line protocol; no built-in delivery guarantees beyond TCP semantics. |
| HTTP Source | Listens on a port for incoming HTTP POST requests and converts request bodies into events using a pluggable handler (JSON handler, blob handler). | Receiving events from web applications, webhooks, or REST-based systems. | Events are accepted on HTTP 200; handler-dependent reliability. |
| Syslog Source | Reads syslog messages (UDP or TCP) and converts them into events. | Centralized syslog collection from network devices and servers. | TCP variant is more reliable than UDP. |
| JMS Source | Reads messages from a JMS queue or topic and converts them into events. | Integrating with enterprise Java Message Service middleware. | Depends on JMS acknowledgment mode. |
| Thrift Source | Listens on a port for events sent over Thrift RPC by a Thrift Sink or the Flume SDK. | Agent-to-agent communication (alternative to Avro). | Transactional; at-least-once delivery. |
| Twitter Source | Connects to Twitter's streaming API (1% sample "firehose") and converts each tweet into an event. | Social media analytics, sentiment analysis ingestion. | Depends on Twitter API connectivity. |
Component 2: Channel
The Channel is the passive buffer that sits between the Source and the Sink. It holds Flume Events in a queue until the Sink consumes and delivers them. Channels are transactional bridges: an event is not removed from the Channel until the Sink successfully confirms delivery to the destination. This ensures that if the Sink fails mid-delivery, the event remains in the Channel for retry.
Importantly, a Source can feed multiple Channels simultaneously (enabling fan-out), but a Sink is fed by exactly one Channel. However, multiple Sinks can consume from the same Channel (covered by Sink Groups in Section 5.6.4).
Component 3: Sink
The Sink removes events from the Channel and delivers them to an external repository or to another Flume agent's Source over the network.
| Sink Type | Mechanism | Typical Use Case |
|---|---|---|
| HDFS Sink | Writes events to HDFS files in text, SequenceFile, Avro, or custom format. Supports configurable rolling policies based on time interval (default 30 seconds), file size (default 1,024 bytes), and event count (default 10 events). Files in progress are marked with .tmp suffix and optional _ prefix (to exclude from MapReduce input). |
Primary production sink for batch analytics workloads. |
| HBase Sink | Writes events to HBase tables using a pluggable serializer. | Real-time random-access storage for event data. |
| MorphlineSolrSink | Runs events through a Morphline command chain that extracts fields and converts them into Solr documents for indexing. | Near-real-time search indexing of ingested events. |
| Elasticsearch Sink | Writes events to an Elasticsearch cluster using the Logstash format. | Log search and analytics with the Elastic Stack. |
| Logger Sink | Logs events at INFO level using SLF4J (typically to the console). | Development, testing, and debugging. |
| Avro Sink | Sends events over Avro RPC to an Avro Source running on another Flume agent. | Building multi-tier distributed ingestion topologies. |
| Thrift Sink | Sends events over Thrift RPC to a Thrift Source on another agent. | Agent-to-agent forwarding (alternative to Avro). |
| File Roll Sink | Writes events to the local filesystem of the agent host. | Local archival or debugging. |
| Null Sink | Discards all events received from the channel. | Load testing (absorbs events without side effects). |
Interceptors: Inline Event Transformers. Interceptors are components attached to a Source that can inspect, modify, or drop events before they enter the Channel. They form a chained processing sequence on the Source side. Common interceptors include:
| Interceptor | Purpose |
|---|---|
| Timestamp | Adds a timestamp header (milliseconds since epoch) to each event. Essential for time-based HDFS partitioning. |
| Host | Adds a host header containing the agent's hostname or IP address. Useful for tracking event origin in multi-host topologies. |
| Static | Sets a fixed key-value header on all events. Useful for tagging events with environment, severity, or source identifiers. |
| UUID | Adds a globally unique id header to each event. Critical for downstream deduplication (since Flume provides at-least-once semantics). |
| Regex extraction | Parses the event body using a regular expression and sets extracted groups as header values. |
| Regex Filtering | Includes or excludes events by matching the event body against a regular expression pattern. |
| Morphline | Filters and transforms events through a Morphline configuration file. Can conditionally drop events or enrich headers. |
5.6.3 Channel Storage Implementations: Memory Channel vs File Channel
The Channel Is the Reliability Boundary. The choice of Channel implementation is the single most important configuration decision in a Flume agent because it determines the trade-off between throughput and data durability. Flume provides two primary channel implementations — Memory Channel and File Channel — and a JDBC Channel backed by an embedded Derby database (rarely used in production).
Detailed Comparison: Memory Channel vs File Channel
| Property | Memory Channel | File Channel |
|---|---|---|
| Storage Mechanism | In-memory Java queue within the agent JVM process. | Transactional write-ahead log on the local filesystem of the agent host. Events are written to disk and fsynced before acknowledgment. |
| Throughput | Very high. No disk I/O bottleneck; events are queued and dequeued in RAM. | Lower than Memory Channel. Every transaction involves a local disk write with an fsync call, adding I/O latency. |
| Latency | Sub-millisecond event handoff. | Milliseconds per transaction (dominated by disk seek and sync time). |
| Durability | None. All buffered events are lost permanently if the agent process crashes, the host reboots, or power is lost. | Full. Events are persisted to disk before the Source's transaction commits. Events survive agent crashes, restarts, and power failures. |
| Capacity | Limited by available JVM heap memory. Default capacity is 100 events. | Limited by available disk space. Default capacity is 1,000,000 events. Will also stop accepting events if free disk space in the checkpoint directory drops below 500 MB (configurable via minimumRequiredSpace). |
| Recovery After Restart | Not possible. Buffered events are gone. | Automatic. On restart, the File Channel reads its checkpoint and data directories to restore all uncommitted events. |
| Ideal Use Case | High-volume non-critical telemetry, development/debugging, or scenarios where a small amount of event loss is acceptable and throughput is paramount. | Mission-critical log ingestion, financial event processing, audit trails, or any scenario where zero data loss across agent failures is required. |
Volatile Memory Channel Risk. The Memory Channel stores events entirely in the agent's JVM heap. If the Flume agent process terminates for any reason — an OutOfMemoryError, a host reboot, a kernel panic, a kill -9 — every event currently buffered in the Memory Channel is permanently and irrecoverably lost. There is no write-ahead log, no checkpoint, and no recovery mechanism. For production ingestion systems where data loss is unacceptable (financial transactions, security audit logs, regulatory compliance data), always use the File Channel. The Memory Channel is appropriate only for best-effort or high-throughput scenarios where occasional data loss has no business impact.
Batching for Efficiency
Flume processes events in batches within each transaction rather than one at a time. The default batch size is 100 events per transaction (configurable per component). This is particularly important for the File Channel because every transaction requires a disk write and fsync call. Processing 100 events per transaction rather than 1 event per transaction dramatically reduces the number of disk syncs and improves throughput by orders of magnitude.
Similarly, the Spooling Directory Source reads files in batches of 100 lines (configurable via batchSize), and the Avro Sink attempts to send 100 events per RPC call to the downstream agent.
5.6.4 Advanced Topologies: Agent Chaining, Fan-Out, and Multiplexing
Beyond Single Agents. Real-world Flume deployments involve multiple agents organized in distributed topologies. A single agent on a single machine is rarely sufficient because: (1) a single machine may not have enough disk or network bandwidth to aggregate logs from hundreds of sources; (2) edge servers generating logs are physically distributed across data centers; and (3) different downstream systems (HDFS for batch analytics, Solr for real-time search) require the same events to be routed to different destinations. Flume addresses all three challenges with three topology patterns: agent chaining, fan-out, and multiplexing.
1. Agent Chaining (Multi-Hop Architecture)
Agent chaining connects multiple Flume agents in series, where each agent performs a stage of processing or aggregation. The link between agents is established using an Avro Sink on the upstream agent sending events over the network to an Avro Source on the downstream agent.
[Edge Servers] [Aggregator Machine] [HDFS]
Edge Agent (Tier 1) Aggregator Agent (Tier 2)
┌─────────────┐ ┌──────────────┐
│ SpoolDir │ │ Avro Source │
│ Source │ │ │
│ ↓ │ RPC │ ↓ │
│ File Channel├───────►│ File Channel │
│ ↓ │ :10000│ ↓ │
│ Avro Sink │ │ HDFS Sink │
└─────────────┘ └──────────────┘
How it works:
- Tier 1 (Edge Agents): Run on each web server or edge machine. They collect local log events using a Spooling Directory Source, buffer them in a File Channel, and forward them via an Avro Sink to a central aggregator.
- Tier 2 (Aggregator Agents): Run on a smaller set of powerful machines. They receive events from many Tier 1 agents through an Avro Source, buffer them in a File Channel, and write them to HDFS using an HDFS Sink.
Why this matters: Aggregation produces fewer, larger HDFS files (rather than many tiny files from individual edge machines), which dramatically improves MapReduce performance and reduces HDFS NameNode memory pressure. The Avro RPC connection between agents is transactional — the Avro Sink will not commit its Channel transaction until the downstream Avro Source's Channel transaction has committed, ensuring at-least-once delivery across the network hop.
Tier configuration detail: Each agent on the same machine must use distinct File Channel checkpoint and data directories to avoid filesystem collisions:
edge.channels.channel1.checkpointDir = /tmp/edge/file-channel/checkpoint
edge.channels.channel1.dataDirs = /tmp/edge/file-channel/data
aggregator.channels.channel2.checkpointDir = /tmp/aggregator/file-channel/checkpoint
aggregator.channels.channel2.dataDirs = /tmp/aggregator/file-channel/data
2. Fan-Out Topology (Replication)
Fan-out delivers events from a single Source into multiple Channels simultaneously, so each Channel feeds a different Sink and destination. This is the default behavior when a Source is connected to multiple Channels.
┌──→ [File Channel] → [HDFS Sink] → HDFS (batch analytics)
[SpoolDir Source] ──┤
└──→ [Memory Channel] → [Solr Sink] → Solr (real-time search)
How it works: The Source replicates each incoming event into every connected Channel. Each Channel-Sink pair operates independently with its own transaction. If one Channel's transaction fails (e.g., the channel is full), the Source's transaction to that channel is rolled back and retried later.
Optional Channels: In a fan-out scenario, you can designate certain channels as optional using selector.optional. If an optional channel's transaction fails, it does not block the Source from committing events to the other (required) channels. This is useful when one destination is critical (HDFS via File Channel) and another is best-effort (Solr indexing via Memory Channel).
Real-world example — Near-Real-Time Indexing: A web log ingestion system fans out events to both an HDFS sink (main repository, required channel) and a MorphlineSolrSink (search index, optional channel). If Solr is temporarily unavailable, events continue flowing to HDFS without interruption.
3. Multiplexing (Header-Based Routing)
Multiplexing extends fan-out with selective routing: instead of replicating every event to every channel, a multiplexing selector inspects event header attributes and routes each event to a specific Channel based on configurable rules.
┌──→ [Channel A] → [Alert Sink] (events where level=ERROR)
[Syslog Source] ────┤
├──→ [Channel B] → [HDFS Sink] (events where level=INFO)
│
└──→ [Channel C] → [Archive Sink] (events where level=DEBUG)
Configuration example:
edge.sources.source1.selector.type = multiplexing
edge.sources.source1.selector.header = level
edge.sources.source1.selector.mapping.ERROR = channelA
edge.sources.source1.selector.mapping.INFO = channelB
edge.sources.source1.selector.default = channelC
The multiplexing selector reads the level header from each event and routes it to the mapped channel. Events with unmapped header values are sent to the default channel. This pattern is essential for content-based routing where different event severities or types must be processed differently.
Sink Groups: Failover and Load Balancing. When multiple second-tier aggregator agents are available, a first-tier agent can use a Sink Group to distribute or failover across them. A Sink Group treats multiple Avro Sinks as a single logical sink with a processor policy:
- Load Balance processor: Distributes events across sinks using round-robin selection (or a pluggable selector). If a sink is unavailable, the next sink is tried. With
backoff = true, failed sinks are blacklisted for exponentially increasing timeout periods (up to 30 seconds). - Failover processor: Assigns priority ordering to sinks. The highest-priority sink is preferred; if it fails, the next-highest is tried. Failed sinks are blacklisted with exponential backoff (up to 30 seconds, controlled by
processor.maxPenalty).
Sink Groups are critical for production resilience — without them, the failure of a single aggregator agent would cause the first-tier agent's File Channel to fill up and eventually lose events.
5.6.5 Flume Reliability Modes
Flume's Delivery Guarantee Spectrum. Flume supports three reliability modes that represent different points on the trade-off curve between performance (throughput, latency) and data safety (zero data loss). The choice of reliability mode is determined by the combination of Channel type, topology configuration, and whether transactional delivery is enabled between agent hops.
1. Best Effort Mode
- Mechanism: Events are transmitted without transactional delivery guarantees. The Source pushes events into the Channel without waiting for commit confirmation, and the Sink delivers events to the destination without transactional rollback on failure.
- Failure Behavior: If a downstream network hop fails, a Channel queue overflows, or the agent crashes, buffered events are dropped silently with no retry.
- Configuration: Typically uses a Memory Channel (no disk persistence) with no retry logic.
- Use Case: High-volume telemetry, sensor data streams, or any scenario where approximate data is sufficient and maximum throughput is the priority.
2. Store-and-Forward Mode
- Mechanism: Combines disk-backed File Channels with the agent's local transaction system. Events are written to the File Channel (persisted to local disk with fsync) before the Source's transaction is committed. If the downstream destination (HDFS, another agent) is unavailable, events remain safely stored in the local File Channel until connectivity is restored, at which point the Sink resumes delivery.
- Failure Behavior: Events survive agent crashes, host reboots, and network partitions. On restart, the File Channel replays its checkpoint and data directories to restore all uncommitted events.
- Limitation: This mode guarantees delivery within a single agent or between two adjacent agents, but does not provide end-to-end acknowledgment from the original event source to the final destination.
- Use Case: Production log ingestion where zero data loss within the Flume infrastructure is required.
3. End-to-End Reliability Mode
- Mechanism: Implements explicit transactional handshakes across the entire chain from the initial event source to the final destination Sink. An event is acknowledged back to the generator only after the final Sink confirms a successful permanent write to the destination storage.
- Failure Behavior: If any component in the chain fails, the event remains at the last successfully committed stage and is retried until end-to-end delivery succeeds.
- Implementation: Achieved by chaining multiple agents with Avro Sink/Source pairs, where each hop uses transactional File Channels. The Avro Sink on Agent N will not commit its Channel transaction until it receives confirmation that Agent N+1's Channel transaction has committed. This cascading transactional guarantee propagates through every tier.
- Use Case: Mission-critical regulatory or audit data where at-least-once delivery from source to final destination must be guaranteed even across multiple network hops.
At-Least-Once, Not Exactly-Once. Even in End-to-End Reliability Mode, Flume guarantees at-least-once semantics, not exactly-once. Duplicates are possible because: (1) after an agent restart, the Spooling Directory Source will re-read events from an uncompleted file even if some events were already committed to the channel; (2) after a Sink restart, events that were written to the destination but whose channel transaction was not yet committed will be re-sent. Exactly-once semantics would require a two-phase commit protocol, which is prohibitively expensive for high-volume event ingestion. In practice, downstream deduplication (using MapReduce, Hive, or Spark jobs with the UUID interceptor) is the standard approach to handling duplicates.
5.6.6 Student Questions and Answers
Q: Why would an enterprise choose a File Channel over a Memory Channel in Flume if Memory Channels offer much higher throughput?
A: A Memory Channel buffers events in volatile RAM within the agent JVM process. If the host machine experiences a power failure, the Flume agent crashes due to an OutOfMemoryError, or the machine reboots, all buffered log data in the Memory Channel is permanently and irrecoverably lost. A File Channel persists events to local disk using a transactional write-ahead log with fsync, guaranteeing that buffered events survive agent crashes and system reboots. For mission-critical ingestion (financial logs, security audit trails, compliance data), the File Channel's zero-data-loss guarantee far outweighs its lower throughput. Additionally, the File Channel's default capacity of 1,000,000 events (versus the Memory Channel's 100 events) provides a much larger buffer to absorb temporary downstream outages.
Q: How do Flume agents support multi-hop logging across multiple physical servers?
A: Flume supports multi-hop topologies by chaining agents using Avro Sink and Avro Source pairs. An edge agent (Tier 1) on a web server collects local log events into its File Channel and uses an Avro Sink to transmit events over the network via Avro RPC to an Avro Source running on a central aggregator agent (Tier 2). The Avro RPC connection is transactional — the Tier 1 agent's Avro Sink will not commit its channel transaction until it receives confirmation that the Tier 2 agent's Avro Source has committed events to its own channel. This guarantees at-least-once delivery across the network hop. The Tier 2 aggregator then writes the accumulated events to HDFS using an HDFS Sink. This architecture produces fewer, larger HDFS files (improving MapReduce efficiency) and allows centralized management of the final storage destination.
Q: What happens if a second-tier aggregator agent becomes unavailable — how does Flume prevent data loss on the first-tier edge agents?
A: If a second-tier aggregator goes down, first-tier agents configured with a single Avro Sink will buffer incoming events in their File Channels (up to the default 1,000,000 event capacity) until the aggregator recovers. However, if the File Channel fills up before the aggregator comes back, new events will be lost. To prevent this, production deployments use Sink Groups with multiple Avro Sinks pointing to different aggregator agents. With a load_balance or failover sink processor, if the primary aggregator is unavailable, events are automatically redirected to a secondary aggregator. This provides resilience against single-aggregator failures without losing events.
Exam Guidance Summary
Consolidated Exam Topics and Question Patterns — Lecture 5
MapReduce and Hive SQL Translation
- Practice converting declarative SQL queries containing
GROUP BY,JOIN, andORDER BYinto multi-stage MapReduce execution diagrams. For each query, identify: - Mapper output keys and intermediate key-value pairs
- Combiner pre-aggregations (and when they are safe vs. unsafe)
- Partitioned Reducers for parallel group aggregation
- Single global sorting Reducer for
ORDER BYacross all groups - Know that Hive compiles HiveQL into a DAG of MapReduce jobs, and that the execution engine can be MapReduce, Tez, or Spark.
YARN Core Architecture and Execution Lifecycle
- Trace the complete 10-step YARN job submission sequence: Client → RM → NM1 → AppMaster → RM → NM2/NM3 → Worker Containers. Detail the interaction between Client, Resource Manager, Node Manager, Application Master, and worker Containers at each step.
- Explain why YARN was decoupled in Hadoop 2.0: JobTracker SPOF, 4,000-node / 40,000-task scalability ceiling, and inability to run non-MapReduce workloads.
- Memorize YARN network ports:
8088— Resource Manager Web UI (HTTP console)8032— Resource Manager IPC / Admin (job submission port)8030— Resource Tracker (NM heartbeats to RM)8042— Node Manager Web UI
YARN Multi-Tenant Schedulers and Resource Calculations
- Solve multi-tenant queue resource percentage allocations using the relative weight formula:
\[ R(q_k) = \frac{w_k}{\sum_{j=1}^{K} w_j} \times 100\% \]
- Explain the operational function of:
- Zero-weight queues (
weight = 0.0): zero guaranteed share, executes only when all higher-priority queues are idle minResourcesfloors: guaranteed baseline under heavy cluster loadmaxResourcesceilings: hard cap preventing monopolization- Know the three scheduler types and when to use each:
- FIFO: simple, single-tenant only
- Fair: dynamic equal sharing, idle capacity borrowing, Cloudera default
- Capacity: hard SLA partitions, queue elasticity, Hortonworks default
- Self-Study Assignment: Study the Capacity Scheduler queue parameters and comparison matrix for upcoming quizzes and final examinations.
Sqoop Command Syntax and Splitting Logic
- Memorize key Sqoop CLI parameters:
--connect,--table,--target-dir,--num-mappers,--split-by,--where,--password-file. - Understand the parallel import splitting formula:
\[ \text{Step Size} = \frac{\text{MAX} - \text{MIN} + 1}{\text{num-mappers}} \]
- Trace mapper queries: given MIN, MAX, and num-mappers, write the exact
WHEREclause for each mapper. - Identify primary key failure modes: explain why multi-mapper Sqoop imports fail on tables without primary keys unless
--split-byor--num-mappers 1is specified. - Know that Sqoop generates Map-only jobs (0 reducers) because raw ingestion needs no key aggregation.
Flume Streaming Component Architecture
- Diagram the Flume Agent architecture: Source → Channel → Sink.
- Compare Memory Channels (high throughput, volatile, events lost on crash) vs File Channels (disk-backed, zero data loss, store-and-forward).
- Differentiate Sqoop vs Flume:
- Sqoop: structured batch RDBMS import via MapReduce jobs on YARN
- Flume: unstructured real-time streaming log ingestion via long-running JVM agents
- Know the three reliability modes: Best Effort, End-to-End, Store-and-Forward.
- Understand agent chaining (multi-hop with Avro Sink/Source), fan-out (one source to multiple channels), and multiplexing (header-based routing).
Key Industry Applications
- Enterprise Data Lake Ingestion with Apache Sqoop — Financial institutions and retail corporations use Sqoop nightly batch workflows to extract transactional database tables from MySQL, Oracle, and DB2 instances into HDFS and Hive Data Lakes, enabling historical trend analytics across millions of customer records. For example, a bank might import all credit card transaction tables each night, then run Hive queries to detect anomalous spending patterns the next morning.
- Real-Time Web Server Log Aggregation with Apache Flume — Large-scale e-commerce platforms deploy Flume agents directly on hundreds of front-end web server nodes using Exec and Spooling Directory Sources to stream web access logs continuously into HDFS and Elasticsearch clusters for real-time fraud detection and search indexing. A typical deployment uses a two-tier agent topology: edge agents on web servers forward events via Avro Sink to central aggregator agents, which write to HDFS with time-partitioned directories.
- Multi-Tenant Enterprise Cluster Orchestration with YARN — Cloud data platforms utilize YARN Fair and Capacity Schedulers to partition multi-petabyte clusters across diverse corporate departments (Data Engineering, Data Science, Executive Analytics), ensuring critical batch ETL pipelines meet strict SLAs while granting interactive query access to data scientists. The Fair Scheduler's dynamic rebalancing ensures that a department's idle capacity is not wasted — it is temporarily borrowed by active queues and reclaimed when needed.
BDA Lecture 5 notes
Sections Breakdown
Recap of the MapReduce execution pipeline (RecordReader, Mapper, Combiner, Shuffle and Sort, Reducer), single- vs multi-reducer topologies, and Hive's compilation of SQL into MapReduce DAGs.
The Hadoop 1.0 to 2.0 redesign, Resource Manager and Node Manager roles, the container abstraction with Linux Cgroups, and the Application Master.
The 10-step YARN job lifecycle, Application Manager vs Scheduler, asynchronous execution, administration ports 8088 and 8032, and a connection-refused troubleshooting walkthrough.
FIFO, Fair, and Capacity schedulers, weight-based queue share calculations, zero-weight best-effort queues, min/max resource bounds, and Dominant Resource Fairness.
Sqoop CLI commands, import splitting logic and step size formula, primary key pitfalls, output formats, a 220,000-record live demo, and exports back to RDBMS.
Flume agent architecture of Source, Channel, and Sink, Memory vs File channels, multi-agent topologies, and the three reliability modes.
Consolidated exam topics and question patterns across MapReduce/Hive translation, the YARN lifecycle, schedulers, Sqoop, and Flume.
Real-world Sqoop data lake ingestion, Flume web log aggregation, and multi-tenant YARN cluster orchestration.
Exam Revision Notes
Below is the distilled, exam-ready core. Every entry comes from the full explanation above. Use this section for rapid review; return to the main notes when a point needs more context.
MapReduce and Hive SQL Translation
Must-know: Convert SQL queries (GROUP BY, ORDER BY, JOIN) into multi-stage MapReduce execution diagrams. Understand single vs multi-reducer topology trade-offs.
\[ \text{HiveQL Query} \longrightarrow \text{Query Parser / Compiler} \longrightarrow \text{Logical Plan} \longrightarrow \text{Physical MapReduce Job(s)} \]
⚠️ Top pitfall: Using a combiner with non-associative operations like mean/average produces incorrect results.
Self-check: How many MapReduce stages does Hive use for SELECT dept, SUM(sales) FROM t GROUP BY dept ORDER BY SUM(sales) DESC?
Connects to: YARN Architecture and Containers, Sqoop Relational Ingestion
YARN Architecture and Container Abstractions
Must-know: Explain why YARN was decoupled in Hadoop 2.0 (JobTracker SPOF, 4,000-node limit, MapReduce-only workloads). Understand RM, NM, Container, AppMaster roles and the component mapping table.
\[ \text{Container} = \{\text{VCores}, \text{RAM (MB)}, \text{Disk I/O Bounds}, \text{Network Bandwidth (MBps)}\} \]
⚠️ Top pitfall: Confusing MapReduce APIs (old vs new) with MapReduce implementations (1 vs 2). Also: atomic container allocation means unused resources are wasted.
Self-check: What is the key difference between the JobTracker and YARN's Resource Manager?
Connects to: MapReduce and Hive Foundation, YARN Execution Flow and Administration, YARN Scheduling and Resource Allocation
YARN Execution Flow and Administration
Must-know: Memorize RM Web UI port 8088 and RM IPC submission port 8032. Understand the 10-step YARN lifecycle, the split between Application Manager (lifecycle) and Scheduler (allocation), and that HDFS and YARN are independent daemons started separately.
⚠️ Top pitfall: HDFS running does NOT mean YARN is running. start-dfs.sh and start-yarn.sh are separate scripts. Sqoop/hadoop jar failures on port 8032 are caused by missing ResourceManager, not missing HDFS.
Self-check: Walk through the 10-step YARN job lifecycle. At which step does the client stop talking to the RM and start talking directly to the AppMaster?
Connects to: YARN Architecture and Containers, YARN Scheduling and Resource Allocation
YARN Scheduling Algorithms and Resource Allocation
Must-know: Three YARN schedulers (FIFO, Fair, Capacity) and their trade-offs; the weight-based queue share formula R(q_k) = w_k / Σw_j × 100%; Dominant Resource Fairness equalizes each app's dominant resource share across CPU and memory; zero-weight queues are best-effort only; minResources = floor guarantee, maxResources = ceiling cap
\[ R(q_k) = \frac{w_k}{\sum_{j=1}^{K} w_j} \times 100\% \]
⚠️ Top pitfall: Confusing Fair Scheduler with Capacity Scheduler — Fair uses dynamic rebalancing and supports DRF, while Capacity uses hard-guaranteed minimums with FIFO within each queue. Also: assuming zero-weight queues never run jobs — they run when all weighted queues are idle.
Self-check: A cluster has 100 CPUs and 10 TB memory. App A requests (2 CPUs, 300 GB) per container; App B requests (6 CPUs, 100 GB) per container. Under DRF, which app gets more containers and why?
Connects to: YARN Architecture and Containers, YARN Execution Flow and Administration
Sqoop Relational Data Ingestion
Must-know: Sqoop converts CLI commands into Map-only MapReduce jobs. The step size formula (MAX - MIN + 1) / num-mappers determines how rows are partitioned across mappers. Without a primary key, multi-mapper imports fail unless --split-by is specified or --num-mappers is set to 1. Four output formats: text (default), SequenceFile, Avro, Parquet. --delete-target-dir removes existing HDFS dir; --append adds to it. Sqoop can also export from HDFS to RDBMS and create Hive tables via --hive-import.
\[ \text{Step Size} = \frac{\text{MAX} - \text{MIN} + 1}{\text{num-mappers}} \]
⚠️ Top pitfall: Multi-mapper import fails without primary key unless --split-by or --num-mappers 1 is specified
Self-check: Given a table with MIN(id)=0, MAX(id)=9999, and --num-mappers 5, what SQL WHERE clause does Mapper 3 execute?
Connects to: MapReduce and Hive Foundation, Flume Streaming Ingestion
Flume Streaming Log Ingestion
Must-know: Flume handles unstructured streaming data (logs, events) with at-least-once semantics, while Sqoop handles structured batch data from RDBMS. Flume agents have Source→Channel→Sink architecture. Memory Channel is fast but volatile; File Channel is slower but durable. Agent chaining uses Avro Sink/Source pairs for multi-hop aggregation. Fan-out replicates events to multiple channels; multiplexing routes by header. All three reliability modes (best-effort, store-and-forward, end-to-end) use at-least-once semantics, not exactly-once.
⚠️ Top pitfall: Confusing Flume with Sqoop (streaming vs batch) or assuming Flume provides exactly-once delivery (it provides at-least-once, with possible duplicates requiring downstream deduplication via UUID interceptor). Another common mistake is assuming Memory Channel provides data durability — it does not; all buffered events are lost on agent crash.
Self-check: A Flume agent uses a Memory Channel and buffers 5,000 events. The agent process crashes. How many events can be recovered? (Answer: Zero — Memory Channel stores events in volatile RAM with no persistence. For zero-data-loss recovery, use a File Channel.)
Connects to: Sqoop Relational Ingestion