fix: stop stranding the Telegram poll loop on every settings save - #12
Merged
Merged
Conversation
Start() replaced the service's stopCh field while the running loop read that field on every iteration, so a loop never observed its own channel being closed: each settings save (Stop(); Start()) left one goroutine parked in a 20 s getUpdates long poll next to a fresh poller, which is what produced the 409 conflicts. A poll run now owns its context, stop channel and completion channel, and the loop receives the run as an argument instead of reading a service field. Stop() waits for the loop to actually exit and is idempotent; an in-flight long poll is aborted through the run's context instead of being waited out; and the 500 ms sleep in Reconfigure() is gone, because it was a guess that did not hold while the loop sat in a long poll. Verified: three new tests fail against the previous implementation (Stop() returned but 1 poll loop was still parked in getUpdates) and pass with it; backend suite 6/6 packages.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The Telegram long-poll loop now owns the channel it watches instead of re-reading a service field:
Start()builds a fresh poll run (context, stop channel, cancel, completion channel) and hands it to the loop as an argument;Stop()detaches the run under the mutex, closes its channel, cancels its context — which aborts an in-flightgetUpdateslong poll instead of waiting it out — and returns only after the loop goroutine has exited. It is idempotent, so a double stop cannot panic on a closed channel;Reconfigure()isStop(); Start()again without the 500 ms sleep, because that sleep was a guess that did not hold while the loop sat in a 20 s poll.Why
Start()reassignedstopChwhile the running loop read the field on every iteration, so a loop could never observe its own channel closing. SinceReconfigure()runs on every settings save, each save stranded one goroutine (plus its HTTP buffers) parked in agetUpdateslong poll next to a fresh poller — which is also what produces the HTTP 409 "terminated by other getUpdates request" conflicts, since Telegram serves one consumer per bot token.Verification
Three new tests use an
httpteststand-in that parks every long poll and counts how many polls from this process are parked at once — more than one means a previous loop is still alive:Stop() returned but 1 poll loop(s) are still parked in getUpdates — the loop did not exit(TestTelegramBot_StopIsIdempotent, and the reconfigure/termination tests with it);Reconfigure()leaves exactly one loop,Stop()terminates promptly, and a secondStop()is a no-op.Full backend suite green (6/6 packages) on
masterplus only this change, checked in a separate worktree so unrelated work in progress could not mask a failure. Frontend untouched.