Skip to content
Closed
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
7d999df
Update DO migration docs
harshil1712 Jan 23, 2025
c989609
Update src/content/docs/durable-objects/reference/durable-objects-mig…
Oxyjun Jan 23, 2025
88bfdac
Update src/content/docs/durable-objects/reference/durable-objects-mig…
Oxyjun Jan 23, 2025
29b43d9
Resolving merge conflict.
Oxyjun Jan 23, 2025
258e52e
Docs enhancement part 1
Oxyjun Jan 23, 2025
a9c8987
add note for create class
harshil1712 Jan 30, 2025
46e363c
Editing the structure for better clarity.
Oxyjun Jan 30, 2025
1cf14cc
Merging HEAD back into the branch.
Oxyjun Jan 31, 2025
fbccebc
Improving explanation and code examples.
Oxyjun Jan 31, 2025
a5b2905
Improving structure.
Oxyjun Feb 11, 2025
c79860b
Creating a new "Concepts" node
Oxyjun Feb 11, 2025
3653945
Initialising D1 RR docs. Introducing Concepts and Glossary.
Oxyjun Feb 11, 2025
0f5d328
Fleshing out the RR chapter.
Oxyjun Feb 11, 2025
efd3905
Rewording extend with continue.
Oxyjun Feb 11, 2025
c91594d
Manually reversing any changes to DO Migrations
Oxyjun Feb 11, 2025
0b47e8f
Merge commit '28cb6c1a1ae9e32af5198c5498e259edba6a2afa' into jun/d1/rr
Oxyjun Feb 11, 2025
c769b63
Merge commit 'b2d52b8e3848364df3d5fef4a77fca95e166f776' into jun/d1/rr
Oxyjun Feb 24, 2025
b2bccd1
Initialising `withSession` in the D1 API docs
Oxyjun Feb 25, 2025
c07415e
Initialising API docs for getBookmark
Oxyjun Feb 25, 2025
9d37df2
Adding type for `bookmark`
Oxyjun Feb 25, 2025
cf40610
Adding placeholder D1 read replication demo
Oxyjun Feb 27, 2025
ca8a352
Updating Data Locations chapter to reference RR.
Oxyjun Mar 3, 2025
79a6785
Updating api playground, minor rephrasing
Oxyjun Mar 4, 2025
0861280
Adding placeholder example.
Oxyjun Mar 5, 2025
1e3d32b
Adding Worker code to RR tutorial.
Oxyjun Mar 6, 2025
d2f0dfe
Fixing Steps component.
Oxyjun Mar 6, 2025
114b2e7
Fixing broken link.
Oxyjun Mar 6, 2025
3ef1ad4
Update src/content/docs/d1/tutorials/test-read-replication/index.mdx
Oxyjun Mar 6, 2025
71fc1e1
Merge commit 'ed9ff0caa8d2d2b68fde67f89f1279163726a73f' into jun/d1/rr
Oxyjun Mar 6, 2025
598f769
Merge branch 'jun/d1/rr' of github.com:cloudflare/cloudflare-docs int…
Oxyjun Mar 6, 2025
17f5cfa
Merge commit '2ebfe0ca296685004ce4a063e4e3032991984b1c' into jun/d1/rr
Oxyjun Mar 12, 2025
7ceabd7
Consolidating Best practices and Concepts into
Oxyjun Mar 12, 2025
229543c
Fixing links, editing the chapter structure.
Oxyjun Mar 12, 2025
07ff81b
Introducing code example better.
Oxyjun Mar 12, 2025
b540f59
Fixing redirects
Oxyjun Mar 12, 2025
bec5654
Adding more links to supplementary information.
Oxyjun Mar 12, 2025
4aba48a
Merge commit '0d7ef0a4e6064fdc22d5aa436090ae4105bfcad3' into jun/d1/rr
Oxyjun Mar 13, 2025
9442780
Updating `withSession` to mention it will default
Oxyjun Mar 13, 2025
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/content/apps/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,11 @@
languages: [TypeScript]
cloudflare: false
updated: 2024-10-07
- link: https://github.com/harshil1712/e-com-d1
id: E-commerce Store
description: An application to showcase D1 read replication in the context of an online store.
tags: []
products: [Workers, D1]
languages: [TypeScript]
cloudflare: true
updated: 2025-02-27
2 changes: 1 addition & 1 deletion src/content/docs/d1/best-practices/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Best practices
pcx_content_type: navigation
sidebar:
order: 3
order: 4
group:
hideIndex: true
---
Expand Down
12 changes: 12 additions & 0 deletions src/content/docs/d1/concepts/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
title: Concepts
pcx_content_type: navigation
sidebar:
order: 3
group:
hideIndex: true
---

