Thread: Proposal: add a debug message about using geqo

Proposal: add a debug message about using geqo

From
KAWAMOTO Masaya
Date:
Hi,

During query tuning, users may want to check if GEQO is used or not
to generate a plan. However, users can not know it by simply counting
the number of tables that appear in SQL. I know we can know it by
enabling GEQO_DEBUG flag, but it needs recompiling, so I think it is
inconvenient. 

So, I would like to propose to add a debug level message that shows
when PostgreSQL use GEQO. That enables users to easily see it by
just changing log_min_messages.

Use cases are as follows:
- When investigating about the result of planning, user can determine
whether the plan is chosen by the standard planning or GEQO.

- When tuning PostgreSQL, user can determine the suitable value of
geqo_threshold parameter.

Best regards.

-- 
KAWAMOTO Masaya <kawamoto@sraoss.co.jp>
SRA OSS, Inc. Japan

Attachment

Re: Proposal: add a debug message about using geqo

From
Ashutosh Bapat
Date:
If we add that information to EXPLAIN output, the user won't need
access to server logs.

May be we need it in both the places.

On Tue, May 10, 2022 at 6:35 AM KAWAMOTO Masaya <kawamoto@sraoss.co.jp> wrote:
>
> Hi,
>
> During query tuning, users may want to check if GEQO is used or not
> to generate a plan. However, users can not know it by simply counting
> the number of tables that appear in SQL. I know we can know it by
> enabling GEQO_DEBUG flag, but it needs recompiling, so I think it is
> inconvenient.
>
> So, I would like to propose to add a debug level message that shows
> when PostgreSQL use GEQO. That enables users to easily see it by
> just changing log_min_messages.
>
> Use cases are as follows:
> - When investigating about the result of planning, user can determine
> whether the plan is chosen by the standard planning or GEQO.
>
> - When tuning PostgreSQL, user can determine the suitable value of
> geqo_threshold parameter.
>
> Best regards.
>
> --
> KAWAMOTO Masaya <kawamoto@sraoss.co.jp>
> SRA OSS, Inc. Japan



-- 
Best Wishes,
Ashutosh Bapat



Re: Proposal: add a debug message about using geqo

From
KAWAMOTO Masaya
Date:
On Tue, 10 May 2022 18:49:54 +0530
Ashutosh Bapat <ashutosh.bapat.oss@gmail.com> wrote:

> If we add that information to EXPLAIN output, the user won't need
> access to server logs.
> 
> May be we need it in both the places.

That sounds a nice idea. But I don't think that postgres shows in the
EXPLAIN output why the plan is selected. Would it be appropriate to
show that GEQO is used in EXPLAIN output?

As a test, I created a patch that add information about GEQO to
EXPLAIN output by the GEQO option. The output example is as follows.
What do you think about the location and content of information about GEQO?

postgres=# explain (geqo) select o.id, o.date, c.name as customer_name, bar.amount as total_amount
from orders o join customer c on o.customer_id = c.id
join (select foo.id as id, sum(foo.amount) as amount
        from (select od.order_id as id, p.name as name, od.quantity as quantity, (p.price * od.quantity) as amount
                  from order_detail od join product p on od.product_id = p.id
                  ) as foo
        group by id) as bar on o.id = bar.id ;
                                               QUERY PLAN
--------------------------------------------------------------------------------------------------------
 Hash Join  (cost=118.75..155.04 rows=200 width=48)
   Hash Cond: (o.customer_id = c.id)
   ->  Hash Join  (cost=94.58..130.34 rows=200 width=20)
         Hash Cond: (o.id = bar.id)
         ->  Seq Scan on orders o  (cost=0.00..30.40 rows=2040 width=12)
         ->  Hash  (cost=92.08..92.08 rows=200 width=12)
               ->  Subquery Scan on bar  (cost=88.08..92.08 rows=200 width=12)
                     ->  HashAggregate  (cost=88.08..90.08 rows=200 width=12)
                           Group Key: od.order_id
                           ->  Hash Join  (cost=37.00..72.78 rows=2040 width=12)
                                 Hash Cond: (od.product_id = p.id)
                                 ->  Seq Scan on order_detail od  (cost=0.00..30.40 rows=2040 width=12)
                                 ->  Hash  (cost=22.00..22.00 rows=1200 width=8)
                                       ->  Seq Scan on product p  (cost=0.00..22.00 rows=1200 width=8)
   ->  Hash  (cost=16.30..16.30 rows=630 width=36)
         ->  Seq Scan on customer c  (cost=0.00..16.30 rows=630 width=36)
 GeqoDetails: GEQO: used, geqo_threshold: 3, Max join nodes: 3
