- Rails 8 remove a dependência de Redis da stack padrão e passa a processar todo o trabalho com SolidQueue, SolidCache e SolidCable sobre um banco de dados relacional (RDB)
- Redis é rápido e confiável, mas traz complexidade operacional em configuração, segurança, gerenciamento de cluster, backups etc.
- SolidQueue implementa processamento paralelo sem contenção usando o recurso
FOR UPDATE SKIP LOCKED do PostgreSQL
- Oferece gratuitamente recursos pagos do Redis + Sidekiq, como tarefas periódicas, controle de concorrência e dashboard de monitoramento (Mission Control)
- Para a maioria das aplicações Rails, o SolidQueue é suficiente; só alguns casos que exigem processamento ultrarrápido e em tempo real ainda precisam manter o Redis
O custo oculto do Redis
- Além do custo simples de hospedagem, o Redis também impõe carga contínua de administração, como instalação, manutenção, configuração de segurança e gerenciamento de cluster HA
- É necessário configurar conexão de rede e firewall entre Rails e Redis, autenticação de cliente e orquestração dos processos do Sidekiq
- Quando ocorre uma falha, é preciso depurar Redis e RDBMS ao mesmo tempo, e também é necessária uma estratégia de backup dupla
- Já em uma stack Rails sem Redis, basta gerenciar apenas o PostgreSQL, o que simplifica a operação
Como o SolidQueue funciona
- Usa o recurso
FOR UPDATE SKIP LOCKED do PostgreSQL para permitir que vários workers peguem tarefas ao mesmo tempo sem contenção de locks
- Estrutura principal de tabelas
solid_queue_jobs: armazena os metadados das tarefas
solid_queue_scheduled_executions: fila de espera das tarefas agendadas
solid_queue_ready_executions: fila de tarefas prontas para execução
- Os processos de worker, dispatcher, scheduler e supervisor fazem polling periódico em tabelas diferentes e cooperam entre si
- Graças ao design MVCC e ao autovacuum do PostgreSQL, até grandes volumes de inserções e exclusões podem ser processados com estabilidade
Agendamento de tarefas recorrentes
- O SolidQueue oferece nativamente tarefas recorrentes no estilo cron, configuradas no arquivo
config/recurring.yml
- O scheduler coloca na fila as tarefas cujo momento de execução chegou e agenda automaticamente a próxima execução
- Usa a biblioteca Fugit para interpretar agendas em linguagem natural e Concurrent::ScheduledTask para criar threads
- Adota o modelo de agendamento determinístico do GoodJob, mantendo a agenda mesmo após reinícios do processo
Recurso de controle de concorrência
- O SolidQueue usa o padrão de semáforo POSIX para limitar a execução simultânea por unidade de trabalho
- Exemplo: com
limits_concurrency to: 1, key: ->(user) { user.id }, apenas 1 tarefa por usuário é executada por vez
- É possível definir a expiração do semáforo (
duration) para evitar conflitos entre tarefas e deadlocks
- Tabelas relacionadas
solid_queue_semaphores: rastreia os limites de concorrência
solid_queue_blocked_executions: armazena as tarefas em espera
Monitoramento com o Mission Control
- Mission Control Jobs é um dashboard gratuito e open source para Rails 8, que pode ser montado facilmente na rota
/jobs
- Principais recursos
- Estado das filas em tempo real, rastreamento de tarefas com falha e controle de retry/descartar
- Visualização da linha do tempo de tarefas agendadas e recorrentes
- Gráficos de métricas e throughput por fila
- Também permite consultas baseadas em SQL, possibilitando análise direta no banco de dados sem ferramentas adicionais
Migração de Sidekiq para SolidQueue
- Etapa 1: definir
config.active_job.queue_adapter = :solid_queue
- Etapa 2: executar
bundle add solid_queue, depois rails solid_queue:install e db:migrate
- Etapa 3: converter os agendamentos cron de
sidekiq.yml para recurring.yml
- Etapa 4: adicionar
jobs: bundle exec rake solid_queue:start ao Procfile
- Etapa 5: remover as gems relacionadas a Redis e Sidekiq
- O código existente em ActiveJob continua funcionando sem modificações
Quando o Redis ainda é necessário
- Processamento contínuo de milhares de tarefas por segundo
- Sistemas em tempo real nos quais latência abaixo de 1 ms é indispensável
- Necessidade de estruturas complexas de pub/sub ou rate limiting/operações de contador sofisticadas
- Como exemplo, a Shopify opera com 833 requisições por segundo e 1.172 processos worker, usando infraestrutura com Redis
Guia prático de implementação
- Ao criar um novo app em Rails 8, SolidQueue, SolidCache e SolidCable são configurados automaticamente
- Recomenda-se configurar uma conexão de banco separada para a fila em
config/database.yml
- Adicionar autenticação ao Mission Control e montar a rota
/jobs
- Adicionar
jobs: bundle exec rake solid_queue:start ao Procfile.dev e executar tudo com bin/dev
- Depois de criar tarefas de teste, é possível verificar o estado no Mission Control
Problemas frequentes e soluções
- Também é possível usar uma configuração com banco único, mas isso reduz a flexibilidade operacional
- No Mission Control em produção, é indispensável adicionar autenticação
- O intervalo padrão de polling é de 1 segundo para tarefas agendadas e 0,2 segundo para tarefas imediatas, adequado para a maioria dos apps
- Ao usar ActionCable/Turbo Streams, é preciso configurar o
SolidCable com uma conexão de banco separada
Escalabilidade e desempenho
- O SolidQueue pode escalar o suficiente para a maioria dos apps Rails
- Com base em PostgreSQL, pode processar 200 a 300 tarefas por segundo, e a 37signals processa 20 milhões de tarefas por dia sem Redis
- Tabela comparativa
| Item |
Redis + Sidekiq |
SolidQueue |
| Complexidade de configuração |
Requer serviço separado |
Usa o banco embutido |
| Linguagem de consulta |
Comandos Redis |
SQL |
| Monitoramento |
Dashboard separado |
Mission Control |
| Cenários de falha |
6 ou mais |
2 |
| Throughput |
Milhares/segundo |
200–300/segundo |
| Mais indicado para |
99,9% dos apps |
95% dos apps |
Conclusão
- Redis e Sidekiq são tecnologias excelentes, mas para a maioria das aplicações Rails geram complexidade e custo excessivos
- O SolidQueue viabiliza operação mais simples, redução de custos e manutenção mais eficiente com base em um único banco de dados
- Na era do Rails 8, a migração para SolidQueue é recomendada como escolha padrão
2 comentários
Redis é bom, mas.
Comentários do Hacker News
Acho que todo autor de open source tem o direito de controlar o escopo do próprio projeto
Mas nossa equipe se arrependeu de ter trocado o good_job pelo SolidQueue
A Basecamp é centrada em MySQL, então não aceita queries específicas do engine de RDBMS. Pelas issues no GitHub, dá para ver que o foco está só em desempenho no MySQL
Além disso, ainda não há suporte a jobs em lote (PR relacionada)
Em JOINs complexos, o MySQL muitas vezes monta mal o plano de execução, então eu uso STRAIGHT_JOIN para forçar a ordem. É uma precaução para o futuro
Estou comparando os dois como candidatos para migrar do resque. O GoodJob não é compatível com o modo transaction do pgbouncer por causa de recursos exclusivos do pg
Precisar de persistência de sessão é incômodo, mas o ganho de desempenho não faz muita diferença na maioria das escalas
Ainda assim, o modelo de desenvolvimento e a legibilidade do código do GoodJob passam muito mais confiança
Se puder simplificar o ambiente de produção, isso sempre é algo bom
Acho que, no Rails, o cenário ideal é uma estrutura que permita mudar facilmente para Redis
Seria bom poder começar com SolidQueue e, ao bater no limite de escalabilidade, migrar para Redis
A maioria dos apps Rails não tem tráfego tão alto, então manter os dois sistemas acaba sendo mais complexo
Claro, há apps que dependem de implementações específicas de fila, mas no caso geral basta mudar a configuração
Também queria saber se ele usa snapshots em conjunto para evitar que o log cresça demais, e se isso funciona também em modo distribuído
Especialmente quando a criação de jobs acontece junto com outras mudanças no banco, perder essa garantia vira um problema
O Redis levava vantagem nisso por ser um armazenamento de estado leve e independente
O SolidQueue não parece deixar essa separação clara (riverqueue.com)
Testei o SolidQueue no meu side project
A conclusão é: se não há problema com o Sidekiq, não existe motivo para trocar
Só vale considerar se você quiser eliminar a infraestrutura de Redis
Em projeto novo, o GoodJob parece mais maduro e tem uma comunidade melhor
O SolidQueue tinha uma UI simples demais, o que incomodou. Sem otimização de índice, a página travava quando o volume de dados aumentava
Também é preciso considerar que usar um RDBMS adiciona o custo de gerenciar pool de conexões
Para quem se preocupa com escalabilidade, o benchmark do Oban em Elixir mostra
um único nó processando um milhão de jobs por minuto. A maioria dos apps tem um volume muito menor do que isso
A estrutura insere 5000 jobs em lote de uma vez, então o TPS real fica em torno de 200
Sem lote, ao inserir jobs individualmente, a carga de transações SQL aumenta muito mais
Nós já armazenávamos jobs no banco antes mesmo do SolidQueue
A vantagem é poder fazer snapshot do estado de produção e levar exatamente isso para o ambiente de desenvolvimento
Mas o rate limiter fica no Redis, para evitar carga excessiva no banco
O limite de filas baseadas em banco é o payload grande
Colocar JSON grande na fila é ineficiente por causa do overhead de escrita no banco
O Redis (Sidekiq) é muito mais rápido nesses casos
SolidQueue+SQLite funciona bem se for apenas para passar a primary key
Mas, se vários workers ficarem fazendo polling no mesmo banco, logo vira gargalo
Acho melhor deixar os dados grandes em um storage externo como S3 e passar só a referência
Fico curioso se existe algum material reunindo resultados de benchmark sobre isso
O SolidQueue menciona SKIP LOCKED, mas manter uma transação aberta por 15 minutos para um job é arriscado
Transações longas acabam com o desempenho do banco e também são vulneráveis a quedas de rede
Esse tipo de estrutura pode levar a um antipadrão. Depois percebi que aparentemente usa um esquema de lease
Tenho simpatia pela filosofia de Postgres for everything
Acho bom unificar tudo em um único PostgreSQL para simplificar
Não sei bem como responder a essa analogia
Fico pensando se ainda vale a pena usar Redis a ponto de aumentar a complexidade
“Um negócio em que latência abaixo de 1 ms importa”... estão dizendo que alguém faz HFT em Rails?
O Postgres vai dominar o mundo