Sending Devise mail via ActiveJob - ruby-on-rails

in one of our apps we have a running ActiveJob / Sidekiq / ActionMailer setup which is working fine. As it's a booking app we send confirmations to users successfully through this setupo.
We're experiencing difficulties trying to get Devise sending mails on user actions (registration, password_reset, etc.) via ActiveJob.
I've did override the default behavior of notification sending within my User model like so:
def send_devise_notification(notification, *args)
devise_mailer.send(notification, self, *args).deliver_later
end
Update
I've come a step closer. I added a queue Devise pushed it's job to which was missing (XXX_default_mailer_queue) inconfig/sidekiq.yml`. Now Devise successfully pushes jobs to the Sidekiq queue. But unfortunately still no mails become sent, Job execution times are far below 1s:
ActionMailer::DeliveryJob JID-fa4bccc79be489f098c7f5ef INFO: done: 0.415
If i switch to devise_mailer.send(notification, self, *args).deliver_now the proper email gets rendered but not sent either.
As I said, our Mail setup is working apart from Devise which is why I assume it to be a Devise specific problem somewhere
What does Devise do here? How can I debug the process? Normally I would jump somewhere in my job or mailer classes but there are none...
best, Andi

Related

Rails Action Mailer with deliver_later on AWS

I have my production app running on AWS but am having issues with the active job - deliver_later on certain mailers. I am successfully sending all emails with deliver_later in development but there is something different in production. Certain mailers work with deliver_later but not my welcome mailer (welcomes new users). So I have to set deliver_now on this welcome mailer to have it actually send the email.
//doesn't work, email is not sent
UserMailer.welcome_email(self).deliver_later
//works
UserMailer.welcome_email(self).deliver_now
The log file from the server show this and nothing more when I use deliver_later:
[ActiveJob] Enqueued ActionMailer::DeliveryJob (Job ID:
d7114-464e-4a90-9721-126650) to Async(mailers) with arguments:
"UserMailer", "welcome_email", "deliver_now", #>
Any help would be appreciated. Thanks.
deliver_now Delivers an email at a time.
If you use deliver_later you have to use ActiveJob runner like Sidekiq you can see this gist Sending emails with ActionMailer and Sidekiq also see this for basic Sidekiq email sent.
Enqueues the email to be delivered through Active Job. When the job runs it will send the email using deliver_now.
UserMailer.welcome_email(self).deliver_later(wait: 1.hour)
method-i-deliver_later
Basically, deliver_later is asynchronous. When you use this method, the email is not sent at the moment, but rather is pushed into a job's queue. If the job is not running, the email will not be sent. deliver_now will send the email at the moment, no matter what is the job's state. Here you can see the documentation for delivery methods.
What ActiveJob backend are you using?
ActiveJob to provide asynchronous execution but does not provide asynchronicity itself.
You need to use some asynchronous backend like sidekiq, delayed_job or something else.
This is because you haven't set the time and rails picking up default time. and in log file also it shows that the deliver_later method is not being called yet.
Try using:
UserMailer.welcome_email(self).deliver_later(wait: 1.minute)
Then check the log after one min.
If you are using delayed_job, you have to run rake jobs:work
https://www.youtube.com/watch?v=AoGPaWEasxs

Rails 4.2.5 - ActiveJob using deliver_now even after specifying deliver_later

I plan to send mails asynchronously in Rails 4.2.5 using ActionMailer#deliver_later.
The AppMailer file uses 'mail' to send across the mail & I am using deliver_later as follows:
AppMailer.project_created_support_mail(#project).deliver_later
However, logs show that the argument passed still is 'deliver_now'
[ActiveJob] [ActionMailer::DeliveryJob] [b341ea8c-ca96-4a4e-8079-572925864352] Performing ActionMailer::DeliveryJob from Inline(mailers) with arguments: "AppMailer", "project_created_support_mail", "deliver_now"
I am assuming there isn't an explicit need to attach a queue like Sidekiq or Resque.
Can someone please help in understanding what I am missing here?
EDIT: The original problem I am facing is that the action to send mail takes ~5 seconds with or without deliver_later. In an attempt to debug the issue, I found out that the argument is still 'deliver_now' (logs above). As pointed out by #torrocus below, 'deliver_now' is called inside the queue. However, the issue that deliver_later takes ~5 seconds still remains.
When you call #deliver_later method you put the job into the queue Active Job. Inside the queue it calls #deliver_now.
deliver_later (ActionMailer::MessageDelivery)
In development I propose to use gem mailcatcher

Rails 4: How to send mail in a separate thread?

I have an application that needs to occasionally send an email blast to the entire user base when an admin does something. This was working fine, but when there are a lot of users, the page for the admin will wait until all the mail is sent, which is undesirable.
To mitigate this, I tried sending email in a new thread:
t = Thread.new do
User.all.each do |user|
Mailer.email(user).deliver
end
end
at_exit{ t.join }
This worked fine, but then in my test suite, I can't test to ensure the email sending works:
# This test now fails with the new Thread above
test "admin action should send email blast" do
assert_difference("ActionMailer::Base.deliveries.count", User.count) do
post :action
end
end
So my questions are:
Is this method the best way to send email in a new Thread? Or is there a gem available that handles this kind of interaction?
How can I test that the emails are sent in my test suite if the sending is done in a new thread? Is there a way to check to wait for all threads to finish?
In rails 4.2 there's a special class that handles jobs called ActiveJob, active job allows you to queue long tasks for another process to handle them in the background, also you can queue tasks for a certain time, like for example "send this email tomorrow at 8 am".
For these queues to get handled you need to choose a backend to handle them, here's a list of backends that support ActiveJob
Each has it's pros and cons, sidekiq for example is a multithread handler, so it uses lower memory, while for example sucker punch uses the same thread as the main server, so it uses a lot less memory, suitable if you have a low memory server that can't handle a second ruby thread.
As for the testing part, rails guide already explains how to test your emails and test things like if emails has been queued or not, and test that the right template was rendered, and if it contains the right text.
I think you want to send mail asynchronously, For that you may use many gems like - delayed jobs, sidekiq etc. I would personally recommend to use sidekiq as its faster and used Redis in memory db behind the scene.
With Rails 4.2, Active jobs are introduced, so using it has advantage that you can switch from one queuing system to other without having any worry at any time and you can specify which queing machinery yiu want like
module YourApp
class Application < Rails::Application
# Be sure to have the adapter's gem in your Gemfile
# and follow the adapter's specific installation
# and deployment instructions.
config.active_job.queue_adapter = :sidekiq
end
end
I prefer using a queue based approach for async tasks. [Delayed job] (https://github.com/collectiveidea/delayed_job) is one option, but I prefer using [sidekiq] (http://sidekiq.org).
[Here] (http://blog.remarkablelabs.com/2013/01/using-sidekiq-to-send-emails-asynchronously) is an example of sending emails asynchronously using sidekiq.
Best way to send emails in separate threads is to use delayed jobs or similar gem.
https://github.com/collectiveidea/delayed_job
With delayed jobs gem, you can also send emails or do something else in different threads at scheduled time.
Another option is to use sidekiq, install sidekiq gem and add this line to application.rb file
config.active_job.queue_adapter = :sidekiq

In Rails 4 how to store all sent emails with attachments and re-send them again later?

In my Rails 4 application I already store all outgoing emails in DB using an observer. In config/initializers/all_emails_observer.rb I have roughly the following code:
class AllEmailObserver
def self.delivered_email(message)
sent_email = SentEmail.new(
sender: message.from.join(';'),
recipients: message.to,
subject: message.subject
)
sent_email.body = if message.html_part || message.text_part
message.html_part.blank? ? message.text_part.body.raw_source : message.html_part.body.raw_source
else
message.body.raw_source
end
sent_email.save!
end
end
ActionMailer::Base.register_observer(AllEmailObserver)
Now I need to add another feature to let admins re-send any of the emails from the email log.
There are a couple of solutions that I could see right off hand:
Add code to store all attachments (regular and inline), then implement a method that would generate a new email and re-send it. I'm working on this now.
Store the mailer class, mailer method and arguments (serialized using GlobalId, for example). When the admin needs to re-send an email - generate a new email from scratch. I don't like this method because ideally I want re-sent emails to have exactly the same content (including attachments) as when they were originally sent. Mailers could have bugs/typos fixed since, but I do want all those bugs and typos to be still there when an email is re-sent.
Now that Rails 4.2 is released and ActiveJob is stable, I didn't find the standard way to do this: all queued emails are immediately deleted after they are sent (I just checked delayed_job backend). There must be a way to maybe preserve a completed mailer job in some separate DB table and then re-queue that job when needed.
I'd love to re-use the email serialization mechanism of ActiveJob to achieve my goal if possible, because I don't want to re-write my observer when Rails 6 is released with a completely changed observer API.
What is the better way to enable re-sending of sent emails?
Please note that I already reviewed similar questions here:
Save each email before sending rails 4
How do I create a Mailer Observer
However both of them are about how to store sent emails and not about how to re-send them later as is.
Thank you!
Alex.

Getting started: Sending an email in Rails with delayed job send email

I am new to delayed job. I am trying to configure delayed job to send an email. In my model I have...
def send_reminder_emails
NsoMailer.send_reminder_emails(self)
end
I have the appropriate send_reminder_emails action in app/mailers/nso_mailer.rb, and a test email in registrations/reminder_email.html.erb.
I have followed directions in https://github.com/collectiveidea/delayed_job to install the delayed job and daemons gems. Now my question is how to configure the job? In this case I think the job should be Delayed::Job.enqueue Registrations.send_email_reminders??? Documentation seems a little lacking on the github wiki. I know there is the script/delayed_job file. Should I be modifying this file?
If anyone can suggest a getting started page or blog that would be nice. I'm running this on my own server (no heroku), also we have our own SMTP server (so no sendgrid or 3rd party mail services). Mail is already configured on the server and I am successfully sending emails. My goal with delayed_job is to send some reminder emails once per day.
Any help or nudge in the right direction greatly appreciated.
If you want to call the method on your model asynchronously then you can do the following:
model_instance.delay.send_reminder_email
If you want to call a method on the mailer directly then you'll do something like (I'm assuming that the method name defined in nso_mailer.rb is reminder_emails):
NsoMailer.delay.reminder_emails(model_instance)
In my projects, I usually have either a service object take care of all the notifications or create a concern which handles the sending of emails.
A good resource to look at is Ryan Bates's Railcast titled 'Service Objects' or read DHH's blog post on using concerns Put chubby models on a diet with concerns
As for your setup questions, if you are deploying via capistrano then you can call bundle exec bin/delayed_job restart after the deployment is successful to restart the DJ daemon.

Resources