Skip to content

Commit e33f371

Browse files
committed
pm re-structuring
1 parent 2735451 commit e33f371

File tree

3 files changed

+119
-106
lines changed

3 files changed

+119
-106
lines changed

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

Lines changed: 58 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
title: Durable Object storage
2+
title: Durable Object Storage
33
pcx_content_type: concept
44
sidebar:
55
order: 6
@@ -15,7 +15,34 @@ import {
1515

1616
The Durable Object Storage API allows <GlossaryTooltip term="Durable Object">Durable Objects</GlossaryTooltip> to access transactional and strongly consistent storage. A Durable Object's attached storage is private to its unique instance and cannot be accessed by other objects.
1717

18-
<Render file="storage-intro-text"/>
18+
The Durable Object Storage API comes with several methods, including SQL, point-in-time-recovery (PITR), key-value (KV), and alarm APIs. Available API methods depend on the storage backend for a Durable Objects class, either [SQLite](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class) or [KV](/durable-objects/reference/durable-objects-migrations/#create-durable-object-class-with-key-value-storage).
19+
20+
:::note[Recommended SQLite-backed Durable Objects]
21+
We recommend all new Durable Object classes use the [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class).
22+
23+
The KV storage backend remains for backwards compatibility, and a migration path from KV storage to SQLite storage for existing Durable Object classes will be available in the future.
24+
:::
25+
26+
<Render file="do-sqlite-storage-no-bill-note"/>
27+
28+
| Methods <sup>1</sup> | SQLite-backed Durable Object class | KV-backed Durable Object class |
29+
| ----------------------- | ---------------------------- | ------------------------ |
30+
| SQL API |||
31+
| PITR API |||
32+
| KV API | ✅ <sup>2, 3</sup> ||
33+
| Alarms API |||
34+
35+
<Details header="Footnotes" open={true}>
36+
37+
<sup>1</sup> Each method is implicitly wrapped inside a transaction, such that its results are atomic and isolated from all other storage operations, even when accessing multiple key-value pairs.
38+
39+
<sup>2</sup> KV API methods like `get()`, `put()`, `delete()`, or `list()` store data in a hidden SQLite table.
40+
41+
<sup>3</sup> KV methods which were previously asynchronous with KV storage (for example, [`get`](/durable-objects/api/storage-api/#get), [`put`](/durable-objects/api/storage-api/#put), [`delete`](/durable-objects/api/storage-api/#delete), [`deleteAll`](/durable-objects/api/storage-api/#deleteall), [`list`](/durable-objects/api/storage-api/#list)) are synchronous, even though they return promises. These methods will have completed their operations before they return the promise.
42+
43+
## Access Storage
44+
45+
Durable Objects gain access to Storage API via the `DurableObjectStorage` interface and accessed by the `DurableObjectState::storage` property. This is frequently accessed via `this.ctx.storage` with the `ctx` parameter passed to the Durable Object constructor.
1946

2047
The following code snippet shows you how to store and retrieve data using the Durable Object Storage API.
2148

@@ -34,25 +61,13 @@ export class Counter extends DurableObject {
3461
}
3562

3663
}
37-
3864
```
3965
</TypeScriptExample>
4066

41-
JavaScript is a single-threaded and event-driven programming language. This means that JavaScript runtimes, by default, allow requests to interleave with each other which can lead to concurrency bugs. The Durable Objects runtime uses a combination of <GlossaryTooltip term="input gate">input gates</GlossaryTooltip> and <GlossaryTooltip term="output gate">output gates</GlossaryTooltip> to avoid this type of concurrency bug when performing storage operations.
42-
43-
:::note[Scope of Durable Object storage]
44-
Note that Durable Object storage is scoped by individual <GlossaryTooltip term="Durable Object">Durable Objects</GlossaryTooltip>.
45-
46-
- An account can have many Durable Object <GlossaryTooltip term="namespace">namespaces</GlossaryTooltip>.
47-
- A namespace can have many Durable Objects.
48-
49-
However, storage is scoped per individual Durable Object.
50-
:::
67+
JavaScript is a single-threaded and event-driven programming language. This means that JavaScript runtimes, by default, allow requests to interleave with each other which can lead to concurrency bugs. The Durable Objects runtime uses a combination of <GlossaryTooltip term="input gate">input gates</GlossaryTooltip> and <GlossaryTooltip term="output gate">output gates</GlossaryTooltip> to avoid this type of concurrency bug when performing storage operations. Learn more with our [blog post](https://blog.cloudflare.com/durable-objects-easy-fast-correct-choose-three/)
5168

5269
## SQL API
5370

54-
<Render file="do-sqlite-storage-no-bill-note"/>
55-
5671
The `SqlStorage` interface encapsulates methods that modify the SQLite database embedded within a Durable Object. The `SqlStorage` interface is accessible via the [`sql` property](/durable-objects/api/storage-api/#) of `DurableObjectStorage` class.
5772

5873
For example, using `sql.exec()`, a user can create a table, then insert rows into the table.
@@ -78,15 +93,9 @@ export class MyDurableObject extends DurableObject {
7893
}
7994
```
8095

81-
:::note[SQLite in Durable Objects]
82-
SQL API methods accessed with `ctx.storage.sql` are only allowed on [Durable Object classes with SQLite storage backend](/durable-objects/reference/durable-objects-migrations/#enable-sqlite-storage-backend-on-new-durable-object-class-migration) and will return an error if called on Durable Object classes with a key-value storage backend.
83-
:::
84-
85-
:::note[Writing to indexes or virtual tables]
86-
When writing data, every index counts as an additional row. However, indexes may be beneficial for read-heavy use cases. Refer to [Index for SQLite Durable Objects](/durable-objects/best-practices/access-durable-objects-storage/#index-for-sqlite-durable-objects).
87-
88-
Writing data to [SQLite virtual tables](https://www.sqlite.org/vtab.html) also counts towards rows written.
89-
:::
96+
* SQL API methods accessed with `ctx.storage.sql` are only allowed on [Durable Object classes with SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class) and will return an error if called on Durable Object classes with a key-value storage backend.
97+
* When writing data, every index counts as an additional row. However, indexes may be beneficial for read-heavy use cases. Refer to [Index for SQLite Durable Objects](/durable-objects/best-practices/access-durable-objects-storage/#index-for-sqlite-durable-objects).
98+
* Writing data to [SQLite virtual tables](https://www.sqlite.org/vtab.html) also counts towards rows written.
9099

91100
### `exec`
92101

@@ -138,7 +147,9 @@ console.log(cursor.toArray()); // prints [{ artistid: 456, artistname: 'Bob' },{
138147
* `rowsWritten`: <Type text='number' />
139148
* The number of rows written so far as part of this SQL `query`. This may increase as you iterate the cursor. The final value is used for [SQL billing](/durable-objects/platform/pricing/#sqlite-storage-backend).
140149

150+
:::note[SQL transactions]
141151
Note that `sql.exec()` cannot execute transaction-related statements like `BEGIN TRANSACTION` or `SAVEPOINT`. Instead, use the [`ctx.storage.transaction()`](/durable-objects/api/storage-api/#transaction) or [`ctx.storage.transactionSync()`](/durable-objects/api/storage-api/#transactionsync) APIs to start a transaction, and then execute SQL queries in your callback.
152+
:::
142153

143154
#### Examples
144155

@@ -160,7 +171,7 @@ let size = ctx.storage.sql.databaseSize;
160171

161172
- `transactionSync(callback)`: <Type text='any' />
162173

163-
- Only available when using [the SQLite-backed storage engine](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend).
174+
- Only available when using SQLite-backed Durable Objects.
164175

165176
- Invokes `callback()` wrapped in a transaction, and returns its result.
166177

@@ -170,7 +181,7 @@ let size = ctx.storage.sql.databaseSize;
170181

171182
## PITR (Point In Time Recovery) API
172183

173-
For [Durable Objects classes with SQL storage](/durable-objects/reference/durable-objects-migrations/#enable-sqlite-storage-backend-on-new-durable-object-class-migration), the following point-in-time-recovery (PITR) API methods are available to restore a Durable Object's embedded SQLite database to any point in time in the past 30 days. These methods apply to the entire SQLite database contents, including both the object's stored SQL data and stored key-value data using the key-value `put()` API. The PITR API is not supported in local development because a durable log of data changes is not stored locally.
184+
For [SQLite-backed Durable Objects](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class), the following point-in-time-recovery (PITR) API methods are available to restore a Durable Object's embedded SQLite database to any point in time in the past 30 days. These methods apply to the entire SQLite database contents, including both the object's stored SQL data and stored key-value data using the key-value `put()` API. The PITR API is not supported in local development because a durable log of data changes is not stored locally.
174185

175186
The PITR API represents points in times using 'bookmarks'. A bookmark is a mostly alphanumeric string like `0000007b-0000b26e-00001538-0c3e87bb37b3db5cc52eedb93cd3b96b`. 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.
176187

@@ -246,14 +257,6 @@ ctx.storage.onNextSessionRestoreBookmark(bookmark);
246257

247258
- Deletes the provided keys and their associated values. Supports up to 128 keys at a time. Returns a count of the number of key-value pairs deleted.
248259

249-
### `deleteAll`
250-
251-
- <code>deleteAll(options <Type text="Object" /> <MetaInfo text="optional" />)</code>: <Type text="Promise" />
252-
253-
- Deletes all stored data, effectively deallocating all storage used by the Durable Object. For Durable Objects with a key-value storage backend, `deleteAll()` removes all keys and associated values for an individual Durable Object. For Durable Objects with a [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend), `deleteAll()` removes the entire contents of a Durable Object's private SQLite database, including both SQL data and key-value data.
254-
- For Durable Objects with a key-value storage backend, an in-progress `deleteAll()` operation can fail, which may leave a subset of data undeleted. Durable Objects with a SQLite storage backend do not have a partial `deleteAll()` issue because `deleteAll()` operations are atomic (all or nothing).
255-
- `deleteAll()` does not proactively delete [Alarms](/durable-objects/api/alarms/). Use [`deleteAlarm()`](/durable-objects/api/alarms/#deletealarm) to delete an alarm.
256-
257260
#### Supported options
258261

259262
- `put()`, `delete()` and `deleteAll()` support the following options:
@@ -331,28 +334,6 @@ The `put()` method returns a `Promise`, but most applications can discard this p
331334

332335
- Same as the option to `get()`, above.
333336

334-
### `transaction`
335-
336-
- `transaction(closureFunction(txn))`: <Type text='Promise' />
337-
338-
- Runs the sequence of storage operations called on `txn` in a single transaction that either commits successfully or aborts.
339-
340-
- Explicit transactions are no longer necessary. Any series of write operations with no intervening `await` will automatically be submitted atomically, and the system will prevent concurrent events from executing while `await` a read operation (unless you use `allowConcurrency: true`). Therefore, a series of reads followed by a series of writes (with no other intervening I/O) are automatically atomic and behave like a transaction.
341-
342-
- `txn`
343-
344-
- Provides access to the `put()`, `get()`, `delete()` and `list()` methods documented above to run in the current transaction context. In order to get transactional behavior within a transaction closure, you must call the methods on the `txn` Object instead of on the top-level `ctx.storage` Object.<br/><br/>Also supports a `rollback()` function that ensures any changes made during the transaction will be rolled back rather than committed. After `rollback()` is called, any subsequent operations on the `txn` Object will fail with an exception. `rollback()` takes no parameters and returns nothing to the caller.
345-
346-
* When using [the SQLite-backed storage engine](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend), the `txn` object is obsolete. Any storage operations performed directly on the `ctx.storage` object, including SQL queries using [`ctx.storage.sql.exec()`](/durable-objects/api/storage-api/#exec), will be considered part of the transaction.
347-
348-
### `sync`
349-
350-
- `sync()`: <Type text='Promise' />
351-
352-
- Synchronizes any pending writes to disk.
353-
354-
- This is similar to normal behavior from automatic write coalescing. If there are any pending writes in the write buffer (including those submitted with [the `allowUnconfirmed` option](/durable-objects/api/storage-api/#supported-options-1)), the returned promise will resolve when they complete. If there are no pending writes, the returned promise will be already resolved.
355-
356337
## Alarms
357338

358339
### `getAlarm`
@@ -383,65 +364,43 @@ The `put()` method returns a `Promise`, but most applications can discard this p
383364

384365
- `setAlarm()` and `deleteAlarm()` support the same options as [`put()`](/durable-objects/api/storage-api/#put), but without `noCache`.
385366

386-
## Storage properties
367+
## Other
387368

388-
### `sql`
369+
### `deleteAll`
389370

390-
`sql` is a readonly property of type `DurableObjectStorage` encapsulating the [SQL API](/durable-objects/api/storage-api/#sql-api).
371+
- <code>deleteAll(options <Type text="Object" /> <MetaInfo text="optional" />)</code>: <Type text="Promise" />
391372

392-
## TypeScript and query results
373+
- Deletes all stored data, effectively deallocating all storage used by the Durable Object. For Durable Objects with a key-value storage backend, `deleteAll()` removes all keys and associated values for an individual Durable Object. For Durable Objects with a [SQLite storage backend](/durable-objects/best-practices/access-durable-objects-storage/#create-sqlite-backed-durable-object-class), `deleteAll()` removes the entire contents of a Durable Object's private SQLite database, including both SQL data and key-value data.
374+
- For Durable Objects with a key-value storage backend, an in-progress `deleteAll()` operation can fail, which may leave a subset of data undeleted. Durable Objects with a SQLite storage backend do not have a partial `deleteAll()` issue because `deleteAll()` operations are atomic (all or nothing).
375+
- `deleteAll()` does not proactively delete [alarms](/durable-objects/api/alarms/). Use [`deleteAlarm()`](/durable-objects/api/alarms/#deletealarm) to delete an alarm.
393376

394-
You can use TypeScript [type parameters](https://www.typescriptlang.org/docs/handbook/2/generics.html#working-with-generic-type-variables) to provide a type for your results, allowing you to benefit from type hints and checks when iterating over the results of a query.
377+
### `transaction`
395378

396-
:::caution
379+
- `transaction(closureFunction(txn))`: <Type text='Promise' />
397380

398-
Providing a type parameter does _not_ validate that the query result matches your type definition. In TypeScript, properties (fields) that do not exist in your result type will be silently dropped.
381+
- Runs the sequence of storage operations called on `txn` in a single transaction that either commits successfully or aborts.
399382

400-
:::
383+
- Explicit transactions are no longer necessary. Any series of write operations with no intervening `await` will automatically be submitted atomically, and the system will prevent concurrent events from executing while `await` a read operation (unless you use `allowConcurrency: true`). Therefore, a series of reads followed by a series of writes (with no other intervening I/O) are automatically atomic and behave like a transaction.
401384

402-
Your type must conform to the shape of a TypeScript [Record](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type) type representing the name (`string`) of the column and the type of the column. The column type must be a valid `SqlStorageValue`: one of `ArrayBuffer | string | number | null`.
385+
- `txn`
403386

404-
For example,
387+
- Provides access to the `put()`, `get()`, `delete()` and `list()` methods documented above to run in the current transaction context. In order to get transactional behavior within a transaction closure, you must call the methods on the `txn` Object instead of on the top-level `ctx.storage` Object.<br/><br/>Also supports a `rollback()` function that ensures any changes made during the transaction will be rolled back rather than committed. After `rollback()` is called, any subsequent operations on the `txn` Object will fail with an exception. `rollback()` takes no parameters and returns nothing to the caller.
405388

406-
```ts
407-
type User = {
408-
id: string;
409-
name: string;
410-
email_address: string;
411-
version: number;
412-
}
413-
```
389+
* When using [the SQLite-backed storage engine](/durable-objects/best-practices/access-durable-objects-storage/#sqlite-storage-backend), the `txn` object is obsolete. Any storage operations performed directly on the `ctx.storage` object, including SQL queries using [`ctx.storage.sql.exec()`](/durable-objects/api/storage-api/#exec), will be considered part of the transaction.
414390

415-
This type can then be passed as the type parameter to a `sql.exec` call:
391+
### `sync`
416392

417-
```ts
418-
// The type parameter is passed between the "pointy brackets" before the function argument:
419-
const result = this.ctx.storage.sql.exec<User>("SELECT id, name, email_address, version FROM users WHERE id = ?", user_id).one()
420-
// result will now have a type of "User"
421-
422-
// Alternatively, if you are iterating over results using a cursor
423-
let cursor = this.sql.exec<User>("SELECT id, name, email_address, version FROM users WHERE id = ?", user_id)
424-
for (let row of cursor) {
425-
// Each row object will be of type User
426-
}
393+
- `sync()`: <Type text='Promise' />
427394

428-
// Or, if you are using raw() to convert results into an array, define an array type:
429-
type UserRow = [
430-
id: string,
431-
name: string,
432-
email_address: string,
433-
version: number,
434-
];
395+
- Synchronizes any pending writes to disk.
435396

436-
// ... and then pass it as the type argument to the raw() method:
437-
let cursor = sql.exec("SELECT id, name, email_address, version FROM users WHERE id = ?", user_id).raw<UserRow>();
397+
- This is similar to normal behavior from automatic write coalescing. If there are any pending writes in the write buffer (including those submitted with [the `allowUnconfirmed` option](/durable-objects/api/storage-api/#supported-options-1)), the returned promise will resolve when they complete. If there are no pending writes, the returned promise will be already resolved.
438398

439-
for (let row of cursor) {
440-
// row is of type User
441-
}
442-
```
399+
## Storage properties
400+
401+
### `sql`
443402

444-
You can represent the shape of any result type you wish, including more complex types. If you are performing a JOIN across multiple tables, you can compose a type that reflects the results of your queries.
403+
`sql` is a readonly property of type `DurableObjectStorage` encapsulating the [SQL API](/durable-objects/api/storage-api/#sql-api).
445404

446405
## Related resources
447406

0 commit comments

Comments
 (0)