Skip to content

fix(telegram): prevent polling hangs with concurrent processing and stall-aware media downloads#284

Closed
badgerbees wants to merge 1 commit into
nextlevelbuilder:mainfrom
badgerbees:fix/telegram-media-stalls
Closed

fix(telegram): prevent polling hangs with concurrent processing and stall-aware media downloads#284
badgerbees wants to merge 1 commit into
nextlevelbuilder:mainfrom
badgerbees:fix/telegram-media-stalls

Conversation

@badgerbees

Copy link
Copy Markdown
Contributor

Problem

The Telegram integration was vulnerable to "deadlock" scenarios where a single slow operation specifically a large or stalled media download would block the entire polling loop, making the bot unresponsive to all users.

Why it matters

In production environments, network jitter or large files (voice notes, images) from one user could effectively crash the bot's availability for everyone else for up to 5 minutes (the default download timeout).

What changed:

  1. Concurrent Message Processing: Refactored the Telegram polling loop in internal/channels/telegram/channel.go to process incoming updates in parallel goroutines. This ensures that slow processing of one message never blocks the receipt of subsequent ones.
  2. Stall-Aware Downloads: Introduced a progressReader in internal/channels/telegram/media.go that monitors the download stream. It now includes a 60-second stall watchdog; if no data is received within this window, the specific download is aborted early, even if the global 5-minute timeout hasn't been reached.
  3. Graceful Cancellation: All child contexts for media downloading are correctly tied to the bot's lifecycle, ensuring clean cleanup when the bot stops.

@viettranx

viettranx commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Thanks for identifying and analyzing this polling hang issue — the idea of using concurrent goroutines for handlers and a progressReader for stall detection was spot on!

We've addressed this in commit 670b295 on main, with some additional improvements building on your PR:

  • Bounded concurrency: Semaphore (max 20) + WaitGroup instead of unbounded go — prevents OOM under burst
  • Graceful shutdown: Stop() waits for handler goroutines to drain (15s timeout)
  • progressReader fix: if n > 0 instead of if n > 0 && err == nil (correct per io.Reader spec), removed unused ctx param
  • Media error feedback: When a file is too large or download fails → notifies both the model (annotated content tag) and the user (Telegram reply)

Thanks for the contribution! 🙏

@viettranx viettranx closed this Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants