Implementación de un Sistema MapReduce Distribuido

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 nReduce particiones 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-X debe seguir el formato %v %v para imprimir clave y valor, respetando la convención mostrada en mrsequential.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.

Etiquetas: Go MapReduce RPC json Programación Concurrente

Publicado el 6-26 18:30