Skip to main content
Bastidores
BastidoresEngineeringDecisions

O que rodar um bot em mais de 2.000 servidores do Discord nos ensinou

Quatro coisas que funcionam de outro jeito em grande escala do que você imagina enquanto escreve o código.

Publicado em por AlphaCloud4 min de leitura

Um bot do Discord em três servidores e o mesmo bot em mais de dois mil não são o mesmo programa em tamanho maior. Algumas suposições que são totalmente inofensivas em pequena escala se tornam, a partir de certo tamanho, justamente os bugs mais difíceis de encontrar.

Aqui estão quatro que nos custaram falhas reais.

1. Falhas não se espalham por igual, elas se escondem

O AlphaCloud roda dividido em vários processos, chamados shards. O Discord decide qual servidor vai para qual shard, e cada shard só conhece os próprios servidores.

Isso produz um tipo de falha que, de fora, parece acaso: as mensagens de boas-vindas funcionam em alguns servidores e em outros não. Nenhum erro no log, nenhum alerta, nada. A causa era uma única chamada de rede sem timeout. Se o outro lado aceita a conexão e depois fica em silêncio, o código espera pelo resto da vida do processo. Tudo o que vem depois nunca acontece e, como nenhum erro é lançado, nada reporta um erro.

A lição não tem nada de espetacular e vale para tudo: toda chamada de saída precisa de um limite máximo. Uma chamada que espera para sempre é pior do que uma que falha, porque falhar pelo menos é visível.

2. Reiniciar não é correção, é prazo

Enquanto não sabíamos a causa, reiniciar o shard afetado sempre ajudava. Esse é exatamente o problema das reinicializações: elas funcionam, e isso tira a pressão de encontrar a causa.

Hoje tratamos "reiniciar ajuda?" como diagnóstico, não como remédio. Se ajuda, o estado quebrado vive na memória, e existe um lugar no código que o monta e nunca o desmonta.

O inverso também vale: tudo o que precisa sobreviver a uma reinicialização pertence ao banco de dados. Se uma operação de várias etapas está rodando, digamos uma mudança de cargo em milhares de membros, e o processo morre no meio, a operação não pode simplesmente sumir. Ela precisa ser detectada e continuada na próxima inicialização.

3. Os limites do Discord valem por bot, não por processo

O Discord limita quantas requisições por segundo um bot pode fazer. A suposição óbvia: mais processos, mais vazão. Errado. O limite é do bot, não do processo. Três shards, cada um com o orçamento completo, somam três vezes a cota, e aí o Discord estrangula os três.

Por isso, cada um dos nossos processos calcula quantos existem no total e pega só a sua parte. Parece óbvio, mas não era, e o bug ficou invisível por muito tempo porque cada processo, isoladamente, parecia se comportar perfeitamente.

Uma armadilha relacionada: tarefas recorrentes rodam em intervalos fixos, digamos a cada cinco minutos. Se todos os shards começam ao mesmo tempo, disparam suas tarefas no mesmo instante para sempre. Desde então, cada processo recebe um deslocamento aleatório ao iniciar.

4. O que as pessoas realmente configuram não é o que você espera

A parte técnica é uma metade. A outra: para a maioria dos servidores, o padrão é a configuração. Um módulo com padrões errados está mal configurado na grande maioria dos servidores, por melhor que seja a página de configurações.

Duas coisas decorrem disso para nós. Primeiro, cada padrão exige mais reflexão do que a própria configuração. Segundo, a pergunta melhor para um módulo novo não é "o que dá para configurar aqui", e sim "o que esse módulo faz quando ninguém configura nada, e isso está certo para um servidor normal".

Esse também é o motivo de uma decisão que, à primeira vista, parece o contrário: desde a v1.8.0, quase toda mensagem que o bot envia pode ser criada livremente. Mas blocos obrigatórios, como o motivo numa DM de advertência ou o menu de categorias num painel de ticket, podem ser movidos e não excluídos. Liberdade de design, sim, mas não a ponto de um servidor construir sem querer algo que parece funcionar e não funciona.

O fio condutor

Três dos quatro são a mesma ideia com roupas diferentes: nesse tamanho, uma falha silenciosa sai mais cara do que uma barulhenta. Um crash é reportado e corrigido. Um recurso que simplesmente para de fazer qualquer coisa pode continuar assim por meses, e você fica sabendo por um relato de bug de alguém que se deu ao trabalho de escrever.

Por isso, a primeira pergunta que fazemos hoje sobre código novo não é "isso funciona?", e sim "como isso fica quando não funciona, e nós perceberíamos?".

Mais artigos