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

changes to option proc loads broke default usage #3182

Closed
HoneyryderChuck opened this issue Oct 4, 2023 · 4 comments
Closed

changes to option proc loads broke default usage #3182

HoneyryderChuck opened this issue Oct 4, 2023 · 4 comments
Assignees
Labels
bug Involves a bug community Was opened by a community member

Comments

@HoneyryderChuck
Copy link
Contributor

HoneyryderChuck commented Oct 4, 2023

Current behaviour

I've had a build failure with the master version of the datadog plugin configuration of this gem I maintain. The error message doesn't show it, but the tests fail here, when loading the default error handler. I could reproduce the backtrace locally, and here it is:

= 1)), "last_error" = 'undefined method `set_error'' for #<Datadog::Tracing::Contrib::Tobox::Configuration::Settings:0x0000000108580418 @options={:enabled=>#<Datadog::Core::Configuration::Option:0x0000000
107c09970 @definition=#<Datadog::Core::Configuration::OptionDefinition:0x0000000107c46410 @default=#<Proc:0x000000010858daf0 /user/dev/tobox/lib/tobox/plugins/datadog/configuration.rb:14>, @
experimental_default_proc=nil, @env=nil, @deprecated_env=nil, @env_parser=nil, @delegate_to=nil, @depends_on=[], @name=:enabled, @on_set=nil, @resetter=nil, @setter=#<Proc:0x0000000107cdc7f8 /user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/core/configuration/option_definition.rb:10 (lambda)>, @type=nil, @type_options={}>, @context=#<Datadog::Tracing::Contrib::Tobox::Configu
ration::Settings:0x0000000108580418 ...>, @value=true, @is_set=true, @value_per_precedence={#<struct Datadog::Core::Configuration::Option::Precedence::Value numeric=0, name=:default>=>true}, @precedence_s
et=#<struct Datadog::Core::Configuration::Option::Precedence::Value numeric=0, name=:default>>, :service_name=>#<Datadog::Core::Configuration::Option:0x00000001089ccc60 @definition=#<Datadog::Core::Config
uration::OptionDefinition:0x0000000107c45d30 @default=nil, @experimental_default_proc=nil, @env=nil, @deprecated_env=nil, @env_parser=nil, @delegate_to=nil, @depends_on=[], @name=:service_name, @on_set=ni
l, @resetter=nil, @setter=#<Proc:0x0000000107cdc7f8 /user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/core/configuration/option_definition.rb:10 (lambda)>, @type=nil, @type_
options={}>, @context=#<Datadog::Tracing::Contrib::Tobox::Configuration::Settings:0x0000000108580418 ...>, @value=nil, @is_set=true, @value_per_precedence={#<struct Datadog::Core::Configuration::Option::P
recedence::Value numeric=0, name=:default>=>nil}, @precedence_set=#<struct Datadog::Core::Configuration::Option::Precedence::Value numeric=0, name=:default>>, :error_handler=>#<Datadog::Core::Configuratio
n::Option:0x00000001087a8ee8 @definition=#<Datadog::Core::Configuration::OptionDefinition:0x0000000107c45bf0 @default=#<Proc:0x00000001082297b0 /user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrac
e-1.14.0/lib/datadog/tracing/span_operation.rb:338>, @experimental_default_proc=nil, @env=nil, @deprecated_env=nil, @env_parser=nil, @delegate_to=nil, @depends_on=[], @name=:error_handler, @on_set=nil, @r
esetter=nil, @setter=#<Proc:0x0000000107cdc7f8 /Users/tiagocardoso/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/core/configuration/option_definition.rb:10 (lambda)>, @type=nil, @type_optio
ns={}>, @context=#<Datadog::Tracing::Contrib::Tobox::Configuration::Settings:0x0000000108580418 ...>, @value=nil, @is_set=false, @value_per_precedence={}, @precedence_set=#<struct Datadog::Core::Configura
tion::Option::Precedence::Value numeric=0, name=:default>>}>
/user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/tracing/span_operation.rb:338:in `block in <class:Events>''
/user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/core/configuration/option.rb:278:in `instance_eval''
/user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/core/configuration/option.rb:278:in `context_eval''
/user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/core/configuration/option.rb:142:in `default_value''
/user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/core/configuration/option.rb:299:in `set_value_from_env_or_default''
/user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/core/configuration/option.rb:119:in `get''
/user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/core/configuration/options.rb:79:in `get_option''
/user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/core/configuration/options.rb:46:in `block in default_helpers''
/user/dev/tobox/.bundle/ruby/3.2.0/gems/ddtrace-1.14.0/lib/datadog/tracing/contrib/configuration/settings.rb:29:in `[]''
/user/dev/tobox/lib/tobox/plugins/datadog.rb:20:in `on_start''
/user/dev/tobox/lib/tobox/fetcher.rb:166:in `block in handle_before_event''
/user/dev/tobox/lib/tobox/fetcher.rb:165:in `each''

