Thread

  1. Bug in asynchronous Append

    Alexander Korotkov <aekorotkov@gmail.com> — 2026-07-03T22:00:16Z

    Hi!
    
    ExecReScanAppend() unconditionally resets callback_pending for all
    AsyncRequests.  The problem is that postgres_fdw keeps its own knowledge
    for the same fact: PgFdwConnState.pendingAreq – a pointer to "pending async
    request" for a given connection.  That connection can be shared by several
    partitions/foreign tables (postgres_fdw caches one connection per
    server+usermapping pair). The blind reset in nodeAppend.c only touches the
    local AsyncRequest.callback_pending; it never touches
    PgFdwConnState.pendingAreq, which correctly points to the still-dangling
    request.
    
    Later, when another partition sharing that same connection gets its own
    ReScan (for instance, its chgParam changed because of the LATERAL
    parameter, and it already has a cursor open), it sends "CLOSE cursor" via
    pgfdw_exec_query().  Before sending any new command on the connection, that
    function first drains whatever request is still outstanding on it:
    
    if (state && state->pendingAreq)
        process_pending_request(state->pendingAreq);
    
    And process_pending_request() starts with:
    
    Assert(areq->callback_pending);
    
    – which fails, because the flag was corrupted some rounds earlier.
    
    The attached patch contains both the reproduction case and the fix.  The
    fix postpones the reset of the callback_pending flag
    to ExecAppendAsyncBegin().  ExecAppendAsyncBegin() performs this cleanup
    along with ExecReScan(), which completes the async fetch.
    
    ------
    Regards,
    Alexander Korotkov
    Supabase
    
  2. Re: Bug in asynchronous Append

    Etsuro Fujita <etsuro.fujita@gmail.com> — 2026-07-04T09:04:59Z

    Hi Alexander,
    
    On Sat, Jul 4, 2026 at 7:00 AM Alexander Korotkov <aekorotkov@gmail.com> wrote:
    > ExecReScanAppend() unconditionally resets callback_pending for all AsyncRequests.  The problem is that postgres_fdw keeps its own knowledge for the same fact: PgFdwConnState.pendingAreq – a pointer to "pending async request" for a given connection.  That connection can be shared by several partitions/foreign tables (postgres_fdw caches one connection per server+usermapping pair). The blind reset in nodeAppend.c only touches the local AsyncRequest.callback_pending; it never touches PgFdwConnState.pendingAreq, which correctly points to the still-dangling request.
    >
    > Later, when another partition sharing that same connection gets its own ReScan (for instance, its chgParam changed because of the LATERAL parameter, and it already has a cursor open), it sends "CLOSE cursor" via pgfdw_exec_query().  Before sending any new command on the connection, that function first drains whatever request is still outstanding on it:
    >
    > if (state && state->pendingAreq)
    >     process_pending_request(state->pendingAreq);
    >
    > And process_pending_request() starts with:
    >
    > Assert(areq->callback_pending);
    >
    > – which fails, because the flag was corrupted some rounds earlier.
    >
    > The attached patch contains both the reproduction case and the fix.  The fix postpones the reset of the callback_pending flag to ExecAppendAsyncBegin().  ExecAppendAsyncBegin() performs this cleanup along with ExecReScan(), which completes the async fetch.
    
    Interesting!  Thanks for the report and patch!  Will review.
    
    Best regards,
    Etsuro Fujita
    
    
    
    
  3. Re: Bug in asynchronous Append

    Alexander Pyhalov <a.pyhalov@postgrespro.ru> — 2026-07-06T14:51:45Z

    Alexander Korotkov писал(а) 2026-07-04 01:00:
    > Hi!
    > 
    > ExecReScanAppend() unconditionally resets callback_pending for all
    > AsyncRequests.  The problem is that postgres_fdw keeps its own
    > knowledge for the same fact: PgFdwConnState.pendingAreq – a pointer
    > to "pending async request" for a given connection.  That connection
    > can be shared by several partitions/foreign tables (postgres_fdw
    > caches one connection per server+usermapping pair). The blind reset in
    > nodeAppend.c only touches the local AsyncRequest.callback_pending; it
    > never touches PgFdwConnState.pendingAreq, which correctly points to
    > the still-dangling request.
    > 
    > Later, when another partition sharing that same connection gets its
    > own ReScan (for instance, its chgParam changed because of the LATERAL
    > parameter, and it already has a cursor open), it sends "CLOSE cursor"
    > via pgfdw_exec_query().  Before sending any new command on the
    > connection, that function first drains whatever request is still
    > outstanding on it:
    > 
    > if (state && state->pendingAreq)
    >     process_pending_request(state->pendingAreq);
    > 
    > And process_pending_request() starts with:
    > 
    > Assert(areq->callback_pending);
    > 
    > – which fails, because the flag was corrupted some rounds earlier.
    > 
    > The attached patch contains both the reproduction case and the fix.
    > The fix postpones the reset of the callback_pending flag to
    > ExecAppendAsyncBegin().  ExecAppendAsyncBegin() performs this cleanup
    > along with ExecReScan(), which completes the async fetch.
    > 
    
    
    Hi. The analysis seems correct to me as well as fix.
    -- 
    Best regards,
    Alexander Pyhalov,
    Postgres Professional