Skip to content

Commit 5a3758b

Browse files
Narrow try_connect except to the retry-eligible error set
ClusterClient.connect's inner try_connect coroutine previously caught Exception to DEBUG-log each failure and re-raise. That pattern silently instruments programming bugs (TypeError, AttributeError, …) which are better left un-wrapped so their tracebacks point at the real source. Narrow to the transport- and cluster-level types that retry_with_backoff actually re-attempts: OSError, TimeoutError, DqliteConnectionError, ClusterError. Matches the _socket_looks_dead / _drain_idle narrowings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1653dab commit 5a3758b

2 files changed

Lines changed: 25 additions & 1 deletion

File tree

src/dqliteclient/cluster.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,13 @@ async def try_connect() -> DqliteConnection:
242242
)
243243
await conn.connect()
244244
return conn
245-
except Exception as exc:
245+
except (OSError, TimeoutError, DqliteConnectionError, ClusterError) as exc:
246+
# Narrow catch: these are the transport- and cluster-level
247+
# failures the retry loop re-attempts. Anything wider would
248+
# silently log-and-re-raise programming bugs (TypeError,
249+
# AttributeError, …) which are better left un-instrumented
250+
# so the traceback points at the real source. Same pattern
251+
# as the _socket_looks_dead / _drain_idle narrowings.
246252
logger.debug(
247253
"ClusterClient.connect attempt %d/%d failed (leader=%r): %s",
248254
attempt,

tests/test_cluster.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,3 +629,21 @@ async def fake_query(_address: str, **_kwargs: object) -> str | None:
629629
# marker; total upper bound well under the raw 150k.
630630
assert len(message) < 3_000
631631
assert "truncated" in message
632+
633+
634+
class TestTryConnectNarrowExcept:
635+
"""Programming bugs (TypeError, AttributeError, …) in the connect
636+
path must propagate without being muted by the DEBUG-log instrument.
637+
"""
638+
639+
async def test_type_error_propagates(self) -> None:
640+
store = MemoryNodeStore(["localhost:9001"])
641+
client = ClusterClient(store, timeout=0.1)
642+
643+
async def fake_find_leader(**_kwargs: object) -> str:
644+
raise TypeError("programming bug")
645+
646+
client.find_leader = fake_find_leader # type: ignore[method-assign]
647+
648+
with pytest.raises(TypeError, match="programming bug"):
649+
await client.connect(max_attempts=1)

0 commit comments

Comments
 (0)