I think this was the lowest-hanging fruit in Rails. Glad they've finally addressed it in core. Years of people using job queues just to send mail is finally over!
But, why doesn't this let me pass objects to Mailer methods? I can't help but think passing ids and finding the object again on the other side is bad design.
Myself, over the past few years, instead of a job queue, I have been using a simple gem called spawn (https://github.com/tra/spawn) to fork processes to asynchronously send mail from Rails. It lets me pass objects around... I hope that this asynchronous ActionMailer gets this functionality as well!
I think the main reason for this is that strange things can happen when you're passing marshalled objects around. For instance, say that your application has just created an Email object which has a "title" attribute, and it has been marshalled and put it into a queue. Your queue is a bit full, so it takes a while for the email to be processed. Right after it has been enqueued, you roll out an update to your code, changing the "title" field to be called "subject". As soon as your Email object is pulled from the queue, you will get NoMethodErrors from your asynchronous mailer code because the unmarshalled object doesn't have a "subject" method yet.
The reason queueing systems like Resque and Sidekiq pass id's around (instead of objects) is to make sure that the asynchronous worker always has a fresh object straigh from the data store. I guess that's why Rails core has decided to implement it like this as well.
> I can't help but think passing ids and finding the object again on the other side is bad design.
It is good design. Why? Because you don't want to pass mutable state (read: your user model) across thread/process boundaries if you can help it. What if the mailer modifies it? What if the caller modifies it while the mailer is working with it? It may no longer be valid. Rather than trying to reason about all possible states it could be in, it's easier just to make the job bootstrap itself with all the data it needs.
Confining instances to threads is far more sane than using mutexes and locks or relying on your runtime's GIL to watch out for you.
Since you're unlikely to be sharing memory with your background processor, what you'd really be doing is marshaling the object on enqueue and unmarshaling on decode. In that case, you're no more susceptible to further modifications to the data than you would be with a DB lookup.
There of course may be other issues with marshaling. E.g., marshaling of procs is particularly tricky. But I've been marshaling with sidekiq for the past couple months and really haven't run into any problems. And it cleaned up the code a fair bit because I basically treat it as an RPC without any of the ceremonious DB lookups at the start of every async method.
What? You don't have to modify it to send a query.
I was discussing a way of structuring your code such that the scenario I described simply cannot happen. Preventing such silliness ahead of time is what robust engineering is all about.
I know people have largely moved to using remote services to send their mail. However, if you're about to introduce a queue only for that then a local MTA seems a more sensible choice. A minimal postfix satellite config is 12 lines - not rocket science and robust as a brick.
I currently us a combo because some of our emails are reports that take a bit to compile. So, the async method makes sense there. But every one of our nodes has a local postfix config and transport up to SendGrid. I trust Postfix will do a better job dealing with temporary outages than I will in Ruby.
I'd imagine this is great for those on PaaS where they can't just install local software like an MTA, too.
With the default Rails.queue you can pass objects to your mailer. The issue is when you start using a background processor that requires the marshalling of the object. It is recommended to not use complex objects but if you are confident that the objects you are passing will marshall properly you should be OK
But, why doesn't this let me pass objects to Mailer methods? I can't help but think passing ids and finding the object again on the other side is bad design.
Myself, over the past few years, instead of a job queue, I have been using a simple gem called spawn (https://github.com/tra/spawn) to fork processes to asynchronously send mail from Rails. It lets me pass objects around... I hope that this asynchronous ActionMailer gets this functionality as well!