From 703cce200bb86b0671b0dfba3ad188165dca71bc Mon Sep 17 00:00:00 2001 From: Jonas Gottschau Date: Wed, 7 Feb 2024 21:54:17 +0100 Subject: [PATCH] used $addFields operator instead of $set for backwards compability (#382) --- ChangeLog.md | 3 + .../Hangfire.Mongo.Sample.ASPNetCore.csproj | 2 +- .../Hangfire.Mongo.Sample.CosmosDB.csproj | 2 +- .../Hangfire.Mongo.Sample.NETCore.csproj | 2 +- .../DistributedLock/MongoDistributedLock.cs | 71 ++++++++++--------- src/Hangfire.Mongo/Hangfire.Mongo.csproj | 10 +-- 6 files changed, 48 insertions(+), 42 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 151d01f..c8364a0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,9 @@ ## Change log +### v1.10.1 +- Use $addFields operator instead of $set when setting DistributedLock heartbeat for support mongo versions < 4.2 + ### v1.10.0 - Update to Hangfire 1.8.9 - Use SlidingInvisibilityTimeout to determine whether a background job is still alive diff --git a/src/Hangfire.Mongo.Sample.ASPNetCore/Hangfire.Mongo.Sample.ASPNetCore.csproj b/src/Hangfire.Mongo.Sample.ASPNetCore/Hangfire.Mongo.Sample.ASPNetCore.csproj index eca7e19..237459d 100644 --- a/src/Hangfire.Mongo.Sample.ASPNetCore/Hangfire.Mongo.Sample.ASPNetCore.csproj +++ b/src/Hangfire.Mongo.Sample.ASPNetCore/Hangfire.Mongo.Sample.ASPNetCore.csproj @@ -2,7 +2,7 @@ net6.0 - 1.10.0 + 1.10.1 MongoDB storage implementation for Hangfire (background job system for ASP.NET applications). Copyright © 2014-2019 Sergey Zwezdin, Martin Lobger, Jonas Gottschau Sergey Zwezdin, Martin Lobger, Jonas Gottschau diff --git a/src/Hangfire.Mongo.Sample.CosmosDB/Hangfire.Mongo.Sample.CosmosDB.csproj b/src/Hangfire.Mongo.Sample.CosmosDB/Hangfire.Mongo.Sample.CosmosDB.csproj index 5751e75..f3a8d28 100644 --- a/src/Hangfire.Mongo.Sample.CosmosDB/Hangfire.Mongo.Sample.CosmosDB.csproj +++ b/src/Hangfire.Mongo.Sample.CosmosDB/Hangfire.Mongo.Sample.CosmosDB.csproj @@ -2,7 +2,7 @@ net6.0 - 1.10.0 + 1.10.1 MongoDB storage implementation for Hangfire (background job system for ASP.NET applications). Copyright © 2014-2019 Sergey Zwezdin, Martin Lobger, Jonas Gottschau Sergey Zwezdin, Martin Lobger, Jonas Gottschau diff --git a/src/Hangfire.Mongo.Sample.NETCore/Hangfire.Mongo.Sample.NETCore.csproj b/src/Hangfire.Mongo.Sample.NETCore/Hangfire.Mongo.Sample.NETCore.csproj index 6601ae2..8bb60a7 100644 --- a/src/Hangfire.Mongo.Sample.NETCore/Hangfire.Mongo.Sample.NETCore.csproj +++ b/src/Hangfire.Mongo.Sample.NETCore/Hangfire.Mongo.Sample.NETCore.csproj @@ -4,7 +4,7 @@ Hangfire.Mongo.Sample.NETCore Exe Hangfire.Mongo.Sample.NETCore - 1.10.0 + 1.10.1 MongoDB storage implementation for Hangfire (background job system for ASP.NET applications). Copyright © 2014-2019 Sergey Zwezdin, Martin Lobger, Jonas Gottschau Sergey Zwezdin, Martin Lobger, Jonas Gottschau diff --git a/src/Hangfire.Mongo/DistributedLock/MongoDistributedLock.cs b/src/Hangfire.Mongo/DistributedLock/MongoDistributedLock.cs index addab25..1bd723b 100644 --- a/src/Hangfire.Mongo/DistributedLock/MongoDistributedLock.cs +++ b/src/Hangfire.Mongo/DistributedLock/MongoDistributedLock.cs @@ -20,7 +20,7 @@ public class MongoDistributedLock : IDisposable private static readonly ILog Logger = LogProvider.For(); private static readonly ThreadLocal> AcquiredLocks - = new ThreadLocal>(() => new Dictionary()); + = new ThreadLocal>(() => new Dictionary()); private readonly string _resource; @@ -43,8 +43,8 @@ private static readonly ThreadLocal> AcquiredLocks /// Database options /// Thrown if lock is not acquired within the timeout /// Thrown if other mongo specific issue prevented the lock to be acquired - public MongoDistributedLock(string resource, - TimeSpan timeout, + public MongoDistributedLock(string resource, + TimeSpan timeout, HangfireDbContext dbContext, MongoStorageOptions storageOptions) { @@ -57,9 +57,12 @@ public MongoDistributedLock(string resource, { throw new ArgumentException($@"The {nameof(resource)} cannot be empty", nameof(resource)); } + if (timeout.TotalSeconds > int.MaxValue) { - throw new ArgumentException($"The timeout specified is too large. Please supply a timeout equal to or less than {int.MaxValue} seconds", nameof(timeout)); + throw new ArgumentException( + $"The timeout specified is too large. Please supply a timeout equal to or less than {int.MaxValue} seconds", + nameof(timeout)); } } @@ -94,6 +97,7 @@ public virtual void Dispose() { return; } + _completed = true; if (!AcquiredLocks.Value.ContainsKey(_resource)) @@ -156,20 +160,22 @@ protected virtual void Acquire(TimeSpan timeout) { ["$setOnInsert"] = new BsonDocument { - [nameof(DistributedLockDto.ExpireAt)] = DateTime.UtcNow.Add(_storageOptions.DistributedLockLifetime) + [nameof(DistributedLockDto.ExpireAt)] = + DateTime.UtcNow.Add(_storageOptions.DistributedLockLifetime) } }; try { var result = _dbContext.DistributedLock.FindOneAndUpdate(filter, update, options); - + // If result is null, it means we acquired the lock if (result == null) { if (Logger.IsTraceEnabled()) { - Logger.Trace($"{_resource} - Acquired"); + Logger.Trace($"{_resource} - Acquired"); } + isLockAcquired = true; } else @@ -189,7 +195,8 @@ protected virtual void Acquire(TimeSpan timeout) if (!isLockAcquired) { - throw new DistributedLockTimeoutException($"{_resource} - Could not place a lock: The lock request timed out."); + throw new DistributedLockTimeoutException( + $"{_resource} - Could not place a lock: The lock request timed out."); } } catch (DistributedLockTimeoutException) @@ -222,8 +229,9 @@ protected virtual void Release() { if (Logger.IsTraceEnabled()) { - Logger.Trace($"{_resource} - Release"); + Logger.Trace($"{_resource} - Release"); } + // Remove resource lock _dbContext.DistributedLock.DeleteOne(new BsonDocument(nameof(DistributedLockDto.Resource), _resource)); } @@ -262,7 +270,8 @@ protected virtual void Cleanup() /// protected virtual void StartHeartBeat() { - var timerInterval = TimeSpan.FromMilliseconds(_storageOptions.DistributedLockLifetime.TotalMilliseconds / 5); + var timerInterval = + TimeSpan.FromMilliseconds(_storageOptions.DistributedLockLifetime.TotalMilliseconds / 5); _heartbeatTimer = new Timer(_ => { // Timer callback may be invoked after the Dispose method call, @@ -270,29 +279,26 @@ protected virtual void StartHeartBeat() lock (_lockObject) { if (_completed) return; - + try { - var filter = new BsonDocument - { - [nameof(DistributedLockDto.Resource)] = _resource - }; - var update = new BsonDocument + var pipeline = new BsonDocument[] { - [nameof(DistributedLockDto.ExpireAt)] = new BsonDocument + new BsonDocument("$match", new BsonDocument { - ["$add"] = new BsonArray + [nameof(DistributedLockDto.Resource)] = _resource + }), + new BsonDocument("$addFields", new BsonDocument + { + [nameof(DistributedLockDto.ExpireAt)] = new BsonDocument { - "$$NOW", - (int) _storageOptions.DistributedLockLifetime.TotalMilliseconds, + ["$add"] = new BsonArray + { + "$$NOW", + (int) _storageOptions.DistributedLockLifetime.TotalMilliseconds, + } } - } - }; - - var pipeline = new BsonDocument[] - { - new BsonDocument("$match", filter), - new BsonDocument("$set", update) + }) }; Stopwatch sw = null; if (Logger.IsTraceEnabled()) @@ -306,8 +312,8 @@ protected virtual void StartHeartBeat() { var serializedModel = new Dictionary { - ["Filter"] = filter, - ["Update"] = update + ["Filter"] = pipeline[0], + ["Update"] = pipeline[1] }; sw.Stop(); var builder = new StringBuilder(); @@ -324,7 +330,7 @@ protected virtual void StartHeartBeat() } }, null, timerInterval, timerInterval); } - + /// /// /// @@ -335,13 +341,14 @@ protected virtual DateTime Wait(string resource, TimeSpan timeout) { if (Logger.IsTraceEnabled()) { - Logger.Trace($"{resource} - Waiting {timeout.TotalMilliseconds}ms"); + Logger.Trace($"{resource} - Waiting {timeout.TotalMilliseconds}ms"); } + using (var resetEvent = new ManualResetEvent(false)) { resetEvent.WaitOne(timeout); } - + return DateTime.UtcNow; } } diff --git a/src/Hangfire.Mongo/Hangfire.Mongo.csproj b/src/Hangfire.Mongo/Hangfire.Mongo.csproj index 353674c..6d80e1e 100644 --- a/src/Hangfire.Mongo/Hangfire.Mongo.csproj +++ b/src/Hangfire.Mongo/Hangfire.Mongo.csproj @@ -1,6 +1,6 @@ - 1.10.0 + 1.10.1 netstandard2.0 $(NoWarn);CS0618 true @@ -21,12 +21,8 @@ Sergey Zwezdin, Jonas Gottschau MongoDB storage implementation for Hangfire (background job system for ASP.NET applications). Hangfire AspNet OWIN MongoDB CosmosDB Long-Running Background Fire-And-Forget Delayed Recurring Tasks Jobs Scheduler Threading Queues - 1.10.0 - - Update to Hangfire 1.8.9 - - Use SlidingInvisibilityTimeout to determine whether a background job is still alive - - BREAKING: Remove InvisibilityTimeout - - Use server time for distributed lock instead of Datetime.UtcNow - - Fixed Job not requeued when requeued while in processing state (#380) + 1.10.1 + - Use $addFields operator instead of $set when setting DistributedLock heartbeat for support mongo versions < 4.2 README.md