Re: hashjoins vs. Bloom filters (yet again)

From: "Matheus Alcantara" <matheusssilv97(at)gmail(dot)com>
To: "Tomas Vondra" <tomas(at)vondra(dot)me>, "Andrew Dunstan" <andrew(at)dunslane(dot)net>, "PostgreSQL Hackers" <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: hashjoins vs. Bloom filters (yet again)
Date: 2026-07-21 17:30:30
Message-ID: DK4FEQS981UW.3L05KVSJ7M6VE@gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Mon Jul 20, 2026 at 7:31 PM -03, Tomas Vondra wrote:
> On 7/20/26 23:31, Matheus Alcantara wrote:
>> On Mon Jul 20, 2026 at 4:17 PM -03, Tomas Vondra wrote:
>>>> So I think that we need better heuristics to decide when to create such
>>>> filters. As I mention above, I'll work on to implement such heuristics
>>>> based on the paper to see if we get better values here.
>>>>
>>>
>>> Possibly. I think it'd be good to know how many new paths we actually
>>> got, and see if we can eliminate at least some of them early (I don't
>>> recall - does add_path compare costs for paths with the same filters)?
>>>
>>
>> I instrumented generate_expected_filter_paths() to count, per base rel,
>> the eligible base paths, the filter combinations, and the pathlist
>> length before/after the add_path() calls. Running the star-schema
>> queries for the fact_sales scan:
>>
>> N basepaths combinations new paths pathlist before -> after
>> 2 3 3 9 5 -> 14
>> 3 4 7 28 7 -> 35
>> 4 5 13 65 9 -> 74
>> 5 6 23 138 11 -> 149
>> 6 7 36 252 13 -> 265
>>
>
> Do I understand correctly this is for paths on the fact_sales, and there
> are no new paths on the other relations (dimensions)?
>

Yes, you are correct. Only fact_sales gets new paths. The dimensions are
pure build sides, nothing above them pushes a filter onto a dimension's
own scan, so find_interesting_bloom_filters returns nothing for them and
no filtered paths are generated.

The snowflake query is the exception. dim_product also gets filtered
paths, because it sits in the middle, it receives a filter built from
dim_category while itself being a build side for the fact join.

>> About your question: yes, add_path() does cost-compare paths that carry
>> the same filter set - they go through the normal cost/pathkeys/rows
>> domination like any other pair. What it deliberately does not do is
>> compare across different filter sets: add_path bails out as soon as
>> expected_filters differ, on the grounds that a scan feeding filter A and
>> a scan feeding filter B serve different parent joins and aren't
>> comparable. I think that's the right call, pruning by cost across filter
>> sets seems wrong, since a more expensive scan carrying a better filter
>> can pay off at the join above.
>>
>> The consequence, though, is that add_path prunes essentially nothing
>> here. The surviving count is basepaths * combinations exactly — 0 pruned
>> in every single query. The fact_sales pathlist goes from N+1 base paths
>> to (N+1)*combinations - ~20x at N=6 - and every one of those extra scan
>> paths then has to be considered as a join input at every level, which I
>> think that is where the superlinear planning time comes from.
>>
>
> Right, that explanation seems plausible.
>
> I'm not sure if you tried with the v5 or v6 patches, but I assume the
> join stuff in v6 would make this somewhat worse due to generating even
> more candidate filters. Well, maybe not for starjoin schemas, but for
> snowflake.
>

I've executed on top of v5 patches, but now I've also executed on v6,
see the results:

N filters basepaths combos generated pathlist before->after
2 2 3 3 9 5 -> 14
3 3 4 7 28 7 -> 35
4 3 5 7 35 9 -> 44
5 3 6 7 42 11 -> 53
6 3 7 7 49 13 -> 62

v6 does generate more candidate filters, but IIUC
enumerate_bloom_filter_build_relids() generate multi relation build
sides (e.g. product JOIN category as the source of a filter on the fact)
that v5 never considered. But those extra candidates get de-duplicated
and then capped at bloom_filter_pushdown_max most selective. So the
surviving filter count stays <= 3 regardless of how many the join
enumeration proposes.

But I think that what you was saying is v6 with v5-0005 which generate
more candidate filters without capping at bloom_filter_pushdown_max. I
didn't look at very deep on v6 patchset yet but I've tried to implement
v5-0005 on top of v6 (by mosting copy and pasting with some required
changes) and I think that you are correct. For startjoin schemas the
result was the same from v5 but for the snowflake case the paths
generated was a bit higher for the fact_tables, 120 generated paths
compared with 72 on v5.

I'll look more deep on v6 changes and I'll share more soon.

--
Matheus Alcantara
EDB: https://www.enterprisedb.com

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Andrey Borodin 2026-07-21 18:08:16 Re: amcheck: add index-all-keys-match verification for B-Tree
Previous Message surya poondla 2026-07-21 17:26:01 Re: Fix races conditions in DropRole() and GrantRole()