Course contentsShow
← Node.js
Lesson 469 of 1,9805. Streams and BackpressureFree lesson

Streaming MapReduce Tasks

Build a distributed data processing architecture where MapReduce stages are connected via streams.

In this lesson, you will learn how to chain Transform streams to build a MapReduce pipeline that processes data piece-by-piece rather than loading massive datasets into memory.

The MapReduce Pipeline

In a classic MapReduce architecture, you perform two main operations: Map (transforming individual data items) and Reduce (aggregating those items into a summary). By using Node.js streams, we can connect these stages into a single, high-performance pipeline.

Because we are using streams, we don't have to wait for the entire file to be "mapped" before the "reduce" stage starts. As soon as the first chunk is processed by the Mapper, it moves immediately to the Reducer.

Connecting the Stages

To build this, we create two Transform streams. The Mapper acts as the first Transform stream, emitting mapped objects. The Reducer acts as a second Transform stream, maintaining an internal state to keep a running total or summary.

const { Transform } = require('stream');

// The Map Stage: Convert raw log lines to specific data points
const mapper = new Transform({
  objectMode: true,
  transform(chunk, encoding, callback) {
    const data = JSON.parse(chunk);
    this.push({ category: data.type, count: 1 });
    callback();
  }
});

// The Reduce Stage: Aggregating counts using internal state
const reducer = new Transform({
  objectMode: true,
  construct() { this.totals = {}; },
  transform(chunk, encoding, callback) {
    this.totals[chunk.category] = (this.totals[chunk.category] || 0) + chunk.count;
    callback();
  },
  flush(callback) {
    this.push(this.totals);
    callback();
  }
});

// Pipe the stages together
inputStream.pipe(mapper).pipe(reducer).pipe(process.stdout);

Why Streams Matter Here

In a standard MapReduce approach, you might finish the entire "Map" phase and write the result to a temporary file before starting "Reduce." With Node.js streams, we bypass that I/O overhead. The data flows through the Mapper directly into the Reducer’s transform method. If the Reducer is slower than the Mapper, the inherent Backpressure mechanism of Node.js streams will automatically signal the Mapper to slow down, preventing your process from crashing due to memory overflow.

Key Takeaway: Streaming MapReduce connects your processing stages into a unified pipeline where the reducer processes data incrementally as it emerges from the mapper, maximizing efficiency through natural backpressure.