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 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})
  end
end

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.

after_create and after_commit,布布扣,bubuko.com

时间: 2025-01-04 21:30:06

after_create and after_commit的相关文章

rails使用bootstrap

在Gemfile文件中添加'bootstrap-sass',再运行bundle install gem 'bootstrap-sass' 在config/application.rb添加一行代码,让bootstrap-sass和asset pipeline兼容 class Application < Rails::Application # Settings in config/environments/* take precedence over those specified here. #

Rails 中的事物处理

1. 使用事物的原因 保证数据一致性, 当其中出现一个失败的时候,操作可以回滚 比如: ActiveRecord::Base.transaction do david.withdrawal(100) # withdrawal失败必须触发 exception mary.deposit(100) # deposit失败必须触发 exception end return false 不会出发操作回滚, 所以事物中要使用!的方法, 比如 save! update! before_save 等callba

Mysql5.7基于GTID的半同步复制

一.GTID是什么 GTID是事务的ID,唯一识别号,全局唯一.随事务记录到Binary Log中,用来标识事务.每个事务有一个Gtid_log_event.GTID的构成:UUID + Sequence Number Sequence Number是MySQL服务器内部的一个事务顺序号.一个MySQL服务器上的事务不会有重复的顺序号(保证服务器内唯一).每个MySQL服务器有一个全局唯一的UUID. GTID的目的简化复制的使用过程和降低复制集群维护的难度,不再依赖Master的binlog文

rails 5 功能新增及改变

1.ApplicationRecord 在Rails4中所有的模型都继承自ActiveRecord::Base,不过在Rails5中新引进了一个叫ApplicationRecord的类,存放在: app/models/application_record.rb中,所有Rails5应用都会有这个类, 它的内容非常简单: class ApplicationRecord < ActiveRecord::Base       self.abstract_class = true   end 就是一个继承

MySQL 5.7 Replication 相关新功能说明

背景: MySQL5.7在主从复制上面相对之前版本多了一些新特性,包括多源复制.基于组提交的并行复制.在线修改Replication Filter.GTID增强.半同步复制增强等.因为都是和复制相关,所以本文将针对这些新特性放一起进行说明,篇幅可能稍长,本文使用的MySQL版本是5.7.13. 1,多源复制(多主一从) MySQL在5.7之后才支持多源复制,之前介绍过MariaDB 多主一从 搭建测试说明,现在介绍如何在MySQL上做多主一从,具体的方法说明可以查看官方文档. 原理:多源复制加入

半同步复制

异步复制: mysql默认的复制就是异步的,主库在执行完客户端提交的事物后会立即返回结果给客户端,并不判断从库是否已经接受并处理,这样就会有一个问题,如果这个时候主库crash,主库上已经提交的事物可能还没有传到从库上,如果此时,强行将从提升为主,可能导致新主上的数据丢失. 全同步复制: 指当主库执行完一个事物,所有的从库都执行了该事物才返回给客户端.因为需要等待所有从库执行完该事物才返回,所以全同步复制对性能影响很严重. 半同步复制: 介于异步复制和全同步复制之间,主库在执行完客户端提交的事务

MySQL5.7新特性:lossless replication 无损复制

MySQL的三种复制方式 asynchronous 异步复制 fully synchronous 全同步复制 Semisynchronous 半同步复制 asynchronous replication 原理:在异步复制中,master写数据到binlog且sync,slave request binlog后写入relay-log并flush disk优点:复制的性能最好缺点:master挂掉后,slave可能会丢失事务代表:MySQL原生的复制 fully synchronous replic

sqlalchemy中使用event设置条件触发短信与邮件通知

一.原因 近期在做短信与邮件通知系统.使用到了这一块.例如,当订单完成以后进行邮件短信的通知.虽然可以采用直接调用接口的方式实现,但有几个原因让我希望使用条件触发的方式 1.由于系统中支持线上线下以及代充值等多种方式,所以在多个地方订单改变状态.这样就让触发通知的代码凌乱分布. 2.系统将来扩建,需要新增加接口.则需要新增加调用的代码. 总而言之,直接调用将会增加维护难度.因此准备在订单的状态首次被置为支付成功时候进行短信与邮件的通知. 二.模块需求 短信与邮件的通知不能影响内部系统的运行,但由

MySQL5.7 大大降低了半同步复制-数据丢失的风险

如果你的生产线开启了半同步复制,那么对数据的一致性会要求较高,但在MySQL5.5/5.6里,会存在数据不一致的风险.有这么一个场景,客户端提交了一个事务,master把binlog发送给slave,在发送的期间,网络出现波动,此时Binlog Dump线程发送就会卡住,要等待slave把binlog写到本地的relay-log里,然后给master一个反馈,等待的时间以rpl_semi_sync_master_timeout参数为准,默认为10秒.在这等待的10秒钟里,在其他会话里,查看刚才的