stamatios
← Voltar ao feed
Revogação de sessões em escala no Canva
Dev & Engenharia

Revogação de sessões em escala no Canva

resumo de ~3 min

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.