2020-10-05 04:40:38 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
|
|
namespace BirthdayBot.BackgroundServices
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Handles the execution of periodic background tasks specific to each shard.
|
|
|
|
|
/// </summary>
|
|
|
|
|
class ShardBackgroundWorker : IDisposable
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// The interval, in seconds, in which background tasks are attempted to be run within a shard.
|
|
|
|
|
/// </summary>
|
2020-10-09 04:56:44 +00:00
|
|
|
|
public const int Interval = 40;
|
2020-10-05 04:40:38 +00:00
|
|
|
|
|
|
|
|
|
private readonly Task _workerTask;
|
|
|
|
|
private readonly CancellationTokenSource _workerCanceller;
|
|
|
|
|
private readonly List<BackgroundService> _workers;
|
|
|
|
|
|
|
|
|
|
private ShardInstance Instance { get; }
|
|
|
|
|
|
|
|
|
|
public ConnectionStatus ConnStatus { get; }
|
|
|
|
|
public BirthdayRoleUpdate BirthdayUpdater { get; }
|
2021-02-02 06:31:24 +00:00
|
|
|
|
public SelectiveAutoUserDownload UserDownloader { get; }
|
2020-10-05 04:40:38 +00:00
|
|
|
|
public DateTimeOffset LastBackgroundRun { get; private set; }
|
2021-10-14 02:06:29 +00:00
|
|
|
|
public string CurrentExecutingService { get; private set; }
|
2020-10-05 04:40:38 +00:00
|
|
|
|
public int ConnectionScore => ConnStatus.Score;
|
|
|
|
|
|
|
|
|
|
public ShardBackgroundWorker(ShardInstance instance)
|
|
|
|
|
{
|
|
|
|
|
Instance = instance;
|
|
|
|
|
_workerCanceller = new CancellationTokenSource();
|
|
|
|
|
|
|
|
|
|
ConnStatus = new ConnectionStatus(instance);
|
|
|
|
|
BirthdayUpdater = new BirthdayRoleUpdate(instance);
|
2021-02-02 06:31:24 +00:00
|
|
|
|
UserDownloader = new SelectiveAutoUserDownload(instance);
|
2020-10-05 04:40:38 +00:00
|
|
|
|
_workers = new List<BackgroundService>()
|
|
|
|
|
{
|
2021-02-02 06:31:24 +00:00
|
|
|
|
{UserDownloader},
|
2020-10-05 04:40:38 +00:00
|
|
|
|
{BirthdayUpdater},
|
2021-05-30 19:00:49 +00:00
|
|
|
|
{new DataRetention(instance)},
|
|
|
|
|
{new ExternalStatisticsReporting(instance)}
|
2020-10-05 04:40:38 +00:00
|
|
|
|
};
|
|
|
|
|
|
2020-10-09 04:56:44 +00:00
|
|
|
|
_workerTask = Task.Factory.StartNew(WorkerLoop, _workerCanceller.Token);
|
2020-10-05 04:40:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
_workerCanceller.Cancel();
|
|
|
|
|
_workerTask.Wait(5000);
|
|
|
|
|
if (!_workerTask.IsCompleted)
|
|
|
|
|
Instance.Log("Dispose", "Warning: Background worker has not yet stopped. Forcing its disposal.");
|
|
|
|
|
_workerTask.Dispose();
|
|
|
|
|
_workerCanceller.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2020-10-09 04:46:36 +00:00
|
|
|
|
/// *The* background task for the shard.
|
|
|
|
|
/// Executes service tasks and handles errors.
|
2020-10-05 04:40:38 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
private async Task WorkerLoop()
|
|
|
|
|
{
|
|
|
|
|
LastBackgroundRun = DateTimeOffset.UtcNow;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
while (!_workerCanceller.IsCancellationRequested)
|
|
|
|
|
{
|
2020-10-10 07:28:11 +00:00
|
|
|
|
await Task.Delay(Interval * 1000, _workerCanceller.Token).ConfigureAwait(false);
|
2020-10-05 04:40:38 +00:00
|
|
|
|
|
2020-10-09 04:46:36 +00:00
|
|
|
|
// ConnectionStatus will always run. Its result determines if remaining tasks also this time.
|
2020-10-10 07:28:11 +00:00
|
|
|
|
await ConnStatus.OnTick(_workerCanceller.Token).ConfigureAwait(false);
|
2020-10-05 04:40:38 +00:00
|
|
|
|
if (!ConnStatus.Stable) continue;
|
|
|
|
|
|
|
|
|
|
// Execute tasks sequentially
|
|
|
|
|
foreach (var service in _workers)
|
|
|
|
|
{
|
2021-10-14 02:06:29 +00:00
|
|
|
|
CurrentExecutingService = service.GetType().Name;
|
2021-06-02 04:47:54 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (_workerCanceller.IsCancellationRequested) break;
|
|
|
|
|
await service.OnTick(_workerCanceller.Token).ConfigureAwait(false);
|
|
|
|
|
}
|
2020-10-05 04:40:38 +00:00
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2021-10-14 02:06:29 +00:00
|
|
|
|
|
2020-10-05 04:40:38 +00:00
|
|
|
|
if (ex is TaskCanceledException)
|
|
|
|
|
{
|
2021-10-14 02:06:29 +00:00
|
|
|
|
Instance.Log(nameof(WorkerLoop), $"{CurrentExecutingService} was interrupted by a cancellation request.");
|
2020-10-05 04:40:38 +00:00
|
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// TODO webhook log
|
2021-10-14 02:06:29 +00:00
|
|
|
|
Instance.Log(nameof(WorkerLoop), $"{CurrentExecutingService} encountered an exception:\n" + ex.ToString());
|
2020-10-05 04:40:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-14 02:06:29 +00:00
|
|
|
|
CurrentExecutingService = null;
|
2020-10-05 04:40:38 +00:00
|
|
|
|
LastBackgroundRun = DateTimeOffset.UtcNow;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (TaskCanceledException) { }
|
|
|
|
|
|
|
|
|
|
Instance.Log(nameof(WorkerLoop), "Background worker has concluded normally.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|