Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

after_commit callback does not fire if the same record got updated with a different reference in the same transaction block #34644

Closed
marunbai opened this issue Dec 6, 2018 · 1 comment
Assignees

Comments

@marunbai
Copy link

marunbai commented Dec 6, 2018

Steps to reproduce

Here is the set up:

There are 2 simple models. Cat and Dog. Cat has a name and Dog has a name and cat_id.

Cat has an after_update callback that will update all its dogs.
Dog has an after_commit callback that will print dog barks on destroy.

The dog callback will not fire if they got called in the same transaction because ActiveRecord::ConnectionAdapters::Transaction#commit_records method will do uniq on records. Sometimes, same record could get pushed into the transaction records array with different state multiple times, like one destroyed and one persisted.

# frozen_string_literal: true

begin
  require "bundler/inline"
rescue LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  # Activate the gem you are reporting the issue against.
  gem "activerecord", "5.2.1"
  gem "sqlite3"
  gem "pry"
end

require "active_record"
require "minitest/autorun"
require "logger"
require "pry"

ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
  create_table :cats do |t|
    t.string :name

    t.timestamps
  end

  create_table :dogs do |t|
    t.string :name
    t.integer :cat_id

    t.timestamps
  end
end

class Cat < ActiveRecord::Base
  has_many :dogs

  after_update :update_dogs

  def update_name(name)
    self.name = name

    save!
  end

  def update_dogs
    dogs.each do |dog|
      dog.name = 'new dog'
      dog.save!
    end
  end
end

class Dog < ActiveRecord::Base
  belongs_to :cat

  after_commit :bark, on: :destroy

  def bark
    puts "dog barks"
  end
end

class BugTest < Minitest::Test
  def test_stuff
    Cat.create(:name => 'cat')
    dog = Dog.new(:name => 'dog_2')
    dog.cat = Cat.last
    dog.save

    puts'----------------------------------'
    puts'Dog should bark in this example: '
    puts

    Dog.transaction do
      dog = Dog.last
      cat = dog.cat
      cat.update_name('cat 2')

      dog.destroy
    end
  end
end

Expected behavior

after_destroy_commit callback bark should fire after dog.destroy in the transaction above.

D, [2018-12-07T10:56:59.999822 #33390] DEBUG -- :    (0.0ms)  begin transaction
D, [2018-12-07T10:57:00.000744 #33390] DEBUG -- :   Dog Load (0.2ms)  SELECT  "dogs".* FROM "dogs" ORDER BY "dogs"."id" DESC LIMIT ?  [["LIMIT", 1]]
D, [2018-12-07T10:57:00.008017 #33390] DEBUG -- :   Cat Load (0.1ms)  SELECT  "cats".* FROM "cats" WHERE "cats"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
D, [2018-12-07T10:57:00.009899 #33390] DEBUG -- :   Cat Update (0.1ms)  UPDATE "cats" SET "name" = ?, "updated_at" = ? WHERE "cats"."id" = ?  [["name", "cat 2"], ["updated_at", "2018-12-07 15:57:00.008533"], ["id", 1]]
D, [2018-12-07T10:57:00.015809 #33390] DEBUG -- :   Dog Load (0.1ms)  SELECT "dogs".* FROM "dogs" WHERE "dogs"."cat_id" = ?  [["cat_id", 1]]
D, [2018-12-07T10:57:00.017123 #33390] DEBUG -- :   Dog Update (0.2ms)  UPDATE "dogs" SET "name" = ?, "updated_at" = ? WHERE "dogs"."id" = ?  [["name", "new dog"], ["updated_at", "2018-12-07 15:57:00.016259"], ["id", 1]]
D, [2018-12-07T10:57:00.018520 #33390] DEBUG -- :   Dog Destroy (0.2ms)  DELETE FROM "dogs" WHERE "dogs"."id" = ?  [["id", 1]]
D, [2018-12-07T10:57:00.018953 #33390] DEBUG -- :    (0.0ms)  commit transaction
dog barks

Actual behavior

after_destroy_commit callback bark does not fire in the example

D, [2018-12-07T10:56:59.999822 #33390] DEBUG -- :    (0.0ms)  begin transaction
D, [2018-12-07T10:57:00.000744 #33390] DEBUG -- :   Dog Load (0.2ms)  SELECT  "dogs".* FROM "dogs" ORDER BY "dogs"."id" DESC LIMIT ?  [["LIMIT", 1]]
D, [2018-12-07T10:57:00.008017 #33390] DEBUG -- :   Cat Load (0.1ms)  SELECT  "cats".* FROM "cats" WHERE "cats"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
D, [2018-12-07T10:57:00.009899 #33390] DEBUG -- :   Cat Update (0.1ms)  UPDATE "cats" SET "name" = ?, "updated_at" = ? WHERE "cats"."id" = ?  [["name", "cat 2"], ["updated_at", "2018-12-07 15:57:00.008533"], ["id", 1]]
D, [2018-12-07T10:57:00.015809 #33390] DEBUG -- :   Dog Load (0.1ms)  SELECT "dogs".* FROM "dogs" WHERE "dogs"."cat_id" = ?  [["cat_id", 1]]
D, [2018-12-07T10:57:00.017123 #33390] DEBUG -- :   Dog Update (0.2ms)  UPDATE "dogs" SET "name" = ?, "updated_at" = ? WHERE "dogs"."id" = ?  [["name", "new dog"], ["updated_at", "2018-12-07 15:57:00.016259"], ["id", 1]]
D, [2018-12-07T10:57:00.018520 #33390] DEBUG -- :   Dog Destroy (0.2ms)  DELETE FROM "dogs" WHERE "dogs"."id" = ?  [["id", 1]]
D, [2018-12-07T10:57:00.018953 #33390] DEBUG -- :    (0.0ms)  commit transaction

System configuration

Rails version: 5.2.1

Ruby version: 2.2.4

@marunbai marunbai changed the title after_commit callback does not fire if the same record got updated with a different reference in the same transaction after_commit callback does not fire if the same record got updated with a different reference in the same transaction block Dec 6, 2018
@gmcgibbon gmcgibbon self-assigned this Dec 10, 2018
@rails-bot
Copy link

rails-bot bot commented Mar 10, 2019

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 5-2-stable branch or on master, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@rails-bot rails-bot bot added the stale label Mar 10, 2019
@rails-bot rails-bot bot closed this as completed Mar 17, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants