Skip to content

[DO] Adding explanation on duration billing of a DO. #22023

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

Open
wants to merge 2 commits into
base: production
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/content/docs/durable-objects/api/state.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ export class MyDurableObject extends DurableObject {

### `waitUntil`

`waitUntil` waits until the promise which is passed as a parameter resolves and can extend a <GlossaryTooltip term= "request context">request context</GlossaryTooltip> up to 30 seconds after the last client disconnects.
`waitUntil` waits until the promise which is passed as a parameter resolves and can extend a request context up to 30 seconds after the last client disconnects.

:::note[`waitUntil` is not necessary]

The request context for a Durable Objects extends at least 60 seconds after the last client disconnects. So `waitUntil` is not necessary. It remains part of the `DurableObjectState` interface to remain compatible with [Workers Runtime APIs](/workers/runtime-apis/context/#waituntil).
A Durable Object will automatically remain active for at least 70 seconds after the last client disconnects if the Durable Object is still waiting on any ongoing work or outbound I/O. So `waitUntil` is not necessary. It remains part of the `DurableObjectState` interface to remain compatible with [Workers Runtime APIs](/workers/runtime-apis/context/#waituntil).

:::

Expand Down
12 changes: 11 additions & 1 deletion src/content/docs/durable-objects/platform/pricing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,14 @@ Yes, although minimal. Empty tables can consume at least a few kilobytes, based

All writes to a SQLite-backed Durable Object stores nominal amounts of metadata in internal tables in the Durable Object, which counts towards your billable storage.

The metadata remains in the Durable Object until you call [`deleteAll()`](/durable-objects/api/storage-api/#deleteall).
The metadata remains in the Durable Object until you call [`deleteAll()`](/durable-objects/api/storage-api/#deleteall).

### When does a Durable Object incur duration charges?

A Durable Object incurs duration charges as long as the JavaScript object is held in memory. Once an object has been evicted from memory, the next time it is needed, it will be recreated (calling the constructor again). There are two factors which decide when an object may be evicted from memory: hibernatability and existence of clients.

A Durable Object is considered hibernatable any time that it is not waiting for any I/O or callbacks that depend on the in-memory state. For example, if an object calls `fetch()` and awaits the response, it is considered to be waiting for I/O, and so cannot hibernate. Less-obviously, if the object calls `setTimeout()` to schedule a callback in the future - no matter how far in the future - then it is considered non-hibernatable, since there would be no way to recreate the callback after hibernating. Additionally, if the Durable Object has received an incoming request and has not fully responded yet, then it cannot be hibernated, because hibernating would mean losing track of the async function which is eventually supposed to return a response to that request.

As an exception, a WebSocket request which has explicitly been accepted using the WebSocket hibernation API allows the Durable Object to hibernate even while the WebSocket is still connected. Once a Durable Object has been in a hibernatable state for 10 consecutive seconds, it hibernates, and duration billing stops.

Even if a Durable Object never becomes hibernatable, it will still be evicted once all clients have gone away. A Durable Object is considered to have clients if any other Worker currently holds a stub pointing to the Durable Object, or is waiting for a response from the Durable Object. An incoming WebSocket connection counts as a client. If the object is currently responding to an alarm event, this also counts as having a client. When not hibernatable, a Durable Object will be evicted from memory after it has had no client for 70-140 seconds (the exact interval varies). But again, if the object is hibernatable, then the 10-second hibernation timeout takes precedence and the 70-140 second no-client timeout is moot.
7 changes: 3 additions & 4 deletions src/content/glossary/durable-objects.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@ entries:
general_definition: |-
A Durable Object alarm is a mechanism that allows you to schedule the Durable Object to be woken up at a time in the future.

- term: "request context"
general_definition: |-
The duration of time that a Durable Object is processing a request, such as a remote procedure call. [Compute duration charges](/durable-objects/platform/pricing) are incurred for the duration of the request context. Note that the request context for a Durable Object extends at least 60 seconds after the last client disconnects.

- term: "input gate"
general_definition: |-
While a storage operation is executing, no events shall be delivered to a Durable Object except for storage completion events. Any other events will be deferred until such a time as the object is no longer executing JavaScript code and is no longer waiting for any storage operations. We say that these events are waiting for the "input gate" to open.
Expand All @@ -76,3 +72,6 @@ entries:
general_definition: |-
A bookmark is a mostly alphanumeric string like `0000007b-0000b26e-00001538-0c3e87bb37b3db5cc52eedb93cd3b96b` which represents a specific state of a SQLite database at a certain point in time. Bookmarks are designed to be lexically comparable: a bookmark representing an earlier point in time compares less than one representing a later point, using regular string comparison.

# - term: "request context"
# general_definition: |-
# The duration of time that a Durable Object is processing a request, such as a remote procedure call. [Compute duration charges](/durable-objects/platform/pricing) are incurred for the duration of the request context. Note that the request context for a Durable Object extends at least 60 seconds after the last client disconnects.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {Render, Markdown, GlossaryTooltip, Details, AnchorHeading } from "~/comp
{ props.product === "durable-objects" && <><AnchorHeading title="Compute billing" depth={2}/></> }
{ props.product === "workers" && <><AnchorHeading title="Compute billing" depth={3}/></> }

Durable Objects are billed only while the Durable Object is active (includes the <GlossaryTooltip term="request context"> request context</GlossaryTooltip>).
Durable Objects are billed only while the Durable Object is active.

| | Free plan | Paid plan |
| -------------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
Expand All @@ -32,7 +32,7 @@ await durableObjectStub.cat(); // billed as a request

<sup>3</sup> Application level auto-response messages handled by [`state.setWebSocketAutoResponse()`](/durable-objects/best-practices/websockets/) will not incur additional wall-clock time, and so they will not be charged.

<sup>4</sup> Duration is billed in wall-clock time as long as the Object is active, but is shared across all requests active on an Object at once. Calling `accept()` on a WebSocket in an Object will incur duration charges for the entire time the WebSocket is connected. Note that the request context for a Durable Object extends for 10 seconds after the last client disconnects. The request context of a Durable Object is evaluated every 70 seconds and the context is ended if it has been active for 70 seconds. If you prefer, use the [WebSocket Hibernation API](/durable-objects/best-practices/websockets/#websocket-hibernation-api) to avoid incurring duration charges once all event handlers finish running.
<sup>4</sup> Duration is billed in wall-clock time as long as the Object is active, but is shared across all requests active on an Object at once. Calling `accept()` on a WebSocket in an Object will incur duration charges for the entire time the WebSocket is connected. If you prefer, use the WebSocket Hibernation API to avoid incurring duration charges once all event handlers finish running. Note that the Durable Object will remain active for 10 seconds after the last client disconnects, and will incur duration charges for this period. For a complete explanation, refer to [When does a Durable Object incur duration charges?](/durable-objects/platform/pricing/#when-does-a-durable-object-incur-duration-charges).

<sup>5</sup> Duration billing charges for the 128 MB of memory your Durable Object is allocated, regardless of actual usage. If your account creates many instances of a single Durable Object class, Durable Objects may run in the same isolate on the same physical machine and share the 128 MB of memory. These Durable Objects are still billed as if they are allocated a full 128 MB of memory.

Expand Down
Loading