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

Correctly set rails.cache.backend span tag for multiple stores #3060

Merged
merged 9 commits into from
Aug 25, 2023

Conversation

marcotc
Copy link
Member

@marcotc marcotc commented Aug 17, 2023

What does this PR do?

This PR fixes the span tag rails.cache.backend in rails.cache spans to correctly represent the cache store object being instrumented.

Motivation

Today, we always set rails.cache.backend to Rails.configuration.cache_store, which is a global application value.
This works correctly for applications with a single cache store, but incorrectly reports this tag when multiple stores are used.

Additional Notes

How to test the change?

All changes are covered by testing.

@marcotc marcotc self-assigned this Aug 17, 2023
@github-actions github-actions bot added integrations Involves tracing integrations tracing labels Aug 17, 2023
@marcotc marcotc changed the title Correctly identify rails.cache.backend for multiple stores Correctly populate rails.cache.backend span tag for multiple stores Aug 17, 2023
@marcotc marcotc changed the title Correctly populate rails.cache.backend span tag for multiple stores Correctly set rails.cache.backend span tag for multiple stores Aug 17, 2023
Comment on lines 112 to 122
# The name of the store is never saved anywhere.
# ActiveSupport looks up stores by converting a symbol into a 'require' path:
# https://github.com/rails/rails/blob/261975dbef77731d2c76f907f1076c5132ebc0e4/activesupport/lib/active_support/cache.rb#L139-L149
#
# Given we are inside the store object itself, we can reverse engineer
# the `require` path by converting the class name to snake case:
# e.g. ActiveSupport::Cache::RedisStore -> active_support/cache/redis_store
#
# We only care about the last part of the path, but it's easier to convert
# the whole class name then extract the last part.
store: self.class.name.underscore.match(/active_support\/cache\/(.*)/)[1]
Copy link
Contributor

@TonyCTHsu TonyCTHsu Aug 18, 2023

Choose a reason for hiding this comment

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

Nice, I think with sufficient repetitions, we should be confident to extract this.

Gemfile Outdated
end
end
# This file was generated by Appraisal

Copy link
Member

Choose a reason for hiding this comment

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

Could you elaborate on why the need for this change? Should we extract this change into a separate PR?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sorry, @GustavoCaso, because it's a draft PR, I make a bunch of mess before getting it ready.
Mucking about with the Gemfile makes my testing easier locally.

Copy link
Member Author

Choose a reason for hiding this comment

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

I also push my changes at the end of day in case my laptop explodes overnight.

Copy link
Member

Choose a reason for hiding this comment

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

I also push my changes at the end of day in case my laptop explodes overnight.

Good call

# (only `RedisStore`, and not `ActiveSupport::Cache::RedisStore`)
# the easiest way to retrieve the store symbol is to convert the fully qualified
# name using Rails-provided methods, then extracting the last part.
store: self.class.name.underscore.match(/active_support\/cache\/(.*)/)[1]
Copy link
Member

@GustavoCaso GustavoCaso Aug 22, 2023

Choose a reason for hiding this comment

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

Would using regex for each command executed on the cache cause performance issues for the customer's application?

We could memoize the result on an instance variable

def store_name
  return @store_name if defined?(@store_name)
  @store_name = self.class.name.underscore.match(/active_support\/cache\/(.*)/)[1]
  @store_name
end

Also, we always are getting the last portion of the match. Could we use split(''/'').last rather than a regex?

def store_name
  return @store_name if defined?(@store_name)
  @store_name = self.class.name.underscore.split('/').last
  @store_name
end

Copy link
Member

Choose a reason for hiding this comment

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

Should we add a comment to explain that underscore is from Rails::ActiveSupport and that this codes works because is executed within the context of Rails

Copy link
Member Author

Choose a reason for hiding this comment

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

Caching is great, I made those changes.

Copy link
Member Author

Choose a reason for hiding this comment

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

Regarding "split+last" vs regex, I'm keeping the regex because it's the version that more closely mirrors ActiveSupport's logic (require "active_support/cache/#{store}").

And because we are only doing it once, as we are now caching, the performance impact will be almost nil.

@GustavoCaso
Copy link
Member

I noticed that on the lib/datadog/tracing/contrib/active_support/cache/patcher.rb file, we have an outdated method
cache_store_class which takes symbol but no longer do anything with it.

def cache_store_class(meth)
::ActiveSupport::Cache::Store
end
def patch_cache_store_read
cache_store_class(:read).prepend(Cache::Instrumentation::Read)
end
def patch_cache_store_read_multi
cache_store_class(:read_multi).prepend(Cache::Instrumentation::ReadMulti)
end
def patch_cache_store_fetch
cache_store_class(:fetch).prepend(Cache::Instrumentation::Fetch)
end

Since we are changing related files, should we update it?

Since that change is not related to the main purpose of the PR, fell free to ignore it

@marcotc
Copy link
Member Author

marcotc commented Aug 24, 2023

I noticed that on the lib/datadog/tracing/contrib/active_support/cache/patcher.rb file, we have an outdated method cache_store_class which takes symbol but no longer do anything with it.

def cache_store_class(meth)
::ActiveSupport::Cache::Store
end
def patch_cache_store_read
cache_store_class(:read).prepend(Cache::Instrumentation::Read)
end
def patch_cache_store_read_multi
cache_store_class(:read_multi).prepend(Cache::Instrumentation::ReadMulti)
end
def patch_cache_store_fetch
cache_store_class(:fetch).prepend(Cache::Instrumentation::Fetch)
end

Since we are changing related files, should we update it?

Since that change is not related to the main purpose of the PR, fell free to ignore it

This is because there's a conditional override for that method, depending if the gem https://github.com/redis-store/redis-activesupport is installed or not. It's still needed for such compatibility:

def cache_store_class(meth)
if patch_redis?(meth)
::ActiveSupport::Cache::RedisStore
else
super
end
end

@marcotc marcotc marked this pull request as ready for review August 24, 2023 19:17
@marcotc marcotc requested review from a team and TonyCTHsu August 24, 2023 19:17
@GustavoCaso
Copy link
Member

This is because there's a conditional override for that method, depending if the gem https://github.com/redis-store/redis-activesupport is installed or not. It's still needed for such compatibility:
dd-trace-rb/lib/datadog/tracing/contrib/active_support/cache/redis.rb

Thanks for the context. It would be nice to note that rails 5.2.0+ has redis cache support. Maybe adding a comment that once we do not support old Rails version we can remove such code 😄

Copy link
Member

@GustavoCaso GustavoCaso left a comment

Choose a reason for hiding this comment

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

LGTM

@marcotc
Copy link
Member Author

marcotc commented Aug 25, 2023

This is because there's a conditional override for that method, depending if the gem redis-store/redis-activesupport is installed or not. It's still needed for such compatibility:
dd-trace-rb/lib/datadog/tracing/contrib/active_support/cache/redis.rb

Thanks for the context. It would be nice to note that rails 5.2.0+ has redis cache support. Maybe adding a comment that once we do not support old Rails version we can remove such code 😄

@GustavoCaso I documented this in code, good call!

@marcotc marcotc merged commit 0692251 into master Aug 25, 2023
162 checks passed
@marcotc marcotc deleted the as-cache branch August 25, 2023 22:58
@github-actions github-actions bot added this to the 1.15.0 milestone Aug 25, 2023
@pbstriker38
Copy link

This fails for custom stores that are not in active_support/cache/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
integrations Involves tracing integrations tracing
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants