diff options
author | bogdanvlviv <bogdanvlviv@gmail.com> | 2017-12-11 23:56:07 +0200 |
---|---|---|
committer | bogdanvlviv <bogdanvlviv@gmail.com> | 2017-12-12 00:32:50 +0200 |
commit | 6ddba9b46c1d307ea7e03d753d0988183b9cb80c (patch) | |
tree | 3317877c34bc72aa6711d6a0419c9ad6290d041a /activerecord/CHANGELOG.md | |
parent | 02e058a2f26097d095061567e597a7aaee4d5c9e (diff) | |
download | rails-6ddba9b46c1d307ea7e03d753d0988183b9cb80c.tar.gz rails-6ddba9b46c1d307ea7e03d753d0988183b9cb80c.tar.bz2 rails-6ddba9b46c1d307ea7e03d753d0988183b9cb80c.zip |
Fix conflicts `counter_cache` with `touch: true` by optimistic locking.
```
# create_table :posts do |t|
# t.integer :comments_count, default: 0
# t.integer :lock_version
# t.timestamps
# end
class Post < ApplicationRecord
end
# create_table :comments do |t|
# t.belongs_to :post
# end
class Comment < ApplicationRecord
belongs_to :post, touch: true, counter_cache: true
end
```
Before:
```
post = Post.create!
# => begin transaction
INSERT INTO "posts" ("created_at", "updated_at", "lock_version")
VALUES ("2017-12-11 21:27:11.387397", "2017-12-11 21:27:11.387397", 0)
commit transaction
comment = Comment.create!(post: post)
# => begin transaction
INSERT INTO "comments" ("post_id") VALUES (1)
UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1,
"lock_version" = COALESCE("lock_version", 0) + 1 WHERE "posts"."id" = 1
UPDATE "posts" SET "updated_at" = '2017-12-11 21:27:11.398330',
"lock_version" = 1 WHERE "posts"."id" = 1 AND "posts"."lock_version" = 0
rollback transaction
# => ActiveRecord::StaleObjectError: Attempted to touch a stale object: Post.
Comment.take.destroy!
# => begin transaction
DELETE FROM "comments" WHERE "comments"."id" = 1
UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) - 1,
"lock_version" = COALESCE("lock_version", 0) + 1 WHERE "posts"."id" = 1
UPDATE "posts" SET "updated_at" = '2017-12-11 21:42:47.785901',
"lock_version" = 1 WHERE "posts"."id" = 1 AND "posts"."lock_version" = 0
rollback transaction
# => ActiveRecord::StaleObjectError: Attempted to touch a stale object: Post.
```
After:
```
post = Post.create!
# => begin transaction
INSERT INTO "posts" ("created_at", "updated_at", "lock_version")
VALUES ("2017-12-11 21:27:11.387397", "2017-12-11 21:27:11.387397", 0)
commit transaction
comment = Comment.create!(post: post)
# => begin transaction
INSERT INTO "comments" ("post_id") VALUES (1)
UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1,
"lock_version" = COALESCE("lock_version", 0) + 1,
"updated_at" = '2017-12-11 21:37:09.802642' WHERE "posts"."id" = 1
commit transaction
comment.destroy!
# => begin transaction
DELETE FROM "comments" WHERE "comments"."id" = 1
UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) - 1,
"lock_version" = COALESCE("lock_version", 0) + 1,
"updated_at" = '2017-12-11 21:39:02.685520' WHERE "posts"."id" = 1
commit transaction
```
Fixes #31199.
Diffstat (limited to 'activerecord/CHANGELOG.md')
-rw-r--r-- | activerecord/CHANGELOG.md | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 89a12d4223..ceef240257 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,86 @@ +* Fix conflicts `counter_cache` with `touch: true` by optimistic locking. + + ``` + # create_table :posts do |t| + # t.integer :comments_count, default: 0 + # t.integer :lock_version + # t.timestamps + # end + class Post < ApplicationRecord + end + + # create_table :comments do |t| + # t.belongs_to :post + # end + class Comment < ApplicationRecord + belongs_to :post, touch: true, counter_cache: true + end + ``` + + Before: + ``` + post = Post.create! + # => begin transaction + INSERT INTO "posts" ("created_at", "updated_at", "lock_version") + VALUES ("2017-12-11 21:27:11.387397", "2017-12-11 21:27:11.387397", 0) + commit transaction + + comment = Comment.create!(post: post) + # => begin transaction + INSERT INTO "comments" ("post_id") VALUES (1) + + UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1, + "lock_version" = COALESCE("lock_version", 0) + 1 WHERE "posts"."id" = 1 + + UPDATE "posts" SET "updated_at" = '2017-12-11 21:27:11.398330', + "lock_version" = 1 WHERE "posts"."id" = 1 AND "posts"."lock_version" = 0 + rollback transaction + # => ActiveRecord::StaleObjectError: Attempted to touch a stale object: Post. + + Comment.take.destroy! + # => begin transaction + DELETE FROM "comments" WHERE "comments"."id" = 1 + + UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) - 1, + "lock_version" = COALESCE("lock_version", 0) + 1 WHERE "posts"."id" = 1 + + UPDATE "posts" SET "updated_at" = '2017-12-11 21:42:47.785901', + "lock_version" = 1 WHERE "posts"."id" = 1 AND "posts"."lock_version" = 0 + rollback transaction + # => ActiveRecord::StaleObjectError: Attempted to touch a stale object: Post. + ``` + + After: + ``` + post = Post.create! + # => begin transaction + INSERT INTO "posts" ("created_at", "updated_at", "lock_version") + VALUES ("2017-12-11 21:27:11.387397", "2017-12-11 21:27:11.387397", 0) + commit transaction + + comment = Comment.create!(post: post) + # => begin transaction + INSERT INTO "comments" ("post_id") VALUES (1) + + UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) + 1, + "lock_version" = COALESCE("lock_version", 0) + 1, + "updated_at" = '2017-12-11 21:37:09.802642' WHERE "posts"."id" = 1 + commit transaction + + comment.destroy! + # => begin transaction + DELETE FROM "comments" WHERE "comments"."id" = 1 + + UPDATE "posts" SET "comments_count" = COALESCE("comments_count", 0) - 1, + "lock_version" = COALESCE("lock_version", 0) + 1, + "updated_at" = '2017-12-11 21:39:02.685520' WHERE "posts"."id" = 1 + commit transaction + ``` + + Fixes #31199. + + *bogdanvlviv* + * Add support for PostgreSQL operator classes to `add_index`. Example: |