Skip to content
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

ActiveRecord#includes gives different results if the includes order is different. #41378

Open
github0013 opened this issue Feb 9, 2021 · 15 comments

Comments

@github0013
Copy link

github0013 commented Feb 9, 2021

Steps to reproduce

I prepared a repo.
https://github.com/github0013/activerecord-includes

Expected behavior

Switching the order in #includes should not change the results. (please see the repo)

  • A.includes(:c_type_xs, :ds).find(subject.id).c_type_xs.size # => 1
  • A.includes(:ds, :c_type_xs).find(subject.id).c_type_xs.size # => 1

Actual behavior

  • A.includes(:c_type_xs, :ds).find(subject.id).c_type_xs.size # => 1
  • A.includes(:ds, :c_type_xs).find(subject.id).c_type_xs.size # => 4

System configuration

Rails version:

  • 5.2.4.1
  • 6.1.1

Ruby version:

  • ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux-musl]
@intrip
Copy link
Contributor

intrip commented Feb 16, 2021

I do confirm it's an issue, here you find a simple reproduction script (FF to add it to the issue description if you like).
The issue is with .preload, which is triggered by includes in such operation. Explicitly using eager_load solves the problem.

I didn't see any existing PRs for this: I'll work on a fix.
/cc @kamipo

@intrip
Copy link
Contributor

intrip commented Feb 17, 2021

I've narrowed a bit the issue and I think it's related to the memoizing in

@already_loaded = owners.all? { |o| o.association(reflection.name).loaded? }

Which doesn't work well when you have a scope in an has_many_through and the through_relation has already been preloaded without using the scope.

I'll try to figure out how to improve this check.

@intrip
Copy link
Contributor

intrip commented Feb 26, 2021

@kamipo #41489 fixes this issue, could you be so kind to take a look at it?
/cc @github0013

@github0013
Copy link
Author

github0013 commented Feb 26, 2021

gem 'rails', git: "https://github.com/intrip/rails.git", branch: "41378-fix-preload-scoped"

Thanks a million!
I tried your branch, and my rspec is all green!
Just wondering if this same fix will be applied to version 5 and 6 because it shows this rails is Rails 7.0.0.alpha.

@intrip
Copy link
Contributor

intrip commented Mar 1, 2021

You're welcome!
From the mainteinance policy looks ike if the PR is accepted it may be ported to 6.1 max

@intrip
Copy link
Contributor

intrip commented Mar 1, 2021

@github0013 could you please reopen? I'd wait for the PR to be accepted/merged before closing the issue 🙂

@github0013 github0013 reopened this Mar 2, 2021
@rails-bot
Copy link

rails-bot bot commented May 31, 2021

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 6-1-stable branch or on main, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@rails-bot rails-bot bot added the stale label May 31, 2021
@github0013
Copy link
Author

any updates?

@rails-bot rails-bot bot removed the stale label May 31, 2021
@intrip
Copy link
Contributor

intrip commented May 31, 2021

#41489 is still waiting for review sorry

@Reedlee
Copy link

Reedlee commented Dec 2, 2021

hello there,

We also spotted this issue in rails 6.0.3.6 and we found the next workarounds. I know that you already prepared a fix which we can use a patch but might be it helpful:

class A < ApplicationRecord
  has_many :bs
  has_many :__bs # by using it you can avoid this issue 
  has_many :cs, through: :bs, source: "cs"
  has_many :c_type_xs, -> { type_x }, through: :__bs, source: "cs"
end

@github0013
Copy link
Author

@Reedlee __ + association name is like a hidden private instance or something?
I am trying to figure out why you can specify __bs as an association name without giving class_name attribute.

@intrip
Copy link
Contributor

intrip commented Dec 3, 2021

The reason it works is because going through __bs uses a different cache, you can find more details in the PR attached.

@Reedlee
Copy link

Reedlee commented Dec 5, 2021

@Reedlee __ + association name is like a hidden private instance or something? I am trying to figure out why you can specify __bs as an association name without giving class_name attribute.

Hello @github0013
It's my bad :) I forgot about adding class_name attribute into the example. So it should be:

class A < ApplicationRecord
  has_many :bs
  has_many :__bs, class_name: "B" # by using it you can avoid this issue. Also, you can use any name instead of __bs 
  has_many :cs, through: :bs, source: "cs"
  has_many :c_type_xs, -> { type_x }, through: :__bs, source: "cs"
end

Hello @intrip
Thank you for your PR. I hope it will be merged soon:)
By the way, I don't know if it matters for your PR. I checked this workaround for eager_load method and it didn't work. It's just for your information that it's good to check eager_load also. :)

@intrip
Copy link
Contributor

intrip commented Dec 6, 2021

@Reedlee If I recall correctly eager_load is not affected by this issue, can you please verify?

@Reedlee
Copy link

Reedlee commented Dec 11, 2021

@intrip hey,

Sorry for the late response.
I checked eager_load in the github0013/activerecord-includes#1:

🤔 It's interesting that this problem with order exists for 5.2.x, 6.0.x. but 6.1.x doesn't have.

Also important to say that the problem with ordering exists if we are trying to get value from cs by filtering them by using bs, please, take a look at c_of_by method.

class A < ApplicationRecord
  has_many :bs
  has_many :cs, through: :bs, source: "cs"

# doesn't have the problem related to ordering for eager_load
  has_many :c_type_xs, -> { type_x }, through: :bs, source: "cs"

# has the problem related to ordering for eager_load
  has_many :c_of_by, -> { where(bs: {name: 'y'}) }, through: :bs, source: "cs"
end

So if we are running cases like:
https://github.com/github0013/activerecord-includes/pull/1/files#diff-f411c2d0fb2322c513f7d9588080a227ef984fd838808d5665962a0ba82d180eR89-R92

intrip added a commit to intrip/rails that referenced this issue Jan 11, 2022
Previously when the same `through_relation` was preloaded multiple times but using
a different scope the relation was incorrectly cached; this led
into returning the wrong results.

The issue is fixed by checking if the relation to preload has a `preload_scope`
and is preloaded from a `through_relation`.

Fixes rails#41378
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants