TaskExecution(IBackgourndTask backgroundTask) { BackgroundTask = backgroundTask; CancellationTokenSource = new CancellationTokenSource(); CancellationToken = CancellationTokenSource.Token; HeartbeatTask = CreateHeartbeatTask(); }
internal static async Task Run(IBackgourndTask task) { using (var runner = new TaskExecution(task)) { await runner.DoRun(); } }
static async Task Run(this IBackgourndTask task) { var start = LocalTime.Now; Log.Info($"Running background task '{task.Name}'"); await Db.Update(task, x => { x.Heartbeat = LocalTime.UtcNow; x.ExecutingInstance = Engine.ExecutionId; }).ConfigureAwait(false); try { await Engine.GetAction(task).ConfigureAwait(false); Log.Info($"Sucessfully ran background task '{task.Name}' in {LocalTime.Now.Subtract(start).ToNaturalTime()}"); } catch (Exception ex) { Log.Error(ex, $"Failed to run background task '{task.Name}' because: " + ex.ToFullMessage()); } finally { await Db.Update(await Db.Reload(task), t => { t.LastExecuted = LocalTime.Now; t.ExecutingInstance = null; t.Heartbeat = null; }).ConfigureAwait(false); } }
static bool ShouldRun(IBackgourndTask task) { var nextExecution = task.LastExecuted.GetValueOrDefault().AddMinutes(task.IntervalInMinutes); if (nextExecution.IsInTheFuture()) { return(false); } Log.Info($"{task.Name} is due."); if (task.Heartbeat is null) { return(true); } if (task.Heartbeat.Value.AddMinutes(task.TimeoutInMinutes) > LocalTime.UtcNow) { Log.Info($"Skipped due background task '{task.Name}' because last attempt is still running on [{task.ExecutingInstance}] instance."); return(false); } Log.Info($"Last attempt to run the background task '{task.Name}' on [{task.ExecutingInstance}] timed out."); return(true); }
static async Task <IBackgourndTask> Update(IBackgourndTask task, Action <IBackgourndTask> action) { var clone = (IBackgourndTask)(await Database.Reload(task)).Clone(); action(clone); clone = await Database.Save(clone); return(await Database.Reload(clone)); }
internal static Task <IBackgourndTask> SendHeartbeat(this IBackgourndTask task) { task.Logger().Info("Recording heartbeat for " + task.Name); return(Update(task, t => { t.Heartbeat = LocalTime.Now; t.ExecutingInstance = ExecutionEngine.Id; })); }
internal static Task RecordExecution(this IBackgourndTask task) { task.Logger().Info("Recording execution for " + task.Name); return(Update(task, t => { t.LastExecuted = LocalTime.Now; t.ExecutingInstance = null; t.Heartbeat = null; })); }
internal static async Task <bool> TryPick(this IBackgourndTask task) { var result = false; LogInfo($"Checking to see if {task.Name} has to run."); using (var scope = Database.CreateTransactionScope()) { LogInfo($"Causing a distributed lock for {task.Name}."); // cause a distributed lock await DataAccess.Create().ExecuteNonQuery($"update {task.GetType().Name.ToPlural()} set Heartbeat = Heartbeat where Name = '{task.Name}'"); task = await Database.Reload(task); var lastExecuted = task.LastExecuted.GetValueOrDefault(); var nextExecution = lastExecuted.AddMinutes(task.IntervalInMinutes); if (nextExecution.IsInTheFuture()) { LogInfo($"Should still wait for running [{task.Name}]. Next execution is at {nextExecution}."); result = false; } else { LogInfo($"{task.Name} should run."); var lastHeartbeat = task.Heartbeat.GetValueOrDefault(); var stillAlive = lastHeartbeat.AddMinutes(task.TimeoutInMinutes).IsInTheFuture(); if (stillAlive) { LogInfo($"[{task.Name}] is already running on [{task.ExecutingInstance}] instance."); } else { LogInfo($"[{task.Name}] is not running. Last heartbeat : " + lastHeartbeat); await task.SendHeartbeat(); } result = !stillAlive; } scope.Complete(); return(result); } }
private Task CreateHeartbeatTask() { return(Task.Run(async() => { while (!CancellationToken.IsCancellationRequested) { try { BackgroundTask = await BackgroundTask.SendHeartbeat(); } catch (Exception ex) { Log.For(this).Error(ex, "Failed to log heartbeat because : " + ex.ToFullMessage()); } await Task.Delay(HEARTBEAT_DELAY); } }, CancellationToken)); }
private Task CreateHeartbeatTask() { return(Task.Run(async() => { while (true) { if (CancellationToken.IsCancellationRequested) { return; } try { BackgroundTask = await BackgroundTask.SendHeartbeat(); } catch (Exception ex) { Log.For(this).Error(ex, "Failed to log heartbeat."); } await Task.Delay(HEARTBEAT_DELAY); } }, CancellationToken)); }
internal static Task GetActionTask(this IBackgourndTask task) => BackgroundProcessManager.Current.GetAction(task);
internal static Task GetAction(IBackgourndTask task) => Actions[task.Name]();