HangfireIO / Hangfire

An easy way to perform background job processing in .NET and .NET Core applications. No Windows Service or separate process required
https://www.hangfire.io
Other
9.43k stars 1.71k forks source link

AsyncLocal<T> retains values on every job run #2409

Open djdd87 opened 6 months ago

djdd87 commented 6 months ago

I implemented AsyncLocal in a particular service method to avoid some repeated processing, however the same service method ends up being called by Hangfire and on every job run the data of AsyncLocal.Value, which should be null on each asynchronous call retains the same value until the app is retarted. The code works perfectly when coming from a SignalR or WebAPI request, it's only Hangfire processes that retain the value.

Code for job setup:

RecurringJobManager manager = new RecurringJobManager();
if (jobEnable)
{
    manager.AddOrUpdate("JobName", Job.FromExpression(() => TaskSchedulingController.DoJob()), "* * * * *", options);
}
else
{
    manager.RemoveIfExists("JobName");
}

Code example for job, which will only ever run successfully once, then will error on every subsequent call:

private static AsyncLocal<string> _test = new AsyncLocal<string>();

public async Task DoJob()
{
    if (_test.Value != null)
    {
        throw new Exception("AsyncLocal is retaining a value");
    }
    _test.Value = "ABC123";
}
odinserj commented 5 months ago

While I agree that AsyncLocal instances shouldn't be preserved across different background job runs, and ExecutionContext should be somehow flushed, but this is potentially a breaking change for those who use this feature. So I'm postponing this to 2.0 version without any specific point in time.

Is it possible to clean these values manually, for example in a try/finally block?

djdd87 commented 5 months ago

Yeah, that's actually how I worked around the issue for the time being.

bt-Knodel commented 4 months ago

3rd parties use AsyncLocal as well. This is a pretty important feature. For example, majority of tracing libraries that aren't through OpenTelemetry depend on it. We use Datadog to trace hangfire jobs and Datadog tracks the active span using AsyncLocal. Removes possibility of clearing/maintaining it manually.