(17 rows)

postgres=# set geqo_threshold to 16;
SET
postgres=# explain (geqo) select ... ;
                                               QUERY PLAN
--------------------------------------------------------------------------------------------------------
 Hash Join  (cost=118.75..155.04 rows=200 width=48)
   Hash Cond: (o.customer_id = c.id)
   ->  Hash Join  (cost=94.58..130.34 rows=200 width=20)
         Hash Cond: (o.id = bar.id)
         ->  Seq Scan on orders o  (cost=0.00..30.40 rows=2040 width=12)
         ->  Hash  (cost=92.08..92.08 rows=200 width=12)
               ->  Subquery Scan on bar  (cost=88.08..92.08 rows=200 width=12)
                     ->  HashAggregate  (cost=88.08..90.08 rows=200 width=12)
                           Group Key: od.order_id
                           ->  Hash Join  (cost=37.00..72.78 rows=2040 width=12)
                                 Hash Cond: (od.product_id = p.id)
                                 ->  Seq Scan on order_detail od  (cost=0.00..30.40 rows=2040 width=12)
                                 ->  Hash  (cost=22.00..22.00 rows=1200 width=8)
                                       ->  Seq Scan on product p  (cost=0.00..22.00 rows=1200 width=8)
   ->  Hash  (cost=16.30..16.30 rows=630 width=36)
         ->  Seq Scan on customer c  (cost=0.00..16.30 rows=630 width=36)
 GeqoDetails: GEQO: not used, geqo_threshold: 16, Max join nodes: 3
(17 rows)

postgres=# explain (analyze, settings, geqo) select ...;
                                                                    QUERY PLAN


------------------------------------------------------------------------------------------------------------------------
--------------------------
 Hash Join  (cost=118.75..155.04 rows=200 width=48) (actual time=0.104..0.113 rows=3 loops=1)
   Hash Cond: (o.customer_id = c.id)
   ->  Hash Join  (cost=94.58..130.34 rows=200 width=20) (actual time=0.042..0.048 rows=3 loops=1)
         Hash Cond: (o.id = bar.id)
         ->  Seq Scan on orders o  (cost=0.00..30.40 rows=2040 width=12) (actual time=0.003..0.005 rows=3 loops=1)
         ->  Hash  (cost=92.08..92.08 rows=200 width=12) (actual time=0.034..0.037 rows=3 loops=1)
               Buckets: 1024  Batches: 1  Memory Usage: 9kB
               ->  Subquery Scan on bar  (cost=88.08..92.08 rows=200 width=12) (actual time=0.031..0.035 rows=3
loops=1)
                     ->  HashAggregate  (cost=88.08..90.08 rows=200 width=12) (actual time=0.030..0.033 rows=3
loops=1)
                           Group Key: od.order_id
                           Batches: 1  Memory Usage: 56kB
                           ->  Hash Join  (cost=37.00..72.78 rows=2040 width=12) (actual time=0.016..0.023 rows=7
loops=
1)
                                 Hash Cond: (od.product_id = p.id)
                                 ->  Seq Scan on order_detail od  (cost=0.00..30.40 rows=2040 width=12) (actual
time=0.0
03..0.004 rows=7 loops=1)
                                 ->  Hash  (cost=22.00..22.00 rows=1200 width=8) (actual time=0.007..0.008 rows=4
loops=
1)
                                       Buckets: 2048  Batches: 1  Memory Usage: 17kB
                                       ->  Seq Scan on product p  (cost=0.00..22.00 rows=1200 width=8) (actual
time=0.00
4..0.006 rows=4 loops=1)
   ->  Hash  (cost=16.30..16.30 rows=630 width=36) (actual time=0.019..0.020 rows=3 loops=1)
         Buckets: 1024  Batches: 1  Memory Usage: 9kB
         ->  Seq Scan on customer c  (cost=0.00..16.30 rows=630 width=36) (actual time=0.014..0.016 rows=3 loops=1)
 Settings: geqo_threshold = '16'
 GeqoDetails: GEQO: not used, geqo_threshold: 16, Max join nodes: 3
 Planning Time: 0.516 ms
 Execution Time: 0.190 ms
(24 rows)




