Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Inverse of on build #12413

Merged
merged 1 commit into from

5 participants

Arthur Nogueira Neves Rafael Mendonça França Claudio Poli Paco Guzmán Joni Töyrylä
Arthur Nogueira Neves
Collaborator

Problem

When building a new relations(any type of relation) we need to setup the inverse_of association.
See #10371

Solution

Call .set_inverse_instance on the association parent. Also remove set_inverse_instance from add_to_target on collection, as we build it before, and we dont need that extra call.

review @rafaelfranca @robin850

Rafael Mendonça França

Was this fixed on 4+?

Arthur Nogueira Neves
Collaborator

yep.. on rails 4, we do almost the same thing. https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/association.rb#L239 I can even refectory on 3.2 to pull those four calls in a method initialize_attributes like rails 4 does.

Rafael Mendonça França

I think is better to stick with the smaller patch.

Rafael Mendonça França rafaelfranca merged commit ccd11d5 into from
Arthur Nogueira Neves arthurnn deleted the branch
Arthur Nogueira Neves
Collaborator

:+1:

Claudio Poli

This breaks Devise for me. Using 3.2-stable. I have not conducted many tests because it got me a while to get to the culprit, however we saw exceptions after a bundle update on user registration.

What happens:

Started POST "/account" for 127.0.0.1 at 2013-10-04 03:09:22 +0200
Processing by Users::RegistrationsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"9qsDa9jS9nMHNgkysAb7mn/6akG+Dp3tvF/iLazx5W4=", "user"=>{"email"=>"masterkain+456y3@gmail.com", "password"=>"[FILTERED]", "password_confirmation"=>"[FILTERED]", "personal_name"=>"", "tos"=>"1"}}
   (0.1ms)  BEGIN
  Account Exists (1.1ms)  SELECT 1 AS one FROM "accounts" WHERE "accounts"."email" = 'masterkain+456y3@gmail.com' LIMIT 1
  User Load (2.8ms)  SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."type" IN ('User') AND "accounts"."authentication_token" = 'QG32Cs1qPzPBubzdTy5L' LIMIT 1
  User Load (0.7ms)  SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."type" IN ('User') AND "accounts"."confirmation_token" = 'XVpypX2aa9sAqbftCh3e' LIMIT 1
  CACHE (0.0ms)  SELECT 1 AS one FROM "accounts" WHERE "accounts"."email" = 'masterkain+456y3@gmail.com' LIMIT 1
  User Load (0.7ms)  SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."type" IN ('User') AND "accounts"."confirmation_token" = 'qt99E6KyXmmjimkSmX57' LIMIT 1
  CACHE (0.0ms)  SELECT 1 AS one FROM "accounts" WHERE "accounts"."email" = 'masterkain+456y3@gmail.com' LIMIT 1
  User Load (0.5ms)  SELECT "accounts".* FROM "accounts" WHERE "accounts"."deleted_at" IS NULL AND "accounts"."type" IN ('User') AND "accounts"."confirmation_token" = 'FJpzpsHxyjqfLJSEF7xm' LIMIT 1
  CACHE (0.0ms)  SELECT 1 AS one FROM "accounts" WHERE "accounts"."email" = 'masterkain+456y3@gmail.com' LIMIT 1

and so forth, until it goes in SystemStackError.

It seems like it's looping on a validation or something, need to investigate more.

Arthur Nogueira Neves
Collaborator

@masterkain Are you sure the error is from this commit, and not from Devise update or something like that? we are using this version on production in a fair big codebase and I didnt see this error yet.(which version of devise are you using?)

Claudio Poli

Yes, I'm fairly sure, the error begins with this commit, devise is unchanged, I'm using 2.2.7.

I immediately suspected rails was involved, so I reverted the bundle update commit (which updated just few gems) and started to try all the rails commits I was missing (about 8), and this is the one generating the infinite loop.

Arthur Nogueira Neves
Collaborator

@masterkain Can you put together a simple rails+devise app with a spec that illustrate this error? I cloned devise 2.2.7, pointed to rails 3-2-stable branch, and all tests are green.

Paco Guzmán

this commit broke our application's build, I'm studying the problem I'll try to provide a test application asap. But basically seems that (as an example).

class Post; has_many :comments; :inverse_of => :title; end
class Comment; belongs_to :post; end

post = Post.new(:comments => [Comment.new])
post.save

If you try to access to the post on the Comment class to validate something the post is nil.

Arthur Nogueira Neves
Collaborator

Ok, I just found out where the error lies on. Submitting a PR with the fix in a few minutes.

Paco Guzmán

Awesome! thanks

Arthur Nogueira Neves
Collaborator

@masterkain @pacoguzman could you guys update to latest 3.2 and let me know if the bug still happens.?
thanks

Paco Guzmán

Everything green now, thanks!

Claudio Poli

I'm afraid this does not solve the problem for me, always on user registration.

Using revision 7ed5bdc

A SystemStackError occurred in registrations#create:

 stack level too deep
 /home/ubuntu/apps/myapp-rails/shared/bundle/ruby/2.0.0/bundler/gems/rails-7ed5bdc83447/activesupport/lib/active_support/notifications/instrumenter.rb:23

I have nothing else in the backtrace.
Better start looking at user's code, but nothing changed in that regard, only Rails.

Rafael Mendonça França
Owner

@masterkain we will need a way to reproduce your issue or we can't do anything.

Rafael Mendonça França
Owner

@masterkain could you help us with this?

Claudio Poli

yeah I'm trying to see where the bug lies, the fact that the stacktrace flags the instrumentation and given I have callbacks using them I have some chance to assemble something for reproducing the behavior.

Claudio Poli

Ok, the error appears to be related to creating an association directly in a before_create, like this:

