after_create and after_commit

A relational database, like mysql, provides transactions to wrap several operations in one unit, make them all pass or all fail. All isolation levels except READ UNCOMMITTED don't allow read data changes until they are committed in other transaction. If you don't realize it, you probably introduce some unexpected errors.

Before

It's common to generate a background job to send emails, tweets or post to facebook wall, like

class Notification < ActiveRecord::Base after_create :asyns_send_notification def async_send_notification NotificationWorker.async_send_notification({:notification_id => id}) end end class NotificationWorker < Workling::Base def send_notification(params) notification = Notification.find(params[:notification_id]) user = notification.user # send notification to user's friends by email end end

It looks fine, every time it creates a notification, generates an asynchronous worker, assigns notification_id to the worker, in the worker it finds the notification by id, then sends notification by email.

You won't see any issue in development, as local db can commit fast. But in production server, db traffic might be huge, worker probably finish faster than transaction commit. e.g.

main processworker process
BEGIN 
INSERT INTO notifications(message, user_id) values('notification message', 1) 
# return id 10 for newly-created notification 
 SELECT * FROM notifications WHERE id = 10
COMMIT 

In this case, the worker process query the newly-created notification before main process commits the transaction, it will raise NotFoundError, because transaction in worker process can't read uncommitted notification from transaction in main process.

Refactor

So we should tell activerecord to generate notification worker after notification insertion transaction committed.

class Notification < ActiveRecord::Base after_commit :asyns_send_notification, :on => :create def async_send_notification NotificationWorker.async_send_notification({:notification_id => id}) end end

Now the transactions order becomes

main processworker process
BEGIN 
INSERT INTO notifications(message, user_id) values('notification message', 1) 
# return id 10 for newly-created notification 
COMMIT 
 SELECT * FROM notifications WHERE id = 10

Worker process won't receive NotFoundErrors any more.

For those callbacks that no need to execute in one transaction, you should always use after_commit to avoid unexpected errors.

after_commit is introduced from rails 3, if you use rails 2, please check out after_commit gem instead.

转载于:https://www.cnblogs.com/wangyuyu/p/3853228.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值