-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathCronJobScheduler.cs
More file actions
110 lines (98 loc) · 4.13 KB
/
Copy pathCronJobScheduler.cs
File metadata and controls
110 lines (98 loc) · 4.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NCrontab;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace NetCoreStack.Jobs
{
internal class CronJobScheduler : IBackgroundTask
{
private readonly IServiceProvider _serviceProvider;
private readonly IThrottler _throttler;
private readonly ILoggerFactory _loggerFactory;
private readonly ILogger _logger;
private readonly ConcurrentDictionary<string, ProcessableCronJob> _jobDictionary;
private readonly IJobStorage _jobStorage;
private readonly Func<CrontabSchedule, TimeZoneInfo, IScheduleInstant> _instantFactory;
private static readonly TimeSpan LockTimeout = TimeSpan.FromMinutes(1);
private static readonly TimeZoneInfo timeZone = TimeZoneInfo.Utc;
public CronJobScheduler(IServiceProvider serviceProvider,
IThrottler throttler,
Func<CrontabSchedule, TimeZoneInfo, IScheduleInstant> instantFactory,
ConcurrentDictionary<string, ProcessableCronJob> jobDictionary,
IJobStorage jobStorage,
ILoggerFactory loggerFactory)
{
_serviceProvider = serviceProvider;
_throttler = throttler;
_instantFactory = instantFactory;
_jobDictionary = jobDictionary;
_jobStorage = jobStorage;
_loggerFactory = loggerFactory;
_logger = _loggerFactory.CreateLogger<CronJobScheduler>();
}
private async Task RunAsync(ProcessableCronJob job, TaskContext context)
{
var cronSchedule = job.Schedule;
var nowInstant = _instantFactory(cronSchedule, timeZone);
var lastInstant = GetLastInstant(job, nowInstant);
if (nowInstant.GetNextInstants(lastInstant).Any())
{
using (var scopedContext = _serviceProvider.CreateScope())
{
var provider = scopedContext.ServiceProvider;
var cronJob = (IJob)provider.GetService(job.Type);
job.CreatedAt = nowInstant.NowInstant;
try
{
Stopwatch sw = new Stopwatch();
sw.Start();
await cronJob.InvokeAsync(context);
sw.Stop();
job.LastRun = nowInstant.NowInstant;
_logger.LogDebug($"{job.ToString()} is executed. Utc: {nowInstant.NowInstant}, TotalSeconds: {sw.Elapsed.TotalSeconds}");
}
catch (Exception ex)
{
_jobStorage.Set(cronJob.Id, cronJob);
_logger.LogError(ex, $"{nameof(CronJobScheduler)} exception");
}
job.Next = nowInstant.NextInstant.HasValue ? (DateTime?)nowInstant.NextInstant.Value : null;
}
}
}
private DateTime GetLastInstant(ProcessableCronJob job, IScheduleInstant instant)
{
DateTime lastInstant;
if (job.LastRun != default(DateTime))
{
lastInstant = job.LastRun;
}
else if(job.CreatedAt != default(DateTime))
{
lastInstant = job.CreatedAt;
}
else if(job.Next.HasValue)
{
lastInstant = job.Next.Value;
lastInstant = lastInstant.AddSeconds(-1);
}
else
{
lastInstant = instant.NowInstant.AddSeconds(-1);
}
return lastInstant;
}
public void Invoke(TaskContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
_throttler.Throttle(context.CancellationToken);
var cronJobs = _jobDictionary.Select(p => p.Value).ToArray();
Task.WhenAll(cronJobs.Select(job => RunAsync(job, context)));
_throttler.Delay(context.CancellationToken);
}
}
}