import { DirectoryListing } from "~/components";

<DirectoryListing />
167 changes: 167 additions & 0 deletions src/content/docs/d1/concepts/read-replication.mdx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc needs:

  • to get to the point MUCH earlier and show code sooner. Give users a better way to understand how this works via code
  • improve the code examples to better show the API and how it helps with session consistency + reading your own writes
  • tradeoffs in consistency modes

it uses too much prose to try to explain instead of showing with code + comments (first) and then walking through it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My strong advice:

  1. Get into a clear code example with opinionated usage very, very early + a crisp ~3-4 sentences on why this is good + useful
  2. Be clear on how you can just start using this (do I need to enable it? configure it? what???)
  3. Make all code examples real - no placeholder strings.
  4. THEN get into the details, the options, the API surface.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much for taking the time to review this @elithrar! I'll work with @vy-ton to see how to best improve this chapter in context of your suggested approach 👍🏻

Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
title: D1 read replication
pcx_content_type: concept
sidebar:
order: 2

---

import { GlossaryTooltip } from "~/components"

D1 read replication is a feature which reduces the <GlossaryTooltip term = "request latency">request latency</GlossaryTooltip> for read requests for users who may be located far away from the <GlossaryTooltip term="primary database instance">primary database instance</GlossaryTooltip>.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @vy-ton - I think this needs to be framed better. “Distributes read-only copies of your database that <…>”


## Primary database instance vs read replicas

When using D1 without read replication, D1 routes all queries (both read and write) to a specific database instance in [one location in the world](/d1/configuration/data-location/), known as the primary database instance. The request latency is dependent on the physical closeness of a user to the primary database instance - it takes less time for light (information) to travel between the user and the primary database instance if that distance is shorter. Users located further away from this database instance experience the longest request latency.

