Description
Current behaviour
Rails 6.1 added a payload to the cache fetches.
The payload ultimately gets passed along to Dalli::Quantize.format_command in the *args
.
.format_command
is the following:
def format_command(operation, args)
placeholder = "#{operation} BLOB (OMITTED)"
command = [operation, *args].join(' ').strip
command = Core::Utils.utf8_encode(command, binary: true, placeholder: placeholder)
Core::Utils.truncate(command, Ext::QUANTIZE_MAX_CMD_LENGTH)
rescue => e
Datadog.logger.debug("Error sanitizing Dalli operation: #{e}")
placeholder
end
The performance regression occurs on the following line:
command = [operation, *args].join(' ').strip
If we end up with a hash in the payload the .join(' ')
method can end up being incredible slow.
Expected behaviour
Same performance as Rails 6.0.
Steps to reproduce
Calling join
on an array that contains a hash will end up calling inspect
on the values in the hash. This is a particular problem when the objects define their own inspect
method:
class Foo
def inspect
"something_slow"
end
end
["bar", {baz: Foo.new}].join(" ") => "bar {:baz=>something_slow}"
Our current workaround is to reject hashes from the payload (which should be fine because prior to Rails 6.1 we weren't passing the payload to the tracing anyway:
module DatadogDalliQuantize
def format_command(operation, args)
string_args = args.reject { |arg| arg.is_a?(Hash) }
super(operation, string_args)
end
end
Datadog::Tracing::Contrib::Dalli::Quantize.singleton_class.prepend(DatadogDalliQuantize)
This works for us but we're not sure if it's best to have dd-trace-rb
reject all hashes or not. 🤷
How does ddtrace
help you?
Environment
- ddtrace version: v1.12.1
- Configuration block (
Datadog.configure ...
): - Ruby version: 3.2.2
- Operating system: All OSes
- Relevant library versions: