Skip to content

State plainly that TimescaleDB leads the cross-engine table - #381

Merged
jdatcmd merged 1 commit into
commandprompt:mainfrom
ChronicallyJD:docs/honest-cross-engine
Aug 4, 2026
Merged

State plainly that TimescaleDB leads the cross-engine table#381
jdatcmd merged 1 commit into
commandprompt:mainfrom
ChronicallyJD:docs/honest-cross-engine

Conversation

@ChronicallyJD

Copy link
Copy Markdown
Collaborator

What

The narrative I wrote for #377 read more favourably than the numbers support. This
corrects the reading. No number changes.

Raised by jd looking at the merged table and asking whether pgColumnar is considerably
slower. It is, and the page did not say so.

Three things were wrong

1. The page never stated the headline result. TimescaleDB is faster than
pgColumnar on every query it completes:

q1 q2 q3 q4 q5 q6 q7 q8
TS faster by (serial) 101x 442x 679x 1.6x 1.6x 6.5x 2.7x 2.1x

The page said TimescaleDB "leads every query that filters one host" — true, and it
reads as though the rest were close. Against heap we win q4/q5/q6/q8 by 20-50% and
lose the other four.

2. One claim rested on a competitor's crash. "pgColumnar leads the wide aggregate
shapes with workers... first on both" is true at P=4 only because TimescaleDB's
parallel arm fails on this host
. In serial, where it runs, it beats us on both q5 and
q6. The method section disclosed the failure; the paragraph making the claim did not
repeat it, and that is where a reader forms the impression.

3. A factual error in the setup list. It said pgColumnar's storage is "in load
order, which is time ascending". It is not: measured correlation with physical order is
0.013 for time and -0.004 for hostname. Neither key is sorted.

And that third point is the explanation, which was missing

Measured on q2 with EXPLAIN (ANALYZE):

Columnar Chunk Groups Total: 667
Columnar Chunk Groups Read: 667
Columnar Chunk Groups Removed by Filter: 0
Rows Removed by Filter: 99995680

Zero groups skipped. It reads all 667 and filters 99,995,680 rows to return 4,320.
The zone maps cannot help because every stripe holds all 4,000 hosts, so each group's
hostname min/max covers the whole set. TimescaleDB answers the same query in ~2 ms by
excluding all but one chunk on time, then reading one hostname segment through an
index.

What I did not do

I did not re-measure a clustered pgColumnar table to make the numbers look better.
The page now says the table measures pgColumnar in the layout that suits it least, that
a user with this shape would cluster on hostname (what segmentby does for
TimescaleDB), that the configuration is unmeasured, and that the q1-q3 gap should not be
read as a property of columnar storage until it is.

Measuring the clustered variant is worth doing and belongs in its own change, with its
own method, rather than folded into a correction of my own framing.

test/ste_check.py passes on every user-facing document.

Conflict note

#380 also touches docs/benchmarks.md. If that merges first I will rebase; the changes
are in different parts of the file.

🤖 Generated with Claude Code

The narrative I wrote for commandprompt#377 read more favourably than the numbers support,
and one claim rested on a competitor crash.

TimescaleDB is faster than pgColumnar on every query it completes: 1.6x on q4
and q5, 2.1x on q8, 2.7x on q7, 6.5x on q6, and 101x, 442x and 679x on q1, q2
and q3. The page did not say so. It said TimescaleDB "leads every query that
filters one host", which is true and reads as though the rest were even.

The claim that pgColumnar "leads the wide aggregate shapes with workers" was
technically true and misleading. pgColumnar is first on q5 and q6 at four
workers because TimescaleDB's parallel arm fails on this host. In serial, where
TimescaleDB runs, it is ahead on both. The method section disclosed the failure;
the paragraph making the claim did not repeat it, and that is where a reader
forms the impression.

The setup list said pgColumnar's storage is "in load order, which is time
ascending". It is not. Measured correlation with physical order is 0.013 for
time and -0.004 for hostname, so neither key is sorted.

That last fact is also the explanation, which the page did not give. Measured on
q2: 667 of 667 row groups read, none removed by filter, 99,995,680 rows filtered
to return 4,320. Every stripe holds all 4,000 hosts, so each group's hostname
minimum and maximum covers the whole set and no group can be skipped.
TimescaleDB excludes all but one chunk on time and reads one hostname segment
through an index, in about 2 ms.

So the table measures pgColumnar in the layout that suits it least, and now says
so. A user with this query shape would cluster on hostname, which is what
segmentby does for TimescaleDB. That configuration is not measured, and the page
now asks the reader not to treat the q1 to q3 gap as a property of columnar
storage until it is.

No numbers changed. This is the reading of them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@jdatcmd jdatcmd left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approved. I verified the three corrections on the bench rather than taking them.

This is the right call and it matters more than usual, because the 1.0-alpha release
notes link to this page.

Claim 3, the factual error, confirmed. The setup list said storage is "in load
order, which is time ascending". From pg_stats on cpu_pgc:

time     correlation =  0.0133
hostname correlation = -0.0036

Neither key is sorted. The old sentence was not imprecise, it was wrong, and it was
the sentence a reader would have used to reason about why q1-q3 are slow.

The mechanism, confirmed. Ran q2 with EXPLAIN (ANALYZE) on the bench:

Columnar Chunk Groups Total: 667
Columnar Chunk Groups Read: 667
Columnar Chunk Groups Removed by Filter: 0
Columnar Vectors Skipped: 0
Rows Removed by Filter: 99995680

667 of 667 read, nothing skipped at either granularity, 99,995,680 rows filtered to
return 4,320. Exactly as the page now says.

Every multiplier checks out against the table: q1 403/4 = 101, q2 2,211/5 = 442,
q3 4,071/6 = 679, q4 1.60, q5 1.62, q6 6.46, q7 2.65, q8 2.09. All eight round to
what the prose claims.

One slip, and it is the kind this PR exists to catch

TimescaleDB answers the same query in about 2 milliseconds.

The table in the same file says 5 ms for q2, and the 442x figure depends on 5.
There is no 2 ms cell anywhere in that row; the closest is q1 at 4.

I am fixing it to 5 on merge rather than sending this back for one word.

On the judgement

The part I would have got wrong is what you did not do: re-measure a clustered
pgColumnar table so the numbers look better. Saying "this table measures pgColumnar in
the layout that suits it least, that configuration is not measured here, do not read
the gap as a property of columnar storage until it is" is harder to write and is the
honest version. A benchmark page that quietly picks the flattering layout is how
benchmark pages lose their value.

Same for putting TimescaleDB's parallel failure in the paragraph that makes the
claim
rather than only in the method. A reader forms the impression at the claim.

The CI red was the Install PostgreSQL 18 step timing out at exit 124, the
package-fetch flake #352 addressed; it cleared on rerun. That is its fifth occurrence
today and probably deserves its own issue.

Merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants