Re: BUG #17344: Assert failed on queiring async_capable foreign table with inheritance - Mailing list pgsql-bugs

From Etsuro Fujita
Subject Re: BUG #17344: Assert failed on queiring async_capable foreign table with inheritance
Date
Msg-id CAPmGK17_Ry1L96N2u3ahTSs8Hz3TQVTJzbtxKNo8z8=ReDhHhw@mail.gmail.com
Whole thread Raw
In response to Re: BUG #17344: Assert failed on queiring async_capable foreign table with inheritance  (Dmitry Dolgov <9erthalion6@gmail.com>)
List pgsql-bugs
On Fri, Dec 31, 2021 at 9:32 PM Dmitry Dolgov <9erthalion6@gmail.com> wrote:
> > On Fri, Dec 31, 2021 at 04:36:42PM +0900, Etsuro Fujita wrote:
> > I think the root cause is that we fail to process a pending async
> > request (if any) in postgresReScanForeignScan() in the case where we
> > just reset the next_tuple counter in that function, without
> > destroying/recreating or rewinding the cursor.  (In the case where we
> > destroy/recreate or rewind the cursor in that function, the pending
> > async request would be processed by pgfdw_exec_query() called from
> > that function.)  This breaks the assumption about ExecAppend() that
> > when called for the first time after ReScan, there are no pending
> > async requests made for async subplans for the Append node, causing
> > the assertion failure in postgresForeignAsyncRequest() called from
> > that ExecAppend().  My oversight in commit 27e1f1456.  :-(
> >
> > To fix, I modified postgresReScanForeignScan() so that we always
> > process a pending async request (if any) before restarting the foreign
> > scan.  Attached is a patch for that.  I tested the patch with the
> > first case, and it addresses the assertion failure.
>
> Yep, makes sense now, thank you. The fix works for me, but I'm curious
> about the requestee condition:
>
>     fsstate->conn_state->pendingAreq->requestee == (PlanState *) node)
>
> You've mentioned that in those cases where the cursor will be
> destroyed/recreated or rewind, pgfdw_exec_query will take care of
> processing pending requests, and looks like it will do this without
> checking the requestee. Just for me to understand, why is this condition
> necessary?

The reason is that the pending request might not need to be processed.
Consider this query:

alter server loopback1 options (add async_capable 'true');
alter server loopback1 options (add use_remote_estimate 'true');
create table a (aa int, constraint a_pkey primary key (aa));
create table loct (aa int, bb text, constraint loct_pkey primary key (aa));
create foreign table b (bb text) inherits (a) server loopback1 options
(table_name 'loct');
insert into a select i from generate_series(1, 10000) i;
insert into b select i, to_char(i, 'FM00000') from generate_series(1, 10000) i;
analyze loct;
analyze a;

explain verbose select * from a t1, a t2 where t1.aa = t2.aa limit 1;
                                            QUERY PLAN
---------------------------------------------------------------------------------------------------
 Limit  (cost=0.29..54.64 rows=1 width=8)
   Output: t1.aa, t2.aa
   ->  Nested Loop  (cost=0.29..2174178.00 rows=40000 width=8)
         Output: t1.aa, t2.aa
         ->  Append  (cost=0.00..700.00 rows=20000 width=4)
               ->  Seq Scan on public.a t1_1  (cost=0.00..145.00
rows=10000 width=4)
                     Output: t1_1.aa
               ->  Async Foreign Scan on public.b t1_2
(cost=100.00..455.00 rows=10000 width=4)
                     Output: t1_2.aa
                     Remote SQL: SELECT aa FROM public.loct
         ->  Append  (cost=0.29..108.65 rows=2 width=4)
               ->  Index Only Scan using a_pkey on public.a t2_1
(cost=0.29..0.31 rows=1 width=4)
                     Output: t2_1.aa
                     Index Cond: (t2_1.aa = t1.aa)
               ->  Async Foreign Scan on public.b t2_2
(cost=100.30..108.33 rows=1 width=4)
                     Output: t2_2.aa
                     Remote SQL: SELECT aa FROM public.loct WHERE
(($1::integer = aa))
(17 rows)

Note that when starting the scan for the outer side, we’ll begin an
asynchronous fetch for the foreign table on the outer side, but we
won’t wait for the results of it until after we get all tuples from
the local table on the outer side (see the documentation note about
the async_capable option for postgres_fdw [1]).  So when rescanning
the inner side after getting the first tuple from the local table on
the outer side, the asynchronous fetch begun for the foreign table on
the outer side will still be in progress, and we’ll have it as
pendingAreq when called from postgresReScanForeignScan() for
rescanning the foreign table on the inner side.  BUT it would be
useless to process the pending request there, because even without
processing the pending request, we can get a matching tuple for the
outer tuple from the local table on the inner side, as the local table
will be scanned first even in the inner side, and then return the
query result.  I think the condition mentioned above is useful to
avoid processing such a useless pending request and return result
tuples more quickly.

Thanks for reviewing!

Best regards,
Etsuro Fujita

[1] https://www.postgresql.org/docs/14/postgres-fdw.html#id-1.11.7.44.11



pgsql-bugs by date:

Previous
From: Vik Fearing
Date:
Subject: Re: BUG #17352: Column CHECK regex error
Next
From: PG Bug reporting form
Date:
Subject: BUG #17354: pg_hba_file_rules always shows verify-ca when auth_method=cert