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

Hangfire InMemory 0.9.0 Object disposed exception #2399

Closed Cynicszm closed 5 months ago

Cynicszm commented 6 months ago

Hi,

We are now getting object disposed exceptions in NUnit (4.1.0) after upgrading Hangfire InMemory from 0.8.1 to 0.9.0

Individual tests pass but fail on committing the write transaction when run concurrently.

Code snippet: `public string CreateToken( TimeSpan expireIn ) { var tokenId = Guid.NewGuid().ToString( "N" ); var resourceKey = "execution-tokens:" + tokenId;

using var connection = JobStorage.Current.GetConnection();
using var transaction = connection.CreateWriteTransaction();
transaction.SetRangeInHash( resourceKey, new Dictionary<string, string>
{
    { "Cancelled", "false" },
    { "Paused", "false" }
} );

// if storage supports manual expiration handling
if ( transaction is JobStorageTransaction jsTransaction )
{
    jsTransaction.ExpireHash( resourceKey, expireIn );
}
transaction.Commit();

return tokenId;

}`

Stack: System.ObjectDisposedException : Cannot access a disposed object. Object name: 'Hangfire.InMemory.InMemoryDispatcher'. at Hangfire.InMemory.InMemoryDispatcher.ThrowObjectDisposedException() in //src/Hangfire.InMemory/InMemoryDispatcher.cs:line 136 at Hangfire.InMemory.InMemoryDispatcher.QueryAndWait(Func`3 query) in //src/Hangfire.InMemory/InMemoryDispatcher.cs:line 58 at Hangfire.InMemory.InMemoryDispatcherBase.QueryAndWait[T](Func3 query) in /_/src/Hangfire.InMemory/InMemoryDispatcherBase.cs:line 126 at Hangfire.InMemory.InMemoryDispatcherBase.QueryAndWait(Action2 query) in //src/Hangfire.InMemory/InMemoryDispatcherBase.cs:line 120 at Hangfire.InMemory.InMemoryTransaction.Commit() in //src/Hangfire.InMemory/InMemoryTransaction.cs:line 51

odinserj commented 6 months ago

InMemoryStorage class now implements IDisposable. So something is calling the Dispose method. Try creating instances of this class explicitly for each test, instead of relying on a static instance in JobStorage.Current. In this case, unit tests will be properly isolated, and you will not have any shared state between test runs.

Cynicszm commented 6 months ago

Thanks for the quick response.

I've moved some of the JobStorage usages around and it appears to be behaving for now.

Appreciate the help.

odinserj commented 6 months ago

Great, thanks for the update!