Skip to content

[BUG]: Rails ActiveSupport Cache instrumentation key duplication #5402

@ggambetti

Description

@ggambetti

Tracer Version(s)

v2.22.0

Ruby Version(s)

3.4.8

Relevent Library and Version(s)

Rails 8.0

Bug Report

Rails ActiveSupport Cache tracing integration appears to duplicate keys when #fetch is called as a result of PreserveOriginalKey unconditionally appending key to the key list.

When a #fetch call occurs, the arguments are processed both by #fetch and again by #write. This causes PreserveOriginalKey to be invoked a second time on an already processed options hash, appending to the :dd_original_keys member. The stack trace is:

I believe normalize_key is more correctly implemented as:

              def normalize_key(key, options)
                options[:dd_original_keys] = options[:dd_original_keys] || [key]

                super
              end

edit: The above code block is incorrect for calls with multiple keys, ie: multi_fetch, multi_write, and multi_read. I think an inclusion check needs to be performed.

              def normalize_key(key, options)
                orig_keys = options[:dd_original_keys] || []
                # Deal with fetch passing keys through normalization twice.
                orig_keys << key unless orig_keys.include?(key)
                options[:dd_original_keys] = orig_keys

                super
              end

Reproduction Code

Adding this test to spec/datadog/tracing/contrib/rails/cache_spec.rb #fetch should break:

    context 'with value' do                                                                                                                                             
      it do                                                                                                                                                             
        expect { fetch }.to eq('default')                                                                                                                               
                                                                                                                                                                        
        expect(span.get_tag('rails.cache.key')).to eq(key)                                                                                                              
      end                                                                                                                                                               
    end

What I've observed in production is that the rails.cache.key tag has the value key + "/" + key.

Configuration Block

No response

Error Logs

No response

Operating System

hub.docker.com ruby:3.4.8-alpine3.23

How does Datadog help you?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugInvolves a bugcommunityWas opened by a community memberintegrationsInvolves tracing integrations

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions