namespace QuantEngine.Core.Scheduling;
using System.Diagnostics;
///
/// 스케줄러 작업 기본 클래스
/// 패턴화/표준화 원칙 적용
///
public abstract class SchedulerJobBase
{
public string JobName { get; }
public string JobId { get; } = Guid.NewGuid().ToString("N")[..12];
protected SchedulerJobBase(string jobName)
{
JobName = jobName ?? throw new ArgumentNullException(nameof(jobName));
}
public async Task ExecuteAsync()
{
var stopwatch = Stopwatch.StartNew();
var startTime = DateTime.UtcNow;
try
{
await OnStartingAsync();
var jobResult = await RunAsync();
stopwatch.Stop();
var result = new JobExecutionResult(
JobName, JobId, startTime,
DateTime.UtcNow, stopwatch.ElapsedMilliseconds,
true, jobResult.Message, jobResult.Data, null);
await OnCompletedAsync(result);
return result;
}
catch (OperationCanceledException ex)
{
stopwatch.Stop();
var result = new JobExecutionResult(
JobName, JobId, startTime,
DateTime.UtcNow, stopwatch.ElapsedMilliseconds,
false, $"Task cancelled: {ex.Message}", null, ex);
await OnFailedAsync(result);
return result;
}
catch (Exception ex)
{
stopwatch.Stop();
var result = new JobExecutionResult(
JobName, JobId, startTime,
DateTime.UtcNow, stopwatch.ElapsedMilliseconds,
false, $"Task failed: {ex.Message}", null, ex);
await OnFailedAsync(result);
return result;
}
}
protected abstract Task RunAsync();
protected virtual Task OnStartingAsync() => Task.CompletedTask;
protected virtual Task OnCompletedAsync(JobExecutionResult result) => Task.CompletedTask;
protected virtual Task OnFailedAsync(JobExecutionResult result) => Task.CompletedTask;
protected async Task RetryAsync(
Func> operation,
int maxRetries = 3,
int initialDelayMs = 1000)
{
for (int attempt = 1; attempt <= maxRetries; attempt++)
{
try { return await operation(); }
catch (Exception) when (attempt < maxRetries)
{
await Task.Delay(initialDelayMs * (int)Math.Pow(2, attempt - 1));
}
}
return await operation();
}
}
public class JobExecutionResult
{
public JobExecutionResult(string jobName, string jobId, DateTime startedAt,
DateTime completedAt, long elapsedMs, bool succeeded, string message,
object? data, Exception? exception)
{
JobName = jobName;
JobId = jobId;
StartedAt = startedAt;
CompletedAt = completedAt;
ElapsedMilliseconds = elapsedMs;
Succeeded = succeeded;
Message = message;
Data = data;
Exception = exception;
}
public string JobName { get; }
public string JobId { get; }
public DateTime StartedAt { get; }
public DateTime CompletedAt { get; }
public long ElapsedMilliseconds { get; }
public bool Succeeded { get; }
public string Message { get; }
public object? Data { get; }
public Exception? Exception { get; }
}
public record JobRunResult
{
public bool Succeeded { get; init; }
public string Message { get; init; } = string.Empty;
public object? Data { get; init; }
public static JobRunResult Success(string message, object? data = null)
=> new() { Succeeded = true, Message = message, Data = data };
public static JobRunResult Failure(string message, object? data = null)
=> new() { Succeeded = false, Message = message, Data = data };
}