fix: keep docker ps off the discord.py event loop in status_cog - #313
Conversation
StatusCog.status() is a discord.py command coroutine and it called
subprocess.run(["docker","ps"], timeout=10) directly. On the bot's event
loop that stalls the gateway heartbeat and every other command for the
duration.
Wrap it in asyncio.to_thread. FileNotFoundError, TimeoutExpired and the
generic handler below all still catch, since to_thread re-raises in the
awaiting coroutine.
Verified inside the running broski-bot container (discord.py 2.4.0) by
loading the module and driving StatusCog.status.callback with a fake
context, against the pre-fix code as a control:
control fixed
2s blocking subprocess 1999.9ms 1.7ms
real call, no docker CLI same embed same embed
The real call still returns "Docker CLI not found", proving the exception
path survives the thread hop.
Scope, stated plainly: this cog is NOT loaded. cogs.status_cog is absent
from the COGS list in cogs/bot.py and nothing imports it. The image also
ships no docker CLI, so even if it were loaded the subprocess would fail
fast rather than block. This fix is correctness insurance for whenever
someone wires the cog up -- it is not repairing a live fault.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 14 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Last of the sync-I/O-on-an-async-loop fixes (#309, #310, #311, #312).
The change
StatusCog.status()is a discord.py command coroutine that calledsubprocess.run(["docker","ps"], timeout=10)directly. On the bot's event loop that stalls the gateway heartbeat and every other command with it. Wrapped inasyncio.to_thread.FileNotFoundError,subprocess.TimeoutExpiredand the generic handler below all still catch —to_threadre-raises in the awaiting coroutine.Verification
Run inside the running
broski-botcontainer (discord.py 2.4.0), loading the module and driving the realStatusCog.status.callbackwith a fake context. Same harness against the pre-fix code frommainas a control:❌ Docker CLI not found❌ Docker CLI not foundThe real call producing the identical embed on both sides proves the exception path survives the thread hop.
Scope — stated plainly, because it matters
This cog is not loaded.
cogs.status_cogis absent from theCOGSlist incogs/bot.py, and nothing imports it:The image also ships no docker CLI (
command -v docker→ not found), so even if the cog were loaded, the subprocess would raiseFileNotFoundErrorimmediately rather than block for 10s.So this is correctness insurance for whenever someone wires the cog up. It is not repairing a live fault. I originally reported it as "blocking your live Discord bot" — that was wrong, and it was wrong because an AST sweep flags files, not executed code.
Deliberately NOT changed:
agents/hyperhealth/main.py:111The same sweep flagged
subprocess.run(["alembic","upgrade","head"], timeout=30)there. It is insideasync def lifespan(), andapp = FastAPI(..., lifespan=lifespan)— uvicorn runs lifespan before it accepts connections. Blocking a loop that isn't serving anything costs nothing, andasyncio.to_threadwould change literally nothing. A false positive in practice.But the logs there exposed something real (not fixed here)
The hyperhealth container command is:
--workers 2forks two uvicorn workers, each runninglifespan, soalembic upgrade headruns once per worker — concurrently. Confirmed in the logs: twohyperhealth.startupevents and twomigrations_completedevents per boot.alembic upgrade headonce before uvicorn started. Migrations execute three times per boot.2026-06-24all of them timed out (Command '['alembic','upgrade','head']' timed out after 30 seconds) and were swallowed aslevel: warning. Today's boot succeeded, andalembic currentreports001 (head), so the DB is fine — but the failure mode is silent.|| alembic stamp 001fallback re-stamps the version table on failure, which is exactly the operation the project notes say never to perform against the shared alembic table.Worth its own issue and its own change; not bundled in here.
🤖 Generated with Claude Code