Twilio send blocks the event loop — offload to a thread #56

Closed
opened 2026-07-18 05:31:21 +00:00 by claude-bot · 3 comments
Contributor

_send_twilio_sms (app/services/notifiers/sms.py:111-124) calls the
synchronous Twilio SDK on the shared event loop (FastAPI + APScheduler +
Discord + Matrix per the lifespan). Wrap in
await asyncio.to_thread(...) (or switch to an httpx call). Grep for other
synchronous SDK calls while there (TimezoneFinder() init in
resolve_location_nws, scheduler.py:141-143, is also heavyweight on-loop —
instantiate once at module level or offload).

Acceptance criteria:

  • Twilio send no longer executes sync HTTP on the event loop
  • TimezoneFinder is constructed once (or off-loop)

Filed from the 2026-07-17 codebase audit (docs/.internal/report-2026-07-17.md), finding F-17.

`_send_twilio_sms` (app/services/notifiers/sms.py:111-124) calls the synchronous Twilio SDK on the shared event loop (FastAPI + APScheduler + Discord + Matrix per the lifespan). Wrap in `await asyncio.to_thread(...)` (or switch to an httpx call). Grep for other synchronous SDK calls while there (`TimezoneFinder()` init in `resolve_location_nws`, scheduler.py:141-143, is also heavyweight on-loop — instantiate once at module level or offload). **Acceptance criteria:** - [ ] Twilio send no longer executes sync HTTP on the event loop - [ ] TimezoneFinder is constructed once (or off-loop) --- _Filed from the 2026-07-17 codebase audit (`docs/.internal/report-2026-07-17.md`), finding F-17._
Author
Contributor

Done in #99 (merged). _send_twilio_sms now wraps the synchronous Twilio SDK messages.create(...) in await asyncio.to_thread(...), so a slow Twilio call no longer blocks the shared event loop. The voip.ms httpx path was already async and is unchanged.

Done in #99 (merged). `_send_twilio_sms` now wraps the synchronous Twilio SDK `messages.create(...)` in `await asyncio.to_thread(...)`, so a slow Twilio call no longer blocks the shared event loop. The voip.ms httpx path was already async and is unchanged.
Author
Contributor

Follow-up: the second acceptance item (TimezoneFinder constructed once / off-loop, in scheduler.py:resolve_location_nws) was out of scope for the notifier-only Phase A change and is being completed in the Phase B pipeline PR (with #53, which also touches scheduler.py). The Twilio event-loop fix landed here in #99.

Follow-up: the second acceptance item (TimezoneFinder constructed once / off-loop, in `scheduler.py:resolve_location_nws`) was out of scope for the notifier-only Phase A change and is being completed in the Phase B pipeline PR (with #53, which also touches `scheduler.py`). The Twilio event-loop fix landed here in #99.
Author
Contributor

Second acceptance item completed in #100 (merged): TimezoneFinder is now a module-level lazy singleton in scheduler.py instead of being constructed on every resolve_location_nws call. Both acceptance items (Twilio off-loop in #99, TimezoneFinder in #100) are now done.

Second acceptance item completed in #100 (merged): `TimezoneFinder` is now a module-level lazy singleton in `scheduler.py` instead of being constructed on every `resolve_location_nws` call. Both acceptance items (Twilio off-loop in #99, TimezoneFinder in #100) are now done.
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/WeatherBot#56
No description provided.