Skip to content

Commit 25675c2

Browse files
Oxyjunvy-ton
andauthored
[DO] Adding explanation on duration billing of a DO. (#22023)
* Adding explanation on duration billing of a DO. Removing glossary term for request context until later. * Implementing feedback from Frederik * Updating wording * Apply suggestions from code review Co-authored-by: Vy Ton <[email protected]> --------- Co-authored-by: Vy Ton <[email protected]>
1 parent 7b50972 commit 25675c2

File tree

4 files changed

+28
-9
lines changed

4 files changed

+28
-9
lines changed

src/content/docs/durable-objects/api/state.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ export class MyDurableObject extends DurableObject {
5353

5454
### `waitUntil`
5555

56-
`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.
56+
`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.
5757

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

60-
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).
60+
A Durable Object will 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).
6161

6262
:::
6363

src/content/docs/durable-objects/platform/pricing.mdx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,24 @@ Yes, although minimal. Empty tables can consume at least a few kilobytes, based
130130

131131
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.
132132

133-
The metadata remains in the Durable Object until you call [`deleteAll()`](/durable-objects/api/storage-api/#deleteall).
133+
The metadata remains in the Durable Object until you call [`deleteAll()`](/durable-objects/api/storage-api/#deleteall).
134+
135+
### When does a Durable Object incur duration charges?
136+
137+
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.
138+
139+
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.
140+
141+
- For example, if an object calls `fetch()` and awaits the response, it is considered to be waiting for I/O, and so cannot hibernate.
142+
143+
- Less obvious to a user, if an 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.
144+
145+
- 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.
146+
147+
- As an exception, a WebSocket request which has explicitly been accepted using the [WebSocket hibernation API](/durable-objects/best-practices/websockets/#websocket-hibernation-api allows a Durable Object to hibernate even while the WebSocket is still connected.
148+
149+
150+
151+
Once a Durable Object has been in a hibernatable state for 10 consecutive seconds, it hibernates, and duration billing stops.
152+
153+
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.

src/content/glossary/durable-objects.yaml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,6 @@ entries:
6060
general_definition: |-
6161
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.
6262
63-
- term: "request context"
64-
general_definition: |-
65-
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.
66-
6763
- term: "input gate"
6864
general_definition: |-
6965
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.
@@ -76,3 +72,6 @@ entries:
7672
general_definition: |-
7773
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.
7874
75+
# - term: "request context"
76+
# general_definition: |-
77+
# 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.

src/content/partials/durable-objects/durable-objects-pricing.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Render, Markdown, GlossaryTooltip, Details, AnchorHeading } from "~/com
88
{ props.product === "durable-objects" && <><AnchorHeading title="Compute billing" depth={2}/></> }
99
{ props.product === "workers" && <><AnchorHeading title="Compute billing" depth={3}/></> }
1010

11-
Durable Objects are billed only while the Durable Object is active (includes the <GlossaryTooltip term="request context"> request context</GlossaryTooltip>).
11+
Durable Objects are billed for duration while the Durable Object is active and running in memory. Requests to a Durable Object keep it active or creates the object if it was inactive, not in memory.
1212

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

3333
<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.
3434

35-
<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. 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.
35+
<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. It is recommended to 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. 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).
3636

3737
<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.
3838

0 commit comments

Comments
 (0)