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

counter_cache called twice with after_create callback #29762

Closed
zhouguangming opened this Issue Jul 12, 2017 · 1 comment

Comments

Projects
None yet
2 participants
@zhouguangming
Contributor

zhouguangming commented Jul 12, 2017

Test Code

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')                                                                                                                                                           
ActiveRecord::Base.logger = Logger.new(STDOUT)                                                                                                                                                                                              
                                                                                                                                                                                                                                            
ActiveRecord::Schema.define do                                                                                                                                                                                                              
  create_table :posts do |t|                                                                                                                                                                                                                
    t.integer :comments_count                                                                                                                                                                                                               
  end                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                            
  create_table :users do |t|                                                                                                                                                                                                                
    t.integer :comments_count                                                                                                                                                                                                               
  end                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                            
  create_table :comments do |t|                                                                                                                                                                                                             
    t.integer :post_id                                                                                                                                                                                                                      
    t.integer :user_id                                                                                                                                                                                                                      
    t.text :context                                                                                                                                                                                                                         
  end                                                                                                                                                                                                                                       
end                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                            
class Post < ActiveRecord::Base                                                                                                                                                                                                             
  has_many :comments                                                                                                                                                                                                                        
end                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                            
class User < ActiveRecord::Base                                                                                                                                                                                                             
  has_many :comments                                                                                                                                                                                                                        
end                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                            
class Comment < ActiveRecord::Base                                                                                                                                                                                                          
  belongs_to :post, counter_cache: true                                                                                                                                                                                                     
  belongs_to :user, counter_cache: true                                                                                                                                                                                                     
                                                                                                                                                                                                                                            
  after_create do                                                                                                                                                                                                                           
    update_attributes(context: 'Hello, World')                                                                                                                                                                                              
  end                                                                                                                                                                                                                                       
end   

class BugTest < Minitest::Test                                                                                                                                                                                                              
  def test_counter_cache                                                                                                                                                                                                                    
    post = Post.create                                                                                                                                                                                                                      
    user = User.create                                                                                                                                                                                                                      
                                                                                                                                                                                                                                            
    comment = Comment.create(post_id: post.id, user_id: user.id)                                                                                                                                                                            
                                                                                                                                                                                                                                            
    assert_equal 1, user.reload.comments_count                                                                                                                                                                                              
  end                                                                                                                                                                                                                                       
end         

Test Log

