12 pontos por xguru 2024-09-26 | 7 comentários | Compartilhar no WhatsApp
  • A Wafris é uma empresa de firewall de aplicação web open source que fornece um cliente middleware para Rails
  • O cliente v1 exigia um armazenamento de dados Redis local, mas na v2 usa SQLite
  • Explica o contexto da decisão de migrar de Redis para SQLite, as considerações de desempenho e as mudanças de arquitetura

TL;DR

  • O SQLite tem coisas em que é bom e coisas em que não é
  • O Redis tem coisas em que é bom e coisas em que não é
  • RDBMSs tradicionais (Postgres/MySQL) têm coisas em que são bons e coisas em que não são
  • Esses armazenamentos de dados não são intercambiáveis diretamente, e tentar fazer isso traz problemas
  • Este texto descreve os testes e o processo de decisão envolvidos na re-arquitetura do cliente v1 baseado em Redis para o cliente v2 baseado em SQLite

Fatores que forçaram a mudança

  • O objetivo da Wafris é facilitar para desenvolvedores protegerem seus sites
  • Por causa de problemas de implantação do Redis, a v1 não alcançava totalmente esse objetivo
  • O Redis foi escolhido porque a equipe trabalhava em ambientes como Heroku, onde era fácil usá-lo, mas muitos usuários enfrentavam problemas de implantação do Redis
  • Fazer o usuário usar um banco separado como o Redis não era o melhor para ele

O que é “velocidade”?

  • O Redis é “mais rápido” que RDBMSs tradicionais, mas ainda exige gerenciar conexão, memória, processos etc.
  • Em ambientes de nuvem, a latência de rede pode ser um grande problema
  • Como as regras do Wafris precisam ser avaliadas a cada requisição HTTP recebida, a latência de rede pode deixar a aplicação mais lenta

Suposição de monólito

  • Existem aplicações totalmente distribuídas, mas a maioria dos apps Rails são “Majestic Monoliths”
  • Apps implantados em várias regiões, divididos em servidores com funções sobrepostas ou apenas parcialmente em Rails têm ainda mais problemas ao usar Redis

Repensando a arquitetura

  • O Wafris é um firewall de aplicação web instalado como middleware do Rails
  • Simplificando em duas etapas: 1) comparar requisições HTTP com regras e 2) reportar o resultado do processamento
  • A “leitura” das regras (etapa 1) é muito mais importante do que a “escrita” (etapa 2)
  • As leituras precisam ser processadas em sequência, não podem falhar e afetam o desempenho percebido pelo usuário
  • As escritas podem ser mais lentas, feitas em lote ou de forma assíncrona

Entra o SQLite

  • Outras pessoas já explicaram bem para quais usos o SQLite é adequado
  • O SQLite não compete com bancos cliente/servidor, e sim com fopen()
  • Ao eliminar a ida e volta pela rede, a expectativa era de que ele fosse muito mais rápido que o Redis
  • Foi decidido avaliar benchmarks entre SQLite e Redis

Benchmark entre SQLite e Redis

  • Fazer benchmark é uma arte obscura de se enganar com números precisos
  • Benchmark de armazenamento de dados é ainda mais complicado
  • Não se queria encontrar uma velocidade absoluta, e sim criar um benchmark específico para os dados e o caso de uso da equipe
  • Tweaks de otimização foram ignorados. A ideia era que o Wafris funcionasse imediatamente ao ser colocado no app
  • Em vez de benchmarks teóricos, foram testados o caminho principal do app e a pior consulta
  • A pior consulta é uma requisição para uma estrutura de dados complexa de “decimal lexical” que mapeia intervalos de IP (IPv4, IPv6) para categorias
  • As buscas por intervalo foram pré-calculadas e gravadas em tabelas SQLite e conjuntos ordenados do Redis
  • A cada requisição HTTP recebida, é preciso comparar o IP da requisição com intervalos personalizados de permitir/bloquear, intervalos GeoIP e intervalos de reputação de IP

Protocolo de teste

  • Os testes foram feitos em um MacBook Air M2, com Redis instalado via Homebrew e um banco SQLite local
  • Os testes usaram um conjunto de dados de intervalos já existente, com 1,2 milhão de itens
  • Vários conjuntos de IP foram executados em SQLite e Redis na mesma ordem
  • Em cada multiplicador, o teste foi executado 5 vezes e a média foi calculada

