Todos os artigos

De 800 mil a um milhão de acessos no Moodle

O que fizemos quando o crescimento repentino transformou pequenas ineficiências em filas enormes, páginas lentas e comportamentos diferentes para alunos, professores e administradores.

DR

A diferença entre acesso, usuário e simultaneidade

O crescimento chegou em poucos dias e a primeira informação que circulou foi a quantidade de acessos, número grande o suficiente para justificar qualquer explicação apressada, mas insuficiente para orientar uma correção, porque oitocentas mil requisições distribuídas ao longo do dia representam uma carga muito diferente de milhares de alunos abrindo o mesmo questionário em poucos minutos, então começamos separando páginas visualizadas, chamadas de arquivos, usuários autenticados, sessões ativas, pessoas simultâneas, tentativas de avaliação, envios e tarefas executadas em segundo plano, relacionando tudo com horário, perfil e curso para descobrir quando a experiência realmente piorava.

Enquanto alguns indicadores mostravam CPU disponível, os alunos relatavam páginas lentas e tarefas demoravam para concluir, sinal de que a média escondia esperas concentradas em banco, armazenamento ou bloqueios, por isso instrumentamos o caminho completo de uma requisição, desde o balanceador até a aplicação, o banco de dados, o Redis, o diretório de arquivos e os serviços executados pelo cron, registrando duração, volume, quantidade de chamadas e filas, depois sobrepusemos essas medições aos momentos de maior reclamação para evitar a prática comum de otimizar aquilo que é fácil de medir e continuar sem tocar no ponto que trava o usuário.

Também reproduzimos a navegação com perfis diferentes, pois o mesmo curso abria rapidamente para um aluno e demorava muito mais para professor ou administrador, que enxergavam blocos adicionais, permissões, informações de conclusão e controles de edição, então cada teste passou a informar papel, número de atividades, quantidade de participantes e recursos visíveis, o que revelou que falar apenas em desempenho do curso escondia várias páginas diferentes construídas a partir do mesmo endereço.

Uma contagem pequena repetida vezes demais

O primeiro gargalo importante estava em uma funcionalidade visual considerada inofensiva, que calculava contagens e situações sempre que professores e administradores abriam o curso, consulta aceitável em uma turma menor, mas muito pesada quando multiplicada por muitos cursos, usuários e acessos simultâneos, então analisamos o plano de execução, o número de linhas percorridas, os filtros aplicados e quantas vezes o mesmo resultado era calculado dentro de uma única página, descobrindo que parte da informação mudava raramente embora fosse reconstruída como se precisasse estar atualizada a cada clique.

Em vez de colocar um cache amplo e torcer para que os números continuassem corretos, separamos o que exigia atualização imediata do que podia ser agregado em segundo plano, criamos dados resumidos por curso e período, invalidamos apenas os resultados afetados por uma mudança e removemos consultas duplicadas durante a mesma requisição, depois comparamos as respostas antes e depois em cursos pequenos, médios e muito grandes, porque uma otimização que reduz o tempo médio pode piorar um caso extremo ou entregar números antigos justamente para o perfil que precisa tomar uma decisão.

Ao mesmo tempo encontramos uma fila crescente de exclusões, notificações, cálculos e tarefas de plugins disputando banco e armazenamento com os alunos, situação agravada quando uma tarefa longa segurava recursos por tempo demais e empurrava todo o restante para frente, por isso medimos duração, frequência, volume processado e possibilidade de retomada, dividimos trabalhos extensos em lotes menores, limitamos concorrência onde havia disputa e separamos filas críticas das rotinas que podiam aguardar, permitindo que uma grande limpeza ou um relatório demorado deixasse de interferir na abertura de uma avaliação.

Testar o que as pessoas realmente faziam

Os primeiros testes de carga usavam páginas simples e produziam números excelentes, mas não representavam o dia real, então reconstruímos os cenários com login, abertura do painel, entrada em cursos, consulta de conclusão, início de questionário, gravações automáticas, envio definitivo, anexos e navegação de professores, misturando essas ações nas proporções observadas nos registros e acrescentando intervalos de leitura, porque usuários artificiais que clicam sem parar criam um tipo de pressão diferente de milhares de pessoas que iniciam uma prova no mesmo horário e permanecem alguns minutos pensando antes de gravar juntas.

Cada execução tinha um objetivo e um limite conhecido, começando por validar se o ambiente sustentava a carga atual sem acumular filas, depois elevando a simultaneidade até encontrar a primeira degradação e repetindo o teste após cada mudança para confirmar que o gargalo havia se movido ou diminuído, enquanto acompanhávamos percentis de resposta em vez de apenas a média, já que uma página rápida para noventa por cento das pessoas ainda pode deixar uma turma inteira esperando se os dez por cento mais lentos estiverem concentrados no questionário mais importante do dia.

A arquitetura foi distribuída aos poucos, mantendo aplicação em mais de um servidor, sessões e caches em serviços compartilhados, arquivos acessíveis de forma consistente e tarefas pesadas fora do caminho das páginas, mas cada divisão trouxe novas verificações, como garantir que uma sessão criada em um servidor fosse lida por outro, impedir que dois trabalhadores executassem o mesmo item e revisar bloqueios e políticas de memória do Redis, que inicialmente estava configurado de uma forma aceitável para um único ambiente e passou a apresentar expulsões e contenções quando recebeu sessões, caches e filas de vários nós.

Capacidade sem adivinhação

Com a plataforma mais estável, transformamos as descobertas em indicadores para a operação, definindo sinais de saturação para aplicação, banco, Redis, armazenamento e cron, junto com limites que permitiam adicionar capacidade antes de a experiência piorar, pois escalar depois da reclamação sempre custa mais e oferece menos tempo para entender o que está acontecendo, enquanto a redução de servidores também passou a respeitar filas, sessões e tarefas em andamento para evitar que a economia de uma hora produzisse erros difíceis de reproduzir.

O armazenamento compartilhado mereceu atenção especial, porque páginas que pareciam depender somente do banco também consultavam metadados e arquivos, backups usavam a mesma camada dos materiais abertos pelos estudantes e múltiplos servidores aumentavam a quantidade de operações simultâneas, então isolamos rotinas, ajustamos padrões de leitura, revimos diretórios temporários e medimos latência por tipo de operação, deixando de tratar espaço disponível como única medida de saúde de um moodledata que precisava responder a um volume muito maior.

Chegar de oitocentos mil a um milhão de acessos não foi resultado de aumentar uma máquina até os gráficos ficarem verdes, mas de entender o trabalho executado por cada perfil, retirar cálculos repetidos, controlar tarefas, testar jornadas reais e distribuir responsabilidades com observabilidade suficiente para saber onde a próxima limitação apareceria, combinação que tornou o crescimento previsível e permitiu discutir capacidade com números em vez de depender da lembrança do último incidente.

O que ficou deste projeto

Escalabilidade começou quando deixamos de perguntar quantos acessos o servidor suportava e passamos a perguntar qual trabalho o sistema realizava em cada acesso, em qual recurso ele esperava e o que poderia ser preparado antes de o usuário chegar.