class Account < ActiveRecord::Base
  has_one :account_stat, inverse_of: :account # counters, billing, etc.

  # Use directly the magic create_association Rails' method.
  before_create :create_account_stat!
end

class AccountStat < ActiveRecord::Base
  belongs_to :account, inverse_of: :account_stat
end

I'm going ahead in a few and try to assemble a reproducible version, but commenting out the before_create or the inverse_of makes the specs pass.

Arthur Nogueira Neves
Collaborator

Interesting, I was able to simulate the SystemStackError

# Running:

E

Finished in 0.090370s, 11.0656 runs/s, 0.0000 assertions/s.

  1) Error:
FirstTest#test_relation:
SystemStackError: stack level too deep
    /Users/arthurnn/dev/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:211

Which is happening in master as well. @rafaelfranca I will write tests for this and submit a patch for it.

Arthur Nogueira Neves arthurnn referenced this pull request from a commit in arthurnn/rails
Arthur Nogueira Neves arthurnn make cycle detection on autosave works on a thread level
Fixes `stack level too deep` error when creating a children object on a parent callback.
This error was only happening when the inverse_of relation was set.

[related #12413]
2ec4032
Rafael Mendonça França rafaelfranca referenced this pull request from a commit
Rafael Mendonça França rafaelfranca Revert "Merge pull request #12413 from arthurnn/inverse_of_on_build"
This reverts commit ccd11d5, reversing
changes made to 54c05ac.

Reason: This caused a regression when the associated record is created
in a before_create callback. See
#12413 (comment)
6a185aa
Rafael Mendonça França

@masterkain could you try 3-2-stable?

Rafael Mendonça França rafaelfranca referenced this pull request from a commit
Rafael Mendonça França rafaelfranca Revert "Merge pull request #12413 from arthurnn/inverse_of_on_build"
This reverts commit ccd11d5, reversing
changes made to 54c05ac.

Reason: This caused a regression when the associated record is created
in a before_create callback. See
#12413 (comment)
38aefa5
Claudio Poli

it seems to work, thanks, I'm going to run the spec suite over to make sure.

Aaron Patterson tenderlove referenced this pull request from a commit
Aaron Patterson tenderlove Merge branch '3-2-15' into 3-2-sec
* 3-2-15:
  bumping to rc3
  Revert "Merge pull request #12413 from arthurnn/inverse_of_on_build"
  Revert "Merge pull request #12443 from arthurnn/add_inverse_of_add_target"
  bumping to rc2
  Merge pull request #12443 from arthurnn/add_inverse_of_add_target
  bumping version to 3.2.15.rc1
  Fix STI scopes using benolee's suggestion. Fixes #11939
eb8807e
Aaron Patterson tenderlove referenced this pull request from a commit
Aaron Patterson tenderlove Merge branch '3-2-sec' into 3-2-stable
* 3-2-sec:
  updating changelogs
  bumping to 3.2.15
  bumping to rc3
  Revert "Merge pull request #12413 from arthurnn/inverse_of_on_build"
  Revert "Merge pull request #12443 from arthurnn/add_inverse_of_add_target"
  bumping to rc2
  Merge pull request #12443 from arthurnn/add_inverse_of_add_target
  bumping version to 3.2.15.rc1
  Remove the use of String#% when formatting durations in log messages

Conflicts:
	activerecord/CHANGELOG.md
5f844d6
wkj wkj referenced this pull request from a commit
Rafael Mendonça França rafaelfranca Revert "Merge pull request #12443 from arthurnn/add_inverse_of_add_ta…
…rget"

This reverts commit 7ed5bdc, reversing
changes made to 31c79e2.

Reason: this caused a regression when the associated record is creted in
a before_create callback.

See #12413 (comment)
9639f65
wkj wkj referenced this pull request from a commit
Rafael Mendonça França rafaelfranca Revert "Merge pull request #12443 from arthurnn/add_inverse_of_add_ta…
…rget"

This reverts commit 7ed5bdc, reversing
changes made to 31c79e2.

Reason: this caused a regression when the associated record is creted in
a before_create callback.

See #12413 (comment)
fbc69ac
Joni Töyrylä

This problem is fixed in commit #14030.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 30, 2013
  1. Arthur Nogueira Neves
This page is out of date. Refresh to see the latest.
6 activerecord/CHANGELOG.md
View
@@ -1,4 +1,10 @@
## unreleased ##
+* Move .set_inverse_instance call to association.build_record method. Everytime a new record is build
+ using the association, we need to try to set the inverse_of relation.
+
+ Fixes #10371.
+
+ *arthurnn*
* When calling the method .find_or_initialize_by_* from a collection_proxy
it should set the inverse_of relation even when the entry was found on the db.
1  activerecord/lib/active_record/associations/association.rb
View
@@ -240,6 +240,7 @@ def build_record(attributes, options)
skip_assign = [reflection.foreign_key, reflection.type].compact
attributes = create_scope.except(*(record.changed - skip_assign))
record.assign_attributes(attributes, :without_protection => true)
+ set_inverse_instance(record)
end
end
end
1  activerecord/lib/active_record/associations/collection_association.rb
View
@@ -350,7 +350,6 @@ def add_to_target(record)
end
callback(:after_add, record)
- set_inverse_instance(record)
record
end
4 activerecord/test/cases/associations/inverse_associations_test.rb
View
@@ -125,8 +125,10 @@ def test_parent_instance_should_be_shared_with_newly_built_child
end
def test_parent_instance_should_be_shared_with_newly_created_child
- m = Man.find(:first)
+ m = Man.create
f = m.create_face(:description => 'haunted')
+
+ assert_equal m.object_id, f.man.object_id
assert_not_nil f.man
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
m.name = 'Bongo'
Something went wrong with that request. Please try again.