-- create_table(:posts)
D, [2017-07-12T17:08:37.748693 #24562] DEBUG -- :    (2.4ms)  SELECT sqlite_version(*)
D, [2017-07-12T17:08:37.749860 #24562] DEBUG -- :    (0.3ms)  CREATE TABLE "posts" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "comments_count" integer)
   -> 0.0251s
-- create_table(:users)
D, [2017-07-12T17:08:37.751869 #24562] DEBUG -- :    (0.2ms)  CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "comments_count" integer)
   -> 0.0010s
-- create_table(:comments)
D, [2017-07-12T17:08:37.753774 #24562] DEBUG -- :    (0.2ms)  CREATE TABLE "comments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "post_id" integer, "user_id" integer, "context" text)
   -> 0.0013s
D, [2017-07-12T17:08:37.766867 #24562] DEBUG -- :    (0.3ms)  CREATE TABLE "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL)
D, [2017-07-12T17:08:37.779055 #24562] DEBUG -- :   ActiveRecord::InternalMetadata Load (0.2ms)  SELECT  "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? LIMIT ?  [["key", "environment"], ["LIMIT", 1]]
D, [2017-07-12T17:08:37.786951 #24562] DEBUG -- :    (0.1ms)  begin transaction
D, [2017-07-12T17:08:37.789930 #24562] DEBUG -- :   SQL (0.4ms)  INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["key", "environment"], ["value", "development"], ["created_at", "2017-07-12 09:08:37.788307"], ["updated_at", "2017-07-12 09:08:37.788307"]]
D, [2017-07-12T17:08:37.791653 #24562] DEBUG -- :    (0.1ms)  commit transaction
Run options: --seed 37134

# Running:

D, [2017-07-12T17:08:37.893087 #24562] DEBUG -- :    (0.1ms)  begin transaction
D, [2017-07-12T17:08:37.894903 #24562] DEBUG -- :   SQL (0.2ms)  INSERT INTO "posts" DEFAULT VALUES
D, [2017-07-12T17:08:37.895674 #24562] DEBUG -- :    (0.1ms)  commit transaction
D, [2017-07-12T17:08:37.899320 #24562] DEBUG -- :    (0.1ms)  begin transaction
D, [2017-07-12T17:08:37.900696 #24562] DEBUG -- :   SQL (0.1ms)  INSERT INTO "users" DEFAULT VALUES
D, [2017-07-12T17:08:37.901585 #24562] DEBUG -- :    (0.1ms)  commit transaction
D, [2017-07-12T17:08:37.909367 #24562] DEBUG -- :    (0.1ms)  begin transaction
D, [2017-07-12T17:08:37.912918 #24562] DEBUG -- :   SQL (0.5ms)  INSERT INTO "comments" ("post_id", "user_id") VALUES (?, ?)  [["post_id", 1], ["user_id", 1]]
D, [2017-07-12T17:08:37.926519 #24562] DEBUG -- :   Post Load (0.2ms)  SELECT  "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
D, [2017-07-12T17:08:37.929551 #24562] DEBUG -- :   SQL (0.2ms)  UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1 WHERE "posts"."id" = ?  [["id", 1]]
D, [2017-07-12T17:08:37.932106 #24562] DEBUG -- :   User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
D, [2017-07-12T17:08:37.933921 #24562] DEBUG -- :   SQL (0.1ms)  UPDATE "users" SET "comments_count" = COALESCE("comments_count", 0) + 1 WHERE "users"."id" = ?  [["id", 1]]
D, [2017-07-12T17:08:37.936466 #24562] DEBUG -- :   SQL (0.2ms)  UPDATE "comments" SET "context" = ? WHERE "comments"."id" = ?  [["context", "Hello, World"], ["id", 1]]
D, [2017-07-12T17:08:37.937936 #24562] DEBUG -- :   SQL (0.1ms)  UPDATE "users" SET "comments_count" = COALESCE("comments_count", 0) + 1 WHERE "users"."id" = ?  [["id", 1]]
D, [2017-07-12T17:08:37.938745 #24562] DEBUG -- :    (0.1ms)  commit transaction
D, [2017-07-12T17:08:37.939958 #24562] DEBUG -- :   User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
F

Finished in 0.060159s, 16.6227 runs/s, 16.6227 assertions/s.

  1) Failure:
BugTest#test_counter_cache [rails-bug-report.rb:60]:
Expected: 1
  Actual: 2

1 runs, 1 assertions, 1 failures, 0 errors, 0 skips

Expected behavior

user.reload.comments_count equal to 1

Actual behavior

user.reload.comments_count equal to 2

Condensed from as I see

D, [2017-07-12T17:08:37.937936 #24562] DEBUG -- :   SQL (0.1ms)  UPDATE "users" SET "comments_count" = COALESCE("comments_count", 0) + 1 WHERE "users"."id" = ?  [["id", 1]]

execute twice, if I remove

  after_create do                                                                                                                                                                                                                           
    update_attributes(context: 'Hello, World')                                                                                                                                                                                              
  end   

from Comment, it will be ok

System configuration

Rails version:
Using rails 5.2.0.alpha from git://github.com/rails/rails.git (at master@e319b01)
Ruby version:
ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]

@sgrif

This comment has been minimized.

Show comment
Hide comment
@sgrif

sgrif Jul 18, 2017

Member

Fixed by 020abad

Member

sgrif commented Jul 18, 2017

Fixed by 020abad

@sgrif sgrif closed this Jul 18, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment