after_create and after_commit

來源:互聯網
上載者:User

標籤:des   http   os   io   for   cti   

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})  endendclass NotificationWorker < Workling::Base  def send_notification(params)    notification = Notification.find(params[:notification_id])    user = notification.user    # send notification to user‘s friends by email  endend

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 process worker 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})  endend

Now the transactions order becomes

main process worker 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.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.