All projects

MapReduce-Reloaded

In active development · big data & distributed systems · Go & Python

A from-scratch rebuild of Google’s MapReduce, as described by Dean and Ghemawat in MapReduce: Simplified Data Processing on Large Clusters⁠ (OSDI 2004). Go is the main implementation. Python is a second implementation of the shared contract and of single-machine mode, so the two can be checked against each other byte for byte.

Work in progress. This page describes the code on 23 October 2026. Every feature of the paper is built in Go. The planned modern extensions (object storage and compression), the benchmarks and the final report are not built yet.

At a glance

23 / 23 features of the paper built, each mapped to its code and a named test; a test fails if the mapping names a file or test that does not exist

350 passing tests: 146 in Go and 204 in Python

50 chaos runs in a row, with random worker kills, stalls and coordinator restarts, that still match single-machine output

The problem

MapReduce is the paper that made large-scale data processing ordinary. You write a map function and a reduce function, and the system handles splitting the input, scheduling thousands of tasks, moving data between machines and surviving the machines that fail along the way. Spark, Hadoop and most modern batch systems descend from it.

I wanted to understand those guarantees by building them, not by reading about them. The aim is a faithful rebuild: every feature in the paper, each with a test that proves it works, then extended with what a 2026 system would use instead of 2004 infrastructure.

Approach

A single-machine oracle first. The first working version runs the whole job in one process (mr run -local). Every later version, distributed or fault tolerant, must produce exactly the same output files. The tests check each version against it.

One contract, two languages. Go and Python share a gRPC and protobuf message contract, a documented on-disk record format with a checksum on every block, and a partition hash written out by hand in both languages. The built-in hash functions of both languages use a random seed per process, so two workers would disagree about which reducer gets a key. Test vectors and golden files hold both implementations to the same bytes.

Small, reviewable steps. Each piece of work is one issue and one pull request, and each phase adds a short design record. A make check quality gate runs every linter and test before each push. There is no hosted CI.

What I built

What chaos testing found

The chaos test runs word count while, at random moments, workers die, new ones join, workers stall for 50–250 ms and the coordinator restarts from its log. A run passes only if the output files and the counters equal single-machine mode. Each run’s choices come from a seed, so a failure can be replayed.

Its first runs failed on seed 7. The output held an extra _temporary directory: a reduce attempt that had already lost, to a backup copy or because its worker was declared dead, kept running and recreated the directory after the job had cleaned it up. Now the job creates that directory when it starts, and attempts never do. An attempt that runs after the job has finished fails and leaves nothing behind.

The test runs in one process, so a “dead” worker behaves like a machine cut off from the network, not one that lost power. Killing containers and dropping packets need the Compose cluster, and that driver is not written yet.

What is planned

Further extensions, such as a full Python coordinator and worker, a standby coordinator, skew handling and Parquet input, are stretch goals and may not be built.

What I’m learning

Stack

Go Python gRPC Protocol Buffers Docker Compose Prometheus Grafana Distributed systems