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

Fixes #30512 - avoid query for smart proxy settings #7864

Merged
merged 1 commit into from
Aug 20, 2020

Conversation

jlsherrill
Copy link
Contributor

@jlsherrill jlsherrill commented Jul 29, 2020

While this is less efficient, the number of
smart proxy features is low. This allows settings
to be read on an unsaved objects, improving testing
and allowing us to rely on settings in before_create

@theforeman-bot
Copy link
Member

Issues: #30512

@jlsherrill
Copy link
Contributor Author

This is needed for Katello/katello#8802

app/models/smart_proxy.rb Outdated Show resolved Hide resolved
Copy link
Member

@ekohl ekohl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind adding the additional part you added here in the PR (that it allows testing on unsaved objects) in the commit message? That really made me understand the reason for this.

Another thing to note is that this allows query caching smart_proxy_features so it may end up more efficient if you call capabilities or setting with more than one feature.

app/models/smart_proxy.rb Outdated Show resolved Hide resolved
ekohl
ekohl previously approved these changes Jul 29, 2020
Copy link
Member

@ekohl ekohl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Any other comments @ShimShtein?

@jlsherrill
Copy link
Contributor Author

jlsherrill commented Jul 29, 2020

One of the katello test failures is unrelated and being worked on, the other is handled here: Katello/katello#8860

@jlsherrill
Copy link
Contributor Author

@ares anyone else that might could take a look?

@@ -89,15 +89,15 @@ def has_feature?(feature_name)
end

def capabilities(feature)
smart_proxy_features.find_by(:feature_id => Feature.find_by(:name => feature)).try(:capabilities)
smart_proxy_features.find { |spf| spf.feature_name.to_s == feature.to_s }.try(:capabilities)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can add some includes statements here and in settings to make it a bit more efficient:

Suggested change
smart_proxy_features.find { |spf| spf.feature_name.to_s == feature.to_s }.try(:capabilities)
smart_proxy_features.includes(:feature).find { |spf| spf.feature_name.to_s == feature.to_s }.try(:capabilities)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually yes, this doesn't seem to work with unsaved objects:

>  SmartProxy.new(:smart_proxy_features => [SmartProxyFeature.new(:feature_id => 1, :capabilities => ['foo'])]).smart_proxy_features
=> [#<SmartProxyFeature:0x00000000128e03c0 smart_proxy_id: nil, feature_id: 1, id: nil, capabilities: ["foo"], settings: {}>]

> SmartProxy.new(:smart_proxy_features => [SmartProxyFeature.new(:feature_id => 1, :capabilities => ['foo'])]).smart_proxy_features.includes(:feature)
=> []


Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So actually reverting back to this would cause only 1 query per object:

    smart_proxy_features.find { |spf| spf.feature_id == Feature.find_by(:name => feature) }.try(:capabilities)

thoughts?

@ekohl
Copy link
Member

ekohl commented Aug 4, 2020

I'm wondering if we should use nulldb to ensure this method doesn't perform any DB queries.

ShimShtein
ShimShtein previously approved these changes Aug 4, 2020
Copy link
Member

@ShimShtein ShimShtein left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK, We can merge it once the tests are green.

@jlsherrill
Copy link
Contributor Author

[test katello]

@jlsherrill
Copy link
Contributor Author

[test Redmine issues]

does that work?

@jlsherrill
Copy link
Contributor Author

[test redmine]

@jlsherrill
Copy link
Contributor Author

[test Redmine]

ShimShtein
ShimShtein previously approved these changes Aug 5, 2020
@@ -89,15 +89,17 @@ def has_feature?(feature_name)
end

def capabilities(feature)
smart_proxy_features.find_by(:feature_id => Feature.find_by(:name => feature)).try(:capabilities)
feature_id = Feature.find_by(:name => feature).try(:id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For my understanding, we have feature_ids = Feature.where(:name => feature_name).pluck(:id) in has_feature?. It feels like this should be the same. Effectively we have 3 places we convert a feature name to an ID very close together, perhaps there are more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has_feature? actually supports multiple features being passed in, which these two methods shouldn't. Although i'm not sure that i see has_feature? actually being used that way

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I doubt it - the name is singular so as an API user I wouldn't expect it to work that way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not saying explicitly, but if you look at the implementation we simply pass the argument to AR, which also accepts an array. So has_feature? can be used with one or more feature names.

It may be worth introducing a third method, find_features_by_name and capabilities method would use it for a single feature. has_feature? would only add pluck and kept the any? part.

It's a small DRY-up I wouldn't insist on, but a valid enhancement.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not saying explicitly, but if you look at the implementation we simply pass the argument to AR, which also accepts an array. So has_feature? can be used with one or more feature names.

As an API consumer I would expect has_features? for an array and wouldn't even try passing an array to has_feature?.

On the other hand, there's probably more refactoring we can do. For example, I wouldn't mind splitting off all the feature related parts into its own file. associate_features is a large method that maybe should even be a service rather than a method on a model. Given that's a larger refactor, I don't insist on DRY-ing up this part.

Writing this down is sometimes more to structure my own thoughts and take others on the same journey than actually asking for it. Then they can make a more informed decision.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dry'd up a bit, i can combine with has_feature? if desired. I don't see any issue within foreman or katello with changing that method to only take 1 in, but its possible some other plugin is doing something silly

ekohl
ekohl previously approved these changes Aug 7, 2020
@ekohl
Copy link
Member

ekohl commented Aug 7, 2020

@ShimShtein / @ares: all green. This looks good to me, but since you were involved as well I'll let you respond as well.

@ShimShtein
Copy link
Member

@jlsherrill, can you leave a comment on the find_feature_by_name method that explains why did you have to do it the "less - efficient" way?
I am sure I will forget the reasoning a week from today :)

@jlsherrill
Copy link
Contributor Author

sorry that took a few days, was out 🤒

@jlsherrill
Copy link
Contributor Author

[test foreman]

1 similar comment
@jlsherrill
Copy link
Contributor Author

[test foreman]

While this is less efficient, the number of
smart proxy features is low. This allows settings
to be read on an unsaved objects, improving testing
and allowing us to rely on settings in before_create.
@ShimShtein ShimShtein merged commit b414d56 into theforeman:develop Aug 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants