Live data from Hacker News

Rails, callbacks, workers, and the race you never expected to lose

logicalfriday.com

11–20 of 28 posts

Re: Rails, callbacks, workers, and the race you never expected to lose

#12
post #11

Emails are like async views. The model should not be responsible for rendering views. That's the responsibility of the controller. Which is why Action Mailer itself is modelled on Action Controller, uses Action View, and so forth.

He's not using the model to render the view, though, he's using it to queue up a future asynchronous request to the relevant mail "controller".

Re: Rails, callbacks, workers, and the race you never expected to lose

#13
post #4

For this and related predictability/absence-of-surprise reasons, I have generally moved away from ActiveRecord callbacks in my Rails projects. It's very easy to forget that you e.g. have an after_save defined for accounts which made sense when accounts necessarily represented people with credit cards in the system but doesn't make sense for e.g. people paying via purchase order. It is also easy to forget an after_sav…

Absolutely agreed - better to make these things explicit somewhere rather than having your business logic implicitly handled by something directly coupled to your persistence layer (with the exception of data integrity tasks as mentioned by danenania). As a sort of half-way house between using lifecycle callbacks and sticking everything in the controller, I've just moved the main app I work on to a pub-sub event syst…

I would love to read a write up on how you accomplished this.

Re: Rails, callbacks, workers, and the race you never expected to lose

#14
post #4

For this and related predictability/absence-of-surprise reasons, I have generally moved away from ActiveRecord callbacks in my Rails projects. It's very easy to forget that you e.g. have an after_save defined for accounts which made sense when accounts necessarily represented people with credit cards in the system but doesn't make sense for e.g. people paying via purchase order. It is also easy to forget an after_sav…

Agreed, I've come to view callbacks as a tool to enforce application-level data integrity and relationships, not a place that higher level business logic should live. The line is often fuzzy, but I think the question you alluded to, "should this happen with every conceivable create/save/etc.?" is a good general guideline. One thing to keep in mind when moving logic out of callbacks is that you'll no longer get the im…

And even if it happens for every create, the fact that the author had to do so much more work just to make something like this work is a sign that Model callbacks may not be the best solution.

Re: Rails, callbacks, workers, and the race you never expected to lose

#15

Earlier quoted context omitted.

Absolutely agreed - better to make these things explicit somewhere rather than having your business logic implicitly handled by something directly coupled to your persistence layer (with the exception of data integrity tasks as mentioned by danenania). As a sort of half-way house between using lifecycle callbacks and sticking everything in the controller, I've just moved the main app I work on to a pub-sub event syst…

I would love to read a write up on how you accomplished this.

I'll post something about it soon - while the changes were wide-ranging for us (because these post-action tasks were initially scattered throughout our app), the extra infrastructure required was really pretty minimal. Most of the actual time spent in this refactoring was writing new tests for code that we'd previously been unable to practically test.

In the meantime, the pub/sub setup we've got is pretty similar to the one presented a little way down the following article:

https://www.theagileplanner.com/blog/building-agile-planner/...

Our listeners sit completely separate from the controllers, though, and we're not using them for anything like handling controller response logic (I'm not sure how I feel about that, although the idea is certainly interesting).

Re: Rails, callbacks, workers, and the race you never expected to lose

#16
post #11

Emails are like async views. The model should not be responsible for rendering views. That's the responsibility of the controller. Which is why Action Mailer itself is modelled on Action Controller, uses Action View, and so forth.

He's not using the model to render the view, though, he's using it to queue up a future asynchronous request to the relevant mail "controller".

Sure, you're right, but only technically. Having an extra collaborator in the model is asking for trouble - there's nothing about a User that requires it to know anything about emailing. It violates all kinds of design principles. Putting your logic for "I should email the User when they sign up" into the User model is using "design by related words".

In traditional Rails design, the controller is exactly the right place for this. Another option is a service layer of some kind, if you do that, e.g. a DCI context, or just another Plain-Ol-Ruby-Object that is in charge of the logic of signing up a user.

The best part of separating out the logical business sequence of signing up a user into its own object (I can see this method in my head, something like save_new_user, sign_in_user, queue_welcome_email) is that it becomes massively easier to test - you can just mock the various calls to its collaborators' public methods because they should all have unit tests.

Re: Rails, callbacks, workers, and the race you never expected to lose

#17
This is a great example of the complexity tradeoff when you choose performance over atomicity. Delayed::Job has lost favor lately, but the semantics of the job becoming visible to the queue runner atomically with the related new record are hard to beat.

Note that even in the proposed solution, after_commit hooks offer no guarantee that the job will ever be enqueued, if for example redis is unavailable, or your process gets reaped between when the database commit and when the after_commit hook gets fired -- the commit has already happened. So if it's really important that your asynchronous task be queued, keeping the queue in your DB has some advantages.

Re: Rails, callbacks, workers, and the race you never expected to lose

#20
post #11

Emails are like async views. The model should not be responsible for rendering views. That's the responsibility of the controller. Which is why Action Mailer itself is modelled on Action Controller, uses Action View, and so forth.

I agree. I've been bitten a couple times by putting this kind of functionality in the model.
Post reply on HN