Design clássico · Sênior
Serviço de lock distribuído
Projeto de entrevista clássico. Pontos centrais: locks distribuídos.
Resposta curta
Projeto de entrevista clássico. Pontos centrais: locks distribuídos. Um lock distribuído serve a dois propósitos distintos: eficiência ou correção. No primeiro, o lock poupa trabalho repetido sem necessidade. No segundo, ele impede que processos concorrentes atrapalhem uns aos outros e estraguem o estado do sistema.
01 · CONTEXTO E REQUISITOS
Para que serve um lock distribuído
Um lock distribuído serve a dois propósitos distintos: eficiência ou correção. No primeiro, o lock poupa trabalho repetido sem necessidade. No segundo, ele impede que processos concorrentes atrapalhem uns aos outros e estraguem o estado do sistema.
Um lock num sistema distribuído não se comporta como um mutex de uma aplicação multithread: nós e rede podem falhar de forma independente, e é isso que torna o problema mais complicado.
Este caso usa como referência o Chubby, o serviço de lock do Google. Ele oferece uma interface parecida com a de um sistema de arquivos distribuído, com locks consultivos, e enfatiza disponibilidade e confiabilidade, não alto desempenho. Foi pensado para sincronização de granularidade grossa e, em particular, para eleger um líder entre servidores equivalentes; o Google File System, por exemplo, usa um lock do Chubby para indicar o seu servidor mestre.
Requisitos funcionais: adquirir e liberar um lock nomeado com exclusão mútua, e liberar o lock quando o cliente some. Requisitos não funcionais: o serviço não pode ser um ponto único de falha, e o recurso protegido precisa continuar seguro mesmo quando um cliente age sem saber que perdeu o lock.
02 · O PROBLEMA DO LEASE
O lock expira, mas o cliente não sabe
Todo lock precisa de prazo: sendo um lease, ele não fica preso para sempre quando o cliente cai. O risco é que, se uma pausa durar mais que o lease e o cliente não perceber que ele expirou, o cliente faça uma alteração insegura.
A pausa longa de coleta de lixo é só um exemplo. Checar a expiração do lock logo antes de escrever não resolve, porque uma pausa pode ocorrer em qualquer ponto, inclusive entre a última checagem e a escrita. Pacotes também podem ser atrasados arbitrariamente em redes Ethernet e IP, e uma escrita pode chegar ao armazenamento depois que o lease já expirou.
O Chubby descreve o mesmo risco em termos gerais: um processo que segura o lock L emite uma requisição R e falha; outro processo adquire L e age antes de R chegar; se R chegar depois, ela pode ser executada sem a proteção de L e sobre dados inconsistentes.
O cliente A pede o lock ao serviço de lock e o recebe, com um prazo de validade.
O cliente A fica pausado, por exemplo por uma pausa de coleta de lixo, e não percebe que o lease venceu.
Com o lease vencido, o cliente B adquire o lock e age sobre o recurso.
O cliente A volta da pausa sem perceber que o lease expirou e ainda grava: a requisição pode ser executada sem a proteção do lock, sobre dados inconsistentes, e o dado pode acabar corrompido.
03 · FENCING
O recurso precisa recusar quem perdeu o lock
A correção é incluir um fencing token em cada escrita ao armazenamento: um número que cresce, por exemplo incrementado pelo serviço de lock, a cada vez que um cliente adquire o lock.
O cliente A adquire o lease e recebe o token 33.
O cliente A entra numa pausa longa e o lease expira.
O cliente B adquire o lease, recebe o token 34 e envia a escrita ao armazenamento com esse token.
O cliente A volta e envia a escrita com o token 33; o armazenamento já processou uma escrita com token maior (34) e rejeita a requisição.
Isso exige que o servidor de armazenamento tenha um papel ativo: verificar os tokens e rejeitar escritas cujo token voltou para trás. Com ZooKeeper como serviço de lock, o zxid ou a versão do znode pode servir de fencing token.
O Chubby oferece o mesmo mecanismo com o nome de sequencer. O detentor do lock pode pedir uma string opaca que descreve o estado do lock logo após a aquisição; ela contém o nome do lock, o modo de aquisição e o número de geração do lock, que aumenta quando o lock passa de livre para ocupado. O servidor que a recebe deve testar se ela ainda é válida e, se não for, rejeitar a requisição.
Para servidores que não suportam sequencers, o Chubby oferece o lock-delay, um mecanismo imperfeito, porém mais fácil: se o lock fica livre porque o detentor falhou ou ficou inacessível, o servidor de lock impede outros clientes de reivindicá-lo por um período. O cliente escolhe esse período até um limite, de um minuto na época, que evita que um cliente defeituoso deixe o recurso indisponível por tempo arbitrário.
| Critério | Lock-delay | Sequencer (token) |
|---|---|---|
| O que faz | Impede outros clientes de reivindicar o lock por um período depois que o detentor falhou ou ficou inacessível | O detentor pode pedir uma string opaca que descreve o estado do lock; o servidor receptor deve testar se ela é válida e rejeitar a requisição se não for |
| Protege servidores sem mudança? | Protege servidores e clientes não modificados de problemas comuns de atraso de mensagens e reinícios | Espera-se que o servidor receptor teste a validade do sequencer |
| Natureza do mecanismo | Imperfeito, porém mais fácil | Os autores o consideram simples de usar |
04 · O SERVIÇO POR DENTRO
Réplicas, consenso e sessões
Eleger um primário entre pares é uma instância do problema de consenso distribuído. Uma célula do Chubby costuma ter cinco réplicas, e três precisam estar rodando para a célula estar de pé. Segundo Kleppmann, adquirir um lock é como uma operação compare-and-set, que exige consenso.
As réplicas usam consenso para eleger um mestre, que precisa de votos da maioria e de promessas de que elas não elegerão outro mestre durante o intervalo do master lease. As réplicas mantêm cópias do banco, mas só o mestre inicia leituras e escritas nele. As escritas são propagadas às réplicas e confirmadas quando alcançam a maioria; as leituras são atendidas só pelo mestre, o que é seguro enquanto o master lease não expirou, pois nenhum outro mestre pode existir.
O cliente consulta as réplicas listadas no DNS para achar o mestre, e as réplicas que não são mestre respondem com a identidade dele.
O cliente dirige seus pedidos ao mestre, que propaga a escrita, que inclui as operações de lock, às réplicas e a confirma quando ela alcança a maioria.
Enquanto o cliente está ativo, a sessão é mantida por KeepAlives periódicos, e os locks dele permanecem válidos enquanto a sessão for válida.
Se o mestre falha, as réplicas rodam a eleição quando seus master leases expiram, e um novo mestre costuma ser eleito em poucos segundos.
Quando o prazo local do lease do cliente expira, ele fica incerto sobre a sessão, esvazia o cache e espera o período de graça, de 45s por padrão.
O novo mestre escolhe um novo número de época, que os clientes devem apresentar em toda chamada, e rejeita chamadas com épocas mais antigas.
A extensão padrão do lease é de 12s, e um mestre sobrecarregado pode usar valores maiores para processar menos KeepAlives. O período de graça permite manter sessões em failovers que excedem o timeout normal do lease.
A eleição de líder com o lock segue um roteiro simples: todos os candidatos abrem o arquivo de lock e tentam adquiri-lo; um consegue e vira primário, enquanto os demais agem como réplicas, e o primário grava sua identidade no arquivo para ser encontrado. Idealmente ele também obtém um sequencer e o repassa aos servidores com que se comunica, que devem confirmar que ele ainda é o primário.
05 · TRADE-OFFS E ARMADILHAS
Granularidade, disponibilidade e uso correto
| Critério | Lock de granularidade grossa | Lock de granularidade fina |
|---|---|---|
| Carga no servidor de lock | Impõe muito menos carga ao servidor | A taxa de transações no serviço cresce com a taxa combinada dos clientes |
| Indisponibilidade do servidor | Como o lock é adquirido raramente, a indisponibilidade temporária atrasa menos os clientes | Até uma breve indisponibilidade pode fazer muitos clientes pararem |
O Chubby se destina a oferecer apenas locks de granularidade grossa. Segundo o paper, é direto para os clientes implementarem seus próprios locks de granularidade fina; uma aplicação pode particionar seus locks em grupos e usar os locks grossos do Chubby para alocar esses grupos a servidores de lock próprios da aplicação.
Quando o lock existe só por eficiência e as quedas não são frequentes, perder um lock ocasionalmente não é grave, segundo Kleppmann.
Os desenvolvedores também costumam tratar o serviço como se estivesse sempre disponível. Eles não percebem a diferença entre um serviço estar no ar e estar disponível para suas aplicações, e muitos escolheram derrubar suas aplicações ao receber o evento de failover do mestre, reduzindo bastante a disponibilidade de seus sistemas.
Perguntas frequentes
O que é Serviço de lock distribuído?
Projeto de entrevista clássico. Pontos centrais: locks distribuídos.
O que o design de Serviço de lock distribuído cobre?
Ele está organizado nestas seções: Para que serve um lock distribuído, O lock expira, mas o cliente não sabe, O recurso precisa recusar quem perdeu o lock, Réplicas, consenso e sessões e Granularidade, disponibilidade e uso correto.
Quanto tempo leva Serviço de lock distribuído e qual é o nível?
Leva cerca de 16 minutos para ler e tem nível Sênior.
Agora projete você mesmo
Rode este caso como uma entrevista na escola de System Design: você toma as decisões, o tutor as questiona e você vê a nota do seu desenho.