Thread
-
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 -
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
-
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