-
Notifications
You must be signed in to change notification settings - Fork 10.4k
[DO] Update WebSocket Hibernation example #17670
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Deploying cloudflare-docs with
|
| Latest commit: |
53c779a
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://f4a32392.cloudflare-docs-7ou.pages.dev |
| Branch Preview URL: | https://update-do-examples.cloudflare-docs-7ou.pages.dev |
|
Files with changes (up to 15) |
| ws.close(code, "Durable Object is closing WebSocket"); | ||
| } | ||
| // Keep track of all WebSocket connections | ||
| sessions = new Map(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is lost every time we hibernate FYI. You would probably want to reconstruct it whenever the constructor() runs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What Milan said, but you probably want something like
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.sessions = new Map();
this.ctx.getWebSockets().forEach((ws) => {
const { ...session } = ws.deserializeAttachment();
this.sessions.set(ws, { ...session });
});
}
You'll also have to ws.serializeAttachment every time that you update sessions.
| // (run the `constructor`) and deliver the message to the appropriate handler. | ||
| this.ctx.acceptWebSocket(server); | ||
|
|
||
| // Keep a copy of value in memory to survive hibernation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused by this comment? Hibernation means evicting and losing all in-memory state. When the DO "hibernates" and then comes back, this.sessions is set to {}.
|
|
||
| async webSocketMessage(sender, message) { | ||
| // Upon receiving a message, get the session associated with the WebSocket connection. | ||
| const session = this.sessions.get(sender); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Every time we hibernate and then receive a new message, this.sessions.get(sender) will not return the associated session because this.sessions is lost whenever we hibernate.
| // Upon receiving a message, get the session associated with the WebSocket connection. | ||
| const session = this.sessions.get(sender); | ||
|
|
||
| // If it is a new connection, generate a new ID for the session. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per above comment, this doesn't actually happen on a "per connection basis". Since this.sessions is cleared whenever we're evicted, the session.id will always not exist the first time we get a message on a pre-existing connection.
I think what you really want to do is something more like:
- When we receive a message, check the
sessionsmap. If we have an associated session there, great, use that. - If we don't have a session, check if we've already serialized something on the WS, i.e.
sender.deserializeAttachment()and check if we have an ID, if we do, create the session and set it onsessionsthis.sessions.set(sender, sendersID);.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, in the DOs constructor() you could just do something like this.ctx.getWebSockets() and then for each load it into sessions.
| ); | ||
|
|
||
| // Send a message to all WebSocket connections, loop over all the connected WebSockets. | ||
| this.ctx.getWebSockets().forEach((ws) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just FYI calling getWebSockets() will unhibernate all your websocket connections. In other words, when the DO hibernates, all connections are also "hibernated", and when the DO wakes back up, we only load your connections back into memory when you need them.
This means that if you call getWebSockets() (bc you wanna broadcast), you load all your WS connections back into memory. If you don't broadcast, and instead only reply to the WS that sent the DO a message, then only that DO is brought back into DO memory.
I don't think we actually explain this well in the docs, but yeah, just something you might wanna know!
| // If it is a new connection, generate a new ID for the session. | ||
| if (!session.id) { | ||
| session.id = crypto.randomUUID(); | ||
| sender.serializeAttachment({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You probably want to actually set the session.id when you first create the websocket in fetch().
|
I think we should make the same changes for the non-hibernation example. New devs might look at regular WS example, then see the Hibernation example and be hesitant to use hibernation because it looks significantly more complicated. If we don't do that, we should at least modify the description of the example, because currently it says "This example is similar to the Build a WebSocket server example, but uses the WebSocket Hibernation API". |
Browser cache needs to be disabled if it's an Edge Cache issue.
* typo * Update src/content/changelogs/kv.yaml * Update src/content/changelogs/kv.yaml
Rename: - Component: CompatibilityDates -> CompatibilityFlags - Files - Collection: compatibility-dates -> compatibility-flags - Schema: CompatibilityDatesSchema -> CompatibilityFlagsSchema Update: - CODEOWNERS - Redirect: https://developers.cloudflare.com/workers/platform/compatibility-dates.json -> compatibility-flags.json
* adding china network video to china product page * updating text on homepage
Summary
This example adds the
serializeAttachmentanddeserialzieAttachemtAPIs to the existing example. It also shows how to send messages with the following condition: