Is there a way to provide code to execute if an Oban worker is cancelled? It’s not listed as one of the callbacks provided by the relevant behaviour, but there might be another way I’m missing?
I hope someone would post a better approach, but I’ve seen Oban.Telemetry — Oban v2.18.3 being used to run callbacks like this.
defmodule MyApp.ObanTelemetry do
require Logger
def handle_event([:oban, :job, :stop], _measures, meta, _config) do
if meta.state == :cancelled do
your_cancel_callback(meta.job)
end
catch
kind, reason ->
message = Exception.format(kind, reason, __STACKTRACE__)
Logger.error(message)
end
end
:telemetry.attach("oban-job-stop", [:oban, :job, :stop], &MyApp.ObanTelemetry.handle_event/4, nil)
Just note that if the callback raises, the telemetry handler would be detached. Hence the catch
clause.
Yep, thanks for the tip.
To be honest because all the call-sites for cancelling the jobs are in my code, I think I might just make a helper function which does the Oban.cancel
call and my own code for cleaning up. Using telemetry is an interesting (if, as you say, a bit hacky) option, though.
There are three situations where a job may be cancelled:
- By returning a
{:cancel, reason}
tuple from yourperform/1
callback - During execution, from
Oban.cancel_job
called programmatically or through the web dashboard - Not during execution, also from
Oban.cancel_job
The first and second variants will emit an [:oban, :job, :stop]
as mentioned by @ruslandoga. However, the third won’t emit a :stop
event because the job isn’t running at that point.
If your goal is to respond to the job being cancelled during execution, then the telemetry approach or Pro’s worker hooks are a good approach.
This leads me to believe that you’re mostly concerned with handling situations two and three from the list above, where cancel_job
is used. In that case, there are engine telemetry events that include information about the cancelled job (id, queue, state
).
That’s an indirect way to handle cancel cleanup though, and if you’re in control of the cancellation I’d favor wrapping the cancel call and the cleanup in a transaction.
Thanks @sorentwo , yep, I’m in control of the cancellation - and I agree that just wrapping in a transaction is better than (ab)using the telemetry for that purpose. Thanks for your advice (and thanks for Oban which is awesome).