When using read replication, D1 introduces multiple “almost up-to-date” copies of the primary database instance which only serve read requests, called <GlossaryTooltip term="read replica"> read replicas </GlossaryTooltip>. D1 creates the read replicas in multiple regions throughout the world [across the Cloudflare network](/d1/concepts/read-replication/#read-replica-locations).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Identified issues

  • Vale Style Guide - (cloudflare.NonStandardQuotes-warning) Use standard single quotes or double quotes only. Do not use any of the following quote mark types: ‘ ’ “ ”. In the text, we found this character: “
  • Vale Style Guide - (cloudflare.NonStandardQuotes-warning) Use standard single quotes or double quotes only. Do not use any of the following quote mark types: ‘ ’ “ ”. In the text, we found this character: ”

Proposed fix

Suggested change
When using read replication, D1 introduces multiple almost up-to-date copies of the primary database instance which only serve read requests, called <GlossaryTooltip term="read replica"> read replicas </GlossaryTooltip>. D1 creates the read replicas in multiple regions throughout the world [across the Cloudflare network](/d1/concepts/read-replication/#read-replica-locations).
When using read replication, D1 introduces multiple "almost up-to-date" copies of the primary database instance which only serve read requests, called <GlossaryTooltip term="read replica"> read replicas </GlossaryTooltip>. D1 creates the read replicas in multiple regions throughout the world [across the Cloudflare network](/d1/concepts/read-replication/#read-replica-locations).

I replaced the curly quotes with standard straight quotes to adhere to the style guide.


A user may be located far away from the primary database instance but close to a read replica. By sending their read requests to the read replica instead of the primary database instance, the user enjoys shorter read request response times. For example, the request latency when using the primary database instance may be 250 ms, versus 20 - 50 ms when using a read replica.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The end user (client) doesn’t choose to do this. Need to reframe all of this.


![D1 read replication concept](/images/d1/d1-read-replication-concept.png)

:::note
All write requests are still forwarded to the primary database instance. Read replication only improves the query response time for read requests.
:::

| Type of database instance | Description | How it handles write queries | How it handles read queries |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------- |
| Primary database instance | The database instance containing the “original” copy of the database | Can serve write queries | Can serve read queries |
| Read replica | A database instance containing an “almost up-to-date” copy of the original database, asynchronously updated against the primary database instance | Forwards any write queries to the primary database instance | Can serve read queries using its own copy of the database |

D1 asynchronously updates read replicas against the primary database instance. This means that at any given time, a read replica may be arbitrarily out of date. The difference between the primary database instance and the read replica is known as the <GlossaryTooltip term="replica lag"> replica lag </GlossaryTooltip>.

## Replica lag and consistency model

To account for replica lag, it is important to consider the consistency model for D1. A consistency model is a logical framework that governs how a database system serves user queries (how the data is updated and accessed) when there are multiple database instances. Different models can be useful in different use cases. Most database systems provide [read committed](https://jepsen.io/consistency/models/read-committed), [snapshot isolation](https://jepsen.io/consistency/models/snapshot-isolation), or [serializable](https://jepsen.io/consistency/models/serializable) consistency models, depending on their configuration.

D1 read replication offers [sequential consistency](https://jepsen.io/consistency/models/sequential). D1 creates a global order of all operations which have taken place on the database, and can identify the latest version of the database that a query has seen, using [bookmarks](/d1/reference/time-travel/#bookmarks). It then serves the query with a database instance that is at least as up-to-date as the query itself.

Sequential consistency has properties such as:

- **Monotonic reads**: If you perform two reads one after the other (read-1, then read-2), read-2 cannot read a version of the database prior to read-1.
- **Monotonic writes**: If you perform write-1 then write-2, all processes observe write-1 before write-2.
- **Writes follow reads**: If you read a value, then perform a write, the subsequent write must be based on the value that was just read.
- **Read my own writes**: If you write to the database, all subsequent reads will see the write.

## How D1 read replication works

### Use read replication via Sessions API

You can use D1 read replication by using D1 Sessions API. A Session encapsulates all the queries from one logical session for your application. For example, a Session may correspond to all queries coming from a particular web browser session.

By using Sessions API for read replication, all of your queries from a single Session read from a version of the database which is as up-to-date as your query. This ensures that the version of the database you are reading is logically consistent with your queries when using read replicas.

### Bookmarks

D1 read replication achieves the total ordering of all operations by attaching a bookmark to each write query within a Session. A bookmark represents the state of a database at a specific point in time. For more information, refer to [Bookmarks](/d1/reference/time-travel/#bookmarks).

### Start a D1 Session

A Session API code block may resemble the following:

```ts
// synchronous
let sess = env.DB_D1.withSession('<constraint> or bookmark')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The examples need to be more real. Show users what a bookmark can be - don’t leave it so abstract.

is it meant to be a user Id? Random? The letter “B” - ?

help users understand what they need to do + make it simple.

const stmt = sess.prepare('<query_string>')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

show a real example SQL query. No placeholders.

// wait for Session condition, queries within batch have casual consistency
const result = await sess.run()
```

- You begin a Session with .withSession, specifying a `<constraint>`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @vy-ton - this makes the API seem more complex than it is. Too many placeholders instead of actually describing a constraint and how it is useful for enabling session consistency for a given client.

- You continue a Session by providing a `bookmark`.
- You prepare your query string with `.prepare`.
- You obtain the result with `.run`.

When starting a Session with `withSession` in your Worker code, you can specify the `<constraint>` parameter, which corresponds to how the Session begins.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again: what is a constraint?


- `first-primary`:
- Directs the first query in the Session (whether read or write) to the primary database instance.
- Subsequent queries in the Session have sequential consistency.
- Example use-case: Reading the score-table of an ongoing sports tournament. It is important for the first read to read the latest data from the primary database instance, but may not need to display subsequent score updates immediately (and therefore suitable to use read replicas for subsequent queries within the same Session).
- `first-unconstrained`:
- Directs the first query in the Session (whether read or write) to any database instance. This could be either the primary database instance or a read replica (note that write queries will be forwarded to the primary database instance).
- Subsequent queries in the Session have sequential consistency.
- Example use-case: Displaying blog articles on a website. It is not crucial to display the latest blog article, and therefore the user can start the Session by reading from a read replica. Subsequent queries can also go to the read replica.

When continuing an existing Session started with `withSession`, you can provide a `bookmark` parameter from an existing Session.

- `bookmark`:
- Continues an existing Session using the provided `bookmark` as the reference point. If you already have a `bookmark` from a different part of the application, you can continue that Session using the same `bookmark`, which ensures you read from a version of the database which is at least as up-to-date as the `bookmark`.
- Subsequent queries in the Session have sequential consistency.
- You can return the last encountered `bookmark` for a given Session using `sess.getBookmark()`.
- Example use-case: (Continuation of the previous example used in `first-primary`) Reading the score-table of an ongoing sports tournament. A user may have started a web browser session and already has a `bookmark`. D1 can serve subsequent read queries within the same Session which is logically consistent by using the `bookmark`.

By default (when the `<constraint>` is either `null` or unspecified), D1 Session uses `first-primary` as the starting condition.

### Example of starting a new Session with `first-primary`

Suppose you want to develop a webpage for an electricity provider which lists the electricity bill statements. An assumption here is that each statement is immutable. Once issued, it never changes.

In this scenario, you want the first request of the page to show a list of all the statements and their issue dates. Therefore, the first request starts a new D1 Session using the constraint `first-primary` to get the latest information (ensuring that the list includes all issued bill statements) from the primary database instance.

Then, when opening an individual electricity bill statement, we can continue using the same Session by passing the `bookmark` from the first query to subsequent requests. Since each bill statement is immutable, any bill statement listed from the first query is guaranteed to be available in subsequent requests using the same Session.

#### Example Worker code

```ts
async function listBillStatements(accountId: string, db: D1Database): ListBillStatementsResult {
const session = db.withSession("first-primary");
const { results } = session.prepare("SELECT * FROM bills WHERE accountId = ?").bind(accountId).run();
return { bookmark: session.getBookmark(), bills: results };
}

async function getBillStatement(accountId: string, billId: string, bookmark: string): GetBillStatementResult {
// NOTE: We achieve sequential consistency with the given `bookmark`.
const session = db.withSession(bookmark);
const { results } = session.prepare("SELECT * FROM bills WHERE accountId = ? AND billId = ? LIMIT 1").bind(accountId, billId).first();
return { bookmark: session.getBookmark(), bill: results };
}
```

### Example of starting a new Session with `first-unconstrained`

Suppose you want to develop a feature for displaying “likes” on a social network application.

The number of likes is a good example of a situation which does not require the latest information all the time. When displaying the number of likes of a post, the first request starts a new D1 Session using the constraint `first-unconstrained`, which will be served by the nearest D1 read replica.

Subsequent interactions on the application should continue using the same Session by passing the `bookmark` from the first query to subsequent requests. This guarantees that all interactions will observe information at least as up-to-date as the initial request, and therefore never show information older than what a user has already observed. The number of likes will be updated with newer counts over time with subsequent requests as D1 asynchronously updates the read replicas with the changes from the primary database.

#### Example Worker code

```js
async function getLikes(postId: string, db: D1Database, bookmark: string | null): GetLikesResult {
// NOTE: Achieve sequential consistency with given bookmark,
// or start a new session that can be served by any replica.
const session = db.withSession(bookmark ?? "first-unconstrained");
const { results } = session.prepare("SELECT * FROM likes WHERE postId = ?").bind(postId).run();
return { bookmark: session.getBookmark(), likes: results };
}
```

## Read replica locations

Currently, D1 automatically creates a read replica in every region, including the region where the primary database instance is located. These regions are:
- ENAM
- WNAM
- WEUR
- EEUR
- APAC
- OC

## Benefits of read replication

A system with multiple read replicas located around the world improves the performance of databases:

- The read throughput increases by distributing load across multiple replicas. Since multiple database instances are able to serve read-only requests, your application can serve a larger number of queries at any given time.
- The query latency decreases for users located close to the read replicas. By shortening the physical distance between a read replica and the user, read query latency decreases, resulting in a faster application.

## Monitor latency

To see the impact of read replication, check the latency for your read-only queries. Refer to the [queryBatchTimeMs](/d1/observability/metrics-analytics/) metric or [view your metrics through the dashboard](/d1/observability/metrics-analytics/#view-metrics-in-the-dashboard).

## Supplementary information

You may wish to refer to the following resources:

- [Building D1: a Global Database](https://blog.cloudflare.com/building-d1-a-global-database/)
2 changes: 1 addition & 1 deletion src/content/docs/d1/configuration/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Configuration
pcx_content_type: navigation
sidebar:
order: 8
order: 9
group:
hideIndex: true
---
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/d1/d1-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ pcx_content_type: navigation
title: REST API
external_link: /api/resources/d1/subresources/database/methods/create/
sidebar:
order: 6
order: 7
---
2 changes: 1 addition & 1 deletion src/content/docs/d1/demos.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pcx_content_type: navigation
title: Demos and architectures
sidebar:
order: 12
order: 13

---

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/d1/examples/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ hideChildren: false
pcx_content_type: navigation
title: Examples
sidebar:
order: 10
order: 11
group:
hideIndex: true
---
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/d1/observability/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Observability
pcx_content_type: navigation
sidebar:
order: 9
order: 10
group:
hideIndex: true
---
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/d1/platform/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pcx_content_type: navigation
title: Platform
sidebar:
order: 12
order: 13
group:
hideIndex: true
---
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/d1/reference/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pcx_content_type: navigation
title: Reference
sidebar:
order: 13
order: 14
group:
hideIndex: true
---
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/d1/sql-api/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: SQL API
pcx_content_type: navigation
sidebar:
order: 5
order: 6
group:
hideIndex: true
---
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/d1/tutorials/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pcx_content_type: navigation
title: Tutorials
hideChildren: true
sidebar:
order: 11
order: 12

---

Expand Down
43 changes: 42 additions & 1 deletion src/content/docs/d1/worker-api/d1-database.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -241,4 +241,45 @@ return new Response(dump, {

#### Return values

- None.
- None.

### `withSession`

Starts a D1 Session which activates read replication.

```ts
const session = env.DB.withSession("<constraint> or bookmark");
```

#### Parameters

- <code>condition</code>: <Type text="String"/><MetaInfo text="Optional"/>
- The starting condition for the D1 Session. `<constraint>` can be one of:
- `first-primary`: Directs the first query in the Session (whether read or write) to the primary database instance. This option is useful if you need to start the Session with the most up-to-date data from the primary database instance.
- `first-unconstrained`: Directs the first query in the Session (whether read or write) to any database instance. This option is useful if you do not need to start the Session with the most up-to-date data, and wish to prioritize minimizing query latency from the very start of the Session.

- <code>bookmark</code>: <Type text="String?"/><MetaInfo text="Optional"/>
- A [`bookmark`](/d1/reference/time-travel/#bookmarks) from an existing D1 Session. This allows you to continue the existing Session using the `bookmark` as a reference point.

#### Return values

- <code>D1DatabaseSession</code>: <Type text="Object"/>
- An object which contains the methods [`prepare()`](/d1/worker-api/d1-database#prepare) and [`getBookmark`](/d1/worker-api/d1-database#getbookmark).

### `getBookmark`

Retrieves the `bookmark` from the D1 Session.

```ts
const session = db.withSession("first-primary");
return { bookmark } = session.getBookmark();
```

#### Parameters

- None

#### Return values

- <code>bookmark</code>: <Type text="String | null"/>
- A [`bookmark`](/d1/reference/time-travel/#bookmarks) which identifies the latest version of the database seen by the write query.
2 changes: 1 addition & 1 deletion src/content/docs/d1/worker-api/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pcx_content_type: navigation
title: Workers Binding API
sidebar:
order: 4
order: 5
---

import { DirectoryListing, Details, Steps } from "~/components";
Expand Down
12 changes: 12 additions & 0 deletions src/content/glossary/d1.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
---
productName: D1
entries:
- term: primary database instance
general_definition: |-
the primary database instance is the original instance of a database. This database instance only exists in one location in the world.
- term: request latency
general_definition: |-
the request latency is the time it takes for a request from a user to be returned by D1.
- term: read replica
general_definition: |-
a read replica is an “almost up-to-date” copy of the primary database instance which only serve read requests. There may be multiple read replicas for a single primary database instance.
- term: replica lag
general_definition: |-
the difference between the primary database instance and a read replica.
- term: "query planner"
general_definition: |-
A component in a database management system which takes a user query and generates the most efficient plan of executing that query (the query plan). For example, the query planner decides which indices to use, or which table to access first.
Loading