Repository navigation
Conversation
Long-poll request failures currently close the socket and force channels to rejoin, even when the server session is still alive. This change retries interrupted requests so channels can stay joined and outgoing pushes can be buffered during recovery. The server retains message batches until acknowledged and deduplicates retried pushes using sequence numbers. The client exposes `socket.onSuspend` and `socket.onResume` callbacks for recovery status; push timeouts still apply. If the session expires, or an older server is encountered during a rolling deploy, the client falls back to the existing reconnect behavior.
Long polling acknowledged batches of messages, which ties the bookkeeping to how long polling groups messages. Both directions now number individual messages, so another transport can resume a session in the future. The long-poll server becomes Phoenix.Transports.Session. It keeps the messages the client did not acknowledge, sends all of them with the number of the last one, and the client drops the ones it already received. Incoming messages must arrive in order, as a gap means that messages were lost. A poll after a failed one resumes the session, which invokes the new optional Phoenix.Socket.resume/3 callback with the params and connect_info of the new request. Returning :error stops the socket, so the client connects again.
With the new `:resumable` WebSocket option, clients that opt in with the `resumable: true` option of the phoenix.js Socket resume their socket over a new WebSocket after the connection failed, without rejoining channels. The socket runs in a Phoenix.Transports.Session, which long polling uses as well. The WebSocket process attaches to it as a pipe. Both sides number their messages like long polling, keep them until the other side confirmed them, and send the missed ones again after resuming. The client resumes with the token of the session and the number of the last message it received, which invokes Phoenix.Socket.resume/3. The new ResumableWebSocket transport resumes the session with backoff, reports interruptions with onSuspend and onResume, and resumes instead of reconnecting when the heartbeat times out. When the session is gone, or the server does not support resuming, it closes so the socket connects again.
A socket that started over a WebSocket can now continue with long polling, and the other way around, as both transports resume the same session with the same message numbers. On the server, a resume detaches the WebSocket of the session, so a client can switch to long polling. Polls the client gave up on when it resumed over a WebSocket expire instead of taking its messages. Long polling sends binary messages in base64 with a "base64:" prefix, which a session can have when it started over a WebSocket. On the client, ResumableWebSocket falls back to long polling when it cannot resume the session over a WebSocket within the longPollFallbackMs option of the socket, for example when a proxy stops allowing WebSockets. It hands the session to a LongPoll, which resumes it with the token and the number of the last message received, and sends the messages the server did not confirm. The socket keeps its connection and channels stay joined.
The comment described a Phoenix 1.8 session receiving a resume, which cannot happen, as clients only resume sessions that numbered their messages. The timeout fires when the session is slow to check the resume. Polling anyway is safe, as the session handles the resume before the flush that follows, so a rejected resume never delivers messages.
The pipe and the session closed WebSockets with bare RFC 6455 codes, which hid why they close. Both now use a map of named codes, and the client uses a shared CLOSE_SESSION_GONE constant for the code that makes the socket connect again when a session is gone.
WebSock.terminate/2 is optional, and the pipe has nothing to clean up, as the session monitors it.
The code they cover already has TODOs for Phoenix 1.10, so the tests point to them as well.
Transports found the session by the pid in the token alone. After a session stopped, another session can get its pid, so an old token could resume it. A token is valid for 2 weeks, and on nodes without distribution, pids look the same after a restart. Clients that resume now send the random topic of the token with :resume and :pipe_attach. A session rejects a topic that is not its own but keeps running for its own client. The WebSocket pipe also requires the endpoint id of the token for pids of its own node, like long polling does. Long polling keeps the subscribe of Phoenix 1.8 for its other requests, so newer nodes and Phoenix 1.8 sessions keep working together during a deploy.
This branch has not been deployed
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.
Works upon #6885 and generalizes it into a
Phoenix.Transports.Session, such that websockets are resumable if configured with(and enabled on the client)
Longpoll is resumable by default.