feat: parallel async processing and compact output mode
Major performance improvements: - Parallel search execution across all queries - Parallel article fetching with 10 concurrent limit - Parallel embeddings with rate limiting (4 concurrent) - Polly integration for retry resilience New features: - Add -v/--verbose flag for detailed output - Compact single-line status mode with braille spinner - StatusReporter service for unified output handling - Query generation and errors hidden in compact mode - ANSI escape codes for clean line updates New files: - Services/RateLimiter.cs - Semaphore-based concurrency control - Services/StatusReporter.cs - Verbose/compact output handler - Models/ParallelOptions.cs - Parallel processing configuration All changes maintain Native AOT compatibility.
This commit is contained in:
128
Services/StatusReporter.cs
Normal file
128
Services/StatusReporter.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace OpenQuery.Services;
|
||||
|
||||
public class StatusReporter : IDisposable
|
||||
{
|
||||
private readonly bool _verbose;
|
||||
private readonly char[] _spinnerChars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
||||
private string? _currentMessage;
|
||||
private CancellationTokenSource? _spinnerCts;
|
||||
private Task? _spinnerTask;
|
||||
private readonly Channel<string> _statusChannel;
|
||||
private readonly Task _statusProcessor;
|
||||
|
||||
public StatusReporter(bool verbose)
|
||||
{
|
||||
_verbose = verbose;
|
||||
_statusChannel = Channel.CreateUnbounded<string>();
|
||||
_statusProcessor = ProcessStatusUpdatesAsync();
|
||||
}
|
||||
|
||||
private async Task ProcessStatusUpdatesAsync()
|
||||
{
|
||||
await foreach (var message in _statusChannel.Reader.ReadAllAsync())
|
||||
{
|
||||
if (_verbose)
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Clear current line using ANSI escape code
|
||||
Console.Write("\r\x1b[K");
|
||||
|
||||
// Write new status with spinner (use first spinner char for static updates)
|
||||
Console.Write($"{_spinnerChars[0]} {message}");
|
||||
|
||||
_currentMessage = message;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateStatus(string message)
|
||||
{
|
||||
_statusChannel.Writer.TryWrite(message);
|
||||
}
|
||||
|
||||
public void ClearStatus()
|
||||
{
|
||||
if (_verbose) return;
|
||||
|
||||
Console.Write("\r\x1b[K");
|
||||
_currentMessage = null;
|
||||
}
|
||||
|
||||
public void WriteFinal(string text)
|
||||
{
|
||||
if (_verbose)
|
||||
{
|
||||
Console.WriteLine(text);
|
||||
return;
|
||||
}
|
||||
|
||||
StopSpinner();
|
||||
Console.Write("\r\x1b[K");
|
||||
Console.Write(text);
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
public void StartSpinner()
|
||||
{
|
||||
if (_verbose || _spinnerCts != null) return;
|
||||
|
||||
_spinnerCts = new CancellationTokenSource();
|
||||
_spinnerTask = Task.Run(async () =>
|
||||
{
|
||||
var spinner = _spinnerChars;
|
||||
var index = 0;
|
||||
while (_spinnerCts is { Token.IsCancellationRequested: false })
|
||||
{
|
||||
if (_currentMessage != null)
|
||||
{
|
||||
Console.Write("\r\x1b[K");
|
||||
var charIndex = index++ % spinner.Length;
|
||||
Console.Write($"{spinner[charIndex]} {_currentMessage}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(100, _spinnerCts.Token);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, _spinnerCts.Token);
|
||||
}
|
||||
|
||||
public void StopSpinner()
|
||||
{
|
||||
if (_spinnerCts == null) return;
|
||||
|
||||
_spinnerCts.Cancel();
|
||||
_spinnerTask?.GetAwaiter().GetResult();
|
||||
_spinnerCts = null;
|
||||
_spinnerTask = null;
|
||||
}
|
||||
|
||||
public void WriteLine(string text)
|
||||
{
|
||||
if (_verbose)
|
||||
{
|
||||
Console.WriteLine(text);
|
||||
return;
|
||||
}
|
||||
|
||||
StopSpinner();
|
||||
ClearStatus();
|
||||
Console.WriteLine(text);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_statusChannel.Writer.Complete();
|
||||
_statusProcessor.GetAwaiter().GetResult();
|
||||
StopSpinner();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user