From cea392eb9683fe5545c968817e039c72c69fd3f9 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Fri, 19 Jul 2019 03:41:05 +0200 Subject: Polymorphic has_one touch: Reset association cache result after create transaction In case of a polymorphic association there's no automatic inverse_of to assign the inverse record. So to get the record there needs to be a query executed, however, if the query fires within the transaction that's trying to create the associated record, no record can be found. And worse, the nil result is cached on the association so after the transaction commits the record can't be found. That's what happens if touch is enabled on a polymorphic has_one association. Consider a Comment with a commentable association that needs to be touched. For `Comment.create(commentable: Post.new)`, the existing code essentially does `commentable.send(:comment)` within the create transaction for the comment and thus not finding the comment. Now we're purposefully clearing the cache in case we've tried accessing the association within the transaction and found no object. Before: ``` kaspth-imac 2.6.3 ~/code/rails/activerecord master *= ARCONN=postgresql bin/test test/cases/associations/has_one_associations_test.rb -n /commit/ Using postgresql Run options: -n /commit/ --seed 46022 D, [2019-07-19T03:30:37.864537 #96022] DEBUG -- : Chef Load (0.2ms) SELECT "chefs".* FROM "chefs" WHERE "chefs"."employable_id" = $1 AND "chefs"."employable_type" = $2 LIMIT $3 [["employable_id", 1], ["employable_type", "DrinkDesignerWithPolymorphicTouchChef"], ["LIMIT", 1]] D, [2019-07-19T03:30:37.865013 #96022] DEBUG -- : Chef Create (0.2ms) INSERT INTO "chefs" ("employable_id", "employable_type") VALUES ($1, $2) RETURNING "id" [["employable_id", 1], ["employable_type", "DrinkDesignerWithPolymorphicTouchChef"]] D, [2019-07-19T03:30:37.865201 #96022] DEBUG -- : TRANSACTION (0.1ms) RELEASE SAVEPOINT active_record_1 D, [2019-07-19T03:30:37.874136 #96022] DEBUG -- : TRANSACTION (0.1ms) ROLLBACK D, [2019-07-19T03:30:37.874323 #96022] DEBUG -- : TRANSACTION (0.1ms) ROLLBACK F Failure: HasOneAssociationsTest#test_polymorphic_has_one_with_touch_option_on_create_wont_cache_assocation_so_fetching_after_transaction_commit_works [/Users/kaspth/code/rails/activerecord/test/cases/associations/has_one_associations_test.rb:716]: --- expected +++ actual @@ -1 +1 @@ -# +nil ``` After: ``` kaspth-imac 2.6.3 ~/code/rails/activerecord master *= ARCONN=postgresql bin/test test/cases/associations/has_one_associations_test.rb -n /commit/ Using postgresql Run options: -n /commit/ --seed 46022 D, [2019-07-19T03:30:22.479387 #95973] DEBUG -- : Chef Create (0.3ms) INSERT INTO "chefs" ("employable_id", "employable_type") VALUES ($1, $2) RETURNING "id" [["employable_id", 1], ["employable_type", "DrinkDesignerWithPolymorphicTouchChef"]] D, [2019-07-19T03:30:22.479574 #95973] DEBUG -- : TRANSACTION (0.1ms) RELEASE SAVEPOINT active_record_1 D, [2019-07-19T03:30:22.482051 #95973] DEBUG -- : Chef Load (0.1ms) SELECT "chefs".* FROM "chefs" WHERE "chefs"."employable_id" = $1 AND "chefs"."employable_type" = $2 LIMIT $3 [["employable_id", 1], ["employable_type", "DrinkDesignerWithPolymorphicTouchChef"], ["LIMIT", 1]] D, [2019-07-19T03:30:22.482317 #95973] DEBUG -- : TRANSACTION (0.1ms) ROLLBACK D, [2019-07-19T03:30:22.482437 #95973] DEBUG -- : TRANSACTION (0.1ms) ROLLBACK . Finished in 0.088498s, 11.2997 runs/s, 22.5994 assertions/s. 1 runs, 2 assertions, 0 failures, 0 errors, 0 skips ``` Notice the select now fires after the commit. --- activerecord/test/schema/schema.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord/test/schema') diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index dd0ff759b6..cae2890c9e 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -1070,6 +1070,7 @@ ActiveRecord::Schema.define do create_table :cake_designers, force: true do |t| end create_table :drink_designers, force: true do |t| + t.string :name end create_table :chefs, force: true do |t| t.integer :employable_id @@ -1077,6 +1078,7 @@ ActiveRecord::Schema.define do t.integer :department_id t.string :employable_list_type t.integer :employable_list_id + t.timestamps end create_table :recipes, force: true do |t| t.integer :chef_id -- cgit v1.2.3