Expected behaviour

I was expecting this to keep working.

I assume this was caused by this change, which is providing a path to migrate away from setting defaults like that. While that may be warranted (and should be done in a major change), it shouldn't happen at the cost of the old API breaking.

FWIW I was following changes in other adapters in order to come up with this patch, which is a bit more complicated than I would have expected to maintain as an external plugin for a 1.x series of a given library (hope what I say makes sense from your perspective).

Steps to reproduce

Load the tobox repo locally, use any supported ruby (you can use 3.2), set up a usable database (postgres is advised), set it up in DATABASE_URL (i.e. export DATABASE_URL=postgresql://user:pass@host/db), and run the datadog test suite (bundle exec ruby -Itest integration_tests/datadog_test.rb).

@HoneyryderChuck HoneyryderChuck added bug Involves a bug community Was opened by a community member labels Oct 4, 2023
@GustavoCaso GustavoCaso self-assigned this Oct 5, 2023
@GustavoCaso
Copy link
Member

GustavoCaso commented Oct 5, 2023

Hi @HoneyryderChuck

Thank you so much for opening the issue.

Yes, from version 1.13.0, we have changed some of the internals of the DSL configuration.

Here is the list of PR with all the changes:
#2931
#2983
#3085
#3086

The whole idea was to simplify the configuration DSL.

Looking at your configuration code, running on the latest ddtrace version your configuration could look like this:

module Datadog
  module Tracing
    module Contrib
      module Tobox
        module Configuration
          class Settings < Contrib::Configuration::Settings
            option :enabled do |o|
              o.env "DD_TOBOX_SIDEKIQ_ENABLED"
              o.type :bool
              o.default true
            end

            option :analytics_enabled do |o|
              o.env "DD_TOBOX_ANALYTICS_ENABLED"
              o.type :bool
              o.default false
            end

            option :analytics_sample_rate do |o|
              o.env "DD_TRACE_TOBOX_ANALYTICS_SAMPLE_RATE"
              o.type :float
              o.default 1.0
            end

            option :service_name
            option :error_handler do |o|
              o.type :proc
              o.default_proc(&Tracing::SpanOperation::Events::DEFAULT_ON_ERROR)
            end
            option :distributed_tracing, default: false
          end
        end
      end
    end
  end
end

I see a problem in your case, as your plugin does not restrict the ddtrace version, meaning you must maintain multiple ddtrace versions.

There is a workaround for this case's error_handler option.

You could try doing:

option :error_handler, default: -> { Tracing::SpanOperation::Events::DEFAULT_ON_ERROR } 

That would evaluate the proc, and it returns another proc. It is a workaround.

The issue with your code is that methods like env_to_bool are being removed from the configuration on the latest versions.

You could have a separate configuration DSL based on the ddtrace version. As you did in your patch, it is not ideal, but also not a huge burden when it comes to maintaining it as the configuration DSL should not change much in the future.

require "datadog/tracing/span_operation"

module Datadog
  module Tracing
    module Contrib
      module Tobox
        module Configuration
          class Settings < Contrib::Configuration::Settings
            if DDTrace::VERSION::STRING >= "1.13.0"
              option :enabled do |o|
                o.env "DD_TOBOX_SIDEKIQ_ENABLED"
                o.type :bool
                o.default true
              end
  
              option :analytics_enabled do |o|
                o.env "DD_TOBOX_ANALYTICS_ENABLED"
                o.type :bool
                o.default false
              end
  
              option :analytics_sample_rate do |o|
                o.env "DD_TRACE_TOBOX_ANALYTICS_SAMPLE_RATE"
                o.type :float
                o.default 1.0
              end
  
              option :service_name
              option :error_handler do |o|
                o.type :proc
                # Workaround to avoid having to use `experimental_default_prc` or `default_proc`
                o.default -> { Tracing::SpanOperation::Events::DEFAULT_ON_ERROR }
              end
              option :distributed_tracing, default: false
            elsif 
              option :enabled do |o|
                o.default { env_to_bool("DD_TOBOX_SIDEKIQ_ENABLED", true) }
                o.lazy
              end
  
              option :analytics_enabled do |o|
                o.default { env_to_bool("DD_TOBOX_ANALYTICS_ENABLED", false) }
                o.lazy
              end
  
              option :analytics_sample_rate do |o|
                o.default { env_to_float("DD_TRACE_TOBOX_ANALYTICS_SAMPLE_RATE", 1.0) }
                o.lazy
              end
  
              option :service_name
              option :error_handler, default: Tracing::SpanOperation::Events::DEFAULT_ON_ERROR
              option :distributed_tracing, default: false
            end
          end
        end
      end
    end
  end
end

Another option would be to specify the minimum version of ddtrace on your project, and once you specify that the minimum version is 1.13.0 or higher, you no longer have to keep the check on the ddtrace version. I understand that this suggestion might not be possible on your part, but just throwing it out there just in case

What do you think?

@HoneyryderChuck
Copy link
Contributor Author

You could try doing:

That does not work. This is the error backtrace:

ArgumentError: wrong number of arguments (given 1, expected 0)
from /user/dev/tobox/lib/tobox/plugins/datadog/configuration.rb:29:in `block in <class:Settings>'

I guess my patch is good enough? Considering I can't afford atm to bump the ddtrace minimum version in a non-major release, supporting the 3 ways to set it up is the way to go considering the lack of a low common denominator? Would be great if the ddtrace gem considered these nuisances when breaking public interface in minor versions, in terms of providing a migration path with deprecation warning.

@GustavoCaso
Copy link
Member

GustavoCaso commented Oct 6, 2023

@HoneyryderChuck

That does not work. This is the error backtrace:

So sorry. I just realised that the code snippet I gave was incorrect

Could you try instead:

option :error_handler do |o|
  o.type :proc
  o.default { Tracing::SpanOperation::Events::DEFAULT_ON_ERROR }
end

If that works the your patch could look like like:

require "datadog/tracing/span_operation"

module Datadog
  module Tracing
    module Contrib
      module Tobox
        module Configuration
          class Settings < Contrib::Configuration::Settings
            if DDTrace::VERSION::STRING >= "1.13.0"
              option :enabled do |o|
                o.env "DD_TOBOX_SIDEKIQ_ENABLED"
                o.type :bool
                o.default true
              end
  
              option :analytics_enabled do |o|
                o.env "DD_TOBOX_ANALYTICS_ENABLED"
                o.type :bool
                o.default false
              end
  
              option :analytics_sample_rate do |o|
                o.env "DD_TRACE_TOBOX_ANALYTICS_SAMPLE_RATE"
                o.type :float
                o.default 1.0
              end
  
              option :service_name
              option :error_handler do |o|
                o.type :proc
                # Workaround to avoid having to use `experimental_default_prc` or `default_proc`
                 o.default { Tracing::SpanOperation::Events::DEFAULT_ON_ERROR }
              end
              option :distributed_tracing, default: false
            elsif 
              option :enabled do |o|
                o.default { env_to_bool("DD_TOBOX_SIDEKIQ_ENABLED", true) }
                o.lazy
              end
  
              option :analytics_enabled do |o|
                o.default { env_to_bool("DD_TOBOX_ANALYTICS_ENABLED", false) }
                o.lazy
              end
  
              option :analytics_sample_rate do |o|
                o.default { env_to_float("DD_TRACE_TOBOX_ANALYTICS_SAMPLE_RATE", 1.0) }
                o.lazy
              end
  
              option :service_name
              option :error_handler, default: Tracing::SpanOperation::Events::DEFAULT_ON_ERROR
              option :distributed_tracing, default: false
            end
          end
        end
      end
    end
  end
end

I guess my patch is good enough?

Yes it is

Would be great if the ddtrace gem considered these nuisances when breaking public interface in minor versions, in terms of providing a migration path with deprecation warning.

That is totally fair. Sorry for any inconvenience we caused you. We will do better next time

@HoneyryderChuck
Copy link
Contributor Author

thx. given that the workaround is viable and information is passed, I'll close the ticket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Involves a bug community Was opened by a community member
Projects
None yet
Development

No branches or pull requests

2 participants