« HE:labs
HE:labs

Use Background Jobs e não deixe o usuário esperando

Postado por Matheus Bras em 31/10/2013

Muitas aplicações Rails precisam enviar emails e muitas deixam o usuário esperando até que o mesmo seja enviado e não usam background jobs para fazer esse trabalho. Vamos tentar melhorar isso.

Vamos imaginar o seguinte cenário. Temos este model User:

1 class User
2   # validations
3   # associations
4 
5   def send_confirmation
6     SomeMailer.confirmation(self).deliver
7   end
8 end

E este controller:

1 class UsersController < ApplicationController
2   def resend_confirmation
3     @user.send_confirmation
4   end
5 end

Então, o usuário vai até a página para pedir o reenvio do email de confirmação e clica no botão. No controller será chamado o método send_confirmation, que está no model User para que o envio do email seja feito. Até aí ok! O email vai ser enviado sem problemas. Porém, o usuário vai precisar esperar até que o envio do email seja efetuado. E por que isso?

O problema nisso é que o Rails é single-threaded. O que significa: quando você quer enviar um email, o Rails precisa parar tudo o que está fazendo para executar o envio. Então o usuário precisa ficar esperando que isso aconteça. Ninguém vai poder interagir com a aplicação enquanto o raio do email seja enviado. E é claro que não queremos isso.

Para resolver isso, usamos Background Jobs! Para esse exemplo, vou usar o delayed_job, mas existem muitas outras soluções para background jobs no Rails: sucker_punch, sidekiq, resque. No caso do delayed_job no Heroku, precisamos de um Worker para executar a fila de jobs.

Criando um job para o envio de email de confirmação

Primeiro, criamos uma classe ConfirmationEmailJob. Podemos colocá-la na pasta app/jobs.

 1 class ConfirmationEmailJob
 2   def perform(user_id)
 3     return if !load_user(user_id)
 4     SomeMailer.confirmation(@user).deliver
 5   end
 6 
 7   private
 8     def load_user
 9       @user = User.find_by(id: user_id)
10     end
11 end

E então, no model User:

1 class User
2   # validations
3   # associations
4 
5   def send_confirmation
6     ConfirmationEmailJob.new.delay.perform(id)
7   end
8 end

Por fim, quando o usuário pedir o reenvio do email de confirmação, vamos enfilerar um job para que ele seja enviado. O delayed_job vai se encarregar de fazer isso em background sem precisar que o usuário fique esperando.

É uma boa prática usar background jobs para outras tarefas como: acessar APIs externas, upload de arquivos muito grandes, geração de PDFs, thumbnails e etc. Algo que for muito intensivo e demorado, deve ser executado com background jobs.

Compartilhe

Sabia que nosso blog agora está no Medium? Confira Aqui!