From 6caa8a4501eb7a96be4fc56e88f16e400aaba61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Pilgaard=20Gr=C3=B8ndahl?= Date: Tue, 27 Feb 2024 20:31:17 +0100 Subject: [PATCH] Disable BackgroundJobService when only IRecurringJobs are registered (#121) * adhering to analyzer suggestions * Do not initiate job polling if only IRecurringJobs are registered --- .../BackgroundJobService.cs | 21 +++++++++++++--- .../BackgroundJobSchedulerTests.cs | 6 ++--- .../BackgroundJobServiceTests.cs | 25 ++++++++++++++++--- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/Pilgaard.BackgroundJobs/BackgroundJobService.cs b/src/Pilgaard.BackgroundJobs/BackgroundJobService.cs index 2e6ba70..9f1805e 100644 --- a/src/Pilgaard.BackgroundJobs/BackgroundJobService.cs +++ b/src/Pilgaard.BackgroundJobs/BackgroundJobService.cs @@ -1,6 +1,7 @@ using System.Diagnostics.Metrics; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Pilgaard.BackgroundJobs; @@ -28,9 +29,10 @@ internal sealed class BackgroundJobService : IBackgroundJobService private readonly IServiceScopeFactory _scopeFactory; private readonly ILogger _logger; private readonly IBackgroundJobScheduler _backgroundJobScheduler; + private readonly BackgroundJobServiceOptions _backgroundJobServiceOptions; private event Func? RecurringJobTimerTriggered; - private static readonly List _recurringJobTimers = new(); + private static readonly List _recurringJobTimers = []; private static readonly Meter _meter = new( name: typeof(BackgroundJobService).Assembly.GetName().Name!, @@ -49,14 +51,17 @@ internal sealed class BackgroundJobService : IBackgroundJobService /// The factory used when constructing background jobs. /// The logger. /// The background job scheduler used to retrieve jobs when they should be run. + /// The options holding all s, used to determine whether the should begin polling for occurrences or not. public BackgroundJobService( IServiceScopeFactory scopeFactory, ILogger logger, - IBackgroundJobScheduler backgroundJobScheduler) + IBackgroundJobScheduler backgroundJobScheduler, + IOptions backgroundJobServiceOptions) { _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _backgroundJobScheduler = backgroundJobScheduler ?? throw new ArgumentNullException(nameof(backgroundJobScheduler)); + _backgroundJobServiceOptions = backgroundJobServiceOptions.Value; } /// @@ -65,19 +70,27 @@ public BackgroundJobService( /// A /// which can be used to cancel the background jobs. /// - /// A which will complete when all background jobs have been run, and there are no more occurrences of any of them. + /// A which will complete when all background jobs have been run, and there are no more occurrences of them. /// public async Task RunJobsAsync(CancellationToken cancellationToken = default) { ScheduleRecurringJobs(cancellationToken); + if (_backgroundJobServiceOptions + .Registrations + .Count(registration => registration.IsRecurringJob is false) is 0) + { + _logger.LogInformation("No {OneTimeJob} or {CronJob} have been registered.", + nameof(IOneTimeJob), nameof(ICronJob)); + return; + } + while (!cancellationToken.IsCancellationRequested) { _logger.LogDebug("Scheduling background jobs."); var backgroundJobsToRun = _backgroundJobScheduler .GetBackgroundJobsAsync(cancellationToken) - .WithCancellation(cancellationToken) .ConfigureAwait(false); await foreach (var registration in backgroundJobsToRun) diff --git a/tests/Pilgaard.BackgroundJobs.Tests/BackgroundJobSchedulerTests.cs b/tests/Pilgaard.BackgroundJobs.Tests/BackgroundJobSchedulerTests.cs index a9aa27c..f59975e 100644 --- a/tests/Pilgaard.BackgroundJobs.Tests/BackgroundJobSchedulerTests.cs +++ b/tests/Pilgaard.BackgroundJobs.Tests/BackgroundJobSchedulerTests.cs @@ -69,7 +69,7 @@ public async Task return_background_jobs_when_they_should_be_run() try { - await foreach (var backgroundJob in backgroundJobs.WithCancellation(cts.Token)) + await foreach (var backgroundJob in backgroundJobs) { var now = DateTime.UtcNow; now.Second.Should().Be(startTime.AddSeconds(index++).Second); @@ -116,7 +116,7 @@ public async Task be_able_to_return_all_types_of_background_job() _testOutput.WriteLine($"[{backgroundJobType}]: {occurrence}"); } - distinctBackgroundJobs.Count.Should().Be(3); + distinctBackgroundJobs.Should().HaveCount(3); } [Fact] @@ -131,7 +131,7 @@ public async Task throw_an_argument_exception_when_background_jobs_have_the_same await using var serviceProvider = _services.BuildServiceProvider(); // Act && Assert - Assert.Throws(() => serviceProvider.GetRequiredService()); + Assert.Throws(serviceProvider.GetRequiredService); } [Fact] diff --git a/tests/Pilgaard.BackgroundJobs.Tests/BackgroundJobServiceTests.cs b/tests/Pilgaard.BackgroundJobs.Tests/BackgroundJobServiceTests.cs index d06c6e2..5e9c04b 100644 --- a/tests/Pilgaard.BackgroundJobs.Tests/BackgroundJobServiceTests.cs +++ b/tests/Pilgaard.BackgroundJobs.Tests/BackgroundJobServiceTests.cs @@ -1,17 +1,14 @@ using Cronos; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; -using Xunit.Abstractions; namespace Pilgaard.BackgroundJobs.Tests; public class backgroundjobservice_should { - private readonly ITestOutputHelper _testOutput; private readonly IServiceCollection _services; - public backgroundjobservice_should(ITestOutputHelper testOutput) + public backgroundjobservice_should() { - _testOutput = testOutput; _services = new ServiceCollection().AddLogging(); } @@ -53,4 +50,24 @@ public async Task run_background_jobs() output3.Should().NotBeNull(); output4.Should().NotBeNull(); } + + [Fact] + public async Task return_early_when_only_recurring_jobs_are_registered() + { + // Arrange + _services.AddBackgroundJobs() + .AddJob("FastRecurringJob", () => { }, TimeSpan.FromSeconds(1)) + .AddJob("FastRecurringJobWithInitialDelay", () => { }, TimeSpan.FromSeconds(1), TimeSpan.Zero); + + await using var serviceProvider = _services.BuildServiceProvider(); + + using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + + var sut = serviceProvider.GetRequiredService(); + + // Act + await sut.RunJobsAsync(cts.Token); + + // The assertion is that RunJobsAsync does not keep running, because it returns early + } }