Restructure solution layout by module

This commit is contained in:
master
2025-10-28 15:10:40 +02:00
parent 95daa159c4
commit d870da18ce
4103 changed files with 192899 additions and 187024 deletions

View File

@@ -0,0 +1,274 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Scheduler.Models;
namespace StellaOps.Scheduler.Queue;
public sealed class PlannerQueueMessage
{
[JsonConstructor]
public PlannerQueueMessage(
Run run,
ImpactSet impactSet,
Schedule? schedule = null,
string? correlationId = null)
{
Run = run ?? throw new ArgumentNullException(nameof(run));
ImpactSet = impactSet ?? throw new ArgumentNullException(nameof(impactSet));
if (schedule is not null && string.IsNullOrWhiteSpace(schedule.Id))
{
throw new ArgumentException("Schedule must have a valid identifier.", nameof(schedule));
}
if (!string.IsNullOrWhiteSpace(correlationId))
{
correlationId = correlationId!.Trim();
}
Schedule = schedule;
CorrelationId = string.IsNullOrWhiteSpace(correlationId) ? null : correlationId;
}
public Run Run { get; }
public ImpactSet ImpactSet { get; }
public Schedule? Schedule { get; }
public string? CorrelationId { get; }
public string IdempotencyKey => Run.Id;
public string TenantId => Run.TenantId;
public string? ScheduleId => Run.ScheduleId;
}
public sealed class RunnerSegmentQueueMessage
{
private readonly ReadOnlyCollection<string> _imageDigests;
private readonly IReadOnlyDictionary<string, string> _attributes;
[JsonConstructor]
public RunnerSegmentQueueMessage(
string segmentId,
string runId,
string tenantId,
IReadOnlyList<string> imageDigests,
string? scheduleId = null,
int? ratePerSecond = null,
bool usageOnly = true,
IReadOnlyDictionary<string, string>? attributes = null,
string? correlationId = null)
{
if (string.IsNullOrWhiteSpace(segmentId))
{
throw new ArgumentException("Segment identifier must be provided.", nameof(segmentId));
}
if (string.IsNullOrWhiteSpace(runId))
{
throw new ArgumentException("Run identifier must be provided.", nameof(runId));
}
if (string.IsNullOrWhiteSpace(tenantId))
{
throw new ArgumentException("Tenant identifier must be provided.", nameof(tenantId));
}
SegmentId = segmentId;
RunId = runId;
TenantId = tenantId;
ScheduleId = string.IsNullOrWhiteSpace(scheduleId) ? null : scheduleId;
RatePerSecond = ratePerSecond;
UsageOnly = usageOnly;
CorrelationId = string.IsNullOrWhiteSpace(correlationId) ? null : correlationId;
_imageDigests = new ReadOnlyCollection<string>(NormalizeDigests(imageDigests));
_attributes = attributes is null
? EmptyReadOnlyDictionary<string, string>.Instance
: new ReadOnlyDictionary<string, string>(new Dictionary<string, string>(attributes, StringComparer.Ordinal));
}
public string SegmentId { get; }
public string RunId { get; }
public string TenantId { get; }
public string? ScheduleId { get; }
public int? RatePerSecond { get; }
public bool UsageOnly { get; }
public string? CorrelationId { get; }
public IReadOnlyList<string> ImageDigests => _imageDigests;
public IReadOnlyDictionary<string, string> Attributes => _attributes;
public string IdempotencyKey => SegmentId;
private static List<string> NormalizeDigests(IReadOnlyList<string> digests)
{
if (digests is null)
{
throw new ArgumentNullException(nameof(digests));
}
var list = new List<string>();
foreach (var digest in digests)
{
if (string.IsNullOrWhiteSpace(digest))
{
continue;
}
list.Add(digest.Trim());
}
if (list.Count == 0)
{
throw new ArgumentException("At least one image digest must be provided.", nameof(digests));
}
return list;
}
private sealed class EmptyReadOnlyDictionary<TKey, TValue>
where TKey : notnull
{
public static readonly IReadOnlyDictionary<TKey, TValue> Instance =
new ReadOnlyDictionary<TKey, TValue>(new Dictionary<TKey, TValue>(0, EqualityComparer<TKey>.Default));
}
}
public readonly record struct SchedulerQueueEnqueueResult(string MessageId, bool Deduplicated);
public sealed class SchedulerQueueLeaseRequest
{
public SchedulerQueueLeaseRequest(string consumer, int batchSize, TimeSpan leaseDuration)
{
if (string.IsNullOrWhiteSpace(consumer))
{
throw new ArgumentException("Consumer identifier must be provided.", nameof(consumer));
}
if (batchSize <= 0)
{
throw new ArgumentOutOfRangeException(nameof(batchSize), batchSize, "Batch size must be positive.");
}
if (leaseDuration <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(leaseDuration), leaseDuration, "Lease duration must be positive.");
}
Consumer = consumer;
BatchSize = batchSize;
LeaseDuration = leaseDuration;
}
public string Consumer { get; }
public int BatchSize { get; }
public TimeSpan LeaseDuration { get; }
}
public sealed class SchedulerQueueClaimOptions
{
public SchedulerQueueClaimOptions(string claimantConsumer, int batchSize, TimeSpan minIdleTime)
{
if (string.IsNullOrWhiteSpace(claimantConsumer))
{
throw new ArgumentException("Consumer identifier must be provided.", nameof(claimantConsumer));
}
if (batchSize <= 0)
{
throw new ArgumentOutOfRangeException(nameof(batchSize), batchSize, "Batch size must be positive.");
}
if (minIdleTime < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(minIdleTime), minIdleTime, "Idle time cannot be negative.");
}
ClaimantConsumer = claimantConsumer;
BatchSize = batchSize;
MinIdleTime = minIdleTime;
}
public string ClaimantConsumer { get; }
public int BatchSize { get; }
public TimeSpan MinIdleTime { get; }
}
public enum SchedulerQueueReleaseDisposition
{
Retry,
Abandon
}
public interface ISchedulerQueue<TMessage>
{
ValueTask<SchedulerQueueEnqueueResult> EnqueueAsync(TMessage message, CancellationToken cancellationToken = default);
ValueTask<IReadOnlyList<ISchedulerQueueLease<TMessage>>> LeaseAsync(SchedulerQueueLeaseRequest request, CancellationToken cancellationToken = default);
ValueTask<IReadOnlyList<ISchedulerQueueLease<TMessage>>> ClaimExpiredAsync(SchedulerQueueClaimOptions options, CancellationToken cancellationToken = default);
}
public interface ISchedulerQueueLease<out TMessage>
{
string MessageId { get; }
int Attempt { get; }
DateTimeOffset EnqueuedAt { get; }
DateTimeOffset LeaseExpiresAt { get; }
string Consumer { get; }
string TenantId { get; }
string RunId { get; }
string? ScheduleId { get; }
string? SegmentId { get; }
string? CorrelationId { get; }
string IdempotencyKey { get; }
IReadOnlyDictionary<string, string> Attributes { get; }
TMessage Message { get; }
Task AcknowledgeAsync(CancellationToken cancellationToken = default);
Task RenewAsync(TimeSpan leaseDuration, CancellationToken cancellationToken = default);
Task ReleaseAsync(SchedulerQueueReleaseDisposition disposition, CancellationToken cancellationToken = default);
Task DeadLetterAsync(string reason, CancellationToken cancellationToken = default);
}
public interface ISchedulerPlannerQueue : ISchedulerQueue<PlannerQueueMessage>
{
}
public interface ISchedulerRunnerQueue : ISchedulerQueue<RunnerSegmentQueueMessage>
{
}