TP1 - MapReduce

Introducción

En este trabajo práctico construirán un sistema MapReduce distribuido. Son dos programas: el coordinador y el worker. Habrá un solo coordinador y uno o más workers ejecutándose en paralelo. En un sistema real los workers se ejecutarían en máquinas distintas, pero acá los ejecutarán todos en una sola.

Los workers hablan con el coordinador por RPC. Cada worker le pide una tarea al coordinador en un loop, lee la entrada de uno o más archivos, ejecuta la tarea, y escribe la salida en uno o más archivos. El coordinador debe darse cuenta si un worker no terminó su tarea en un tiempo razonable (para este trabajo práctico, diez segundos) y darle esa misma tarea a otro worker.

El diseño sigue el paper de MapReduce. Tengan en cuenta una diferencia de vocabulario: el paper le dice master a lo que acá llamamos coordinador.

El transporte entre el coordinador y los workers es gRPC. El contrato del servicio lo diseñan ustedes: definir los RPCs y los mensajes que viajan es parte del trabajo.

Preparación del entorno

Necesitan tres cosas instaladas.

Go 1.22 o superior. Si no lo tienen, sigan la documentación oficial.

protoc y los dos plugins de Go. Los necesitan desde el comienzo:

# macOS
brew install protobuf
# Debian / Ubuntu
sudo apt install protobuf-compiler

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

El código Go generado a partir del .proto no viene versionado en el repositorio: lo generan ustedes con make proto. Hasta que no lo ejecuten, el proyecto no compila. Así que esto tienen que tenerlo instalado desde el principio, no recién cuando modifiquen mr/mrpb/mr.proto.

El repositorio base. La cátedra les va a proveer un link para acceder al repositorio de este trabajo práctico. Ese repositorio lo genera y lo mantiene la cátedra, y ya viene con el código base. El trabajo debe desarrollarse y entregarse ahí: no está permitido usar repositorios propios. Pueden consultar los detalles en la sección ¿Cómo es la entrega de los TPs? y en la Política de colaboración del FAQ.

Una vez que lo tengan clonado, adentro van a ver esto:

$ ls
README.md  src

Lo primero que hay que hacer, antes de compilar nada, es generar el código Go del .proto:

$ cd src
$ make proto

Les damos una implementación secuencial de MapReduce en src/main/mrsequential.go. Ejecuta las tareas map y reduce una por vez, en un solo proceso. También les damos un par de aplicaciones MapReduce: conteo de palabras en mrapps/wc.go, e indexado de texto en mrapps/indexer.go. Pueden ejecutar el conteo de palabras secuencial así:

$ cd src/main
$ go build -buildmode=plugin ../mrapps/wc.go
$ rm -f mr-out*
$ go run mrsequential.go wc.so pg-*.txt
$ sort mr-out-0 | head
A 509
ABOUT 2
ACT 8
ACTRESS 1
ACTUAL 8
ADLER 1
ADVENTURE 12
ADVENTURES 7

mrsequential.go deja su salida en mr-out-0. La entrada son los archivos pg-xxx.txt. Tómense un rato para leerlo: les va a servir de referencia, y pueden reutilizar parte de su código.

El trabajo

El trabajo consiste en implementar un MapReduce distribuido, formado por dos programas: el coordinador y el worker. Habrá un solo coordinador y uno o más workers ejecutándose en paralelo.

El worker (o los workers) le va a pedir tareas al coordinador, va a leer la entrada de un archivo, ejecutar la tarea, y escribir la salida en un archivo. El coordinador debe darse cuenta si un worker no terminó su tarea en diez segundos, y darle esa tarea a otro worker.

Lo que tienen que escribir va en tres archivos:

Archivo Qué va ahí
src/mr/mrpb/mr.proto El contrato gRPC: el servicio, sus RPCs y los mensajes que viajan. Viene con un RPC Example que está solo a modo de ejemplo.
src/mr/coordinator.go El coordinador.
src/mr/worker.go El worker.

Cada vez que editen el .proto tienen que regenerar el código Go:

$ cd src
$ make proto

Para ejecutarlo manualmente, primero compilen una aplicación MapReduce como plugin:

$ cd src/main
$ go build -buildmode=plugin ../mrapps/wc.go

En una terminal, arranquen el coordinador:

$ rm -f mr-out*
$ go run mrcoordinator.go sock123 pg-*.txt

sock123 es el nombre del socket Unix por el que se comunican el coordinador y los workers. Los archivos pg-*.txt son las entradas: cada uno es un split, es decir, la entrada de una tarea map.

En una o más terminales adicionales, arranquen workers:

$ go run mrworker.go wc.so sock123

Los workers y el coordinador tienen que ejecutarse desde el mismo directorio, porque sock123 es una ruta relativa y porque los archivos intermedios se escriben ahí.

Cuando terminen, miren la salida en mr-out-*. Cuando el trabajo esté completo, los archivos de salida ordenados y concatenados tienen que dar lo mismo que la versión secuencial:

$ cat mr-out-* | sort | head
A 509
ABOUT 2
ACT 8
ACTRESS 1
ACTUAL 8
ADLER 1
ADVENTURE 12
ADVENTURES 7

Les damos un conjunto de tests en src/mr/mr_test.go. Los tests verifican que las aplicaciones wc e indexer den la salida correcta con la entrada pg-*.txt, que las tareas map y reduce corran en paralelo, y que la implementación se recupere de workers que se caen a mitad de una tarea.

$ cd src
$ make mr

Si ejecutan los tests sin modificar el esqueleto, no van a terminar nunca: el coordinador no finaliza, porque Done() devuelve false para siempre.

Cuando esté todo listo tienen que pasar los siete tests: TestWc, TestIndexer, TestMapParallel, TestReduceParallel, TestJobCount, TestEarlyExit y TestCrashWorker.

Algunas reglas

Consejos

Entrega

La entrega se hace en el mismo repositorio provisto por la cátedra en el que desarrollaron el trabajo. La fecha de entrega está en la página del Calendario.

Este es un trabajo práctico individual. Antes de entregar, repasen la Política de colaboración y lo que dice el FAQ sobre entregas tardías y uso de IA.