DeutschLernen/GermanApp/Infrastructure/Services/MistralCircuitBreaker.cs
Lasse Rune Hansen 2b11367a97
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
feat(backend/infrastructure): implement Mistral API Connector (Phase 0 of AI Services)
- Add Domain/Interfaces/IMistralConnector.cs with AiServiceException and AiErrorCode enum
- Add Infrastructure/Configuration/MistralConfig.cs with validation
- Add Infrastructure/Services/MistralConnector.cs with HTTP client implementation
- Add Infrastructure/Services/MistralRateLimiter.cs for rate limiting
- Add Infrastructure/Services/MistralCircuitBreaker.cs for circuit breaker pattern
- Add Application/Models/MistralRequest.cs with request models
- Add Application/Models/MistralResponse.cs with response models
- Update Program.cs to register IMistralConnector with MemoryCache, HttpClient, and config
- Update GermanApp.csproj with existing dependencies
- Update docs/features/ai-services.md with Phase 0 completion

Phase 0 of AI Services feature is complete. Mistral API Connector is ready for use by MistralService.

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
2026-06-09 18:56:37 +02:00

125 lines
3.5 KiB
C#

using System;
namespace GermanApp.Infrastructure.Services;
/// <summary>
/// Circuit breaker implementation for Mistral API.
/// Prevents cascading failures by temporarily blocking requests after too many failures.
/// This is part of the Infrastructure layer.
/// </summary>
public class MistralCircuitBreaker
{
private readonly int _failureThreshold;
private readonly TimeSpan _resetTimeout;
private int _failureCount = 0;
private DateTime _lastFailureTime = DateTime.MinValue;
private readonly object _lock = new();
/// <summary>
/// Creates a new circuit breaker.
/// </summary>
/// <param name="failureThreshold">Number of consecutive failures before opening the circuit</param>
/// <param name="resetTimeout">Time to wait before attempting to close the circuit</param>
public MistralCircuitBreaker(int failureThreshold, TimeSpan resetTimeout)
{
_failureThreshold = failureThreshold;
_resetTimeout = resetTimeout;
}
/// <summary>
/// Gets whether the circuit is currently closed (allowing requests).
/// </summary>
public bool IsClosed
{
get
{
if (_failureCount >= _failureThreshold)
{
// Circuit is open, check if reset timeout has elapsed
if (DateTime.UtcNow - _lastFailureTime > _resetTimeout)
{
// Reset the circuit
lock (_lock)
{
if (_failureCount >= _failureThreshold &&
DateTime.UtcNow - _lastFailureTime > _resetTimeout)
{
_failureCount = 0;
_lastFailureTime = DateTime.MinValue;
}
}
}
else
{
return false;
}
}
return true;
}
}
/// <summary>
/// Gets the current failure count.
/// </summary>
public int FailureCount => _failureCount;
/// <summary>
/// Gets the last failure time.
/// </summary>
public DateTime LastFailureTime => _lastFailureTime;
/// <summary>
/// Records a successful request, resetting the failure count.
/// </summary>
public void RecordSuccess()
{
lock (_lock)
{
_failureCount = 0;
_lastFailureTime = DateTime.MinValue;
}
}
/// <summary>
/// Records a failed request, incrementing the failure count.
/// </summary>
public void RecordFailure()
{
lock (_lock)
{
_failureCount++;
_lastFailureTime = DateTime.UtcNow;
}
}
/// <summary>
/// Resets the circuit breaker to its initial state.
/// </summary>
public void Reset()
{
lock (_lock)
{
_failureCount = 0;
_lastFailureTime = DateTime.MinValue;
}
}
/// <summary>
/// Gets the time remaining until the circuit can be reset.
/// Returns TimeSpan.Zero if circuit is closed or already eligible for reset.
/// </summary>
public TimeSpan TimeUntilReset
{
get
{
if (_failureCount < _failureThreshold)
return TimeSpan.Zero;
var elapsed = DateTime.UtcNow - _lastFailureTime;
if (elapsed >= _resetTimeout)
return TimeSpan.Zero;
return _resetTimeout - elapsed;
}
}
}