Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
title: Hyperdrive no longer caches queries using STABLE PostgreSQL functions
description: Hyperdrive marks queries containing STABLE PostgreSQL functions as uncacheable, preventing stale cached results.
products:
- hyperdrive
date: 2026-02-23
---

Hyperdrive now treats queries containing PostgreSQL `STABLE` functions as uncacheable, in addition to `VOLATILE` functions.

Previously, only functions [that PostgreSQL categorizes](https://www.postgresql.org/docs/current/xfunc-volatility.html) as `VOLATILE` (for example, `RANDOM()`, `LASTVAL()`) were detected as uncacheable. `STABLE` functions (for example, `NOW()`, `CURRENT_TIMESTAMP`, `CURRENT_DATE`) were incorrectly allowed to be cached.

Because `STABLE` functions can return different results across different SQL statements within the same transaction, caching their results could serve stale or incorrect data. This change aligns Hyperdrive's caching behavior with PostgreSQL's function volatility semantics.

If your queries use `STABLE` functions, and you were relying on them being cached, move the function call to your application code and pass the result as a query parameter. For example, instead of `WHERE created_at > NOW()`, compute the timestamp in your Worker and pass it as `WHERE created_at > $1`.

Hyperdrive uses text-based pattern matching to detect uncacheable functions. References to function names like `NOW()` in SQL comments also cause the query to be marked as uncacheable.

For more information, refer to [Query caching](/hyperdrive/concepts/query-caching/) and [Troubleshoot and debug](/hyperdrive/observability/troubleshooting/).
101 changes: 73 additions & 28 deletions src/content/docs/hyperdrive/concepts/query-caching.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ sidebar:
order: 3
---

import { TabItem, Tabs } from "~/components";
import { TabItem, Tabs, WranglerConfig } from "~/components";

Hyperdrive automatically caches all cacheable queries executed against your database when query caching is turned on, reducing the need to go back to your database (incurring latency and database load) for every query which can be especially useful for popular queries. Query caching is enabled by default.

Expand All @@ -18,45 +18,87 @@ Besides determining the difference between a `SELECT` and an `INSERT`, Hyperdriv
For example, a read query that populates the front page of a news site would be cached:

<Tabs>
<TabItem label="PostgreSQL">
<TabItem label="PostgreSQL">

```sql
-- Cacheable
SELECT * FROM articles WHERE DATE(published_time) =
CURRENT_DATE() ORDER BY published_time DESC LIMIT 50
-- Cacheable: uses a parameterized date value instead of CURRENT_DATE
SELECT * FROM articles WHERE DATE(published_time) = $1
ORDER BY published_time DESC LIMIT 50
```

</TabItem>
<TabItem label="MySQL">

```sql
-- Cacheable
SELECT * FROM articles WHERE DATE(published_time) =
CURDATE() ORDER BY published_time DESC LIMIT 50
-- Cacheable: uses a parameterized date value instead of CURDATE()
SELECT * FROM articles WHERE DATE(published_time) = ?
ORDER BY published_time DESC LIMIT 50
```

</TabItem>
</Tabs>

Mutating queries (including `INSERT`, `UPSERT`, or `CREATE TABLE`) and queries that use [functions designated as `volatile` by PostgreSQL](https://www.postgresql.org/docs/current/xfunc-volatility.html) are not cached:
Mutating queries (including `INSERT`, `UPSERT`, or `CREATE TABLE`) and queries that use functions designated as [`volatile`](https://www.postgresql.org/docs/current/xfunc-volatility.html) or [`stable`](https://www.postgresql.org/docs/current/xfunc-volatility.html) by PostgreSQL are not cached:

<Tabs>
<TabItem label="PostgreSQL">
```sql
-- Not cached
-- Not cached: mutating queries
INSERT INTO users(id, name, email) VALUES(555, 'Matt', 'hello@example.com');

-- Not cached: LASTVAL() is a volatile function
SELECT LASTVAL(), * FROM articles LIMIT 50;

-- Not cached: NOW() is a stable function
SELECT * FROM events WHERE created_at > NOW() - INTERVAL '1 hour';
```

</TabItem>
<TabItem label="MySQL">
```sql
-- Not cached
-- Not cached: mutating queries
INSERT INTO users(id, name, email) VALUES(555, 'Thomas', 'hello@example.com');

-- Not cached: LAST_INSERT_ID() is a volatile function
SELECT LAST_INSERT_ID(), * FROM articles LIMIT 50;

-- Not cached: NOW() returns a non-deterministic value
SELECT * FROM events WHERE created_at > NOW() - INTERVAL 1 HOUR;
```

</TabItem>
</Tabs>

Common PostgreSQL functions that are **not cacheable** include:

| Function | PostgreSQL volatility category | Cached |
| ------------------- | ------------------------------ | ------ |
| `NOW()` | STABLE | No |
| `CURRENT_TIMESTAMP` | STABLE | No |
| `CURRENT_DATE` | STABLE | No |
| `CURRENT_TIME` | STABLE | No |
| `LOCALTIME` | STABLE | No |
| `LOCALTIMESTAMP` | STABLE | No |
| `TIMEOFDAY()` | VOLATILE | No |
| `RANDOM()` | VOLATILE | No |
| `LASTVAL()` | VOLATILE | No |
| `TXID_CURRENT()` | STABLE | No |

Only functions designated as `IMMUTABLE` by PostgreSQL (functions whose return value never changes for the same inputs) are compatible with Hyperdrive caching. If your query uses a `STABLE` or `VOLATILE` function, move the function call to your application code and pass the resulting value as a query parameter instead.

:::caution[Function detection is text-based]
Hyperdrive uses text-based pattern matching to detect uncacheable functions in your queries. This means that even references to function names inside SQL comments will cause the query to be marked as uncacheable.

For example, the following query would **not** be cached because `NOW()` appears in the comment:

```sql
-- We removed NOW() to keep this query cacheable
SELECT * FROM api_keys WHERE hash = $1 AND deleted = false;
```

Avoid referencing uncacheable function names anywhere in your query text, including comments.
:::

## Default cache settings

The default caching behaviour for Hyperdrive is defined as below:
Expand Down Expand Up @@ -129,24 +171,27 @@ export default {

The Wrangler configuration remains the same both for PostgreSQL and MySQL.

```jsonc title="wrangler.jsonc"
<WranglerConfig>

```jsonc
{
"hyperdrive": [
{
"binding": "HYPERDRIVE",
"id": "<YOUR_HYPERDRIVE_CACHE_ENABLED_CONFIGURATION_ID>",
},
{
// Rest of file
"hyperdrive": [
{
"binding": "HYPERDRIVE",
"id": "<YOUR_HYPERDRIVE_CACHE_ENABLED_CONFIGURATION_ID>"
},
{
"binding": "HYPERDRIVE_CACHE_DISABLED",
"id": "<YOUR_HYPERDRIVE_CACHE_DISABLED_CONFIGURATION_ID>"
}
]
}
```
"binding": "HYPERDRIVE_CACHE_DISABLED",
"id": "<YOUR_HYPERDRIVE_CACHE_DISABLED_CONFIGURATION_ID>",
},
],
}
```

</WranglerConfig>

## Next steps

- Learn more about [How Hyperdrive works](/hyperdrive/concepts/how-hyperdrive-works/).
- Learn how to [Connect to PostgreSQL](/hyperdrive/examples/connect-to-postgres/) from Hyperdrive.
- Review [Troubleshooting common issues](/hyperdrive/observability/troubleshooting/) when connecting a database to Hyperdrive.
- For more information, refer to [How Hyperdrive works](/hyperdrive/concepts/how-hyperdrive-works/).
- To connect to PostgreSQL, refer to [Connect to PostgreSQL](/hyperdrive/examples/connect-to-postgres/).
- For troubleshooting guidance, refer to [Troubleshoot and debug](/hyperdrive/observability/troubleshooting/).
7 changes: 5 additions & 2 deletions src/content/docs/hyperdrive/observability/metrics.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sidebar:

import { DashButton } from "~/components";

Hyperdrive exposes analytics that allow you to inspect query volume, query latency and cache ratios size across all and/or each Hyperdrive configuration in your account.
Hyperdrive exposes analytics that allow you to inspect query volume, query latency, and cache hit ratios for each Hyperdrive configuration in your account.

## Metrics

Expand All @@ -23,6 +23,8 @@ Hyperdrive currently exports the below metrics as part of the `hyperdriveQueries
| Query Latency | `queryLatency` | The time (in milliseconds) required to query (and receive results) from your database, as measured from your Hyperdrive connection pool(s). |
| Event Status | `eventStatus` | Whether a query responded successfully (`complete`) or failed (`error`). |

The `volatile` cache status indicates the query contains a PostgreSQL function categorized as `STABLE` or `VOLATILE` (for example, `NOW()`, `RANDOM()`). Refer to [Query caching](/hyperdrive/concepts/query-caching/) for details on which functions affect cacheability.

Metrics can be queried (and are retained) for the past 31 days.

## View metrics in the dashboard
Expand All @@ -32,6 +34,7 @@ Per-database analytics for Hyperdrive are available in the Cloudflare dashboard.
1. In the Cloudflare dashboard, go to the **Hyperdrive** page.

<DashButton url="/?to=/:account/workers/hyperdrive" />

2. Select an existing Hyperdrive configuration.
3. Select the **Metrics** tab.

Expand All @@ -41,7 +44,7 @@ You can optionally select a time window to query. This defaults to the last 24 h

You can programmatically query analytics for your Hyperdrive configurations via the [GraphQL Analytics API](/analytics/graphql-api/). This API queries the same datasets as the Cloudflare dashboard, and supports GraphQL [introspection](/analytics/graphql-api/features/discovery/introspection/).

Hyperdrives's GraphQL datasets require an `accountTag` filter with your Cloudflare account ID. Hyperdrive exposes the `hyperdriveQueriesAdaptiveGroups` dataset.
Hyperdrive's GraphQL datasets require an `accountTag` filter with your Cloudflare account ID. Hyperdrive exposes the `hyperdriveQueriesAdaptiveGroups` dataset.

## Write GraphQL queries

Expand Down
12 changes: 10 additions & 2 deletions src/content/docs/hyperdrive/observability/troubleshooting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@

| Error Code | Details | Recommended fixes |
| ---------- | ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `2008` | Bad hostname. | Hyperdrive could not resolve the database hostname. Confirm it exists in public DNS. |

Check warning on line 18 in src/content/docs/hyperdrive/observability/troubleshooting.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
| `2009` | The hostname does not resolve to a public IP address, or the IP address is not a public address. | Hyperdrive can only connect to public IP addresses. Private IP addresses, like `10.1.5.0` or `192.168.2.1`, are not currently supported. |

Check warning on line 19 in src/content/docs/hyperdrive/observability/troubleshooting.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
| `2010` | Cannot connect to the host:port. | Hyperdrive could not route to the hostname: ensure it has a public DNS record that resolves to a public IP address. Check that the hostname is not misspelled. |

Check warning on line 20 in src/content/docs/hyperdrive/observability/troubleshooting.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
| `2011` | Connection refused. | A network firewall or access control list (ACL) is likely rejecting requests from Hyperdrive. Ensure you have allowed connections from the public Internet. |

Check warning on line 21 in src/content/docs/hyperdrive/observability/troubleshooting.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
| `2012` | TLS (SSL) not supported by the database. | Hyperdrive requires TLS (SSL) to connect. Configure TLS on your database. |

Check warning on line 22 in src/content/docs/hyperdrive/observability/troubleshooting.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
| `2013` | Invalid database credentials. | Ensure your username is correct (and exists), and the password is correct (case-sensitive). |

Check warning on line 23 in src/content/docs/hyperdrive/observability/troubleshooting.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
| `2014` | The specified database name does not exist. | Check that the database (not table) name you provided exists on the database you are asking Hyperdrive to connect to. |

Check warning on line 24 in src/content/docs/hyperdrive/observability/troubleshooting.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
| `2015` | Generic error. | Hyperdrive failed to connect and could not determine a reason. Open a support ticket so Cloudflare can investigate. |

Check warning on line 25 in src/content/docs/hyperdrive/observability/troubleshooting.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)
| `2016` | Test query failed. | Confirm that the user Hyperdrive is connecting as has permissions to issue read and write queries to the given database. |

Check warning on line 26 in src/content/docs/hyperdrive/observability/troubleshooting.mdx

View workflow job for this annotation

GitHub Actions / Semgrep

semgrep.style-guide-potential-date-year

Potential year found. Documentation should strive to represent universal truth, not something time-bound. (add [skip style guide checks] to commit message to skip)

### Failure to connect

Expand Down Expand Up @@ -60,9 +60,17 @@
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `Uncaught Error: No such module "node:<module>"` | Your Cloudflare Workers project or a library that it imports is trying to access a Node module that is not available. | Enable [Node.js compatibility](/workers/runtime-apis/nodejs/) for your Cloudflare Workers project to maximize compatibility. |

### Driver errors
### Uncached queries

If your queries are not being cached despite Hyperdrive having caching enabled, check the following:

- **Stable or volatile PostgreSQL functions in your query**: Queries that contain PostgreSQL functions categorized as `STABLE` or `VOLATILE` are not cacheable. Common examples include `NOW()`, `CURRENT_TIMESTAMP`, `CURRENT_DATE`, `RANDOM()`, and `LASTVAL()`. To resolve this, move the function call to your application code and pass the result as a query parameter. For example, instead of `WHERE created_at > NOW()`, compute the timestamp in your Worker and pass it as a parameter: `WHERE created_at > $1`. Refer to [Query caching](/hyperdrive/concepts/query-caching/) for a full list of uncacheable functions.

If your queries are not getting cached despite Hyperdrive having caching enabled, your driver may be configured such that your queries are not cacheable by Hyperdrive. This may happen if you are using the [Postgres.js](https://github.com/porsager/postgres) driver with [`prepare: false:`](https://github.com/porsager/postgres?tab=readme-ov-file#prepared-statements). To resolve this, enable prepared statements with `prepare: true`.
**Function names in SQL comments**: Hyperdrive uses text-based pattern matching to detect uncacheable functions. References to function names like `NOW()` in SQL comments cause the query to be treated as uncacheable, even if the function is not actually called. Remove any references to uncacheable function names from your query text, including comments.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
**Function names in SQL comments**: Hyperdrive uses text-based pattern matching to detect uncacheable functions. References to function names like `NOW()` in SQL comments cause the query to be treated as uncacheable, even if the function is not actually called. Remove any references to uncacheable function names from your query text, including comments.
- **Function names in SQL comments**: Hyperdrive uses text-based pattern matching to detect uncacheable functions. References to function names like `NOW()` in SQL comments cause the query to be treated as uncacheable, even if the function is not actually called. Remove any references to uncacheable function names from your query text, including comments.


- **Driver configuration**: Your driver may be configured such that your queries are not cacheable by Hyperdrive. This may happen if you are using the [Postgres.js](https://github.com/porsager/postgres) driver with [`prepare: false`](https://github.com/porsager/postgres?tab=readme-ov-file#prepared-statements). To resolve this, enable prepared statements with `prepare: true`.

### Driver errors

| Error Message | Details | Recommended fixes |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
Expand Down
Loading