using System.Collections.Concurrent; using System.Collections.Generic; using StellaOps.Scheduler.Models; namespace StellaOps.Scheduler.WebService.GraphJobs; internal sealed class InMemoryGraphJobStore : IGraphJobStore { private readonly ConcurrentDictionary _buildJobs = new(StringComparer.Ordinal); private readonly ConcurrentDictionary _overlayJobs = new(StringComparer.Ordinal); public ValueTask AddAsync(GraphBuildJob job, CancellationToken cancellationToken) { _buildJobs[job.Id] = job; return ValueTask.FromResult(job); } public ValueTask AddAsync(GraphOverlayJob job, CancellationToken cancellationToken) { _overlayJobs[job.Id] = job; return ValueTask.FromResult(job); } public ValueTask GetJobsAsync(string tenantId, GraphJobQuery query, CancellationToken cancellationToken) { var normalized = query.Normalize(); var buildJobs = _buildJobs.Values .Where(job => string.Equals(job.TenantId, tenantId, StringComparison.Ordinal)) .Where(job => normalized.Status is null || job.Status == normalized.Status) .OrderByDescending(job => job.CreatedAt) .Take(normalized.Limit ?? 50) .ToArray(); var overlayJobs = _overlayJobs.Values .Where(job => string.Equals(job.TenantId, tenantId, StringComparison.Ordinal)) .Where(job => normalized.Status is null || job.Status == normalized.Status) .OrderByDescending(job => job.CreatedAt) .Take(normalized.Limit ?? 50) .ToArray(); return ValueTask.FromResult(GraphJobCollection.From(buildJobs, overlayJobs)); } public ValueTask GetBuildJobAsync(string tenantId, string jobId, CancellationToken cancellationToken) { if (_buildJobs.TryGetValue(jobId, out var job) && string.Equals(job.TenantId, tenantId, StringComparison.Ordinal)) { return ValueTask.FromResult(job); } return ValueTask.FromResult(null); } public ValueTask GetOverlayJobAsync(string tenantId, string jobId, CancellationToken cancellationToken) { if (_overlayJobs.TryGetValue(jobId, out var job) && string.Equals(job.TenantId, tenantId, StringComparison.Ordinal)) { return ValueTask.FromResult(job); } return ValueTask.FromResult(null); } public ValueTask> UpdateAsync(GraphBuildJob job, GraphJobStatus expectedStatus, CancellationToken cancellationToken) { if (_buildJobs.TryGetValue(job.Id, out var existing) && string.Equals(existing.TenantId, job.TenantId, StringComparison.Ordinal)) { if (existing.Status == expectedStatus) { _buildJobs[job.Id] = job; return ValueTask.FromResult(GraphJobUpdateResult.UpdatedResult(job)); } return ValueTask.FromResult(GraphJobUpdateResult.NotUpdated(existing)); } throw new KeyNotFoundException($"Graph build job '{job.Id}' not found."); } public ValueTask> UpdateAsync(GraphOverlayJob job, GraphJobStatus expectedStatus, CancellationToken cancellationToken) { if (_overlayJobs.TryGetValue(job.Id, out var existing) && string.Equals(existing.TenantId, job.TenantId, StringComparison.Ordinal)) { if (existing.Status == expectedStatus) { _overlayJobs[job.Id] = job; return ValueTask.FromResult(GraphJobUpdateResult.UpdatedResult(job)); } return ValueTask.FromResult(GraphJobUpdateResult.NotUpdated(existing)); } throw new KeyNotFoundException($"Graph overlay job '{job.Id}' not found."); } public ValueTask> GetOverlayJobsAsync(string tenantId, CancellationToken cancellationToken) { var jobs = _overlayJobs.Values .Where(job => string.Equals(job.TenantId, tenantId, StringComparison.Ordinal)) .ToArray(); return ValueTask.FromResult>(jobs); } }