Renamed namespaces, classes, and commands from `DEXJob` to `ProfileJob` to align with the new "Profiles" context. Updated pipeline behaviors (`CheckQueryExecutionBehavior`, `MainQueryExecutionBehavior`, `ReCRequestExecutionBehavior`) to handle `TriggeringProfileJobCommand`. Refactored unit tests to reflect the new naming convention, including mock setups and assertions. Updated `DtoExtensions` to return `TriggeringProfileJobBatchCommand`. Adjusted queries and dependency injection to use the new `Profiles` namespace. Performed general refactoring to replace all references to "DEXJob" with "ProfileJob" in method names, variables, and documentation for consistency and clarity.
83 lines
3.5 KiB
C#
83 lines
3.5 KiB
C#
using ECMJobRunner.Application.Profiles.Queries;
|
|
using ECMJobRunner.WebCron.Extensions;
|
|
using Hangfire;
|
|
using MediatR;
|
|
|
|
namespace ECMJobRunner.WebCron.ProfileWorker;
|
|
|
|
/// <summary>
|
|
/// Handles the core business logic for synchronizing profiles between the database and Hangfire.
|
|
/// Responsible for adding, updating, and removing recurring jobs based on profile configuration.
|
|
/// </summary>
|
|
/// <param name="Logger">Logger for diagnostic output.</param>
|
|
/// <param name="JobManager">Hangfire recurring job manager.</param>
|
|
/// <param name="Mediator">MediatR mediator for executing commands and queries.</param>
|
|
/// <param name="ProfileCache">Thread-safe cache for tracking profile state.</param>
|
|
public class ProfileWork(ILogger<ProfileWork> Logger, IRecurringJobManager JobManager, IMediator Mediator, ProfileCache ProfileCache)
|
|
{
|
|
/// <summary>
|
|
/// Synchronizes active profiles from the database with Hangfire recurring jobs.
|
|
/// </summary>
|
|
/// <param name="stoppingToken">Cancellation token for graceful shutdown.</param>
|
|
/// <remarks>
|
|
/// Execution flow:
|
|
/// <list type="number">
|
|
/// <item><description>Fetches active profiles from database via MediatR query</description></item>
|
|
/// <item><description>Removes jobs from Hangfire that no longer exist in the database</description></item>
|
|
/// <item><description>Adds or updates jobs with changed schedules</description></item>
|
|
/// <item><description>Updates local cache to reflect current state</description></item>
|
|
/// </list>
|
|
/// Uses HashSet for O(1) job existence checks to optimize performance.
|
|
/// </remarks>
|
|
/// <returns>A task representing the asynchronous synchronization operation.</returns>
|
|
public async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
// Fetch active profiles from database
|
|
var profiles = await Mediator.Send(new GetProfileQuery()
|
|
{
|
|
Active = true,
|
|
IncludeSqlJobs = true
|
|
}, stoppingToken);
|
|
|
|
// Create HashSet for O(1) lookup performance
|
|
var profileJobIds = profiles.Select(p => p.JobId()).ToHashSet();
|
|
|
|
// Remove jobs from Hangfire that no longer exist in the database
|
|
foreach (var cachedJobId in ProfileCache.GetAllJobIds())
|
|
{
|
|
if (!profileJobIds.Contains(cachedJobId))
|
|
{
|
|
// Remove job from Hangfire if it no longer exists in the database
|
|
JobManager.RemoveIfExists(cachedJobId);
|
|
ProfileCache.TryRemove(cachedJobId, out _);
|
|
Logger.LogInformation("Job {JobId} removed", cachedJobId);
|
|
}
|
|
}
|
|
|
|
// Add or update jobs in Hangfire based on the database profiles
|
|
foreach (var profile in profiles)
|
|
{
|
|
if (ProfileCache.TryGetValue(profile.JobId(), out var currentProfile)
|
|
&& currentProfile!.Schedule == profile.Schedule)
|
|
continue;
|
|
|
|
// Add or update recurring job using MediatR command
|
|
JobManager.AddOrUpdate<IMediator>(
|
|
profile.JobId(),
|
|
mediator => mediator.Send(profile.ToJob(), stoppingToken),
|
|
profile.Schedule,
|
|
new RecurringJobOptions
|
|
{
|
|
TimeZone = TimeZoneInfo.Local
|
|
}
|
|
);
|
|
|
|
// Store/update in local cache
|
|
ProfileCache.AddOrUpdate(profile.JobId(), profile);
|
|
|
|
Logger.LogInformation("Job {JobId} registered with schedule: {Schedule}",
|
|
profile.JobId(), profile.Schedule);
|
|
}
|
|
}
|
|
}
|