> On Tue, May 10, 2022 at 6:35 AM KAWAMOTO Masaya <kawamoto@sraoss.co.jp> wrote:
> >
> > Hi,
> >
> > During query tuning, users may want to check if GEQO is used or not
> > to generate a plan. However, users can not know it by simply counting
> > the number of tables that appear in SQL. I know we can know it by
> > enabling GEQO_DEBUG flag, but it needs recompiling, so I think it is
> > inconvenient.
> >
> > So, I would like to propose to add a debug level message that shows
> > when PostgreSQL use GEQO. That enables users to easily see it by
> > just changing log_min_messages.
> >
> > Use cases are as follows:
> > - When investigating about the result of planning, user can determine
> > whether the plan is chosen by the standard planning or GEQO.
> >
> > - When tuning PostgreSQL, user can determine the suitable value of
> > geqo_threshold parameter.
> >
> > Best regards.
> >
> > --
> > KAWAMOTO Masaya <kawamoto@sraoss.co.jp>
> > SRA OSS, Inc. Japan
> 
> 
> 
> -- 
> Best Wishes,
> Ashutosh Bapat


-- 
KAWAMOTO Masaya <kawamoto@sraoss.co.jp>
SRA OSS, Inc. Japan

Attachment

Re: Proposal: add a debug message about using geqo

From
Jacob Champion
Date:
On Wed, Jun 1, 2022 at 11:09 PM KAWAMOTO Masaya <kawamoto@sraoss.co.jp> wrote:
> That sounds a nice idea. But I don't think that postgres shows in the
> EXPLAIN output why the plan is selected. Would it be appropriate to
> show that GEQO is used in EXPLAIN output?

I'm reminded of Greenplum's "Optimizer" line in its EXPLAIN output
[1], so from that perspective I think it's intuitive.

> As a test, I created a patch that add information about GEQO to
> EXPLAIN output by the GEQO option. The output example is as follows.
> What do you think about the location and content of information about GEQO?

I am a little surprised to see GeqoDetails being printed for a plan
that didn't use GEQO, but again that's probably because I'm used to
GPDB's Optimizer output. And I don't have a lot of personal experience
using alternative optimizers.

One way to think about it might be, if we had ten alternatives, would
we want a line for each showing why it wasn't selected, or just one
line showing the optimizer that was selected? The latter is more
compact but doesn't help you debug why something else wasn't chosen, I
suppose...

--Jacob

[1]
https://docs.vmware.com/en/VMware-Tanzu-Greenplum/6/greenplum-database/GUID-ref_guide-sql_commands-EXPLAIN.html#examples-4



Re: Proposal: add a debug message about using geqo

From
"David G. Johnston"
Date:
On Fri, Jul 22, 2022 at 1:20 PM Jacob Champion <jchampion@timescale.com> wrote:
On Wed, Jun 1, 2022 at 11:09 PM KAWAMOTO Masaya <kawamoto@sraoss.co.jp> wrote:
> That sounds a nice idea. But I don't think that postgres shows in the
> EXPLAIN output why the plan is selected. Would it be appropriate to
> show that GEQO is used in EXPLAIN output?

I'm reminded of Greenplum's "Optimizer" line in its EXPLAIN output
[1], so from that perspective I think it's intuitive.

> As a test, I created a patch that add information about GEQO to
> EXPLAIN output by the GEQO option. The output example is as follows.
> What do you think about the location and content of information about GEQO? 

I am a little surprised to see GeqoDetails being printed for a plan
that didn't use GEQO, but again that's probably because I'm used to
GPDB's Optimizer output. And I don't have a lot of personal experience
using alternative optimizers.

I agree this should be part of explain output.

I would not print the current value of geqo_threshold and leave setting display the exclusive purview of the settings option.

The presentation of only a single geqo result seems incorrect given that multiple trees can exist.  In the first example below the full outer join causes 3 relations to be seen as a single relation at the top level (hence max join nodes = 4) while in the inner join case we see all 6 join nodes.  There should be two outputs of GEQO in the first explain, one with join nodes of 3 and the existing one with 4.

I also don't see the point of labelling them "max"; "join nodes" seems sufficient.

While it can probably be figured out from the rest of the plan, listing the names of the join nodes may be useful (and give join nodes some company).

David J.

postgres=# explain (verbose, geqo) with gs2 (v2) as materialized ( select * from generate_series(1,1) ) select * from gs2 as gs4 full outer join (select gs2a.v2 from gs2 as gs2a, gs2 as gs2b) as gs5 using (v2), generate_series(1, 1) as gs (v1) cross join gs2 as gs3 where v1 IN (select v2 from gs2);
                                             QUERY PLAN                                            
