
Revogação de sessões em escala no Canva
O problema
O Canva gerencia sessões de centenas de milhões de usuários. Cada requisição backend precisa identificar o usuário autenticado, o que significa responder essa pergunta centenas de milhares de vezes por segundo. As informações de sessão ficam em cookies criptografados, mas quando um usuário é deslogado ou suas permissões mudam, é necessário revogar esses cookies em tempo quase real.
Para isso, cada gateway mantém um cache em memória com as revogações dos últimos 12 horas (tempo máximo de refresh dos tokens). O problema: com centenas de pods de gateway, cada um puxando mais de um milhão de revogações do MySQL no startup, os deploys viravam uma "stampede" coordenada no banco de dados.
A solução: S3 como cache intermediário
Redis foi avaliado e descartado por não oferecer durabilidade forte e por transferir o problema para outro datastore. A escolha foi S3, que oferece durabilidade e serve downloads de arquivos grandes a baixo custo.
As revogações são particionadas em segmentos de 30 minutos, cada um correspondendo a um objeto no S3. Cada revogação é compactada em apenas 16 bytes (principal + timestamp de login), usando manipulação de bits. Os arrays são ordenados, permitindo busca binária eficiente. O gateway opera diretamente sobre os bytes baixados, sem transformação adicional.
Resultado: redução de 87,5% no footprint de memória em relação à implementação anterior (que usava múltiplos objetos Java por revogação).
Mantendo os chunks atualizados
Um worker assíncrono escaneia o banco, busca lotes de revogações novas, insere no array ordenado do chunk atual e re-uploada para o S3. Múltiplas réplicas do worker rodam simultaneamente para alta disponibilidade.
Para evitar corridas de dados (lost updates), usam PUT condicional do S3 como controle de concorrência otimista. ZooKeeper leader election é usado como otimização para reduzir conflitos, mas a corretude não depende dele.
Apesar da complexidade teórica (O(N²) para construir um chunk de N revogações), na prática o worker atinge mais de 2.000 revogações por segundo - acima do necessário - porque sistemas modernos processam arrays densos com extrema eficiência, e o gargalo real é latência de rede.
Download dos chunks
No startup, cada pod baixa apenas os chunks dos últimos 12 horas (identificáveis pelo timestamp no nome do objeto). Para manter o cache atualizado em tempo real, os gateways usam GET condicional do S3 para re-baixar chunks apenas quando mudaram. Mesmo com 1 milhão de revogações em 30 minutos, são apenas 16 MB algumas vezes por minuto - insignificante comparado ao tráfego total dos gateways.
Resultados
- Deploys mais rápidos.
- Read replicas do MySQL reduzidas para apenas 2 (redundância).
- Footprint de memória reduzido em 87,5%.
- Carga do banco agora escala de forma previsível com throughput de escrita e tráfego do site, em vez de depender do número de instâncias fazendo download simultâneo.
Lição principal
A diferença entre escalar na teoria e na prática é enorme. Constantes importam: um único worker ordenando centenas de milhares de registros parece ineficiente no papel, mas empiricamente excede os requisitos. O artigo também destaca o valor de testar múltiplas soluções em infraestrutura real antes de escolher, algo facilitado por agentes de código com IA.