save progress

This commit is contained in:
StellaOps Bot
2026-01-06 09:42:02 +02:00
parent 94d68bee8b
commit 37e11918e0
443 changed files with 85863 additions and 897 deletions

View File

@@ -0,0 +1,179 @@
// <copyright file="HlcSchedulerDequeueService.cs" company="StellaOps">
// Copyright (c) StellaOps. Licensed under AGPL-3.0-or-later.
// </copyright>
using Microsoft.Extensions.Logging;
using StellaOps.HybridLogicalClock;
using StellaOps.Scheduler.Persistence.Postgres.Models;
using StellaOps.Scheduler.Persistence.Postgres.Repositories;
namespace StellaOps.Scheduler.Queue.Hlc;
/// <summary>
/// Implementation of HLC-ordered scheduler job dequeuing.
/// </summary>
public sealed class HlcSchedulerDequeueService : IHlcSchedulerDequeueService
{
private readonly ISchedulerLogRepository _logRepository;
private readonly ILogger<HlcSchedulerDequeueService> _logger;
/// <summary>
/// Creates a new HLC scheduler dequeue service.
/// </summary>
public HlcSchedulerDequeueService(
ISchedulerLogRepository logRepository,
ILogger<HlcSchedulerDequeueService> logger)
{
_logRepository = logRepository ?? throw new ArgumentNullException(nameof(logRepository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <inheritdoc />
public async Task<SchedulerHlcDequeueResult> DequeueAsync(
string tenantId,
int limit,
string? partitionKey = null,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(tenantId);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(limit);
var entries = await _logRepository.GetByHlcOrderAsync(
tenantId,
partitionKey,
limit,
cancellationToken).ConfigureAwait(false);
// Get total count for pagination info
var totalCount = await _logRepository.CountByHlcRangeAsync(
tenantId,
startTHlc: null,
endTHlc: null,
partitionKey,
cancellationToken).ConfigureAwait(false);
_logger.LogDebug(
"Dequeued {Count} of {Total} entries in HLC order. TenantId={TenantId}, PartitionKey={PartitionKey}",
entries.Count,
totalCount,
tenantId,
partitionKey ?? "(all)");
return new SchedulerHlcDequeueResult(
entries,
totalCount,
RangeStartHlc: null,
RangeEndHlc: null);
}
/// <inheritdoc />
public async Task<SchedulerHlcDequeueResult> DequeueByRangeAsync(
string tenantId,
HlcTimestamp? startHlc,
HlcTimestamp? endHlc,
int limit,
string? partitionKey = null,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(tenantId);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(limit);
var startTHlc = startHlc?.ToSortableString();
var endTHlc = endHlc?.ToSortableString();
var entries = await _logRepository.GetByHlcRangeAsync(
tenantId,
startTHlc,
endTHlc,
limit,
partitionKey,
cancellationToken).ConfigureAwait(false);
var totalCount = await _logRepository.CountByHlcRangeAsync(
tenantId,
startTHlc,
endTHlc,
partitionKey,
cancellationToken).ConfigureAwait(false);
_logger.LogDebug(
"Dequeued {Count} of {Total} entries in HLC range [{Start}, {End}]. TenantId={TenantId}",
entries.Count,
totalCount,
startTHlc ?? "(unbounded)",
endTHlc ?? "(unbounded)",
tenantId);
return new SchedulerHlcDequeueResult(
entries,
totalCount,
startHlc,
endHlc);
}
/// <inheritdoc />
public async Task<SchedulerHlcDequeueResult> DequeueAfterAsync(
string tenantId,
HlcTimestamp afterHlc,
int limit,
string? partitionKey = null,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(tenantId);
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(limit);
var afterTHlc = afterHlc.ToSortableString();
var entries = await _logRepository.GetAfterHlcAsync(
tenantId,
afterTHlc,
limit,
partitionKey,
cancellationToken).ConfigureAwait(false);
// Count remaining entries after cursor
var totalCount = await _logRepository.CountByHlcRangeAsync(
tenantId,
afterTHlc,
endTHlc: null,
partitionKey,
cancellationToken).ConfigureAwait(false);
_logger.LogDebug(
"Dequeued {Count} entries after HLC {AfterHlc}. TenantId={TenantId}, PartitionKey={PartitionKey}",
entries.Count,
afterTHlc,
tenantId,
partitionKey ?? "(all)");
return new SchedulerHlcDequeueResult(
entries,
totalCount,
afterHlc,
RangeEndHlc: null);
}
/// <inheritdoc />
public async Task<SchedulerLogEntry?> GetByJobIdAsync(
string tenantId,
Guid jobId,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(tenantId);
var entry = await _logRepository.GetByJobIdAsync(jobId, cancellationToken).ConfigureAwait(false);
// Verify tenant isolation
if (entry is not null && !string.Equals(entry.TenantId, tenantId, StringComparison.Ordinal))
{
_logger.LogWarning(
"Job {JobId} found but belongs to different tenant. RequestedTenant={RequestedTenant}, ActualTenant={ActualTenant}",
jobId,
tenantId,
entry.TenantId);
return null;
}
return entry;
}
}