Resultado dos testes

  • O SQLite venceu o Redis com folga (no caso de uso específico da equipe)
  • O SQLite foi cerca de 3 vezes mais rápido que uma instância local do Redis
  • Isso antes mesmo de considerar a latência de rede
  • Mesmo que o SQLite só empatasse com o Redis, ainda haveria ganho por eliminar o tempo de rede

O que ficou fora do gráfico

  • Mesmo que o desempenho do SQLite fosse 2 vezes pior no benchmark, na prática ele ainda poderia ser mais rápido por causa da latência de rede
  • Não importa o quão poderoso seja o servidor Redis, ainda existem limites de largura de banda, conexões e latência entre regiões
  • O SQLite permite uma escalabilidade horizontal quase infinita “de graça”
  • O onboarding com SQLite fica muito melhor. O usuário talvez nem perceba que ele está sendo usado
  • Dá para extrair mais desempenho do Redis, mas foi difícil convencer usuários a mudar a configuração do Redis

O resultado é só o começo

  • Foi comprovado que o SQLite é mais rápido que o Redis, mas existem trade-offs reais
  • Os testes acima não consideram escrita
  • Para gerenciar a competição entre leituras e escritas, ainda são necessários conexão com banco, pool de conexões, transações etc.
  • Assim como um supercarro elétrico tem dificuldade para carregar blocos de concreto, o SQLite não deve ser usado em funções para as quais não é adequado

Construindo uma arquitetura de sincronização

  • Na v1 (Redis), quando o usuário atualizava regras no Wafris Hub, as regras no armazenamento Redis eram atualizadas
  • Isso não funciona com SQLite, porque não dá para fazer “push” para o servidor web
  • Na v2 (SQLite): 1) o usuário atualiza regras no Wafris Hub 2) em intervalos regulares, o cliente verifica regras atualizadas 3) quando há atualização, baixa um banco SQLite totalmente novo
  • Isso reduz bastante a responsabilidade do usuário por instalação e configuração
  • A taxa de sucesso na instalação do cliente v2 aumentou 3 vezes

Arquitetura distribuída com SQLite

  • Considere um app Rails implantado em um provedor de nuvem com auto scaling ativado
  • Se as requisições aumentam de 100 req/s para 10.000 req/s, as instâncias de computação escalam, mas o banco não
  • Na prática, esse é o principal motivo de apps Rails caírem por sobrecarga
  • Sincronizar o banco SQLite em cada instância de computação resolve isso, porque mantém todas as chamadas locais

E quanto às escritas?

  • O app foi dividido em caminhos de leitura (avaliação de regras) e escrita (relatórios), e então o caminho de escrita foi ignorado
  • O caminho de escrita foi redesenhado como 1) reportar conectando-se ao Wafris Hub de forma assíncrona 2) enviar relatórios em lote 3) remover completamente escritas em banco do cliente
  • Isso pode não funcionar para outras pessoas, mas a equipe só se preocupa com usuários que querem um cliente Wafris rápido e fácil de implantar

Conclusão

  • Há grande satisfação com a arquitetura v2 usando SQLite
  • Ela já ajuda muitos sites a resistirem a ataques e permanecerem online
  • Ficou muito mais fácil começar, reduzindo o trabalho de suporte da equipe e o incômodo para os usuários
  • Isso é visto como uma vitória para uma internet mais segura e protegida

7 comentários

 
aer0700 2024-09-26

O SQLite é bom o bastante, mas, neste caso, dá a impressão de que era simplesmente um caso de uso que não combinava muito com o Redis...

 
kandk 2024-09-26

Fazer o benchmark em um M2 é meio...

 
colossus 2024-09-29

Então precisa medir tudo isso em cada instância da AWS? Você está esperando demais do open source.

 
toru123 2024-09-27

Foi feito no mesmo ambiente de servidor; isso é um problema?
Para benchmark, é necessário usar uma CPU específica...?

 
superwoou 2024-09-26

Que tipo de problema pode haver no que foi feito no m2? (além do fato de que o ambiente de serviço real não usa um processador m2)

 
kandk 2024-09-26

Esse é o problema. Fazer experimentos em laboratório e afirmar: isto é perfeito para uso comercial!

 
[Este comentário foi ocultado.]