0

After our provider upgraded our pg-14 database from 14.17 to 14.19, with timescaledb extension 2.5.2, our app completely broke because of awful database performances.

Our analysis showed that the issue was from our queries that used the timestamp type. The following query takes ages to execute (5-10mn)

We generate them with psycopg cursor as such

QUERY = """SELECT m.time, m.value FROM measurements m WHERE m.series_id = %(series_id)s AND m.time >= %(start)s AND m.time <= %(end)s ORDER BY m.time""" cur.execute(QUERY, {"series_id": SERIES_ID, "start": START, "end": END}) 

This generates the following query

SELECT m.time, m.value FROM measurements m WHERE m.time >= '2020-01-02T00:00:00+00:00'::timestamp AND m.time <= '2023-01-01T00:00:00+00:00'::timestamp AND m.series_id= %s ORDER BY m.time 

When we analyze it, we get Query read 1.7 GB from disk (or system disk cache)

explain analyze

Full analyze: https://explain.depesz.com/s/TGIg#stats

I have read that best practice is to use timestamptz (source) but I cannot find what could have caused that change in the release notes for 14.18 nor 14.19.

If this type is not recommended, why does cursor generates queries like that ?

To fix this, we executed a query built with a python string instead of passing arguments to cursor. The following query executes instantaneously.

QUERY = f"""SELECT m.time, m.value FROM measurements m WHERE m.series_id = '{SERIES_ID}' AND m.time >= '{START}' AND m.time <= '{END}' ORDER BY m.time""" cur.execute(QUERY) 

This generates the following query

SELECT m.time, m.value FROM measurements m WHERE m.time >= '2020-01-02T00:00:00+00:00' AND m.time <= '2023-01-01T00:00:00+00:00' AND m.series_id = %s ORDER BY m.time 

This query works just fine, do you know what caused this huge performance change between the pg versions ?

When we analyze it we get Query read 4.6 MB from disk (or system disk cache) explain analyze

Full analyze: https://explain.depesz.com/s/tVBM#stats


Some information about the table

Column types

column | data type -----------+-------------- time | timestamptz value | float8 series_id | text 

Indexes

tablename |indexname |indexdef | ------------------------+----------------------------------------------------------+--------------------------------------------------------------------------------------------------------+ measurements |measurements_series_id_time |CREATE INDEX measurements_series_id_time ON public.measurements USING btree (series_id, "time" DESC) measurements |measurements_pk |CREATE UNIQUE INDEX measurements_pk ON public.measurements USING btree ("time", series_id) measurements |measurements_time_idx |CREATE INDEX measurements_time_idx ON public.measurements USING btree ("time" DESC) 

Some information about the execution environment

  • python : ^3.10
  • psycopg: ^3.1.4
8
  • Share the table and index DDL as well as output of explain(analyze,buffers,settings,verbose) for each variant of the query. As already pointed out, make sure the timezone and datestyle settings are the same when comparing these - the performance could be different but more importantly, these could actually yield different results, invalidating such test. Commented Oct 8 at 17:06
  • What is the type as defined in the table for m.time? Commented Oct 8 at 19:22
  • Please paste the full text plans you get from the explain(analyze,buffers,settings,verbose) for each case, not pictures of summaries. Also, what db vendor is this and do you know their update procedure? Was that a simple in-place pg_upgrade? Did you try running vacuum, analyze, reindex on the tables in question? Commented Oct 9 at 10:01
  • 1) Is the measurements table a TimescaleDB hyper table? 2) If yes to 1) then, timescaledb 2.5.2 was released 2022-02-09 and the current version 2.22.1. You might think about upgrading. 3) Where are you getting the read from disk numbers? 4) What happens if in the first query you do AND m.time >= %(start)s::timestamptz AND m.time <= %(end)s::timestamptz. 5) Add to question text the Python driver you using to do the queries and it's version. Commented Oct 9 at 20:28
  • @Zegarek I've updated the initial post. Please tell me if I can provide anything else that'd be useful. The provider is Scaleway. Unfortunately, I don't know their update procedure. We didn't run vacuum but we ran analyze. We did not run reindex but we tried deleting and recreating the index Commented Oct 10 at 13:11

1 Answer 1

3

In the bad plan after the upgrade, all the row count estimates for the scans of the individual partitions is 1, which is far off the actual row count. That is very likely the cause of the bad performance after the upgrade.

It seems that your provider forgot to gather optimizer statistics after running pg_upgrade.

Kudos to Frank Pachot for pointing that out in a comment.

Sign up to request clarification or add additional context in comments.

7 Comments

Thank you. I'm not sure what you mean by "the parameter must be set differently in the respective sessions". This query is executed on the same system, the only thing that differs is what I shared, which is the way we built it with cursor. Regarding your comment on what it means to "break", I've updated my post but what I initially said was that the app broke because of performances. With your feedback, I've added details to make that more clear
That means that my guess is quite wrong. Please add the results of EXPLAIN (ANALYZE, BUFFERS) for both the slow and the fast query to the question, then I can try to come up with a useful answer.
I did that, thank you. I had to use an external snippet sharing tool because otherwise my post exceed the length limit
I cannot read these plans. Please don't use JSON plans, use the text format. Post a link to explain.depesz.com .
Thanks for the feedback, I've updated my post
The first execution plan has bad cardinality estimation (rows=1) and uses the index on time_idx without series_id. It seems that you have lost the statistics during the upgrade The datatype should not matter here as the conversion is done on the constant
Yes, that must be the cause. I rewrote the answer accordingly.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.