diff --git a/public/__redirects b/public/__redirects index 82ecccc6e16b91f..a20acaff46ee7ba 100644 --- a/public/__redirects +++ b/public/__redirects @@ -492,6 +492,8 @@ /durable-objects/get-started/video-series/serverless-websocket/ /durable-objects/video-tutorials/ 301 /durable-objects/get-started/video-series/ /durable-objects/video-tutorials/ 301 +/durable-objects/what-are-durable-objects/ /durable-objects/concepts/what-are-durable-objects/ 301 + # email-routing /email-routing/enable-email-routing/ /email-routing/get-started/enable-email-routing/ 301 /email-routing/get-started/email-addresses/ /email-routing/setup/email-routing-addresses/ 301 diff --git a/src/assets/images/durable-objects/lifecycle-of-a-do.png b/src/assets/images/durable-objects/lifecycle-of-a-do.png new file mode 100644 index 000000000000000..7a4c368aaafc99c Binary files /dev/null and b/src/assets/images/durable-objects/lifecycle-of-a-do.png differ diff --git a/src/content/changelog/durable-objects/2025-04-07-durable-objects-free-tier.mdx b/src/content/changelog/durable-objects/2025-04-07-durable-objects-free-tier.mdx index 54561cb0b766741..25605a1e9e060bd 100644 --- a/src/content/changelog/durable-objects/2025-04-07-durable-objects-free-tier.mdx +++ b/src/content/changelog/durable-objects/2025-04-07-durable-objects-free-tier.mdx @@ -45,5 +45,5 @@ Free plan [limits](/durable-objects/platform/pricing/) apply to Durable Objects For more information, checkout: -- [Documentation](/durable-objects/what-are-durable-objects/) +- [Documentation](/durable-objects/concepts/what-are-durable-objects/) - [Zero-latency SQLite storage in every Durable Object blog](https://blog.cloudflare.com/sqlite-in-durable-objects/) diff --git a/src/content/changelog/durable-objects/2025-06-25-actors-package-alpha.mdx b/src/content/changelog/durable-objects/2025-06-25-actors-package-alpha.mdx index 55fd6d468528de2..6253d63af7f385b 100644 --- a/src/content/changelog/durable-objects/2025-06-25-actors-package-alpha.mdx +++ b/src/content/changelog/durable-objects/2025-06-25-actors-package-alpha.mdx @@ -11,7 +11,7 @@ The new [@cloudflare/actors](https://www.npmjs.com/package/@cloudflare/actors) l The `@cloudflare/actors` library is a new SDK for Durable Objects and provides a powerful set of abstractions for building real-time, interactive, and multiplayer applications on top of Durable Objects. With beta uasge and feedback, `@cloudflare/actors` will become the recommended way to build on Durable Objects and draws upon Cloudflare's experience building products/features on Durable Objects. -The name "actors" originates from the [actor programming model](/durable-objects/what-are-durable-objects/#actor-programming-model), which closely ties to how Durable Objects are modelled. +The name "actors" originates from the [actor programming model](/durable-objects/concepts/what-are-durable-objects/#actor-programming-model), which closely ties to how Durable Objects are modelled. The `@cloudflare/actors` library includes: @@ -28,7 +28,7 @@ import { Storage } from "@cloudflare/actors/storage"; export class ChatRoom extends DurableObject { storage: Storage; - + constructor(ctx: DurableObjectState, env: Env) { super(ctx, env) this.storage = new Storage(ctx.storage); @@ -50,7 +50,7 @@ export class ChatRoom extends DurableObject { } ``` -`@cloudflare/actors` library introduces the `Actor` class pattern. `Actor` lets you access Durable Objects without writing the Worker that communicates with your Durable Object (the Worker is created for you). By default, requests are routed to a Durable Object named "default". +`@cloudflare/actors` library introduces the `Actor` class pattern. `Actor` lets you access Durable Objects without writing the Worker that communicates with your Durable Object (the Worker is created for you). By default, requests are routed to a Durable Object named "default". ```js export class MyActor extends Actor { diff --git a/src/content/changelog/workers/2025-05-14-python-worker-durable-object.mdx b/src/content/changelog/workers/2025-05-14-python-worker-durable-object.mdx index 3beb3bac2e54c23..dac12e020d79dfe 100644 --- a/src/content/changelog/workers/2025-05-14-python-worker-durable-object.mdx +++ b/src/content/changelog/workers/2025-05-14-python-worker-durable-object.mdx @@ -11,9 +11,9 @@ import { WranglerConfig } from "~/components"; You can now create [Durable Objects](/durable-objects/) using [Python Workers](/workers/languages/python/). A Durable Object is a special kind of -Cloudflare Worker which uniquely combines compute with storage, enabling stateful +Cloudflare Worker which uniquely combines compute with storage, enabling stateful long-running applications which run close to your users. For more info see -[here](https://developers.cloudflare.com/durable-objects/what-are-durable-objects/). +[here](/durable-objects/concepts/what-are-durable-objects/). You can define a Durable Object in Python in a similar way to JavaScript: @@ -26,7 +26,7 @@ class MyDurableObject(DurableObject): def __init__(self, ctx, env): self.ctx = ctx self.env = env - + def on_fetch(self, request): result = self.ctx.storage.sql.exec("SELECT 'Hello, World!' as greeting").one() return Response(result.greeting) diff --git a/src/content/docs/agents/platform/limits.mdx b/src/content/docs/agents/platform/limits.mdx index 059bc3b4cac1d76..c680f4919ad9672 100644 --- a/src/content/docs/agents/platform/limits.mdx +++ b/src/content/docs/agents/platform/limits.mdx @@ -22,7 +22,7 @@ Many limits are inherited from those applied to Workers scripts and/or Durable O --- -[^1]: Yes, really. You can have tens of millions of Agents running concurrently, as each Agent is mapped to a [unique Durable Object](/durable-objects/what-are-durable-objects/) (actor). +[^1]: Yes, really. You can have tens of millions of Agents running concurrently, as each Agent is mapped to a [unique Durable Object](/durable-objects/concepts/what-are-durable-objects/) (actor). [^2]: You can deploy up to [500 scripts per account](/workers/platform/limits/), but each script (project) can define multiple Agents. Each deployed script can be up to 10 MB on the [Workers Paid Plan](/workers/platform/pricing/#workers) [^3]: Compute (CPU) time per Agent is limited to 30 seconds, but this is refreshed when an Agent receives a new HTTP request, runs a [scheduled task](/agents/api-reference/schedule-tasks/), or an incoming WebSocket message. diff --git a/src/content/docs/durable-objects/concepts/durable-object-lifecycle.mdx b/src/content/docs/durable-objects/concepts/durable-object-lifecycle.mdx new file mode 100644 index 000000000000000..343497a01aeb5a2 --- /dev/null +++ b/src/content/docs/durable-objects/concepts/durable-object-lifecycle.mdx @@ -0,0 +1,77 @@ +--- +title: Lifecycle of a Durable Object +pcx_content_type: concept +sidebar: + order: 3 + +--- + +import { Render, TypeScriptExample } from "~/components"; + +This section describes the lifecycle of a [Durable Object](/durable-objects/concepts/what-are-durable-objects/). + +To use a Durable Object you need to create a [Durable Object Stub](/durable-objects/api/stub/). In its simplest form, this looks like the following snippet: + +```ts +// Assume a DurableObjectNamespace binding MY_DURABLE_OBJECT +// Every unique ID refers to an individual instance of the Durable Object class +const id = env.MY_DURABLE_OBJECT.idFromName("foo"); +const stub = env.MY_DURABLE_OBJECT.get(id); +``` + +Once we have the Durable Object Stub, we can now invoke methods on the Durable Object. Note that the above two lines do not yet send any request to the remote Durable Object. + +The following line invokes the `sayHello()` method (which is an [RPC method](/durable-objects/best-practices/create-durable-object-stubs-and-send-requests/#invoke-rpc-methods)) of the Durable Object class bound to the `MY_DURABLE_OBJECT` binding: + +```ts +// All invoked methods need to be awaited. +const rpcResponse = await stub.sayHello(); +``` + +At this point, the caller sends a request to the Durable Object identified by the stub. The lifecycle of the Durable Object begins. + +## Durable Object Lifecycle state transitions + +A Durable Object can be in one of the following states at any moment: + +| State | Description | +|--------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Active, in-memory** | The Durable Object runs, in memory, and handles incoming requests. | +| **Idle, in-memory non-hibernateable** | The Durable Object waits for the next incoming request/event, but does not satisfy the criteria for hibernation. | +| **Idle, in-memory hibernateable** | The Durable Object waits for the next incoming request/event and satisfies the criteria for hibernation. It is up to the runtime to decide when to hibernate the Durable Object. Currently, it is after 10 seconds of inactivity while in this state. | +| **Hibernated** | The Durable Object is removed from memory. Hibernated WebSocket connections stay connected. | +| **Inactive** | The Durable Object is completely removed from the host process and might need to cold start. This is the initial state of all Durable Objects. | + +This is how a Durable Object transitions among these states (each state is in a rounded rectangle). + +![Lifecycle of a Durable Object](~/assets/images/durable-objects/lifecycle-of-a-do.png) + +Assuming a Durable Object does not run, the first incoming request or event (like an alarm) will execute the `constructor()` of the Durable Object class, then run the corresponding function invoked. + +At this point the Durable Object is in the **active in-memory state**. + +If it continuously receives requests or events within 10 seconds of each other, the Durable Object will remain in this state. + +After 10 seconds of no incoming request or events, the runtime can now hibernate the Durable Object. Hibernation will only occur if **all** of the below are true: + - No `setTimeout`/`setInterval` scheduled callbacks are set. + - No in-progress `fetch()` waiting for a remote request exists. + - No WebSocket standard API is used. + - No request/event is still being processed. + +If all conditions are met, the Durable Object will transition into a **hibernated** state. + +:::caution +When hibernated, the in-memory state is discarded, so ensure you persist all important information in the Durable Object's storage. +::: + +If any of the above conditions are false, the Durable Object remains in-memory, in the **idle, in-memory, non-hibernateable** state. + +In case of an incoming request or event while in the **hibernated** state, the `constructor()` will run again, and the corresponding function invoked will run. + +While in the **idle, in-memory, non-hibernateable** state, after 70-140 seconds of inactivity (no incoming requests or events), the Durable Object will be evicted entirely from memory and potentially from the Cloudflare host and transition to the **inactive** state. + +Objects in the **hibernated** state keep their Websocket clients connected, and the runtime decides if and when to move the object to a different host, thus restarting the lifecycle. + +The next incoming request or event starts the cycle again. + +As explained in [When does a Durable Object incur duration charges?](/durable-objects/platform/pricing/#when-does-a-durable-object-incur-duration-charges), a Durable Object incurs charges only when it is **actively running in-memory**, or when it is **idle in-memory and non-hibernateable**, the two states highlighted in red in the above diagram. \ No newline at end of file diff --git a/src/content/docs/durable-objects/concepts/index.mdx b/src/content/docs/durable-objects/concepts/index.mdx new file mode 100644 index 000000000000000..4eb7b34769063cc --- /dev/null +++ b/src/content/docs/durable-objects/concepts/index.mdx @@ -0,0 +1,12 @@ +--- +title: Concepts +pcx_content_type: navigation +sidebar: + order: 3 + group: + hideIndex: true +--- + +import { DirectoryListing } from "~/components"; + + diff --git a/src/content/docs/durable-objects/what-are-durable-objects.mdx b/src/content/docs/durable-objects/concepts/what-are-durable-objects.mdx similarity index 100% rename from src/content/docs/durable-objects/what-are-durable-objects.mdx rename to src/content/docs/durable-objects/concepts/what-are-durable-objects.mdx diff --git a/src/content/docs/durable-objects/get-started.mdx b/src/content/docs/durable-objects/get-started.mdx index 98b5ec1da58b608..8d307aa9afda65c 100644 --- a/src/content/docs/durable-objects/get-started.mdx +++ b/src/content/docs/durable-objects/get-started.mdx @@ -14,7 +14,7 @@ This guide will instruct you through: - Instantiating and communicating with a Durable Object from another Worker. - Deploying a Durable Object and a Worker that communicates with a Durable Object. -If you wish to learn more about Durable Objects, refer to [What are Durable Objects?](/durable-objects/what-are-durable-objects/). +If you wish to learn more about Durable Objects, refer to [What are Durable Objects?](/durable-objects/concepts/what-are-durable-objects/). ## Quick start diff --git a/src/content/docs/durable-objects/index.mdx b/src/content/docs/durable-objects/index.mdx index 1cb686b7b5fe107..8382a31e85cb03b 100644 --- a/src/content/docs/durable-objects/index.mdx +++ b/src/content/docs/durable-objects/index.mdx @@ -34,7 +34,7 @@ SQLite-backed Durable Objects are now available on the Workers Free plan with th -For more information, refer to the full [What are Durable Objects?](/durable-objects/what-are-durable-objects/) page. +For more information, refer to the full [What are Durable Objects?](/durable-objects/concepts/what-are-durable-objects/) page. ***