Design clássico · Pleno
Contador distribuído (likes e views de celebridade)
Projeto de entrevista clássico. Pontos centrais: hot key e contadores fragmentados e agendamento de jobs.
Resposta curta
Projeto de entrevista clássico. Pontos centrais: hot key e contadores fragmentados e agendamento de jobs. O contador de curtidas de uma postagem é o exemplo que a própria documentação do Firestore usa para documentos que funcionam como contador, ao lado dos favoritos de um item.
01 · O PROBLEMA
Um número que todo mundo incrementa
O contador de curtidas de uma postagem é o exemplo que a própria documentação do Firestore usa para documentos que funcionam como contador, ao lado dos favoritos de um item.
O limite aparece quando o contador é um único documento. No Firestore não é possível atualizar um único documento a uma taxa ilimitada e, com incrementos frequentes o bastante, surge contenção nas atualizações desse documento.
Cada curtida chega à API de curtidas.
A API incrementa um único documento, que guarda o valor do contador.
Quando os incrementos ficam frequentes o bastante, as atualizações desse documento passam a disputar entre si.
Sob volume alto e sustentado de operações simultâneas, o documento único pode virar um ponto quente.
A documentação de escala descreve o mesmo ponto quente: volumes altos e sustentados de operações simultâneas em um único documento podem causar um ponto quente nele, e a recomendação é alterar o modelo de dados para dividir ou replicar os dados entre vários documentos.
02 · FRAGMENTAR O CONTADOR
Muitos documentos, uma soma
A solução da documentação é o contador distribuído: cada contador é um documento com uma subcoleção de fragmentos, e o valor do contador é a soma dos valores dos fragmentos. É a ideia de fragmentar a escrita aplicada a um único número.
Cada curtida chega à API de curtidas, como antes.
Para registrar a curtida, a API escolhe um fragmento aleatório e incrementa só ele.
A curtida seguinte sorteia outro fragmento, e as escritas se distribuem entre documentos diferentes.
O valor do contador é a soma dos valores dos fragmentos.
A vazão de escrita cresce de forma linear com o número de fragmentos.
Para incrementar, escolhe-se um fragmento aleatório. A vazão de escrita cresce de forma linear com o número de fragmentos: um contador distribuído com 10 fragmentos suporta 10 vezes mais escritas que um contador tradicional. No exemplo de código da documentação, cada fragmento suporta ser incrementado uma vez por segundo, por isso são necessários vários fragmentos para permitir incrementos mais frequentes.
03 · O PREÇO DA LEITURA
Fragmentar empurra o custo para quem lê
A documentação aponta o custo do número de fragmentos. Com poucos fragmentos, algumas transações precisam repetir antes de ter sucesso e a escrita fica mais lenta; com fragmentos demais, as leituras ficam mais lentas e mais caras.
Ler o valor custa mais a cada fragmento acrescentado: o custo de leitura cresce de forma linear com o número de fragmentos, porque toda a subcoleção de fragmentos precisa ser carregada.
Na prática, isso vira uma pergunta de dimensionamento: quanto o contador recebe de escrita e quanto ele é lido.
04 · TOTAL CONSOLIDADO E JOBS
Ler um número pronto
Uma forma de compensar o custo de leitura é manter o total em um documento de total separado, atualizado em cadência mais lenta, e fazer os clientes lerem esse documento. A contrapartida é que os clientes precisam esperar a atualização desse documento, em vez de calcular o total lendo todos os fragmentos logo após cada atualização.
As curtidas continuam indo para os fragmentos, como antes.
Ler o valor somando os fragmentos exige carregar toda a subcoleção, e o custo cresce de forma linear com o número deles.
O documento de total é atualizado em cadência mais lenta que as escritas; neste desenho, quem faz isso é o job de consolidação.
Os clientes leem o total desse documento, em vez de somar os fragmentos.
A contrapartida é esperar a atualização do documento de total, em vez de calcular o total logo após cada atualização.
| Aspecto | Soma dos fragmentos | Documento de total |
|---|---|---|
| Como o valor é obtido | Soma de todos os fragmentos a cada consulta | O cliente lê o documento de total |
| Custo de leitura | Cresce de forma linear com o número de fragmentos | Compensa o custo de leitura dos fragmentos |
| Frescor do total | Calculado logo após cada atualização | O cliente espera a atualização do documento de total |
Um job agendado é um jeito natural de implementar essa cadência mais lenta. O intervalo entre execuções é uma decisão de produto: quão atrasado o número pode aparecer para quem lê. O total passa a convergir para a soma dos fragmentos com atraso, uma troca típica de consistência eventual.
05 · ALTERNATIVAS E FECHAMENTO
Fragmentar, replicar ou incrementar em memória
A documentação do Firestore no modo Datastore indica duas saídas: use fragmentação ou replicação para lidar com pontos quentes. A replicação guarda N cópias da mesma entidade e permite uma taxa de leitura N vezes maior; a fragmentação serve para escrever a uma taxa maior e quebra a entidade em partes menores.
Fora do Firestore, o padrão de contador do Redis usa operações atômicas de incremento: a ideia é enviar um comando INCR ao Redis a cada operação.
Perguntas frequentes
O que é Contador distribuído (likes e views de celebridade)?
Projeto de entrevista clássico. Pontos centrais: hot key e contadores fragmentados e agendamento de jobs.
O que o design de Contador distribuído (likes e views de celebridade) cobre?
Ele está organizado nestas seções: Um número que todo mundo incrementa, Muitos documentos, uma soma, Fragmentar empurra o custo para quem lê, Ler um número pronto e Fragmentar, replicar ou incrementar em memória.
Quanto tempo leva Contador distribuído (likes e views de celebridade) e qual é o nível?
Leva cerca de 14 minutos para ler e tem nível Pleno.
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.