Credo and detection of possible mistaken placement of 'use'

Hello, all!

I’m new to Elixir and recently I’ve just came across with an issue that didn’t look so trivial at first, but solution was indeed (Function overriding causes warning).

The issue is related to Oban and there was a module like:

defmodule OurWorker do
 
 require Logger
 
 use OpenTelemetryDecorator
 @decorate_all simple_trace()
 
 use Oban.Worker,
   queue: :some_queue,
   max_attempts: 120,
   unique: [period: 30, states: [:available, :scheduled, :executing]]
 
def backoff(%Job{attempt: attempt}), do: 10
 
end

Where backoff() function was set to a fixed 10s interval.

It was able to note that this interval wasn’t being taken into account, being considered the default Worker.backoff() (exponential 15s).

It took a long time to figure out this, and it was only possible by decompiling this code (Unveil Erlang Code of Your Elixir Project) and realising that Worker.backoff() wasn’t being overridden:

backoff(#{'__struct__' := 'Elixir.Oban.Job'} = _@1) ->
   'Elixir.OpenTelemetry.Span',
   'Elixir.OpenTelemetry.Tracer',
   _@2 = 'Elixir.OpenTelemetry.Tracer':current_span_ctx(),
   _@4 = case 'Elixir.Logger':metadata() of
             [{request_id, _@3}] -> [{request_id, _@3}];
             _ -> []
         end,
   otel_tracer:with_span(opentelemetry:get_tracer('OurWorker'),
                         <<"OurWorker.backoff/1">>,
                         #{parent => _@2, attributes => _@4},
                         fun (_) -> 'Elixir.Oban.Worker':backoff(_@1) end);
 
backoff(#{attempt := _attempt@1,
         '__struct__' := 'Elixir.Oban.Job'}) ->
   'Elixir.OpenTelemetry.Span',
   'Elixir.OpenTelemetry.Tracer',
   _@1 = 'Elixir.OpenTelemetry.Tracer':current_span_ctx(),
   _@3 = case 'Elixir.Logger':metadata() of
             [{request_id, _@2}] -> [{request_id, _@2}];
             _ -> []
         end,
   otel_tracer:with_span(opentelemetry:get_tracer('OurWorker'),
                         <<"OurWorker.backoff/1">>,
                         #{parent => _@1, attributes => _@3},
                         fun (_) -> 10 end).

Moving up use Oban.Worker to the top of the module happily solved the issue.

I kind of understand that this has to do with macro issues, so placement is kind of important, but not so obvious…

So, the question is: would it make sense to detect this kind of “misplacement” of a use? Maybe on credo?

Sorry if the question doesn’t makes any sense, I’m still on my first learning steps.

3 Likes

The second clause in that Erlang dump shows that the function is being overridden, but it seems that the preceding definition (generated by OpenTelemetryDecorator) matches any Oban.Job struct and the overriding one never executes.

Moving use Oban.Worker up above use OpenTelemetryDecorator “fixes” this by not decorating the initial definition (that calls Oban.Worker.backoff/1).

5 Likes

Awesome @al2o3cr! Thanks a ton! :smiley: