using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; namespace StellaOps.Provcache; public sealed partial class WriteBehindQueue { /// public async Task RunAsync(CancellationToken stoppingToken) { _logger.LogInformation( "Write-behind queue started with batch size {BatchSize}, interval {IntervalMs}ms", _options.WriteBehindBatchSize, _options.WriteBehindFlushIntervalMs); var batch = new List(_options.WriteBehindBatchSize); var flushInterval = TimeSpan.FromMilliseconds(_options.WriteBehindFlushIntervalMs); while (!stoppingToken.IsCancellationRequested) { try { batch.Clear(); using var cts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken); cts.CancelAfter(flushInterval); try { while (batch.Count < _options.WriteBehindBatchSize) { var item = await _channel.Reader.ReadAsync(cts.Token).ConfigureAwait(false); batch.Add(item); Interlocked.Decrement(ref _currentQueueDepth); } } catch (OperationCanceledException) when (!stoppingToken.IsCancellationRequested) { // Timeout reached, process current batch } if (batch.Count > 0) { await ProcessBatchAsync(batch, stoppingToken).ConfigureAwait(false); } } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { break; } catch (Exception ex) { _logger.LogError(ex, "Error in write-behind queue processing loop"); await Task.Delay(1000, stoppingToken).ConfigureAwait(false); } } await DrainAsync(stoppingToken).ConfigureAwait(false); _logger.LogInformation("Write-behind queue stopped"); } }