o problema
Os sistemas de arquivos distribuídos que existiam antes — AFS, xFS, Frangipani, GPFS, Lustre — foram desenhados para um mundo em que a máquina é confiável, o arquivo é pequeno, o cliente relê o que acabou de ler e a interface é POSIX. O Google tinha o contrário disso em todos os eixos. Milhares de máquinas de peça barata: alguma coisa está quebrada agora, e alguma coisa não vai voltar. Arquivos de vários GB contendo milhões de documentos web, onde gerenciar bilhões de arquivinhos de KB seria inviável mesmo que o file system aguentasse. Mutação quase sempre por append, leitura quase sempre sequencial em streaming — cache de bloco no cliente não paga o próprio custo de coerência quando o working set não cabe em lugar nenhum.
Havia ainda um padrão que nenhum file system da época atendia: centenas de produtores, um por máquina, escrevendo simultaneamente no mesmo arquivo, que serve de fila produtor-consumidor ou de merge de resultados. Com write tradicional em offset, isso exige um lock manager distribuído. Pagar esse preço em cada job de processamento de dados era caro demais.
a ideia
Em vez de implementar a semântica que as aplicações teoricamente poderiam querer, os autores olharam para o que as aplicações de fato faziam e cortaram tudo que não era usado. Duas concessões da aplicação compram toda a simplicidade do sistema: a interface não é POSIX (ganha uma operação de append atômico onde quem escolhe o offset é o file system) e o modelo de consistência é relaxado (região concorrente fica consistente mas indefinida, e o append pode deixar padding e duplicata pelo caminho).
Com isso na mão, dá para fazer o que a literatura mandava não fazer: um master único, centralizado, com todo o estado em memória. Ele decide colocação e replicação com conhecimento global, e nunca toca em dado de usuário.
como funciona
Cada arquivo é fatiado em chunks fixos de 64 MB, cada um com um handle de 64 bits imutável, gravado como arquivo Linux comum em três chunkservers por padrão, espalhados por racks diferentes.
O master guarda três coisas: namespace, mapeamento arquivo→chunks e localização das réplicas. Tudo em memória. As duas primeiras persistem num operation log replicado em máquinas remotas — a resposta ao cliente só sai depois do flush local e remoto — com checkpoint em formato B-tree que é mapeado direto para a memória, sem parsing. A terceira não persiste: o master pergunta aos chunkservers no startup e se mantém atualizado pelos HeartBeats.
Leitura: o cliente converte offset em índice de chunk, pede ao master, recebe handle e localizações (mais as dos chunks seguintes, de brinde), cacheia e fala direto com a réplica mais próxima.
Escrita: o master dá um lease de 60 segundos a uma réplica, a primary, que serializa as mutações; todas as réplicas aplicam na mesma ordem. O fluxo de dados é desacoplado do de controle — o cliente empurra os bytes por uma cadeia linear de chunkservers escolhida por proximidade de IP, em pipeline sobre TCP, cada máquina usando toda a banda de saída e repassando assim que recebe. Só depois vai o comando à primary.
No record append, o cliente manda só o dado. Se ele não couber no chunk atual, a primary preenche o resto com padding e manda tentar no próximo — por isso o append é limitado a um quarto do chunk. Se falhar em alguma réplica, o cliente repete, e é daí que nascem as duplicatas. Réplica atrasada é detectada por número de versão do chunk e coletada como lixo; corrupção em disco, por checksum de 32 bits a cada bloco de 64 KB.
o que isso custou
A complexidade não sumiu, mudou de lugar: foi para a aplicação. Todo leitor precisa de registros auto-identificáveis, com checksum para descartar fragmento e padding, e id único para deduplicar quando a operação não é idempotente. O GFS não promete réplicas idênticas, só que o dado foi escrito ao menos uma vez.
O master é um ponto único para mutação. Reinicia em segundos, mas fica capenga por 30 a 60 segundos até coletar as localizações dos chunkservers, e os shadow masters só servem leitura, com metadado possivelmente atrasado. Como o cliente cacheia localização, ele pode ler de uma réplica velha dentro da janela do timeout.
A escrita individual é fraca — 6,3 MB/s para um cliente, metade do limite, e os autores escrevem que “writes are slower than we would like”. O append agregado cai de 6,0 para 4,8 MB/s de 1 para 16 clientes, porque o gargalo é a banda dos chunkservers que guardam o último chunk. Arquivo pequeno e muito lido vira hot spot: aconteceu com um executável de um chunk só, lançado em centenas de máquinas ao mesmo tempo. A coleta de lixo preguiçosa atrapalha quando o disco está apertado. E três réplicas gastam mais espaço bruto que RAID — o próprio paper diz que está explorando paridade e erasure codes. Os autores também avisam para não generalizar as medições: o GFS foi ajustado às aplicações do Google e vice-versa.
onde isso aparece hoje
O HDFS é essencialmente uma reimplementação aberta deste desenho: coordenador único de metadados, bloco grande, três réplicas, otimização para append e leitura sequencial. O MapReduce, publicado no ano seguinte, assume um armazenamento com exatamente estas propriedades embaixo. E a linha do “explorando erasure codes” virou o padrão de armazenamento frio na década seguinte, inclusive no próprio HDFS.