Created
August 19, 2017 23:12
-
-
Save dany1468/e383681cd1e5cb44d9fa23a6c2c393b0 to your computer and use it in GitHub Desktop.
Rails でのバックグラウンドジョブの実行と transaction 利用について ref: http://qiita.com/dany1468/items/f9278b43d4ecb63c2920
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ActiveRecord::Base.transaction do | |
| ActiveRecord::Base.after_transaction_commit { run_some_background_job } | |
| # run_some_background_job has not run yet | |
| end | |
| # now, it has run | |
| # this one runs immediately, since we are outside a transaction | |
| ActiveRecord::Base.after_transaction_commit { some_other_task } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class UserMailWorker | |
| include Sidekiq::Worker | |
| def perform(user_id) | |
| user = User.find(user_id) | |
| # 送信処理 | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| User.transaction do | |
| user = User.create!(param) | |
| UserMailWorker.perform_async(user.id) | |
| # 他の transaction 内でするべき処理など | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module ActiveRecord | |
| module ConnectionAdapters # :nodoc: | |
| module DatabaseStatements | |
| def add_transaction_record(record) | |
| current_transaction.add_record(record) | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module ActiveRecord | |
| module ConnectionAdapters | |
| class Transaction #:nodoc: | |
| attr_reader :connection, :state, :records, :savepoint_name | |
| attr_writer :joinable | |
| def initialize(connection, options, run_commit_callbacks: false) | |
| @connection = connection | |
| @state = TransactionState.new | |
| @records = [] | |
| @joinable = options.fetch(:joinable, true) | |
| @run_commit_callbacks = run_commit_callbacks | |
| end | |
| def add_record(record) | |
| records << record | |
| end | |
| def commit_records | |
| ite = records.uniq | |
| while record = ite.shift | |
| if @run_commit_callbacks | |
| record.committed! | |
| else | |
| # if not running callbacks, only adds the record to the parent transaction | |
| record.add_to_transaction | |
| end | |
| end | |
| ensure | |
| ite.each { |i| i.committed!(should_run_callbacks: false) } | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module ActiveRecord | |
| module ConnectionAdapters | |
| class TransactionManager #:nodoc: | |
| def initialize(connection) | |
| @stack = [] | |
| @connection = connection | |
| end | |
| def begin_transaction(options = {}) | |
| # 略 | |
| end | |
| def commit_transaction | |
| @connection.lock.synchronize do | |
| transaction = @stack.last | |
| begin | |
| transaction.before_commit_records | |
| ensure | |
| @stack.pop | |
| end | |
| transaction.commit | |
| # 🏁 ここで commit_records | |
| transaction.commit_records | |
| end | |
| end | |
| def rollback_transaction(transaction = nil) | |
| @connection.lock.synchronize do | |
| transaction ||= @stack.pop | |
| transaction.rollback | |
| transaction.rollback_records | |
| end | |
| end | |
| def within_new_transaction(options = {}) | |
| @connection.lock.synchronize do | |
| begin | |
| transaction = begin_transaction options | |
| yield | |
| rescue Exception => error | |
| if transaction | |
| rollback_transaction | |
| after_failure_actions(transaction, error) | |
| end | |
| raise | |
| ensure | |
| unless error | |
| if Thread.current.status == "aborting" | |
| rollback_transaction if transaction | |
| else | |
| begin | |
| # 🏁 ここで commit_transaction | |
| commit_transaction | |
| rescue Exception | |
| rollback_transaction(transaction) unless transaction.state.completed? | |
| raise | |
| end | |
| end | |
| end | |
| end | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class AsyncRecord | |
| def initialize(*args) | |
| @args = args | |
| end | |
| def has_transactional_callbacks? | |
| true | |
| end | |
| def committed!(*_, **__) | |
| do_after_commit | |
| end | |
| def rolledback!(*_, **__) | |
| # after_rollback 相当の処理 | |
| end | |
| def do_after_commit | |
| # after_commit の処理 | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| def perform_after_transaction(*args) | |
| async_record = AsyncRecord.new(*args) | |
| if ActiveRecord::Base.connection.transaction_open? | |
| ActiveRecord::Base. | |
| connection. | |
| current_transaction. | |
| add_record(async_record) | |
| else | |
| async_record.do_after_commit | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| User.transaction do | |
| user = User.create!(param) | |
| # 他の transaction 内でするべき処理など | |
| end | |
| UserMailWorker.perform_async(user.id) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Conversation < ActiveRecord::Base | |
| enum status: [:inactive :active, :archived] | |
| after_update_commit :notify_active | |
| def notify_active | |
| if previous_changes.key?(:status) && previous_changes[:status].first.to_s == 'active' | |
| NotifyWorker.perform_async(id) | |
| end | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Conversation < ActiveRecord::Base | |
| enum status: [:inactive :active, :archived] | |
| aasm column: :status, enum: true do | |
| state :inactive, initial: true | |
| state :active | |
| state :archived | |
| event :activate, after_commit: :notify_active do | |
| transitions from: :inactive, to: :active | |
| end | |
| event :archive do | |
| transitions from: %i(inactive active), to: :archive | |
| end | |
| end | |
| def notify_active | |
| # activate イベントでしか実行されないので、ここに条件は不要 | |
| NotifyWorker.perform_async(id) | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module ActiveRecord | |
| module Transactions | |
| def destroy #:nodoc: | |
| with_transaction_returning_status { super } | |
| end | |
| def save(*) #:nodoc: | |
| rollback_active_record_state! do | |
| with_transaction_returning_status { super } | |
| end | |
| end | |
| def save!(*) #:nodoc: | |
| with_transaction_returning_status { super } | |
| end | |
| def touch(*) #:nodoc: | |
| with_transaction_returning_status { super } | |
| end | |
| def with_transaction_returning_status | |
| status = nil | |
| self.class.transaction do | |
| add_to_transaction | |
| begin | |
| status = yield | |
| rescue ActiveRecord::Rollback | |
| clear_transaction_record_state | |
| status = nil | |
| end | |
| raise ActiveRecord::Rollback unless status | |
| end | |
| status | |
| ensure | |
| if @transaction_state && @transaction_state.committed? | |
| clear_transaction_record_state | |
| end | |
| end | |
| def add_to_transaction | |
| if has_transactional_callbacks? | |
| self.class.connection.add_transaction_record(self) | |
| else | |
| sync_with_transaction_state | |
| set_transaction_state(self.class.connection.transaction_state) | |
| end | |
| remember_transaction_record_state | |
| end | |
| private | |
| def has_transactional_callbacks? | |
| !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty? | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| module AASM | |
| module Persistence | |
| module ORM | |
| def aasm_fire_event(state_machine_name, name, options, *args, &block) | |
| if aasm_supports_transactions? && options[:persist] | |
| event = self.class.aasm(state_machine_name).state_machine.events[name] | |
| event.fire_callbacks(:before_transaction, self, *args) | |
| event.fire_global_callbacks(:before_all_transactions, self, *args) | |
| begin | |
| # 🏁 ここの super で lifecycle が実行される | |
| # https://github.com/aasm/aasm#lifecycle | |
| success = if options[:persist] && use_transactions?(state_machine_name) | |
| aasm_transaction(requires_new?(state_machine_name), requires_lock?(state_machine_name)) do | |
| super | |
| end | |
| else | |
| super | |
| end | |
| # ☠️ after_commit は単に lifecycle が終了した時に呼ばれるだけで、transaction とは関係が無い | |
| if success | |
| event.fire_callbacks(:after_commit, self, *args) | |
| event.fire_global_callbacks(:after_all_commits, self, *args) | |
| end | |
| success | |
| ensure | |
| event.fire_callbacks(:after_transaction, self, *args) | |
| event.fire_global_callbacks(:after_all_transactions, self, *args) | |
| end | |
| else | |
| super | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class User < ApplicationRecord | |
| after_create_commit :send_mail | |
| def send_mail | |
| UserMailWorker.perform_async(id) | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment