Repository navigation
Harden the Discord gateway: backoff, resume across restarts, identify budget, fetch upgrade - #65
Merged
Merged
Conversation
… budget Review the DiscordGateway Durable Object against dcartertwo/discord-gateway-cloudflare-do and adopt its reconnect and protocol handling where it fits ragbot (docs/gateway-review.md). - Exponential reconnect backoff with jitter (1 s to 5 min), reset on READY or RESUMED. Cron and the watchdog leave a pending reconnect alone. - Check Discord's session start limit via GET /gateway/bot before every IDENTIFY and wait for the reset when it is spent; exceeding it resets the bot token. Accept only wss gateway URLs under discord.gg. - Persist the resumable session (gatewaySession) so a restart or deploy resumes and Discord replays missed events; processed: markers drop duplicates, so the sequence is flushed on heartbeat ACK. - Close 4003 starts a new session; op 9 waits 1-5 s; first heartbeat is jittered. - One-minute watchdog, hourly marker sweep in 128-key delete batches, and re-arm the watchdog when the alarm handler fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W2eaxgWffaEQUDb3KhteYv
A refused WebSocket handshake now reports its HTTP status instead of looking like close 1006, so the gateway can react to it: - 408, 429 and 5xx back off, respecting Retry-After. - A resume host that refuses the upgrade starts a new session. - Any other refusal stops rapid retries like a fatal close code; cron or /gateway/start retries. The storage update is awaited so the alarm that triggered the connect cannot immediately re-enable it. - Network errors and timeouts back off instead of failing the caller. The runtime probe connects through the real upgrade and checks that a refused handshake surfaces its status and Retry-After across the FFI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W2eaxgWffaEQUDb3KhteYv
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W2eaxgWffaEQUDb3KhteYv
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.
Reviews the
DiscordGatewayDurable Object against dcartertwo/discord-gateway-cloudflare-do and adopts its reconnect and protocol handling where it fits ragbot. The full comparison and decisions are indocs/gateway-review.md.Why
session_start_limit. Exceeding that limit makes Discord reset the bot token.processed:marker sweep could pass more than storage's 128-key delete limit. The resulting exception also killed the watchdog alarm until the next cron run.new WebSocket(), every refused handshake looked like close 1006.Changes
/gateway/startretries immediately.GET /gateway/botbefore every IDENTIFY. When the budget is spent, the gateway waits for the reset. OnlywssURLs underdiscord.ggare accepted.gatewaySession(session ID, resume URL, sequence, bot user ID). A restarted object resumes and Discord replays missed events; the existingprocessed:markers drop duplicates. The sequence is saved on heartbeat ACK, not on every event.fetch()WebSocket upgrade:Retry-After./gateway/startretries.Testing
pnpm run check,pnpm test(134 tests),pnpm run test:runtimeandpywrangler deploy --dry-run.tests/runtime_worker.py) stubs/gateway/bot, so it never contacts Discord. It now also covers:gatewaySessionsurviving the real Durable Object storage round trip;Retry-After.Deployment
Merge, then run
op run --env-file=.env -- pnpm run deploy. On first start, the new code finds nogatewaySession, so it starts a fresh session through the budget check. Restarts after that resume. Afterwards, checkGET /gateway/healthforconnected: trueand smoke-test a mention.🤖 Generated with Claude Code
https://claude.ai/code/session_01W2eaxgWffaEQUDb3KhteYv