----------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.07..0.21 rows=1 width=12)
   Output: COALESCE(gs4.v2, gs2a.v2), gs.v1, gs3.v2
   CTE gs2
     ->  Function Scan on pg_catalog.generate_series  (cost=0.00..0.01 rows=1 width=4)
           Output: generate_series.generate_series
           Function Call: generate_series(1, 1)
   ->  Nested Loop  (cost=0.06..0.16 rows=1 width=12)
         Output: gs.v1, gs4.v2, gs2a.v2
         ->  Nested Loop  (cost=0.02..0.06 rows=1 width=4)
               Output: gs.v1
               Join Filter: (gs.v1 = gs2.v2)
               ->  Function Scan on pg_catalog.generate_series gs  (cost=0.00..0.01 rows=1 width=4)
                     Output: gs.v1
                     Function Call: generate_series(1, 1)
               ->  HashAggregate  (cost=0.02..0.03 rows=1 width=4)
                     Output: gs2.v2
                     Group Key: gs2.v2
                     ->  CTE Scan on gs2  (cost=0.00..0.02 rows=1 width=4)
                           Output: gs2.v2
         ->  Hash Full Join  (cost=0.03..0.10 rows=1 width=8)
               Output: gs4.v2, gs2a.v2
               Hash Cond: (gs2a.v2 = gs4.v2)
               ->  Nested Loop  (cost=0.00..0.05 rows=1 width=4)
                     Output: gs2a.v2
                     ->  CTE Scan on gs2 gs2b  (cost=0.00..0.02 rows=1 width=0)
                           Output: gs2b.v2
                     ->  CTE Scan on gs2 gs2a  (cost=0.00..0.02 rows=1 width=4)
                           Output: gs2a.v2
               ->  Hash  (cost=0.02..0.02 rows=1 width=4)
                     Output: gs4.v2
                     ->  CTE Scan on gs2 gs4  (cost=0.00..0.02 rows=1 width=4)
                           Output: gs4.v2
   ->  CTE Scan on gs2 gs3  (cost=0.00..0.02 rows=1 width=4)
         Output: gs3.v2
 GeqoDetails: GEQO: used, geqo_threshold: 2, Max join nodes: 4
(35 rows)

postgres=# explain (verbose, geqo) with gs2 (v2) as materialized ( select * from generate_series(1,1) ) select * from gs2 as gs4 join (select gs2a.v2 from gs2 as gs2a, gs2 as gs2b) as gs5 using (v2), generate_series(1, 1) as gs (v1) cross join gs2 as gs3 where v1 IN (select v2 from gs2);
                                                QUERY PLAN                                                
----------------------------------------------------------------------------------------------------------
 Nested Loop  (cost=0.02..0.18 rows=1 width=12)
   Output: gs4.v2, gs.v1, gs3.v2
   CTE gs2
     ->  Function Scan on pg_catalog.generate_series  (cost=0.00..0.01 rows=1 width=4)
           Output: generate_series.generate_series
           Function Call: generate_series(1, 1)
   ->  Nested Loop  (cost=0.00..0.14 rows=1 width=12)
         Output: gs.v1, gs4.v2, gs3.v2
         ->  Nested Loop  (cost=0.00..0.11 rows=1 width=8)
               Output: gs.v1, gs4.v2
               ->  Nested Loop Semi Join  (cost=0.00..0.04 rows=1 width=4)
                     Output: gs.v1
                     Join Filter: (gs.v1 = gs2.v2)
                     ->  Function Scan on pg_catalog.generate_series gs  (cost=0.00..0.01 rows=1 width=4)
                           Output: gs.v1
                           Function Call: generate_series(1, 1)
                     ->  CTE Scan on gs2  (cost=0.00..0.02 rows=1 width=4)
                           Output: gs2.v2
               ->  Nested Loop  (cost=0.00..0.05 rows=1 width=4)
                     Output: gs4.v2
                     Join Filter: (gs2a.v2 = gs4.v2)
                     ->  CTE Scan on gs2 gs2a  (cost=0.00..0.02 rows=1 width=4)
                           Output: gs2a.v2
                     ->  CTE Scan on gs2 gs4  (cost=0.00..0.02 rows=1 width=4)
                           Output: gs4.v2
         ->  CTE Scan on gs2 gs3  (cost=0.00..0.02 rows=1 width=4)
               Output: gs3.v2
   ->  CTE Scan on gs2 gs2b  (cost=0.00..0.02 rows=1 width=0)
         Output: gs2b.v2
 GeqoDetails: GEQO: used, geqo_threshold: 2, Max join nodes: 6
(30 rows)