ActionMailer execution timeout - ruby-on-rails

When trying to send an email to the user for reseting their password, I keep getting an execution timed out error. Other mailer functions work, so I know that the config settings are correct. The header reads: "Timeout::Error in Password resetsController#create"
Here is the password_resets_controller:
def create
#user = User.find_by_email(params[:email])
if #user
User.deliver_password_reset_instructions(#user.id)
flash[:notice] = "Instructions to reset your password have been emailed to you. " +
"Please check your email."
redirect_to '/'
else
flash[:notice] = "No user was found with that email address"
render :action => :new
end
end
Here is the method inside of User.rb
def self.deliver_password_reset_instructions(user_id)
user = User.find(user_id)
user.reset_perishable_token!
Emailer.deliver_password_reset_instructions(user)
end
Finally, here is the actual method inside of emailer.rb:
default_url_options[:host] = "http://0.0.0.0:3000" #development
def password_reset_instructions(user)
#subject = "Application Password Reset"
#from = 'Notice#myApp.com'
#recipients = user.email
#sent_on = Time.now
#body["edit_password_reset_url"] = edit_password_reset_url(user.perishable_token)
#headers["X-SMTPAPI"] = "{\"category\" : \"Password Recovery\"}"#send grid category header
end
Why is "Password" in the error message referred to causing a timeout::error

Sending email (or other long running processes) from the main controller request thread is not a good idea. The sending of the email can time out for a variety of reasons that are not under your control (e.g. the outbound email delivery server being down) and you don't want your application server and users to suffer due to that.
A better approach is to use a queuing mechanism like Delayed Job (DJ) to queue these email tasks, and have them be processed outside of your controller threads.
See https://github.com/collectiveidea/delayed_job
Integration of this (or another queuing system) into your rails app is fairly simple. And rails 4 is said to have built in queuing services (which I'm yet to use) http://blog.remarkablelabs.com/2012/12/asynchronous-action-mailer-rails-4-countdown-to-2013.
For instance, if you use DJ in your app, the new code will look like below
def self.deliver_password_reset_instructions(user_id)
user = User.find(user_id)
user.reset_perishable_token!
# this is the only line that changes
Emailer.delay.deliver_password_reset_instructions(user)
end
The jobs are stored in the database, and re-tried when errors like time outs happen.
You can read more about DJ on the github page.

Related

Pass job results to other job with Sidekiq

I make an API call to fetch an email address. On success I'd like to pass this email address to another job. I use sidekiq and sidekiq-batch gems.
class HandleWebhookJob
def perform
batch = Sidekiq::Batch.new
batch.description = "Handling webhook"
batch.on(:success, HandleWebhookJob::OtherJob, { email: #email })
batch.jobs do
#email = GetEmailJob.perform_async # returns email address
end
end
class OtherJob
def on_success(status, options)
puts options # no email address here - nil
# need to pass it to UseEmailJob.perfom_async(options[:email])
end
end
end
I assume that assigning the result of GetEmailJob to #email won't work. Can't find an example of how to do this and if it's even possible.
I think that you get jid in #email.
Can you save emails in GetEmailJob to some storage (e.g. redis) with prefix:jid key and take them from there?

What does the 3rd line of code from the below snippet does?

def create
chef = Chef.find_by(email: params[:email])
if chef && chef.authenticate(params[:password])
**session[:chef_id] = chef.id**
flash[:success] = "You logged In"
redirect_to recipes_path
else
flash.now[:danger] = "Check your email or password"
render 'new'
end
end
What does
session[:chef_id] = chef.id
do? Is that session[:chef_id] a kind of variable or something? To which the id of a chef is assigned? Can I use some other name there?
That's storing the chef_id in the session, which is a way of persisting data accross multiple requests. It's not specific to Rails, as the session is also available in all web applications. You could read more on the Rails session here and more on web sessions here
session[:chef_id] = :foo stores chef_id key in session with value of :foo.
So you can fetch assigned :foo value later by calling session[:chef_id].
In your code it is assigned to id of chef who has email equals to params[:email].
You can name :chef_id whatever your like but I think it is pretty normal name.

Where can i put this rabbitmq consumer code?

I have this code of a rabbitmq consumer using bunny that should listen to messages published to a rabbitmq queue and display a flash notice on the view whenever a message is consumed. The consumer is running in a different session from the producer though they are in the same application. The application uses a direct exchange which uses the message receiver's email as the routing_key. I would like when a message with a routing_key similar to the current_user's email is published, a flash message is displayed for that user indicating that he has a new message without refreshing the page. I want behavior similar to Facebook notifications.
The producer code looks like this:
class MessagesController < ApplicationController
def create
#message = Message.new(message_params)
#message.creator_id = current_user.id
#message.receiver_id = params[:message][:receiver_id]
if #message.save
email = #message.receiver.email
$message_exchange.publish(#message.content, :routing_key => email)
redirect_to user_path(current_user)
end
end
The consumer code: looks like this:
email = current_user.email
$message_queue.bind($message_exchange, :routing_key => email)
logger.info " [*] Waiting for messages. To exit press CTRL+C"
$message_queue.subscribe(:manual_ack => true) do |delivery_info, properties, body|
logger.info " [x] #{delivery_info.routing_key}:#{body}"
if delivery_info.routing_key == current_user.email
flash[:notice] = "you have new message: #{body}"
end
end
The problem is that i don't know where to put the consumer code. I have tried putting the code as a method in the application controller but this does not seem to work. Any suggestion on how to do this better is highly appreciated.

Email body not sending with Action Mailer and Resque

I'm queuing up emails using ActionMailer and Resque. I'm not that familiar with either, but I have the basics down. Right now I'm debugging why when enqueueing emails, the email is sent but there is no body.. Mystifying.
When I execute ArtistReminderWorker.perform(8,2) the email is sent with the body. Great success!
When I execute Resque.enqueue(ArtistReminderWorker, 8, 2) the email is sent without the body. Mystery.
I thought these two statements were functionally the same, no?
At first I thought that the Resque worker wasn't able to look up the record, so I moved the database lookup from the worker into the mailer right before the mail command.
Here are my Worker and Mailer classes.
artist_reminder_worker.rb
class ArtistReminderWorker
#queue = :artist_reminders_queue
def self.perform(event_id, user_id)
ArtistReminderMailer.artist_reminder_email(event_id, user_id).deliver
end
end
artist_reminder_mailer.rb
class ArtistReminderMailer < ActionMailer::Base
default from: 'no-reply#example.com'
def artist_reminder_email(event_id, user_id)
#user = User.find(user_id)
#event = Event.find(event_id)
#url = dashboard_url
subject = "You have an upcoming gig at #{#event.venue.name}"
mail(to: #user.email, subject: subject)
end
end
My views are:
views/artist_reminder_mailer/artist_reminder_email.slim views/artist_reminder_mailer/artist_reminder_email.text.slim
Let me know if I can provide any more detail, thanks in advance!
Solved it. It wasn't actually anything I was doing wrong, but rather that I hadn't restarted the worker rake task in a while.
I guess when updating the Worker classes, you have to restart the worker.. Makes sense.

How does one use delayed_job to make an Rails 3.0 ActionMailer run asynchronously? Encountering ArgumentErrors

I'm trying to delay a notification email to be sent to users upon signing up to my app. The emails are sent using an ActionMailer which I call InitMailer. The way I am trying to delay the jobs is using collectiveidea's delayed_job https://github.com/collectiveidea/delayed_job. To do this you can see that i specify handle_asynchronously after defining the method initial_email:
class InitMailer < ActionMailer::Base
default :from => "info#blahblahblah.com"
def initial_email(user)
#user = user
#url = "http://www.blahblahblah.com"
mail(:to => user.email,
:subject => "Welcome to my website!"
)
end
handle_asynchronously :initial_email
end
However, I encounter an argument error in my log file "delayed_job.log":
Class#initial_email failed with ArgumentError: wrong number of arguments (1 for 0) - 5
failed attempts
For your information, the email is sent in a controller using the line:
#user = InitUser.new(params[:init_user])
InitMailer.delay.initial_email(#user)
Additionally, when I set up my code without the delay, the emails were sent out without problem (except for the fact that it slowed down my app waiting for gmail servers)
Where is causing the errors here? How can I get the delayed mail to send properly?
Due to the way that Rails3 implements mailers, there are some unusual workarounds for delayed_jobs. For instance, you have seen that to delay the mailing, you write
ExampleMailer.delay.example(user)
While typically you would have to write handle_asynchronously after the method definition, in the case of mailers this declaration (for some reason) prevents that delayed job from working.
So in this code, drop the declaration entirely:
class InitMailer < ActionMailer::Base
default :from => "info#blahblahblah.com"
def initial_email(user)
#user = user
#url = "http://www.blahblahblah.com"
mail(:to => user.email,
:subject => "Welcome to my website!"
)
end
#No handle_asynchronously needed here
end

Resources