DEV Community

Guilherme Silva
Guilherme Silva

Posted on

Solid Queue 101: Processamento em Background no Rails 8.

O Rails 8 acabou de ser lançado e chegou trazendo muitas novidades para os desenvolvedores ruby(e também para os frameworks de outras linguagens que provavelmente vão se inspirar nele :p). E uma delas, que vou falar nesse artigo, é a gem Solid Queue instalada por padrão ao iniciar um projeto.

O que é o Solid Queue?

Solid Queue é uma gem utilizada para realizar tarefas em segundo plano(background jobs), desenvolvido na 37signals, que como eles mesmos dizem na descrição da gem é um: “Backend de enfileiramento baseado em banco de dados”.

Esse tipo de ferramenta é utilizada para quando você tem uma determinada aplicação e não quer que sua requisição atrase ou fique bloqueada enquanto espera por uma resposta.

Imagine que nós precisamos criar uma aplicação que recebe pedidos de alguma venda qualquer e junto a isso precisa notificar ao cliente sempre que o status do seu pedido é atualizado, ou seja, se foi enviado, embalado, entregue, etc.

A notificação ao cliente, seja através de um email ou por sms, pode demorar alguns segundos e com isso bloquear toda a operação nesse período, fazendo com que nossa aplicação fique "esperando" a notificação ser enviada antes de continuar.

Como o Solid Queue resolve isso então?

Através de filas, queues em inglês, onde os jobs são enfileirados para serem processados em segundo plano pelos workers.

Perai! Worker, background job, tarefa em segundo plano... é tudo a mesma coisa, então?

Não, mas muitas pessoas usam os termos indiscriminadamente para se referir a mesma coisa. Um worker é a parte do seu sistema responsável por processar os jobs(as tarefas a serem feitas) em segundo plano, é ele quem vai pegar o job da fila(queue) para executar. Embora os termos estejam interligados, eles não são a mesma coisa, porém se complementam. Para nós falantes do português, a tradução dos termos é literalmente "Trabalho" e "Trabalhador", o que deixa tudo mais simples de se entender.

É como se um cozinheiro, ou worker, esperasse pelos pedidos, ou jobs, para começar a preparar e entregar um prato.

O background job é a tarefa assíncrona que você coloca na fila para ser processada.
O worker é o processo que pega essa tarefa na fila e a executa.

Colocando em prática.

Sabendo que vamos criar um sistema de pedido onde um usuário acompanha as atualizações do seu pedido, precisamos de duas tabelas. Aqui nós temos que ter a tabela do pedido, e uma outra tabela que fique responsável por guardar os status desse pedido, assim como sua respectiva mensagem, que vou chamar de order_update. A separação da tabela de order com a das atualizações é uma boa forma de manter o registro das atualizações realizadas, assim como é ótimo para auditoria e coisas relacionadas, pois mantém o histórico dos passos que um pedido passou até ser finalizado.

Primeiramente, vamos precisar criar os recursos, models e rodar as migrations.

De order: rails g resource Order order_items:integer customer_name customer_email status:integer

De order_update: rails g model OrderUpdate status:integer message order:references

Dentro do nosso model de Order vamos usar o status como um enum, da seguinte forma:

class Order < ApplicationRecord
  enum status: { delivered: 0, cancelled: 1, shipped: 2, received: 3, prepared: 4 }
end
Enter fullscreen mode Exit fullscreen mode

Como o Solid Queue já vem adicionado por padrão, vamos apenas precisar mudar algumas coisas nos arquivos de configuração :)

  • Inclua no environment/development.rb a seguinte linha config.active_job.queue_adapter = :solid_queue.

  • Em seguida, no arquivo environment/production.rb apague a seguinte linha config.solid_queue.connects_to = { database: { writing: :queue } }

    Como nós não vamos utilizar para esse exemplo um banco de dados separado apenas para o solid queue, essa linha não se torna necessária.

  • Vai precisar também copiar todo o conteúdo de db/queue_schema.rb e colocar dentro de uma migration e em seguida apagar o db/queue_schema.rb. Mais uma vez, como não vamos ter um banco separado para as filas, precisamos passar as tabelas que foram criadas pro Solid Queue para o nosso db/schema.rb.

    você pode só criar uma migration vazia e colar todo o db/queue_schema.rb dentro do método change e aí é só rodar :)

