wanlitao / HangfireExtension

Hangfire Extension plugins
Apache License 2.0
22 stars 32 forks source link

Lock timeout exceptions happen every time #12

Closed dermeister0 closed 7 years ago

dermeister0 commented 7 years ago

My workers crash every time:

Error occurred during execution of 'Worker #2d5f9ca3' process. Execution will be retried (attempt 1 of 2147483647) in 00:00:01 seconds.

System.ApplicationException: This operation returned because the timeout period expired. (Exception from HRESULT: 0x800705B4)
   at System.Threading.ReaderWriterLock.AcquireWriterLockInternal(Int32 millisecondsTimeout)
   at System.Threading.ReaderWriterLock.AcquireWriterLock(TimeSpan timeout)
   at Hangfire.SQLite.SQLiteStorage.CreateAndOpenConnection(Boolean isWriteLock)
   at Hangfire.SQLite.SQLiteStorage.UseConnection[T](Func`2 func, Boolean isWriteLock)
   at Hangfire.SQLite.SQLiteStorage.UseConnection(Action`1 action, Boolean isWriteLock)
   at Hangfire.SQLite.SQLiteFetchedJob.RemoveFromQueue()
   at Hangfire.Server.Worker.Execute(BackgroundProcessContext context)
   at Hangfire.Server.ServerProcessExtensions.Execute(IServerProcess process, BackgroundProcessContext context)
   at Hangfire.Server.AutomaticRetryProcess.Execute(BackgroundProcessContext context)
   at System.Threading.ReaderWriterLock.AcquireWriterLockInternal(Int32 millisecondsTimeout)
   at System.Threading.ReaderWriterLock.AcquireWriterLock(TimeSpan timeout)
   at Hangfire.SQLite.SQLiteStorage.CreateAndOpenConnection(Boolean isWriteLock) in C:\Work\External\HangfireExtension\Hangfire.SQLite\SQLiteStorage.cs:line 214
   at Hangfire.SQLite.SQLiteStorage.UseConnection[T](Func`2 func, Boolean isWriteLock) in C:\Work\External\HangfireExtension\Hangfire.SQLite\SQLiteStorage.cs:line 176
   at Hangfire.SQLite.SQLiteStorage.UseConnection(Action`1 action, Boolean isWriteLock) in C:\Work\External\HangfireExtension\Hangfire.SQLite\SQLiteStorage.cs:line 163
   at Hangfire.SQLite.SQLiteJobQueue.Dequeue(String[] queues, CancellationToken cancellationToken) in C:\Work\External\HangfireExtension\Hangfire.SQLite\SQLiteJobQueue.cs:line 69
   at Hangfire.SQLite.SQLiteStorageConnection.FetchNextJob(String[] queues, CancellationToken cancellationToken) in C:\Work\External\HangfireExtension\Hangfire.SQLite\SQLiteStorageConnection.cs:line 68
   at Hangfire.Server.Worker.Execute(BackgroundProcessContext context)
   at Hangfire.Server.ServerProcessExtensions.Execute(IServerProcess process, BackgroundProcessContext context)
   at Hangfire.Server.AutomaticRetryProcess.Execute(BackgroundProcessContext context)

Not sure if it happens due to my code (I integrated Hangfire to ASP.NET Core, but used Hangfire.SQLite without changes).

dermeister0 commented 7 years ago

The problem code is in Dequeue method:

                _storage.UseConnection(connection =>
                {
                    fetchedJob = connection.Query<FetchedJob>(
                               fetchNextJobSqlTemplate,
                               new { queues = queues, fetchedAt = DateTime.UtcNow })
                               .SingleOrDefault();

                    if (fetchedJob == null)
                    {
                        cancellationToken.WaitHandle.WaitOne(_options.QueuePollInterval);
                        cancellationToken.ThrowIfCancellationRequested();
                    }
                    else
                    {
                        // update
                        connection.Execute(dequeueJobSqlTemplate,
                            new { id = fetchedJob.Id, fetchedAt = DateTime.UtcNow });
                    }
                }, true);

Correct lock handling:

                _storage.UseConnection(connection =>
                {
                    fetchedJob = connection.Query<FetchedJob>(
                               fetchNextJobSqlTemplate,
                               new { queues = queues, fetchedAt = DateTime.UtcNow })
                               .SingleOrDefault();
                }, true);

                if (fetchedJob == null)
                {
                    cancellationToken.WaitHandle.WaitOne(_options.QueuePollInterval);
                    cancellationToken.ThrowIfCancellationRequested();
                }
                else
                {
                    _storage.UseConnection(connection =>
                    {
                        // update
                        connection.Execute(dequeueJobSqlTemplate,
                        new { id = fetchedJob.Id, fetchedAt = DateTime.UtcNow });
                    }, true);
                }
wanlitao commented 7 years ago

Version 1.2.0 is now available,please update nuget reference to retry

dermeister0 commented 7 years ago

It's fixed. Thanks!