Scaling PartyKit servers with Hibernation
PartyKit simplifies building realtime multiplayer applications. However, as your app grows, maintaining WebSocket connections requires a lot of memory. Hibernation API is a pathway for scaling your app to tens of thousands of connections. It is done by offloading the memory burden from the room process to the PartyKit platform.
This page provides an overview of the Hibernation API — what it is, how it works, when it’s useful, and how to implement it.
Opting into Hibernation
Benefits
Hibernation allows a single room to handle vastly more active connections than it otherwise would:
- Without Hibernation: up to 100 connections per room
- With Hibernation: up to 32,000 connections per room
Note that even with Hibernation, the practical maximum amount of connections may vary depending on the performance characteristics of the code you run on PartyKit (for example, memory use or CPU time). In PartyKit apps, memory use depends on:
- active connections,
- code size — for example, third-party libraries,
- state — class fields you keep in memory between requests,
- dynamic allocations — memory used by JavaScript for local variables, execution stack, and others.
How Hibernation works
By default, PartyKit keeps the Party.Server
instance in memory as long as there are connected WebSockets.
With Hibernation, the party hibernates (goes to sleep) when it’s not actively handling messages. This means that the Party.Server
instance is unallocated by the platform. However, the open connections to clients are still maintained and the clients don’t notice anything. As soon as a client sends a message, a new Server is instantiated and the constructor and onStart
callback are executed again.
This process can happen quite frequently. With no messages from any client, alarms, or other background processes keeping the room alive, hibernation may be triggered even after a few seconds.
Who is it for
Hibernation is well-suited for the following use cases:
- A party will handle more than 100 connected clients simultaneously.
- A party will have infrequent writes (when clients rarely send messages), which will lower the usage cost.
- A party is based on HTTP-only writes, when WebSockets are used solely for pushing messages to clients (for example, webhooks).
- You should always opt into hibernation when you don’t need to maintain in-memory state between messages. This will lower the cost as the party is unloaded from memory when it’s not being used. A good example here is message passing between clients like a relay server.
Who is it not for
Hibernation does not perform well in the following cases:
- If your server depends for message handling on state that is expensive to recreate (for example, when it includes an API call to an external service to fetch data),
- If you’re building with
Yjs
asy-partykit
doesn’t currently support Hibernation. (We are working on this.)
Limitations
While using Hibernation, please note that:
- Attaching event handlers manually in
onConnect
will not work. As soon as the party hibernates, these handlers are lost. UseonMessage
andonClose
instead. - The local
partykit dev
development environment does not hibernate. This means the behaviour of the code while developing can be different from behaviour on the hosted platform. (We are working on this.)
To better understand the limitations, check the Cloudflare documentation.
Patterns
Hibernation will work better with certain programming patterns.
Partial state loading
Instead of loading the full state from storage in the onStart
lifecycle method, only read from storage the data you need, and only when you need it. Additionally, store state under multiple keys instead of one.
If your Server instance requires state, you will need to persist it to storage, an external database, or an API, and reload it when the party is woken up from hibernation.
If loading the state is expensive (for example, because of the large amounts of state stored in party storage or a slow API call), message handling may be slower, as the party will have to wait for the state to load before running in the onMessage
callback.