diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs new file mode 100644 index 000000000..205b3c75a --- /dev/null +++ b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/BackgroundService.cs @@ -0,0 +1,83 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +using Microsoft.Extensions.Hosting; + +namespace Ordering.API.Infrastructure.HostedServices +{ + // Copyright (c) .NET Foundation. All rights reserved. + // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + + /// + /// Base class for implementing a long running . + /// IMPORTANT: This base class is implemented in .NET Core 2.1 - Since this microservice is still in .NET Core 2.0, we're using the class within the project + /// When .NET Core 2.1 is released, this class should be removed and you should use the use implemented by the framework + /// https://github.com/aspnet/Hosting/blob/712c992ca827576c05923e6a134ca0bec87af4df/src/Microsoft.Extensions.Hosting.Abstractions/BackgroundService.cs + /// + /// + public abstract class BackgroundService : IHostedService, IDisposable + { + private Task _executingTask; + private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource(); + + /// + /// This method is called when the starts. The implementation should return a task that represents + /// the lifetime of the long running operation(s) being performed. + /// + /// Triggered when is called. + /// A that represents the long running operations. + protected abstract Task ExecuteAsync(CancellationToken stoppingToken); + + /// + /// Triggered when the application host is ready to start the service. + /// + /// Indicates that the start process has been aborted. + public virtual Task StartAsync(CancellationToken cancellationToken) + { + // Store the task we're executing + _executingTask = ExecuteAsync(_stoppingCts.Token); + + // If the task is completed then return it, this will bubble cancellation and failure to the caller + if (_executingTask.IsCompleted) + { + return _executingTask; + } + + // Otherwise it's running + return Task.CompletedTask; + } + + /// + /// Triggered when the application host is performing a graceful shutdown. + /// + /// Indicates that the shutdown process should no longer be graceful. + public virtual async Task StopAsync(CancellationToken cancellationToken) + { + // Stop called without start + if (_executingTask == null) + { + return; + } + + try + { + // Signal cancellation to the executing method + _stoppingCts.Cancel(); + } + finally + { + // Wait until the task completes or the stop token triggers + await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken)); + } + + } + + public virtual void Dispose() + { + _stoppingCts.Cancel(); + } + } + + +} diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs index 3fa70e20b..26017dfd0 100644 --- a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs +++ b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/GracePeriodManagerService.cs @@ -12,8 +12,7 @@ using System.Threading; using System.Threading.Tasks; - public class GracePeriodManagerService - : HostedService + public class GracePeriodManagerService : BackgroundService { private readonly OrderingSettings _settings; private readonly ILogger _logger; @@ -25,26 +24,28 @@ { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); - _settings = settings?.Value ?? throw new ArgumentNullException(nameof(settings)); } - protected override async Task ExecuteAsync(CancellationToken cancellationToken) + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - while (true) + _logger.LogDebug($"GracePeriod background task is starting."); + + stoppingToken.Register(() => _logger.LogDebug($"#1 GracePeriod background task is stopping.")); + + while (!stoppingToken.IsCancellationRequested) { - if (cancellationToken.IsCancellationRequested) - { - break; - } + _logger.LogDebug($"GracePeriod background task is doing background work."); CheckConfirmedGracePeriodOrders(); - await Task.Delay(_settings.CheckUpdateTime, cancellationToken); + await Task.Delay(_settings.CheckUpdateTime, stoppingToken); continue; } + _logger.LogDebug($"GracePeriod background task is stopping."); + await Task.CompletedTask; } @@ -54,10 +55,11 @@ var orderIds = GetConfirmedGracePeriodOrders(); + _logger.LogDebug($"GracePeriod sent a ."); foreach (var orderId in orderIds) { - var confirmGracePeriodEvent = new GracePeriodConfirmedIntegrationEvent(orderId); - _eventBus.Publish(confirmGracePeriodEvent); + var gracePeriodConfirmedEvent = new GracePeriodConfirmedIntegrationEvent(orderId); + _eventBus.Publish(gracePeriodConfirmedEvent); } } diff --git a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/HostedService.cs b/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/HostedService.cs deleted file mode 100644 index 98ee4907a..000000000 --- a/src/Services/Ordering/Ordering.API/Infrastructure/HostedServices/HostedService.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Microsoft.Extensions.Hosting; -using System.Threading; -using System.Threading.Tasks; - -namespace Ordering.API.Infrastructure.HostedServices -{ - public abstract class HostedService : IHostedService - { - // Example untested base class code kindly provided by David Fowler: https://gist.github.com/davidfowl/a7dd5064d9dcf35b6eae1a7953d615e3 - - private Task _executingTask; - private CancellationTokenSource _cts; - - public Task StartAsync(CancellationToken cancellationToken) - { - // Create a linked token so we can trigger cancellation outside of this token's cancellation - _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - - // Store the task we're executing - _executingTask = ExecuteAsync(_cts.Token); - - // If the task is completed then return it, otherwise it's running - return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask; - } - - public async Task StopAsync(CancellationToken cancellationToken) - { - // Stop called without start - if (_executingTask == null) - { - return; - } - - // Signal cancellation to the executing method - _cts.Cancel(); - - // Wait until the task completes or the stop token triggers - await Task.WhenAny(_executingTask, Task.Delay(-1, cancellationToken)); - - // Throw if cancellation triggered - cancellationToken.ThrowIfCancellationRequested(); - } - - // Derived classes should override this and execute a long running method until - // cancellation is requested - protected abstract Task ExecuteAsync(CancellationToken cancellationToken); - } -}