[ZEPPELIN-6092] Send server-initiated websocket ping frames to keep connections alive - #5432
Open
HwangRock wants to merge 1 commit into
Open
[ZEPPELIN-6092] Send server-initiated websocket ping frames to keep connections alive#5432HwangRock wants to merge 1 commit into
HwangRock wants to merge 1 commit into
Conversation
…onnections alive
Keep-alive is client-driven only: both UIs send an application-level
{"op":"PING"} every 10 seconds and the server never writes first. When the
client timer stops -- a backgrounded tab under Chrome's intensive throttling,
a discarded tab, a sleeping laptop -- nothing resets the idle timer and the
connection dies.
Send a WebSocket protocol ping frame from the server on a schedule. The peer
answers automatically per RFC 6455 section 5.5.2, so no client change is
needed, and writing to the session resets Jetty's idle timeout along with any
intermediate proxy's idle timer.
Also make the idle timeout configurable. setupNotebookServer() never called
setDefaultMaxSessionIdleTimeout(), so the effective value was whatever Jetty
defaulted to -- 300000ms under Jetty 9, 30 seconds under Jetty 11.
Measured with a client that sends nothing after the handshake, against
Jetty 11's 30s default:
before: closed at 30.0s, code=1001 'Connection Idle Timeout'
after: still connected at 120.0s
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 is this PR for?
Zeppelin's websocket keep-alive is client-driven only. Both UIs send an application-level
{"op":"PING"}every 10 seconds (websocket-event.factory.js,message.ts) and the server answers nothing —case PING:inNotebookServer.onMessage()is a barebreak. The server never writes first, so a connection survives only as long as the client's timer keeps firing.That timer is not reliable. Chrome's intensive throttling drops background-tab timers to once per minute once the tab has been hidden for a few seconds, the chain count reaches 5, and no WebRTC is in use — all of which a 10-second
setIntervalsatisfies within a minute. An open websocket is not an exemption; only WebRTC is. A discarded tab or a sleeping laptop stops the timer outright. When it stops, nothing resets the idle timer and the connection dies.This PR has the server send a WebSocket protocol ping frame on a schedule. Per RFC 6455 section 5.5.2 the peer answers with a pong automatically, so no client-side change is required, and writing to the session resets Jetty's idle timeout —
SocketChannelEndPoint.flush()callsIdleTimeout.notIdle()— along with any intermediate proxy's idle timer. This is what the nginx websocket proxying guide recommends as well: "the proxied server can be configured to periodically send WebSocket ping frames to reset the timeout and check if the connection is still alive."The PR also makes the idle timeout itself configurable.
setupNotebookServer()inZeppelinServersets the text message buffer size but never callssetDefaultMaxSessionIdleTimeout(), so the effective value has always been whatever Jetty defaults to. That default is not stable across versions:WebSocketPolicyused 300000ms under Jetty 9, whileWebSocketConstants.DEFAULT_IDLE_TIMEOUTis 30 seconds under Jetty 11. The new key restores an explicit value and gives operators a knob.Two new keys, documented in
zeppelin-site.xml.template,zeppelin-env.sh.template, anddocs/setup/operation/configuration.md:zeppelin.websocket.heartbeat.interval0or negative disables the heartbeat.zeppelin.websocket.idle.timeoutNotebookSocket.sendPing()swallows and logs its exceptions so one dead session cannot break the loop over the others. The scheduler runs on a single daemon thread and starts lazily on the first connection.What type of PR is it?
Improvement
What is the Jira issue?
ZEPPELIN-6092
How should this be tested?
Unit tests cover the ping send path, the disabled-when-non-positive case, isolation of a failing session, and the two configuration keys.
End to end, with a client that sends nothing after the handshake, against Jetty 11's 30-second default idle timeout:
Before
After — same client, same 30-second idle timeout, heartbeat pinned to 10 seconds
Zero
WebSocketTimeoutExceptionin the server log across the 120-second window. The idle timeout is identical between the two runs, so the difference comes from the heartbeat writes alone.To reproduce: set
zeppelin.websocket.idle.timeoutto30000andzeppelin.websocket.heartbeat.intervalto10000, open a websocket to/ws, send nothing after the handshake, and watch for a close past the 30-second mark.Questions:
PINGpath is untouched.