Counter cache double increment when cache column has custom name #14038

Closed
pioz opened this Issue Feb 13, 2014 · 5 comments

4 participants

@pioz

Here my test, with Rails 4.1.0.beta1 there is a double increment of counter cache value if the column name is customized:

gem 'activerecord', '4.1.0.beta1'

require 'sqlite3'
require 'active_record'
require 'minitest/autorun'

ActiveRecord::Base.establish_connection 'sqlite3:///:memory:'

ActiveRecord::Schema.define do
  create_table(:users) do |t|
    t.string :username
    t.integer :followings_count, default: 0
    t.integer :followers_count, default: 0
    t.timestamps
  end
  create_table(:follows) do |t|
    t.integer :user_id
    t.integer :following_id
    t.timestamps
  end
end


class User < ActiveRecord::Base
  has_many :follows, dependent: :destroy
  has_many :followings, through: :follows

  has_many :inverse_follows, class_name: 'Follow', foreign_key: :following_id, dependent: :destroy
  has_many :inverse_followings, through: :inverse_follows, source: :user
end

class Follow < ActiveRecord::Base
  belongs_to :user, counter_cache: :followings_count
  belongs_to :following, class_name: 'User', counter_cache: :followers_count
end


class BugTest < Minitest::Test
  def test_counter_cache
    user1 = User.create!
    user2 = User.create!

    assert_equal 0, user1.followings_count
    assert_equal 0, user1.followers_count
    assert_equal 0, user2.followings_count
    assert_equal 0, user2.followers_count

    user1.followings << user2
    assert_equal 1, user1.reload.followings_count
    assert_equal 0, user1.reload.followers_count
    assert_equal 0, user2.reload.followings_count
    assert_equal 1, user2.reload.followers_count

    user2.followings << user1
    assert_equal 1, user1.reload.followings_count
    assert_equal 1, user1.reload.followers_count
    assert_equal 1, user2.reload.followings_count
    assert_equal 1, user2.reload.followers_count

    user2.followings.destroy(user1)
    assert_equal 1, user1.reload.followings_count
    assert_equal 0, user1.reload.followers_count
    assert_equal 0, user2.reload.followings_count
    assert_equal 1, user2.reload.followers_count
  end
end

My output:

-- create_table(:users)
   -> 0.0088s
-- create_table(:follows)
   -> 0.0003s
Run options: --seed 43192

# Running:

F

Finished in 0.027318s, 36.6059 runs/s, 183.0295 assertions/s.

  1) Failure:
BugTest#test_counter_cache [counter_cache_test.rb:49]:
Expected: 1
  Actual: 2

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

If I dont change the name of counter cache column belongs_to :user, counter_cache: true the test pass with no errors.

@senny senny added the activerecord label Feb 13, 2014
@arturmalecki

Hi @pioz I have checked this one and you're right. There is something wrong with this counter.

irb(main):006:0> u1.followings << u2
   (0.0ms)  begin transaction
  SQL (0.6ms)  INSERT INTO "follows" ("created_at", "following_id", "updated_at", "user_id") VALUES (?, ?, ?, ?)  [["created_at", "2014-02-13 12:10:58.140865"], ["following_id", 2], ["updated_at", "2014-02-13 12:10:58.140865"], ["user_id", 1]]
  SQL (0.3ms)  UPDATE "users" SET "followings_count" = COALESCE("followings_count", 0) + 1 WHERE "users"."id" = 1
  SQL (0.1ms)  UPDATE "users" SET "followers_count" = COALESCE("followers_count", 0) + 1 WHERE "users"."id" = 2
  SQL (0.1ms)  UPDATE "users" SET "followings_count" = COALESCE("followings_count", 0) + 1 WHERE "users"."id" = 1

I will try to fix it.

@rafaelfranca
Ruby on Rails member

This is a dup of #10865. Lets use that issue

@pioz

This is the #14038 ...

@rafaelfranca
Ruby on Rails member

Wrong link. Updated

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