---
title: "How We Fixed Long-Running PostgreSQL now( ) Queries (and Made Them Lightning Fast)"
published: 2022-06-22T09:00:24.000-04:00
updated: 2025-12-09T15:45:09.000-05:00
excerpt: "Help requests about slowdowns in PostgreSQL now( ) queries are a thing of the past. Learn how we fixed it in TimescaleDB 2.7 for lightning-fast performance (up to 400x faster!)."
tags: Announcements & Releases, PostgreSQL Performance, PostgreSQL, #CTA-signup
authors: Sven Klemm
---

> **TimescaleDB is now Tiger Data.**

It was just another regular Wednesday in our home offices when we received a question in the [forum](https://www.timescale.com/forum) about a query with the Postgres now() function. A TimescaleDB user with dozens of tables of IoT data reported a slow degradation in query performance and a creeping server CPU usage. After struggling with the issue, they turned to our community for help.

<iframe src="https://giphy.com/embed/QBAzA0CaPCKwGg3pDs" width="480" height="270" frameborder="0" class="giphy-embed" allowfullscreen=""></iframe>

[via GIPHY](https://giphy.com/gifs/groundhogday-groundhog-day-movie-QBAzA0CaPCKwGg3pDs)

That same question came up in our forum, [Community Slack](http://timescaledb.slack.com), and [support](https://www.timescale.com/support) more often than we’d like. We could relate to this particular pain point because we also struggled with it in partitioned vanilla PostgreSQL. After a closer look at the user’s query, we found the usual suspect: the issue of high planning time in the presence of many chunks—[in Timescale slang, chunks are data partitions within a table](https://docs.timescale.com/timescaledb/latest/overview/core-concepts/hypertables-and-chunks/)—and in a query using a rather common function: `now()`.

Usually, the problem with these queries is that the chunk exclusion happens late. Chunk exclusion is what happens when some data partitions are not even considered during the query to speed up the process. The logic is simple: the fewer data a query has to go through, the faster it is.

However, the problem is that `now()`, [similarly to other stable functions in PostgreSQL](https://www.postgresql.org/docs/current/xfunc-volatility.html), is not considered during plan-time chunk exclusion, those precious moments in which your machine is trying to find the quickest way to execute your query while excluding some of your data partitions to further speed up the process. So, your chunks are only excluded later, at execution time, which results in higher plan time—and yes, you guessed it—slower performance.

Until now, every time this issue popped up, we knew what to do. We had written a wrapper function, marked as immutable, that would call the `now()` function and whose only purpose was to add the immutable marking so that PostgreSQL would consider it earlier during plan-time chunk exclusion, thus improving query performance.

Well, not anymore.

**Today, we’re announcing the optimization of the `now()` function with the release of TimescaleDB 2.7**, which solves this problem by natively performing as our previous workaround.

In this blog post, we’ll look at the basics of the `now()` function, explain how it works in vanilla PostgreSQL and our previous TimescaleDB version, and wrap everything up with a description of our optimization, which evaluates `now()`expressions during plan-time chunk exclusion, significantly reducing planning time. Finally, we include a performance comparison that will blow you away (all we can say for now is “more than 400 times faster”).

<iframe src="https://giphy.com/embed/Gpu3skdN58ApO" width="480" height="382" frameborder="0" class="giphy-embed" allowfullscreen=""></iframe>

[via GIPHY](https://giphy.com/gifs/funny-elephant-fast-Gpu3skdN58ApO)

If you are already a TimescaleDB user, [check out our docs for instructions on how to upgrade](https://docs.timescale.com/timescaledb/latest/how-to-guides/update-timescaledb/). If you are using Timescale, upgrades are automatic, so all you need to do is sit back and enjoy this very fast ride! (New to Timescale? [You can start a free 30-day trial, no credit card required](https://console.cloud.timescale.com/signup).)

## now( ) in Vanilla PostgreSQL

Queries with `now()` expressions are common in time-series data to retrieve readings of the last five minutes, three hours, three days, or other time intervals. In sum, [`now()` is a function](https://www.postgresql.org/docs/current/functions-datetime.html) that returns the current time or, more accurately, the start time of the current transaction. These queries usually only need data from the most recent partition in a [hypertable](https://www.tigerdata.com/blog/database-indexes-in-postgresql-and-timescale-cloud-your-questions-answered), also called chunk.

A query to retrieve readings from the last five minutes could look like this:

```sql
SELECT * FROM hypertable WHERE time > now() - interval ‘5 minutes’;
```

To understand our users' slowdown, it’s vital to know that constraints in PostgreSQL can be constified at different stages in the planning process. The problem with `now()` is that it can only be constified during execution because the planning and execution times may differ.

Since `now()` is a stable function, it’s not considered for plan-time constraint exclusion; therefore, all chunks will have to be part of the planning process. For hypertables with many chunks, this query's total execution time is often dominated by planning time, resulting in poor query performance.

If we dig a little deeper with the EXPLAIN output, we can see that all chunks of the hypertable are part of the plan, painfully increasing it.  

```sql
 Append  (cost=0.00..1118.94 rows=1097 width=20)
   ->  Seq Scan on _hyper_3_38356_chunk  (cost=0.00..1.01 rows=1 width=20)
         Filter: ("time" > now())
   ->  Seq Scan on _hyper_3_38357_chunk  (cost=0.00..1.01 rows=1 width=20)
         Filter: ("time" > now())
   ->  Seq Scan on _hyper_3_38358_chunk  (cost=0.00..1.01 rows=1 width=20)
         Filter: ("time" > now())
   ->  Seq Scan on _hyper_3_38359_chunk  (cost=0.00..1.01 rows=1 width=20)
         Filter: ("time" > now())
   ->  Seq Scan on _hyper_3_38360_chunk  (cost=0.00..1.01 rows=1 width=20)
         Filter: ("time" > now())
   ->  Seq Scan on _hyper_3_38361_chunk  (cost=0.00..1.01 rows=1 width=20)
         Filter: ("time" > now())
```

We had to do something to improve this, and so we did.

## now( ) in TimescaleDB

As proud builders on top of PostgreSQL, we wanted to come up with a solution. So in previous versions of TimescaleDB, we did not use the `now()` expression for plan-time constraint exclusion.

In turn, we implemented constraint exclusion at execution time in a bid to improve query performance. If you want to learn more about how we did this, [check out this blog post, which offers a detailed behind-the-scenes explanation of what happens when you execute a query in PostgreSQL](https://timescale.ghost.io/blog/implementing-constraint-exclusion-for-faster-query-performance/).

While the resulting plan does look much slimmer than the original, all the chunks were still considered during planning and removed only during execution. So, even though the resulting plan looks very different (look at those 1,096 excluded chunks), the effort is very similar to the vanilla PostgreSQL plan.

```sql
Custom Scan (ChunkAppend) on metrics1k  (cost=0.00..1113.45 rows=1097 width=20)
   Chunks excluded during startup: 1096
   ->  Seq Scan on _hyper_3_39453_chunk  (cost=0.00..1.01 rows=1 width=20)
         Filter: ("time" > now())
```

Close, but not good enough.

## now( ) We're Talking

With our latest release, TimescaleDB 2.7, we approached things differently, adding an optimization that would allow the evaluation of `now()` expressions during plan-time chunk exclusion.

Looking at the root of the problem, the reason why `now()` would not be correct is due to prepared statements. If you execute `now()` but only use that value in a transaction half an hour later, the value does not reflect the `current time—now()`—anymore.

However, **it will still hold true for certain expressions even as time goes by.** For example, `time >= now()` will be true at this moment, in 5 minutes and 10 hours. So, when optimizing this, we looked for expressions that held as time passed and used those during plan-time exclusion.  
  
The initial implementation of this feature works for intervals of hours, minutes, and seconds (e.g., `now() - ‘1 hour’`).  
  
As you can see from the EXPLAIN output, chunks are no longer excluded during execution. The exclusion happens earlier, during planning, speeding up the query. Success!

```sql
 Custom Scan (ChunkAppend) on metrics1k  (cost=0.00..1.02 rows=1 width=20)
   Chunks excluded during startup: 0
   ->  Seq Scan on _hyper_3_39453_chunk  (cost=0.00..1.02 rows=1 width=20)
         Filter: (("time" > '2022-05-24 12:41:31.266968+02'::timestamp with time zone) AND ("time" > now()))
```

In the next TimescaleDB version, 2.8, we are removing the initial limitations of the `now()` optimization, making it also available in intervals of months and years. This means that you will be able to make the most of this improvement in a wider range of situations, as any `time > now() - Interval`expression will be usable during plan-time chunk exclusion.

```sql
 Custom Scan (ChunkAppend) on metrics1k  (cost=0.00..1.02 rows=1 width=20)
   Chunks excluded during startup: 0
   ->  Seq Scan on _hyper_3_39453_chunk  (cost=0.00..1.02 rows=1 width=20)
         Filter: ("time" > now())
```

This code is already [committed](https://github.com/timescale/timescaledb/pull/4397) in our [GitHub repo](https://github.com/timescale/timescaledb/pull/4393), and will be available shortly.

## How Does It Work?

But how did we make this current version happen? The optimization works by rewriting the constraint. For example:  

```sql
time > now() - INTERVAL ‘5 min’
```

turns into

```sql
(("time" > (now() - '00:05:00'::interval)) AND ("time" > '2022-06-10 09:58:04.224996+02'::timestamp with time zone))
```

This means that the constified part of the constraint will be used during plan-time chunk exclusion. And, assuming that time only moves forward, the result will still be correct even in the presence of prepared statements, as the original constraint is ANDed with the constified value.

Rewriting the constraint makes the constified value available to plan-time constraint exclusion, leading to massive reductions in planning time, especially in the presence of many chunks.

So we know that this translates into faster queries. But how fast?

## Performance Comparison—now( ) That Is Fast!

As shown in our table, the optimization’s performance improvement scales with the total number of chunks in the hypertables. The more data partitions you’re dealing with, the more you’ll notice the speed improvement—**up to 401x faster in TimescaleDB 2.7** for a total of 20,000 chunks when compared to the previous version.

`now()`that is fast. 🔥

![](https://storage.ghost.io/c/6b/cb/6bcb39cf-9421-4bd1-9c9d-fa7b6755ba0e/content/images/2022/06/Screenshot-2022-06-23-at-10.19.45.png)

_The table lists the total execution time of the query (at the beginning of the post) on hypertables with a different number of chunks_

## now( ) Go Try It

There are few things more satisfying for a developer than solving a problem for your users, especially a recurring one. Achieving such performance optimization is just the icing on the cake.

If you want to experience the lightning-fast performance of PostgreSQL `now()`queries for yourself, TimescaleDB 2.7 is available for Timescale and self-managed TimescaleDB.

-   If you are a Timescale user, you will be automatically upgraded to TimescaleDB 2.7. No action is required from your side. You can also create a free Timescale account to get [a free 30-day trial](https://console.cloud.timescale.com/signup) (no credit card required).
-   If you are using TimescaleDB in your own instances, [check out our docs for instructions on how to upgrade](https://docs.timescale.com/timescaledb/latest/how-to-guides/update-timescaledb/).

Once you’re using TimescaleDB, connect with us! You can find us in our [Community Slack](http://slack.timescale.com/) and the [Timescale Community Forum](http://timescale.com/forum/). We’ll be more than happy to answer any question on query performance improvements, TimescaleDB, PostgreSQL, or other time-series issues.