Radar Tech: Shopify trocou Redis por MySQL nas reservas de inventario e descobriu que o gargalo nem era a query
Um caso-classico de simplificacao de stack: consolidar reservas em um unico banco com transacoes ACID e, na hora de escalar, perceber que o problema era o uso de conexoes, nao a consulta.
Em pleno pico de Black Friday, com um recorde de vendas por minuto, a plataforma de e-commerce precisava decidir: manter um armazenamento separado para as reservas de inventario ou consolidar tudo em um unico banco relacional.
O sistema de protecao contra oversell funciona assim: no checkout, uma unidade de estoque fica reservada por alguns minutos (hold), e quando o pagamento e processado, a reserva vira uma deducao permanente (claim). O problema e que, com as reservas num cache (Redis) e o saldo no banco (MySQL), esses dois passos nao podiam ser envolvidos em UMA transacao atomica.
O problema da falta de atomicidade
Quando a atualizacao do saldo e a limpeza da reserva acontecem em sistemas separados, o fallback pode gerar dois modos de falha:
- Oversell: a venda e confirmada mas a unidade nunca e deduzida do ledger.
- Undersell: a unidade e deduzida mas continua marcada como reservada.
Alem disso, o Redis nao tinha consciencia multi-location e exigia operar um cluster separado. A resposta foi corajosa e simples: mover as reservas para o mesmo MySQL do ledger e envolver tudo em transacoes ACID.
Pool de reservas em vez de uma linha por unidade
Uma linha por unidade nao escava em escala (um item com 50 mil unidades em 10 locations geraria 500 mil linhas). A solucao foi manter um pool limitado de linhas de reserva (cap de 1.000 por item/location), consumidas pela reserva e recarregadas por um processo de replenishment a partir do ledger.
A licao mais importante: o gargalo nao estava onde parecia
Depois da migracao, a equipe ainda batia num teto de throughput abaixo da meta. A latencia P90 da query estava aceitavel, a CPU nao estava no limite e as queries ja estavam otimizadas. O que nao estava claro era quem segurava as conexoes do banco.
Ao adicionar atribuicao por-caller (marcando cada conexao com o processo de negocio e agregando no proxy), descobriram que processos mantinham transacoes longas e consumiam o pool de conexoes. Limpar esses locks e ajustar a configuracao removeu o teto - sem mudar o modelo de dados.
O que a arquitetura moderna deveria aprender
- Se voce esta recorrendo a Redis, Kafka ou uma camada de coordenacao so para exclusao mutua de alto throughput, talvez seu banco relacional ja resolva.
- Quando os numeros nao batem (CPU baixa, pool esgotado), instrumente o caminho completo: a resposta costuma estar no plumbing, nao no mecanismo.
- Rollout gradual com kill switch preserva a capacidade de reverter em caso de erro.
Quer reproduzir esse raciocinio na sua equipe? Publicamos um pacote de prompt (ADR) que ensina um agente a escrever decisoes de arquitetura com esse mesmo rigor, com few-shot e harness de verificacao, para consolidar stack e investigar gargalos sem pular para conclusoes.