feat: Jobs infrastructure, endpoints, UI and worker prioritization
This commit is contained in:
@@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Predictalytics.Infrastructure.Data;
|
||||
using Predictalytics.Application.Interfaces;
|
||||
using Predictalytics.Domain.Interfaces;
|
||||
|
||||
namespace Predictalytics.Worker.Services;
|
||||
|
||||
@@ -40,23 +41,40 @@ public class TraderAnalyticsWorker : BackgroundService
|
||||
|
||||
private async Task RunAnalyticsAsync(CancellationToken ct)
|
||||
{
|
||||
List<int> traderIds;
|
||||
List<int> traderIds = new List<int>();
|
||||
Domain.Entities.BackgroundJob? activeJob = null;
|
||||
|
||||
using (var scope = _services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
// Find traders who have never been analyzed, or whose last analysis was before their latest trade.
|
||||
// Prioritize never-analyzed traders.
|
||||
traderIds = await db.Traders
|
||||
.Where(t => t.LastAnalyzedAt == null || t.Trades.Any(tr => tr.ExecutedAt > t.LastAnalyzedAt))
|
||||
.OrderBy(t => t.LastAnalyzedAt == null ? 0 : 1)
|
||||
.ThenBy(t => t.LastAnalyzedAt)
|
||||
.Select(t => t.Id)
|
||||
.Take(500) // Limit batch size to prevent long-running loops without save
|
||||
.ToListAsync(ct);
|
||||
var jobRepo = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
activeJob = await jobRepo.GetNextPendingJobAsync(Predictalytics.Domain.Enums.JobType.TraderAnalysis, ct);
|
||||
|
||||
if (activeJob != null && activeJob.TraderId.HasValue)
|
||||
{
|
||||
traderIds.Add(activeJob.TraderId.Value);
|
||||
activeJob.Status = Predictalytics.Domain.Enums.JobStatus.InProgress;
|
||||
activeJob.StartedAt = DateTime.UtcNow;
|
||||
await jobRepo.UpdateAsync(activeJob, ct);
|
||||
}
|
||||
else
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
// Find traders who have never been analyzed, or whose last analysis was before their latest trade.
|
||||
// Prioritize never-analyzed traders.
|
||||
traderIds = await db.Traders
|
||||
.Where(t => t.LastAnalyzedAt == null || t.Trades.Any(tr => tr.ExecutedAt > t.LastAnalyzedAt))
|
||||
.OrderBy(t => t.LastAnalyzedAt == null ? 0 : 1)
|
||||
.ThenBy(t => t.LastAnalyzedAt)
|
||||
.Select(t => t.Id)
|
||||
.Take(500) // Limit batch size to prevent long-running loops without save
|
||||
.ToListAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("Found {Count} active or unanalyzed traders to update", traderIds.Count);
|
||||
if (traderIds.Count > 0)
|
||||
{
|
||||
_logger.LogInformation("Found {Count} active or unanalyzed traders to update", traderIds.Count);
|
||||
}
|
||||
|
||||
foreach (var id in traderIds)
|
||||
{
|
||||
@@ -65,13 +83,36 @@ public class TraderAnalyticsWorker : BackgroundService
|
||||
using var traderScope = _services.CreateScope();
|
||||
var pnlEngine = traderScope.ServiceProvider.GetRequiredService<IPositionPnLEngine>();
|
||||
await pnlEngine.RecalculateTraderPositionsAsync(id, ct);
|
||||
|
||||
if (activeJob != null && activeJob.TraderId == id)
|
||||
{
|
||||
using var jobScope = _services.CreateScope();
|
||||
var updateJobRepo = jobScope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
activeJob.Status = Predictalytics.Domain.Enums.JobStatus.Completed;
|
||||
activeJob.CompletedAt = DateTime.UtcNow;
|
||||
await updateJobRepo.UpdateAsync(activeJob, ct);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error recalculating positions/PnL for trader {TraderId}", id);
|
||||
if (activeJob != null && activeJob.TraderId == id)
|
||||
{
|
||||
try {
|
||||
using var jobScope = _services.CreateScope();
|
||||
var updateJobRepo = jobScope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
activeJob.Status = Predictalytics.Domain.Enums.JobStatus.Failed;
|
||||
activeJob.CompletedAt = DateTime.UtcNow;
|
||||
activeJob.ErrorMessage = ex.Message;
|
||||
await updateJobRepo.UpdateAsync(activeJob, CancellationToken.None);
|
||||
} catch { /* Ignore secondary errors */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation("Trader analytics update complete.");
|
||||
if (traderIds.Count > 0)
|
||||
{
|
||||
_logger.LogInformation("Trader analytics update complete.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user