feat: gateway timeout
Summary
While Discordrb has been working flawlessly on my machine, after I moved to another place, it starts seeing errors like tihs frequently:
[WARN : heartbeat @ 2025-10-15 20:45:43.756] Last heartbeat was not acked, so this is a zombie connection! Reconnecting
[ERROR : heartbeat @ 2025-10-15 20:45:43.756] The websocket connection has closed: (no information)
[ERROR : websocket @ 2025-10-15 21:00:53.264] An error occurred in the main websocket loop!
[ERROR : websocket @ 2025-10-15 21:00:53.264] Exception: #<Errno::ETIMEDOUT: Connection timed out>
[ERROR : websocket @ 2025-10-15 21:00:53.264] /usr/local/lib/ruby/3.4.0/openssl/buffering.rb:160:in 'OpenSSL::SSL::SSLSocket#sysread'
[ERROR : websocket @ 2025-10-15 21:00:53.264] /usr/local/lib/ruby/3.4.0/openssl/buffering.rb:160:in 'OpenSSL::Buffering#readpartial'
[ERROR : websocket @ 2025-10-15 21:00:53.264] /usr/local/bundle/gems/discordrb-3.5.0/lib/discordrb/gateway.rb:602:in 'Discordrb::Gateway#websocket_loop'
[ERROR : websocket @ 2025-10-15 21:00:53.264] /usr/local/bundle/gems/discordrb-3.5.0/lib/discordrb/gateway.rb:579:in 'Discordrb::Gateway#connect'
[ERROR : websocket @ 2025-10-15 21:00:53.264] /usr/local/bundle/gems/discordrb-3.5.0/lib/discordrb/gateway.rb:473:in 'block in Discordrb::Gateway#connect_loop'
[ERROR : websocket @ 2025-10-15 21:00:53.264] <internal:kernel>:168:in 'Kernel#loop'
[ERROR : websocket @ 2025-10-15 21:00:53.265] /usr/local/bundle/gems/discordrb-3.5.0/lib/discordrb/gateway.rb:472:in 'Discordrb::Gateway#connect_loop'
[ERROR : websocket @ 2025-10-15 21:00:53.265] /usr/local/bundle/gems/discordrb-3.5.0/lib/discordrb/gateway.rb:168:in 'block in Discordrb::Gateway#run_async'
[INFO : websocket @ 2025-10-15 21:00:53.265] Instant reconnection flag was set - reconnecting right away
[INFO : websocket @ 2025-10-15 21:00:54.008] Discord using gateway protocol version: 9, requested: 9
Notice that the time between the two errors is 15min 10s (and it always is whenever this happens), which is a pretty long time for a Discord bot to be down. This timeout threshold is probably set somewhere in /proc/sys/net/ipv4/tcp_*(and nowhere else sets a timeout, so it will otherwise just hang indefinitely), but I am not sure. Anyway, the workaround that I have come up with requires some modification to Discordrb which I have done in this PR. It should not affect any existing users, but will help in this particular case where the socket becomes zombie before the first hello message.
I am not sure whether the 2s initial timeout I set here is too short or not, but I think it will not normally be exceeded even in very poor network conditions. If it is too short, I can make it to be configurable by an environment variable so that people can set this according to the network condition.
Added
Sets a timeout on the socket connected to Discord gateway.
Changed
Deprecated
Removed
Fixed
Sorry please ignore the clanker it was a misclick on mobile
Sorry I should've run rubocop myself.
Personally, I am a fan of this, but we should make this something that's opt-in. Like the
check_heartbeat_acks=setter
Basically I rewrote the behavior of check_heartbeat_acks. The user opts out by setting it to false.