Feito isso, agora é preciso colocar o código no controller.

  • Vá em app/controllers/orders_controller.rb, e dentro da sua action que vai realizar a atualização digite o seguinte:
  order = Order.find(params[:id])
  new_status = params[:status]

  if order.update!(status: new_status)
    OrderUpdate.create!(order: order, status: new_status, message: "Status atualizado para #{new_status}")
    OrderStatusNotificationJob.perform_later(order.id, new_status)
    render json: { message: "Enfileirado com sucesso o/" }
  end
Enter fullscreen mode Exit fullscreen mode

Como pode reparar, assim que o status é atualizado com o novo status, criamos um novo registro em OrderUpdate para manter o histórico e em seguida chamamos o OrderStatusNotificationJob.perform_later. O método perform_later é o responsável por colocar o nosso job na fila para ser processado quando estiver disponível, é igual o garçom mandando os pedidos anotados para a cozinha :)

Mas para usá-lo, primeiro precisamos criá-lo:

  • Dentro de app/jobs, crie o seguinte arquivo:
class OrderStatusNotificationJob < ApplicationJob
  def perform(order_id, status)
    order = Order.find(order_id)
    message = I18n.t("order_status.#{status}")

    puts "Enviando notificação para o cliente #{order.customer_name}: #{message}"
  end
end
Enter fullscreen mode Exit fullscreen mode

E também criei uma internacionalização dos status para não precisar fazer uma estrutura de controle que checa o nome do status e crie uma mensagem personalizada, então caso queira também pode copiar isso aqui e colar em config/locales/pt-BR.yml:

pt-BR:
  order_status:
    received: "Seu pedido foi recebido!"
    prepared: "Seu pedido está sendo preparado!"
    shipped: "Seu pedido foi enviado!"
    delivered: "Seu pedido foi entregue!"
Enter fullscreen mode Exit fullscreen mode

Repare que coloquei um puts com a mensagem "Enviando notificação..." apenas para ilustrar o nosso exemplo, fique a vontade para colocar o código que quiser, fazer integração com twilio, action_mailer, enfim, o que seu sistema precisar que seja feito em segundo plano.

Para verificar se está tudo funcionando, basta olhar os logs da sua aplicação.

Logs

Aqui o status atualizado foi enviado como received, atualizando a tabela de orders no banco de dados e incluindo o nosso job na tabela de jobs do Solid Queue solid_queue_jobs (lembra que a proposta dele é ser em baseado banco de dados?)

Após registrar o nosso job no banco, o Solid Queue seleciona ele, insere em outra tabela que informa os jobs que estão prontos para execução, e dessa forma o worker sabe que tem coisa na fila para ele processar. Legal, né?

Select e inserção do jobs nas tabelas

E ao rodar bin/jobs no terminal da sua aplicação, você conseguirá visualizar o worker realizando seu trabalho e executando o job, mostrando no terminal a mensagem da notificação:

Mensagem de notificação no terminal

E pronto! Sua app já está pronta para rodar código em background, simples, não? Sem a necessidade de subir um servidor Sidekiq com Redis para executar os seus jobs, sem instalar múltiplas gems e sem dor de cabeça para configurar.

Por fim...

O intuito deste artigo foi mostrar um pouco da gem, instruir o uso de forma simplificada, e não um tutorial com todas as funcionalidades. Você pode ver mais no próprio github da gem github.com/solid_queue, e verificar as diversas formas de aplicar em seu código o que a ferramenta disponilibiza, e eu recomendo a leitura deste artigo para entender mais a fundo como Solid Queue funciona por baixo dos panos.

Referências

Github Solid Queue
Introducing Solid Queue - 37 Signals

Top comments (0)