Skip to content

Conversation

@harshil1712
Copy link
Contributor

Summary

This example adds the serializeAttachment and deserialzieAttachemt APIs to the existing example. It also shows how to send messages with the following condition:

  • Send message to the client
  • Send message to all the connected clients
  • Send message to all the connected clients except sender

@github-actions github-actions bot added size/m product:durable-objects Durable Objects: https://developers.cloudflare.com/workers/learning/using-durable-objects/ labels Oct 21, 2024
@cloudflare-workers-and-pages
Copy link

Deploying cloudflare-docs with  Cloudflare Pages  Cloudflare Pages

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

View logs

@github-actions
Copy link
Contributor

ws.close(code, "Durable Object is closing WebSocket");
}
// Keep track of all WebSocket connections
sessions = new Map();
Copy link
Contributor

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.

Copy link
Contributor

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.
Copy link
Contributor

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);
Copy link
Contributor

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.
Copy link
Contributor

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:

  1. When we receive a message, check the sessions map. If we have an associated session there, great, use that.
  2. 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 on sessions this.sessions.set(sender, sendersID);.

Copy link
Contributor

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) => {
Copy link
Contributor

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({
Copy link
Contributor

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().

@MellowYarker
Copy link
Contributor

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".

cf-scott and others added 16 commits December 3, 2024 20:27
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
@github-actions github-actions bot added product:network-interconnect product:network product:notifications product:page-shield Issues or PRs related to Page Shield product:pages product:pub-sub Pub/Sub: https://developers.cloudflare.com/pub-sub product:pulumi product:queues Cloudflare Queues: https://developers.cloudflare.com/queues product:r2 R2 object storage: https://developers.cloudflare.com/r2 product:radar product:reference-architecture product:registrar Related to Registrar product product:rules Related to rules product:ruleset-engine product:security-center product:spectrum Related to Spectrum product product:speed product:ssl Related to SSL product:stream Related to Stream product product:style-guide product:support product:turnstile product:vectorize Vectorize: https://developers.cloudflare.com/vectorize/ product:waf product:waiting-room Related to Waiting Room product product:warp-client product:workers Related to Workers product product:workers-ai Workers AI: https://developers.cloudflare.com/workers-ai/ product:workflows Workflows: https://developers.cloudflare.com/workflows/ product:zaraz labels Dec 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

product:aegis product:ai-gateway AI Gateway: https://developers.cloudflare.com/ai-gateway/ product:analytics Related to Analytics product product:api-shield product:argo-smart-routing product:automatic-platform-optimization product:bots Related to Bots product product:browser-rendering product:byoip Related to BYOIP product:cache Issues or PRs related to Cache product:calls product:china-network product:cloudflare-for-platforms product:cloudflare-one product:d1 D1: https://developers.cloudflare.com/d1/ product:data-localization product:ddos-protection product:developer-spotlight product:dns Issues or PRs related to DNS product:durable-objects Durable Objects: https://developers.cloudflare.com/workers/learning/using-durable-objects/ product:email-routing product:email-security product:firewall Related to Firewall product product:fundamentals product:health-checks product:hyperdrive Hyperdrive: https://developers.cloudflare.com/hyperdrive/ product:images Related to Image Resizing product product:kv product:learning-paths product:load-balancing Related to Load Balancing product product:logs Related to Logs product:magic-cloud-networking product:magic-transit product:magic-wan product:network product:network-interconnect product:notifications product:page-shield Issues or PRs related to Page Shield product:pages product:pub-sub Pub/Sub: https://developers.cloudflare.com/pub-sub product:pulumi product:queues Cloudflare Queues: https://developers.cloudflare.com/queues product:r2 R2 object storage: https://developers.cloudflare.com/r2 product:radar product:reference-architecture product:registrar Related to Registrar product product:rules Related to rules product:ruleset-engine product:security-center product:spectrum Related to Spectrum product product:speed product:ssl Related to SSL product:stream Related to Stream product product:style-guide product:support product:turnstile product:vectorize Vectorize: https://developers.cloudflare.com/vectorize/ product:waf product:waiting-room Related to Waiting Room product product:warp-client product:workers Related to Workers product product:workers-ai Workers AI: https://developers.cloudflare.com/workers-ai/ product:workflows Workflows: https://developers.cloudflare.com/workflows/ product:zaraz product:1.1.1.1 Related to 1.1.1.1 product size/xl

Projects

None yet

Development

Successfully merging this pull request may close these issues.