fix - count circuit breaker failures per logical call NOT per retry + actionable errors and placeholder in lidarr connection test

This commit is contained in:
Harvey 2026-04-03 23:49:50 +01:00
parent d65e1a7971
commit 491947ecab
4 changed files with 24 additions and 9 deletions

View file

@ -244,10 +244,6 @@ def with_retry(
last_exception = e
elapsed_ms = int((time.time() - attempt_start) * 1000)
is_non_breaking = isinstance(e, non_breaking_exceptions) if non_breaking_exceptions else False
if circuit_breaker and (not is_non_breaking or circuit_breaker.state == CircuitState.HALF_OPEN):
await circuit_breaker.arecord_failure()
if attempt >= max_attempts:
total_elapsed_ms = int((time.time() - start_time) * 1000)
logger.error(
@ -295,6 +291,11 @@ def with_retry(
await asyncio.sleep(delay)
if circuit_breaker and last_exception:
is_non_breaking = isinstance(last_exception, non_breaking_exceptions) if non_breaking_exceptions else False
if not is_non_breaking or circuit_breaker.state == CircuitState.HALF_OPEN:
await circuit_breaker.arecord_failure()
raise last_exception
return wrapper

View file

@ -132,10 +132,19 @@ class SettingsService:
root_folders=root_folders
)
except ExternalServiceError as e:
logger.warning(f"Lidarr connection test failed: {e}")
detail = str(e)
logger.warning(f"Lidarr connection test failed: {detail}")
if "No address associated with hostname" in detail or "Name or service not known" in detail:
hint = "DNS resolution failed — check the hostname is reachable from inside the container"
elif "Connection refused" in detail:
hint = "Connection refused — check the port and that Lidarr is running"
elif "timed out" in detail.lower() or "timeout" in detail.lower():
hint = "Connection timed out — check network/firewall settings"
else:
hint = detail
return LidarrVerifyResponse(
success=False,
message="Couldn't reach Lidarr",
message=f"Couldn't reach Lidarr: {hint}",
quality_profiles=[],
metadata_profiles=[],
root_folders=[]
@ -144,7 +153,7 @@ class SettingsService:
logger.exception(f"Failed to verify Lidarr connection: {e}")
return LidarrVerifyResponse(
success=False,
message="Couldn't finish the connection test",
message=f"Couldn't finish the connection test: {e}",
quality_profiles=[],
metadata_profiles=[],
root_folders=[]

View file

@ -68,10 +68,15 @@ async def test_breaking_exception_still_trips_circuit():
call_count += 1
raise _ServiceDown("down")
# Each call records one failure after all retries exhausted (not per retry)
with pytest.raises(_ServiceDown):
await fail()
assert cb.failure_count == 1
assert cb.state == CircuitState.CLOSED
assert call_count == 3
call_count = 0
with pytest.raises(_ServiceDown):
await fail()
assert cb.state == CircuitState.OPEN

View file

@ -82,7 +82,7 @@
type="url"
bind:value={form.data.lidarr_url}
class="input input-bordered w-full"
placeholder="http://localhost:8686"
placeholder="http://lidarr:8686 or http://<host-ip>:8686"
/>
</div>