perrich / Hangfire.MemoryStorage

A memory storage for Hangfire.
Apache License 2.0
131 stars 43 forks source link

Using multiple instances of a 'test host' that arranges Hangfire using the 'MemoryStorage' provider #39

Open bhehe opened 2 years ago

bhehe commented 2 years ago

Context

I'm using a 'test host' based approach in some of my integration tests and each test case/method creates its own discrete instance.

When registering Hangfire we're using the MemoryStorage provider and seeing some odd behaviors that we're suspecting may relate to use of this storage provider. On the main page @ https://github.com/perrich/Hangfire.MemoryStorage there is mention of the following:

What I'm observing happening is that we can run the tests 1-by-1 and they pass just fine. But when running them as a suite/set of tests they fail with 1 passing (first one ran) and the others then failing. We're using xUnit and we have measures in place to prevent concurrent/parallel execution of the tests that leverage the test host so I don't think that's the root cause here.

Previously we would see these failures and had little to no real clues to go on, but I've recently leveraged the newer method for triggering recurring jobs that does return a JobId when successful. Our helper code for triggering jobs includes a guard/check for null values being returned (indicating that it couldn't trigger the job) which throws an exception. Now with that check in place, I can see that the 1st test host instance / test case executes fine; It builds the test host/registers the job & triggers the job with the test passing. But the 2nd & subsequent test cases are failing saying they can't even trigger the referenced job. I know the job is registered by the test as they pass when ran individually so that doesn't seem like it should be the root-cause.

When I started to analyze why the job couldn't even be triggered, I started looking into where anything stateful could be complicit and the use of the MemoryStorage came to mind. With the statement on the main repo page about using static members to manage state in this provider I thought I had my 'Ah Ha!' moment - but on reviewing the current code it doesn't appear to truly be using static state management.

I reviewed the following files to try and trace the behavior & state management/lifetime:

The impression I have is that we are using a unique instance of a test host for each test method/case, we use Hangfire's extension methods to register it and then we register each job using the RecurringJob.AddOrUpdate method. While registering Hangfire we call the 'UseMemoryStorage' method passing in the MemoryStorageOptions and that in turn would get a new instance of Data for each discrete use of the test host/Hangfire.

As an attempt to verify if the storage provider and static state management was complicit, I updated the tests to register all of the jobs in each test case so I'd know "yes they're registered" but that didn't resolve the issue.

Questions

1 - Is the state really 'static' as described on the main page?

If I am using multiple instances of a test host, each with their own discrete registration of Hangfire & use of MemoryStorage provider would you expect to see the overall state of registered jobs be impacted as described? I'm wondering if that comment is perhaps from an older version and just out of date (perhaps needing removed/clarified).

2 - Would Hangfire itself be holding onto the StorageConnection (which is MemoryProvider) and yield the behavior described?

Once I noted the statement on this storage provider about static state, I started off here but I'm now wondering if there's something on the Hangfire side that would 'hold onto' the state this provider is using.

bhehe commented 2 years ago

The question as to "really is static" remains, but I realized there is another 'in memory' storage provider variant with the other one coming from the Hangfire team itself, I swapped to this alternate implementation and my problem remains.

Disregard this issue other than potentially clarifying the static angle and perhaps updating the page to reflect the actual current state.

Thanks!

perrich commented 2 years ago

You've right, if this version does not fit to the purpose the more recent MemoryProvider could resolve it.

bhehe commented 2 years ago

fwiw, if anyone encounters this later. the same issue exists with the Hangfire-provided version of the in-memory storage provider.

When switching to use of a SQL Server local DB for storage, the issue does not occur. So this appears to be an issue with the in-memory approach generically and how those storage providers manage their internal state for the lifetime of the test runner.

perrich commented 2 years ago

Thank you for your check and advise.