-
Notifications
You must be signed in to change notification settings - Fork 3
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
[feature] Allow using an ActiveRecord::Relation as a dependency #18
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to be able to specify queries from finder methods? e.g. .find(), .pluck(), which immediately execute the query instead of lazy loading it like ActiveRecord::Relation?
Nope, this would not make sense IMO, it's the same as depends on an array of fixed values. Though I think we would expect people to call where.to_a
/ find
/ pluck
to set depends_on
dynamically sometimes. Those queries in the depends_on block might be cached elsewhere separately.
@@ -40,6 +43,20 @@ def depends_on(dependency, **conditions) | |||
end | |||
end | |||
|
|||
def extract_dependencies_for_relation(relation) | |||
connection = ActiveRecord::Base.connection | |||
query, binds = connection.send(:to_sql_and_binds, relation.arel) # Is there another way to get this info? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is the right method to use here! we need to thoroughly test the rails versions we support before remove the "beta" tag anyway 😄
Note this method returns a tuple of 3: https://github.com/rails/rails/blob/291a3d2ef29a3842d1156ada7526f4ee60dd2b59/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb#L42
sql, binds, preparable
) unless is_query_cached | ||
extracted_dependency = connection.dependency_of(:exec_query, query, nil, binds) | ||
ensure | ||
RedisMemo::MemoizeQuery::CachedSelect.reset_current_query |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it safe to also hijack the thread local variables that are shared between activerecord/redismemo in this case?
I think there might be an issue: this assumes people won't run actual queries (exec_query) in the depends_on
which might not be true. If there is an exec_query
while extracting dependencies, it might continue to use whatever thread locals we have set since we have not cleared them yet. We can probably just reset everything before calling dependency_of tho
RedisMemo::ArgumentError, | ||
"Invalid Arel dependency. Query is not enabled for RedisMemo caching." | ||
) unless is_query_cached | ||
extracted_dependency = connection.dependency_of(:exec_query, query, nil, binds) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in this dependency_of, we will can still pick up the thread locals from the previous extract_bind_params
.
I do like the with_new_query_context
idea tho! Perhaps we should wrap dependency_of in that block instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we should wrap dependency_of in that block instead.
Hm I think an issue is the memoized exec_query
dependency block relies on the bind parameters being extracted previously in the overridden method here. I explicitly set the current_query thread local variable, and expect extract_bind_params
to set the bind params thread local variable (retrieved in CachedSelect.current_query_bind_params
).
I think we can get around this but would have to change the logic in the exec_query dependency block to be able to extract bind params on the fly.
I think in this dependency_of, we will can still pick up the thread locals from the previous extract_bind_params.
Hm not sure I understand this. Unless another db query occurs within the block that with_new_query_context
is wrapped around, we should be okay right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I guess
Unless another db query occurs within the block that
with_new_query_context
is wrapped around, we should be okay right?
Oh yeah I guess that's true. We don't call exec_query in exec_query.
The case I was thinking about is to have
memoize_method :site_names do |teacher|
teacher.sites.each do |site|
depends_on Site.where(id: site.id)
end
end
but the inner exaction would not be affected by the outer query anyway.
RedisMemo::ArgumentError, | ||
"Invalid Arel dependency. Query is not enabled for RedisMemo caching." | ||
) unless is_query_cached | ||
extracted_dependency = connection.dependency_of(:exec_query, query, nil, binds) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I guess
Unless another db query occurs within the block that
with_new_query_context
is wrapped around, we should be okay right?
Oh yeah I guess that's true. We don't call exec_query in exec_query.
The case I was thinking about is to have
memoize_method :site_names do |teacher|
teacher.sites.each do |site|
depends_on Site.where(id: site.id)
end
end
but the inner exaction would not be affected by the outer query anyway.
Summary
Enable using an ActiveRecord::Relation object as a dependency of a memoized method. Do this by:
ActiveRecord::Relation
while extracting dependencies inRedisMemo::Dependency.depends_on
Follow up:
.find(), .pluck()
, which immediately execute the query instead of lazy loading it like ActiveRecord::Relation?Testing
Added specs