using Bit.Core.Exceptions; using Bit.Core.Jobs; using Bit.Core.Settings; using Quartz; namespace Bit.Billing.Jobs; public class JobsHostedService( GlobalSettings globalSettings, IServiceProvider serviceProvider, ILogger logger, ILogger listenerLogger, ISchedulerFactory schedulerFactory) : BaseJobsHostedService(globalSettings, serviceProvider, logger, listenerLogger) { private List AdHocJobKeys { get; } = []; private IScheduler? _adHocScheduler; public override async Task StartAsync(CancellationToken cancellationToken) { Jobs = new List> { new(typeof(AliveJob), AliveJob.GetTrigger()), new(typeof(ReconcileAdditionalStorageJob), ReconcileAdditionalStorageJob.GetTrigger()) }; await base.StartAsync(cancellationToken); } public static void AddJobsServices(IServiceCollection services) { services.AddTransient(); services.AddTransient(); services.AddTransient(); // add this service as a singleton so we can inject it where needed services.AddSingleton(); services.AddHostedService(sp => sp.GetRequiredService()); } public async Task InterruptAdHocJobAsync(CancellationToken cancellationToken = default) where T : class, IJob { if (_adHocScheduler == null) { throw new InvalidOperationException("AdHocScheduler is null, cannot interrupt ad-hoc job."); } var jobKey = AdHocJobKeys.FirstOrDefault(j => j.Name == typeof(T).ToString()); if (jobKey == null) { throw new NotFoundException($"Cannot find job key: {typeof(T)}, not running?"); } logger.LogInformation("CANCELLING ad-hoc job with key: {JobKey}", jobKey); AdHocJobKeys.Remove(jobKey); await _adHocScheduler.Interrupt(jobKey, cancellationToken); } public async Task RunJobAdHocAsync(CancellationToken cancellationToken = default) where T : class, IJob { _adHocScheduler ??= await schedulerFactory.GetScheduler(cancellationToken); var jobKey = new JobKey(typeof(T).ToString()); var currentlyExecuting = await _adHocScheduler.GetCurrentlyExecutingJobs(cancellationToken); if (currentlyExecuting.Any(j => j.JobDetail.Key.Equals(jobKey))) { throw new InvalidOperationException($"Job {jobKey} is already running"); } AdHocJobKeys.Add(jobKey); var job = JobBuilder.Create() .WithIdentity(jobKey) .Build(); var trigger = TriggerBuilder.Create() .WithIdentity(typeof(T).ToString()) .StartNow() .Build(); logger.LogInformation("Scheduling ad-hoc job with key: {JobKey}", jobKey); await _adHocScheduler.ScheduleJob(job, trigger, cancellationToken); } }