zendesk / ruby-kafka

A Ruby client library for Apache Kafka
http://www.rubydoc.info/gems/ruby-kafka
Apache License 2.0
1.27k stars 338 forks source link

Kafka emit to Datadog is broken under ruby 3 #945

Closed nevinera closed 1 year ago

nevinera commented 2 years ago

I'm not sure how to set up an isolated example - in order to create the issue you need to have ruby-kafka and dogstatsd-ruby installed, and attempt to emit an event to the latter from the former under ruby 3.

This code attempts to invoke the dogstatsd increment method (and various others) with an options hash and a single tags keyword argument, but the method it's forwarding to in dogstatsd-ruby expects a hash argument and zero keyword arguments. Under ruby3 that produces an exception like

ArgumentError: wrong number of arguments (given 3, expected 1..2)
/usr/src/app/vendor/bundle/ruby/3.0.0/gems/dogstatsd-ruby-5.5.0/lib/datadog/statsd.rb:159:in `increment'
/usr/src/app/vendor/bundle/ruby/3.0.0/gems/ruby-kafka-1.5.0/lib/kafka/datadog.rb:107:in `emit'
/usr/src/app/vendor/bundle/ruby/3.0.0/gems/ruby-kafka-1.5.0/lib/kafka/datadog.rb:100:in `block (2 levels) in <class:StatsdSubscriber>'

I could put up a PR that would produce identical behavior in ruby2, but probably it should be accompanied by an addition to your circle config that will execute your test suite against ruby 3, and I don't think I can get that correct from outside.

The monkey-patch I'm using to get past the error currently in our ruby3 branch looks like this:

unless Kafka::VERSION == "1.5.0"
  fail("Kafka 1.5.0 is monkey-patched in config/initializers/monkey_patch_ruby_kafka.rb")
end

module Kafka
  module Datadog
    class StatsdSubscriber < ActiveSupport::Subscriber
      private

      # Original implementation in ruby-kafka 1.5.0, in lib/kafka/datadog.rb, at
      # https://github.com/zendesk/ruby-kafka/blob/v1.5.0/lib/kafka/datadog.rb#L104-L108
      #
      # def emit(type, *args, tags: {})
      #   tags = tags.map {|k, v| "#{k}:#{v}" }.to_a
      #
      #   Kafka::Datadog.statsd.send(type, *args, tags: tags)
      # end
      #
      # But the datadog methods it's actually sending to do not accept keyword arguments,
      # so in ruby3 this fails. We need to implement the 'collapsing' behavior the ruby 2
      # uses for merging keyword arguments onto an options hash.

      def emit(type, *args, tags: {})
        tags = tags.map { |k, v| "#{k}:#{v}" }.to_a

        if args.last.is_a?(Hash)
          args.last[:tags] = tags
        else
          args << {tags: tags}
        end

        Kafka::Datadog.statsd.send(type, *args)
      end
    end
  end
end
github-actions[bot] commented 1 year ago

Issue has been marked as stale due to a lack of activity.

nevinera commented 1 year ago

Problem still seems to be present, but it doesn't look like anyone has investigated.