diff --git a/src/Hangfire.Mongo.Tests/MongoStorageFacts.cs b/src/Hangfire.Mongo.Tests/MongoStorageFacts.cs index 01a3ce5..da0a0c8 100644 --- a/src/Hangfire.Mongo.Tests/MongoStorageFacts.cs +++ b/src/Hangfire.Mongo.Tests/MongoStorageFacts.cs @@ -42,6 +42,21 @@ public void Ctor_ThrowsAnException_WhenStorageOptionsValueIsNull() Assert.Equal("storageOptions", exception.ParamName); } + + [Fact] + public void Ctor_DoesNotSupportCappedAndTailNotificationChosen_ThrowsAnException() + { + var exception = Assert.Throws(() => new MongoStorage( + MongoClientSettings.FromConnectionString("mongodb://localhost"), + "test", + new MongoStorageOptions + { + CheckQueuedJobsStrategy = CheckQueuedJobsStrategy.TailNotificationsCollection, + SupportsCappedCollection = false + })); + + Assert.Contains("CheckQueuedJobsStrategy, cannot be TailNotificationsCollection if", exception.Message); + } [Fact] public void GetMonitoringApi_ReturnsNonNullInstance() diff --git a/src/Hangfire.Mongo/CosmosDB/CosmosStorageOptions.cs b/src/Hangfire.Mongo/CosmosDB/CosmosStorageOptions.cs index 956d4b4..8142163 100644 --- a/src/Hangfire.Mongo/CosmosDB/CosmosStorageOptions.cs +++ b/src/Hangfire.Mongo/CosmosDB/CosmosStorageOptions.cs @@ -16,6 +16,7 @@ public CosmosStorageOptions() CheckConnection = false; MigrationLockTimeout = TimeSpan.FromMinutes(2); Factory = new CosmosFactory(); + SupportsCappedCollection = false; } } } \ No newline at end of file diff --git a/src/Hangfire.Mongo/Migration/Steps/Version09/00_CreateSignalCollection.cs b/src/Hangfire.Mongo/Migration/Steps/Version09/00_CreateSignalCollection.cs index fb1c9cf..6be273d 100644 --- a/src/Hangfire.Mongo/Migration/Steps/Version09/00_CreateSignalCollection.cs +++ b/src/Hangfire.Mongo/Migration/Steps/Version09/00_CreateSignalCollection.cs @@ -14,7 +14,7 @@ internal class CreateSignalCollection : IMongoMigrationStep public bool Execute(IMongoDatabase database, MongoStorageOptions storageOptions, IMongoMigrationContext migrationContext) { - if (storageOptions is CosmosStorageOptions) + if (!storageOptions.SupportsCappedCollection) { return true; } diff --git a/src/Hangfire.Mongo/Migration/Steps/Version17/00_AddNotificationsCollection.cs b/src/Hangfire.Mongo/Migration/Steps/Version17/00_AddNotificationsCollection.cs index 4d20a6b..0e515e1 100644 --- a/src/Hangfire.Mongo/Migration/Steps/Version17/00_AddNotificationsCollection.cs +++ b/src/Hangfire.Mongo/Migration/Steps/Version17/00_AddNotificationsCollection.cs @@ -11,7 +11,10 @@ internal class AddNotificationsCollection : IMongoMigrationStep public bool Execute(IMongoDatabase database, MongoStorageOptions storageOptions, IMongoMigrationContext migrationContext) { - if (storageOptions is CosmosStorageOptions) return true; + if (!storageOptions.SupportsCappedCollection) + { + return true; + } database.CreateCollection(storageOptions.Prefix + ".notifications", new CreateCollectionOptions { diff --git a/src/Hangfire.Mongo/MongoStorage.cs b/src/Hangfire.Mongo/MongoStorage.cs index cc65771..a70cf53 100644 --- a/src/Hangfire.Mongo/MongoStorage.cs +++ b/src/Hangfire.Mongo/MongoStorage.cs @@ -33,7 +33,7 @@ public class MongoStorage : JobStorage /// Storage options /// protected readonly MongoStorageOptions StorageOptions; - + /// /// DB context /// @@ -42,19 +42,20 @@ public class MongoStorage : JobStorage /// /// Enabled Hangfire features. To change enabled features, inherit this class and override 'HasFeature' method /// - public ReadOnlyDictionary Features { get; protected set; } = new ReadOnlyDictionary(new Dictionary(StringComparer.OrdinalIgnoreCase) + public ReadOnlyDictionary Features { get; protected set; } = new ReadOnlyDictionary( + new Dictionary(StringComparer.OrdinalIgnoreCase) { - { JobStorageFeatures.ExtendedApi, true }, - { JobStorageFeatures.JobQueueProperty, true }, - { JobStorageFeatures.Connection.BatchedGetFirstByLowest, true }, - { JobStorageFeatures.Connection.GetUtcDateTime, true }, - { JobStorageFeatures.Connection.GetSetContains, true }, - { JobStorageFeatures.Connection.LimitedGetSetCount, true }, - { JobStorageFeatures.Transaction.AcquireDistributedLock, true }, - { JobStorageFeatures.Transaction.CreateJob, true }, - { JobStorageFeatures.Transaction.SetJobParameter, true }, - { JobStorageFeatures.Monitoring.DeletedStateGraphs, true }, - { JobStorageFeatures.Monitoring.AwaitingJobs, true } + {JobStorageFeatures.ExtendedApi, true}, + {JobStorageFeatures.JobQueueProperty, true}, + {JobStorageFeatures.Connection.BatchedGetFirstByLowest, true}, + {JobStorageFeatures.Connection.GetUtcDateTime, true}, + {JobStorageFeatures.Connection.GetSetContains, true}, + {JobStorageFeatures.Connection.LimitedGetSetCount, true}, + {JobStorageFeatures.Transaction.AcquireDistributedLock, true}, + {JobStorageFeatures.Transaction.CreateJob, true}, + {JobStorageFeatures.Transaction.SetJobParameter, true}, + {JobStorageFeatures.Monitoring.DeletedStateGraphs, true}, + {JobStorageFeatures.Monitoring.AwaitingJobs, true} }); /// @@ -73,10 +74,10 @@ public MongoStorage(MongoClientSettings mongoClientSettings, string databaseName /// Client settings for MongoDB /// Database name /// Storage options - public MongoStorage(MongoClientSettings mongoClientSettings, string databaseName, MongoStorageOptions storageOptions) + public MongoStorage(MongoClientSettings mongoClientSettings, string databaseName, + MongoStorageOptions storageOptions) : this(new MongoClient(mongoClientSettings), databaseName, storageOptions) { - } /// @@ -92,10 +93,22 @@ public MongoStorage(IMongoClient mongoClient, string databaseName, MongoStorageO { throw new ArgumentNullException(nameof(databaseName)); } + StorageOptions = storageOptions ?? throw new ArgumentNullException(nameof(storageOptions)); + + if (storageOptions.CheckQueuedJobsStrategy == CheckQueuedJobsStrategy.TailNotificationsCollection && + storageOptions.SupportsCappedCollection == false) + { + throw new NotSupportedException( + $"{nameof(MongoStorageOptions.CheckQueuedJobsStrategy)}, cannot be {CheckQueuedJobsStrategy.TailNotificationsCollection}" + + $" if {nameof(MongoStorageOptions.SupportsCappedCollection)} is false" + ); + } + DatabaseName = databaseName; MongoClient = mongoClient ?? throw new ArgumentNullException(nameof(mongoClient)); - StorageOptions = storageOptions ?? throw new ArgumentNullException(nameof(storageOptions)); - HangfireDbContext = StorageOptions.Factory.CreateDbContext(mongoClient, databaseName, storageOptions.Prefix); + + HangfireDbContext = + StorageOptions.Factory.CreateDbContext(mongoClient, databaseName, storageOptions.Prefix); if (StorageOptions.CheckConnection) { @@ -107,24 +120,25 @@ public MongoStorage(IMongoClient mongoClient, string databaseName, MongoStorageO MongoMigrationManager.MigrateIfNeeded(storageOptions, HangfireDbContext.Database); } } - + private void CheckConnection() { using (var cts = new CancellationTokenSource(StorageOptions.ConnectionCheckTimeout)) { try { - HangfireDbContext.Database.RunCommand((Command)"{ping:1}", cancellationToken: cts.Token); + HangfireDbContext.Database.RunCommand((Command) "{ping:1}", + cancellationToken: cts.Token); } catch (Exception e) { - throw new MongoConnectException(HangfireDbContext, CreateObscuredConnectionString(), StorageOptions.ConnectionCheckTimeout, e); + throw new MongoConnectException(HangfireDbContext, CreateObscuredConnectionString(), + StorageOptions.ConnectionCheckTimeout, e); } } } /// - public override bool HasFeature([NotNull] string featureId) { if (featureId == null) throw new ArgumentNullException(nameof(featureId)); @@ -135,7 +149,6 @@ public override bool HasFeature([NotNull] string featureId) } - /// /// Returns Monitoring API object /// @@ -167,7 +180,11 @@ public override IEnumerable GetComponents() yield return StorageOptions.Factory.CreateMongoJobQueueWatcher(HangfireDbContext, StorageOptions); break; case CheckQueuedJobsStrategy.TailNotificationsCollection: - yield return StorageOptions.Factory.CreateMongoNotificationObserver(HangfireDbContext, StorageOptions); + if (StorageOptions.SupportsCappedCollection) + { + yield return StorageOptions.Factory.CreateMongoNotificationObserver(HangfireDbContext, + StorageOptions); + } break; } } @@ -187,8 +204,8 @@ public override void WriteOptionsToLog(ILog logger) /// public override string ToString() { - - return $"Connection string: {CreateObscuredConnectionString()}, database name: {DatabaseName}, prefix: {StorageOptions.Prefix}"; + return + $"Connection string: {CreateObscuredConnectionString()}, database name: {DatabaseName}, prefix: {StorageOptions.Prefix}"; } private string CreateObscuredConnectionString() diff --git a/src/Hangfire.Mongo/MongoStorageOptions.cs b/src/Hangfire.Mongo/MongoStorageOptions.cs index 2906bce..3f44c25 100644 --- a/src/Hangfire.Mongo/MongoStorageOptions.cs +++ b/src/Hangfire.Mongo/MongoStorageOptions.cs @@ -98,6 +98,14 @@ public TimeSpan QueuePollInterval _queuePollInterval = value; } } + + /// + /// Indicates if the underlying storage supports capped collections + /// default set to true. + /// MongoStorage will throw NotSupportedException if 'SupportsCappedCollection' is false + /// and 'CheckQueuedJobsStrategy' is TailNotificationsCollection + /// + public bool SupportsCappedCollection { get; set; } = true; /// /// Invisibility timeout