Skip to content

Commit

Permalink
Do not initiate job polling if only IRecurringJobs are registered
Browse files Browse the repository at this point in the history
  • Loading branch information
NielsPilgaard committed Feb 27, 2024
1 parent ac86fe2 commit 3229c3c
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 8 deletions.
21 changes: 17 additions & 4 deletions src/Pilgaard.BackgroundJobs/BackgroundJobService.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Diagnostics.Metrics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Pilgaard.BackgroundJobs;

Expand Down Expand Up @@ -28,9 +29,10 @@ internal sealed class BackgroundJobService : IBackgroundJobService
private readonly IServiceScopeFactory _scopeFactory;
private readonly ILogger<BackgroundJobService> _logger;
private readonly IBackgroundJobScheduler _backgroundJobScheduler;
private readonly BackgroundJobServiceOptions _backgroundJobServiceOptions;

private event Func<object, EventArgs, BackgroundJobRegistration, CancellationToken, Task>? RecurringJobTimerTriggered;
private static readonly List<IDisposable> _recurringJobTimers = new();
private static readonly List<IDisposable> _recurringJobTimers = [];

private static readonly Meter _meter = new(
name: typeof(BackgroundJobService).Assembly.GetName().Name!,
Expand All @@ -49,14 +51,17 @@ internal sealed class BackgroundJobService : IBackgroundJobService
/// <param name="scopeFactory">The factory used when constructing background jobs.</param>
/// <param name="logger">The logger.</param>
/// <param name="backgroundJobScheduler">The background job scheduler used to retrieve jobs when they should be run.</param>
/// <param name="backgroundJobServiceOptions">The options holding all <see cref="BackgroundJobRegistration"/>s, used to determine whether the <see cref="BackgroundJobService"/> should begin polling for occurrences or not.</param>
public BackgroundJobService(
IServiceScopeFactory scopeFactory,
ILogger<BackgroundJobService> logger,
IBackgroundJobScheduler backgroundJobScheduler)
IBackgroundJobScheduler backgroundJobScheduler,
IOptions<BackgroundJobServiceOptions> 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;
}

/// <summary>
Expand All @@ -65,19 +70,27 @@ public BackgroundJobService(
/// <param name="cancellationToken">A <see cref="CancellationToken"/>
/// which can be used to cancel the background jobs.</param>
/// <returns>
/// A <see cref="Task"/> which will complete when all background jobs have been run, and there are no more occurrences of any of them.
/// A <see cref="Task"/> which will complete when all background jobs have been run, and there are no more occurrences of them.
/// </returns>
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)
Expand Down
25 changes: 21 additions & 4 deletions tests/Pilgaard.BackgroundJobs.Tests/BackgroundJobServiceTests.cs
Original file line number Diff line number Diff line change
@@ -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();
}

Expand Down Expand Up @@ -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<IBackgroundJobService>();

// Act
await sut.RunJobsAsync(cts.Token);

// The assertion is that RunJobsAsync does not keep running, because it returns early
}
}

0 comments on commit 3229c3c

Please sign in to comment.