El objetivo es desarrollar un sistema MapReduce que contenga procesos de tipo worker y coordinador.
Se proporciona una versión secuencial de un solo hilo en el archivo src/main/mrsequential.go, que ejecuta una sola tarea Map seguida de una Reduce. También existen aplicaciones de ejemplo como el contador de palabras en mrapps/wc.go.
Para probar el contador de palabras, se pueden seguir estos pasos en la terminal:
cd ~/6.824/src/main
go build -race -buildmode=plugin ../mrapps/wc.go
rm mr-out*
go run -race mrsequential.go wc.so pg*.txt
more mr-out-0
El indicador -race activa la detección de condiciones de carrera en Go, lo cual es útil durante el desarrollo.
La implementación distribuida requiere un coordinador y múltiples workers que operan en paralelo. Los workers se comunican con el coordinador mediante RPC, solicitan tareas, leen archivos de entrada y generan archivos de salida.
Si un worker no finaliza su tarea dentro de un límite de tiempo de 10 segundos, el coordinador debe reasignar dicha tarea a otro worker disponible.
El código base se encuentra en mrcoordinator.go y mrworker.go, los cuales no deben alterarse. Toda la lógica personalizada debe escribirse en mr/coordinator.go, mr/worker.go y mr/rpc.go.
Para ejecutar una versión distribuida del contador de palabras, primero compilar el plugin:
go build -race -buildmode=plugin ../mrapps/wc.go
Luego, iniciar el coordinador con los archivos de entrada:
rm mr-out*
go run -race mrcoordinator.go pg-*.txt
En terminales separadas, lanzar los workers:
go run -race mrworker.go wc.so
Los resultados en los archivos mr-out-* deben coincidir con los de la versión secuencial.
El script de pruebas test-mr.sh verifica la exactitud de los resultados, el paralelismo en las tareas Map y Reduce, y la recuperación ante fallos de workers.
Reglas de Implementación
- Cada worker Map debe dividir su salida en
nReduceparticiones para las tareas Reduce. - Los archivos de salida final deben nombrarse como
mr-out-X, donde X es el identificador de la tarea Reduce. - Cada línea en
mr-out-Xdebe seguir el formato%v %vpara imprimir clave y valor, respetando la convención mostrada enmrsequential.go. - Los archivos intermedios generados por los Map workers deben almacenarse localmente con el nombre
mr-X-Y, siendo X el número de tarea Map e Y el número de tarea Reduce.
Consejos Prácticos
Para almacenar los pares clave-valor intermedios, se recomienda usar codificación JSON. Ejemplo de escritura en un archivo:
codificador := json.NewEncoder(archivo)
for _, parClaveValor := range paresKV {
if err := codificador.Encode(&parClaveValor); err != nil {
// manejar error de codificación
}
}
Para leer los pares desde un archivo intermedio:
decodificador := json.NewDecoder(archivo)
var listaPares []KeyValue
for {
var kv KeyValue
if err := decodificador.Decode(&kv); err != nil {
break
}
listaPares = append(listaPares, kv)
}
La función ihash(key) puede utilizarse para determinar a qué tarea Reduce asignar una clave específica.
El coordinador debe proteger el acceso a datos compartidos con mutexes, ya que actúa como servidor RPC concurrente.
Los workers pueden implementar un mecanismo de sondeo periódico usando time.Sleep() entre solicitudes, o el coordinador puede emplear sync.Cond para gestionar esperas.
Para simular fallos durante pruebas, se puede usar el plugin mrapps/crash.go. Para garantizar atomicidad en las escrituras, utilice archivos temporales con ioutil.TempFile y renombre con os.Rename.
Los archivos intermedios y de salida se generan en el directorio de trabajo actual, pero durante las pruebas se ubican en mr-tmp.