Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@bandetto
Copy link
Member

@bandetto bandetto commented Jun 27, 2025

When querying pg_locks (or using pg_lock_status()), approximately 75% of backend memory allocations for resulting tuples weren't registered with Vmtracker or Resource Group Control. This memory would also leak if the query was cancelled or failed.

This happened because CdbDispatchCommand(), which is used by pg_locks, internally uses libpq to obtain the results, which were allocated as PQresult structures with bare malloc(), even on the server side.

Another issue is that if a query fails under resource group manager due to a lack of memory, the query is cancelled due to an error. The coordinator would still try to receive tuples from segments upon cancellation. Retrieving tuples requires more memory allocations, which may cause the same OOM error again. The query will get cancelled due to an error, entering into a recursion and leading to an eventual crash.

The first commit fixes both the memory leak and untracked memory issues by enforcing Vmtracker routines for PGresult allocations on the server-side. The original code is unchanged from #1231, besides addressing errcode-related macro redefinition warnings by un-defining them first.

The second commit prevents error recursion, upon query cancellation. It implements proper query cancellation by explicitly ignoring any libpq data from segments without extra memory allocations. The said issue cannot be reproduced without first commit. The original code was updated since #1231, based on #1668.

The PR contains several patches as separate, atomic commits, and therefore should be rebased. ADCC extension tests should pass before merging the PR.


When approved the PR will be moved to Greengage repo for merge. ABI tests for adb-6.x-dev are broken.

@bandetto bandetto force-pushed the ADBDEV-7690-7691 branch 2 times, most recently from ea58a4d to 3e21592 Compare June 27, 2025 12:50
bandetto added a commit that referenced this pull request Jun 27, 2025
When querying pg_locks (or using `pg_lock_status()`), approximately 75% of
backend memory allocations for resulting tuples weren't registered with
Vmtracker or Resource Group Control. This memory would also leak if the query
was cancelled or failed.

This happened because `CdbDispatchCommand()`, which is used by `pg_locks`,
internally uses libpq to obtain the results, which were allocated as PQresult
structures with bare `malloc()`, even on the server side.

This patch fixes both untracked memory issues by enforcing Vmtracker routines
for `PGresult` allocations on the server-side.

Please note that the original error from ADBDEV-7145, which is an
`AbortTransaction()` recurstion due to an OOM error while cancelling a query,
is fixed only in the next commit.

Ticket: ADBDEV-7691
bandetto added a commit that referenced this pull request Jun 27, 2025
Previosly, if a query fails under resource group manager due to a lack of
memory, the query is cancelled due to an error.

The coordinator would still try to receive tuples from segments upon
cancellation. Retrieving tuples requires more memory allocations, which may
cause the same OOM error again. The query will get cancelled due to an error,
entering into a recursion and leading to an eventual crash.

This patch implements proper query cancellation by explicitly ignoring any
libpq data from segments without extra memory allocations.

Ticket: ADBDEV-7690
@bandetto bandetto changed the title ADBDEV-7691: Track PQresult allocations in server code, ADBDEV-7690: Gracefully handle OOM when cancelling the query ADBDEV-7691: Track PQresult allocations in server side, ADBDEV-7690: Gracefully handle OOM when cancelling a query Jun 27, 2025
@bandetto bandetto changed the title ADBDEV-7691: Track PQresult allocations in server side, ADBDEV-7690: Gracefully handle OOM when cancelling a query ADBDEV-7691, ADBDEV-7690: Track PQresult allocations in server side, Gracefully handle OOM when cancelling a query Jun 27, 2025
bandetto added a commit that referenced this pull request Jun 30, 2025
When querying pg_locks (or using `pg_lock_status()`), approximately 75% of
backend memory allocations for resulting tuples weren't registered with
Vmtracker or Resource Group Control. This memory would also leak if the query
was cancelled or failed.

This happened because `CdbDispatchCommand()`, which is used by `pg_locks`,
internally uses libpq to obtain the results, which were allocated as PQresult
structures with bare `malloc()`, even on the server side.

This patch fixes both untracked memory issues by enforcing Vmtracker routines
for `PGresult` allocations on the server-side.

Please note that the original error from ADBDEV-7145, which is an
`AbortTransaction()` recurstion due to an OOM error while cancelling a query,
is fixed only in the next commit.

Ticket: ADBDEV-7691
bandetto added a commit that referenced this pull request Jun 30, 2025
Previosly, if a query fails under resource group manager due to a lack of
memory, the query is cancelled due to an error.

The coordinator would still try to receive tuples from segments upon
cancellation. Retrieving tuples requires more memory allocations, which may
cause the same OOM error again. The query will get cancelled due to an error,
entering into a recursion and leading to an eventual crash.

This patch implements proper query cancellation by explicitly ignoring any
libpq data from segments without extra memory allocations.

Ticket: ADBDEV-7690
@bandetto bandetto force-pushed the ADBDEV-7690-7691 branch 2 times, most recently from 9b90def to 8013158 Compare July 1, 2025 08:22
bandetto added a commit that referenced this pull request Jul 1, 2025
When querying pg_locks (or using `pg_lock_status()`), approximately 75% of
backend memory allocations for resulting tuples weren't registered with
Vmtracker or Resource Group Control. This memory would also leak if the query
was cancelled or failed.

This happened because `CdbDispatchCommand()`, which is used by `pg_locks`,
internally uses libpq to obtain the results, which were allocated as PQresult
structures with bare `malloc()`, even on the server side.

This patch fixes both untracked memory issues by enforcing Vmtracker routines
for `PGresult` allocations on the server-side.

Please note that the original error from ADBDEV-7145, which is an
`AbortTransaction()` recurstion due to an OOM error while cancelling a query,
is fixed only in the next commit.

Ticket: ADBDEV-7691
bandetto added a commit that referenced this pull request Jul 1, 2025
Previosly, if a query fails under resource group manager due to a lack of
memory, the query is cancelled due to an error.

The coordinator would still try to receive tuples from segments upon
cancellation. Retrieving tuples requires more memory allocations, which may
cause the same OOM error again. The query will get cancelled due to an error,
entering into a recursion and leading to an eventual crash.

This patch implements proper query cancellation by explicitly ignoring any
libpq data from segments without extra memory allocations.

Ticket: ADBDEV-7690
bandetto added a commit that referenced this pull request Jul 1, 2025
Previosly, `cdbconn_discardResults()` has proved to be unrealiable in case of
OOM, as it would still try to read the results, causing errors in recursion if
called when aborting a query.

This patch refactors the said function by transferring code from another
similar procedure, to make sure we're discarding results in a more robust way,
adressing all libpq pitfalls instead of mindlessly retrying, as opposed to the
past behavior.

Ticket: ADBDEV-7690
@bandetto bandetto force-pushed the ADBDEV-7690-7691 branch from 8013158 to 907999e Compare July 1, 2025 08:43
bandetto added a commit that referenced this pull request Jul 1, 2025
When querying pg_locks (or using `pg_lock_status()`), approximately 75% of
backend memory allocations for resulting tuples weren't registered with
Vmtracker or Resource Group Control. This memory would also leak if the query
was cancelled or failed.

This happened because `CdbDispatchCommand()`, which is used by `pg_locks`,
internally uses libpq to obtain the results, which were allocated as PQresult
structures with bare `malloc()`, even on the server side.

This patch fixes both untracked memory issues by enforcing Vmtracker routines
for `PGresult` allocations on the server-side.

Please note that the original error from ADBDEV-7145, which is an
`AbortTransaction()` recurstion due to an OOM error while cancelling a query,
is fixed only in the next commit.

Ticket: ADBDEV-7691
bandetto added a commit that referenced this pull request Jul 1, 2025
Previosly, if a query fails under resource group manager due to a lack of
memory, the query is cancelled due to an error.

The coordinator would still try to receive tuples from segments upon
cancellation. Retrieving tuples requires more memory allocations, which may
cause the same OOM error again. The query will get cancelled due to an error,
entering into a recursion and leading to an eventual crash.

This patch implements proper query cancellation by explicitly ignoring any
libpq data from segments without extra memory allocations.

Ticket: ADBDEV-7690
@bandetto bandetto force-pushed the ADBDEV-7690-7691 branch from 907999e to c5f2f45 Compare July 1, 2025 09:02
bandetto added a commit that referenced this pull request Jul 1, 2025
Previosly, `cdbconn_discardResults()` has proved to be unrealiable in case of
OOM, as it would still try to read the results, causing errors in recursion if
called when aborting a query.

This patch refactors the said function by transferring code from another
similar procedure, to make sure we're discarding results in a more robust way,
adressing all libpq pitfalls instead of mindlessly retrying, as opposed to the
past behavior.

Ticket: ADBDEV-7690
bandetto added a commit that referenced this pull request Jul 21, 2025
When querying pg_locks (or using `pg_lock_status()`), approximately 75% of
backend memory allocations for resulting tuples weren't registered with
Vmtracker or Resource Group Control. This memory would also leak if the query
was cancelled or failed.

This happened because `CdbDispatchCommand()`, which is used by `pg_locks`,
internally uses libpq to obtain the results, which were allocated as PQresult
structures with bare `malloc()`, even on the server side.

This patch fixes both untracked memory issues by enforcing Vmtracker routines
for `PGresult` allocations on the server-side.

Please note that the original error from ADBDEV-7145, which is an
`AbortTransaction()` recurstion due to an OOM error while cancelling a query,
is fixed only in the next commit.

Ticket: ADBDEV-7691
bandetto added a commit that referenced this pull request Jul 21, 2025
Previosly, if a query fails under resource group manager due to a lack of
memory, the query is cancelled due to an error.

The coordinator would still try to receive tuples from segments upon
cancellation. Retrieving tuples requires more memory allocations, which may
cause the same OOM error again. The query will get cancelled due to an error,
entering into a recursion and leading to an eventual crash.

This patch implements proper query cancellation by explicitly ignoring any
libpq data from segments without extra memory allocations.

Ticket: ADBDEV-7690
bandetto added a commit that referenced this pull request Jul 21, 2025
Previosly, `cdbconn_discardResults()` has proved to be unrealiable in case of
OOM, as it would still try to read the results, causing errors in recursion if
called when aborting a query.

This patch refactors the said function by transferring code from another
similar procedure, to make sure we're discarding results in a more robust way,
adressing all libpq pitfalls instead of mindlessly retrying, as opposed to the
past behavior.

Ticket: ADBDEV-7690
@bandetto bandetto force-pushed the ADBDEV-7690-7691 branch 4 times, most recently from c61249d to 4709631 Compare July 23, 2025 05:51
@RekGRpth

This comment was marked as resolved.

pqSaveErrorResult(conn);

/* Make sure PQgetResult() calls are not blocking. */
PQconsumeInput(conn);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't you check what this function returned?!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function returns 0 on error, and you ignore it!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why shouldn't it be ignored? Errors will be caught by PQgetResult() and a condition at the end.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see similar function processResults

Copy link
Member Author

@bandetto bandetto Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still can't understand you and I don't see a point in checking the return code. Can you please explain the reason?

bandetto added a commit that referenced this pull request Jul 25, 2025
When querying pg_locks (or using `pg_lock_status()`), approximately 75% of
backend memory allocations for resulting tuples weren't registered with
Vmtracker or Resource Group Control. This memory would also leak if the query
was cancelled or failed.

This happened because `CdbDispatchCommand()`, which is used by `pg_locks`,
internally uses libpq to obtain the results, which were allocated as PQresult
structures with bare `malloc()`, even on the server side.

This patch fixes both untracked memory issues by enforcing Vmtracker routines
for `PGresult` allocations on the server-side.

Including `postgres.h` in frontend code causes several errcode-related macro
redefinition warnings. They are now un-definined first.

Please note that the original error from ADBDEV-7145, which is an
`AbortTransaction()` recurstion due to an OOM error while cancelling a query,
is fixed only in the next commit.

Ticket: ADBDEV-7691
bandetto added a commit that referenced this pull request Jul 25, 2025
Previosly, if a query fails under resource group manager due to a lack of
memory, the query is cancelled due to an error.

The coordinator would still try to receive tuples from segments upon
cancellation. Retrieving tuples requires more memory allocations, which may
cause the same OOM error again. The query will get cancelled due to an error,
entering into a recursion and leading to an eventual crash.

This patch implements proper query cancellation by explicitly ignoring any
libpq data from segments without extra memory allocations.

Ticket: ADBDEV-7690
bandetto added a commit that referenced this pull request Jul 25, 2025
When querying pg_locks (or using `pg_lock_status()`), approximately 75% of
backend memory allocations for resulting tuples weren't registered with
Vmtracker or Resource Group Control. This memory would also leak if the query
was cancelled or failed.

This happened because `CdbDispatchCommand()`, which is used by `pg_locks`,
internally uses libpq to obtain the results, which were allocated as PQresult
structures with bare `malloc()`, even on the server side.

This patch fixes both untracked memory issues by enforcing Vmtracker routines
for `PGresult` allocations on the server-side.

Including `postgres.h` in frontend code causes several errcode-related macro
redefinition warnings. They are now un-definined first.

Please note that the original error from ADBDEV-7145, which is an
`AbortTransaction()` recurstion due to an OOM error while cancelling a query,
is fixed only in the next commit.

Ticket: ADBDEV-7691
bandetto added a commit that referenced this pull request Jul 25, 2025
Previosly, if a query fails under resource group manager due to a lack of
memory, the query is cancelled due to an error.

The coordinator would still try to receive tuples from segments upon
cancellation. Retrieving tuples requires more memory allocations, which may
cause the same OOM error again. The query will get cancelled due to an error,
entering into a recursion and leading to an eventual crash.

This patch implements proper query cancellation by explicitly ignoring any
libpq data from segments without extra memory allocations.

Ticket: ADBDEV-7690
@RekGRpth
Copy link
Member

Still, I suggest separating the patch for correcting the recursive error into a separate PR and accepting it first.

bandetto added a commit that referenced this pull request Jul 29, 2025
Previosly, if a query fails under resource group manager due to a lack of
memory, the query is cancelled due to an error.

The coordinator would still try to receive tuples from segments upon
cancellation. Retrieving tuples requires more memory allocations, which may
cause the same OOM error again. The query will get cancelled due to an error,
entering into a recursion and leading to an eventual crash.

This patch implements proper query cancellation by explicitly ignoring any
libpq data from segments without extra memory allocations.

Ticket: ADBDEV-7690
@bandetto
Copy link
Member Author

Still, I suggest separating the patch for correcting the recursive error into a separate PR and accepting it first.

I disagree. The current approach was chosen since the recursive error patch cannot be tested without the first commit.

* might not be enough memory to discard the result properly.
* Let's get the big guns out.
*/
tryToWipeResults(dispatchResult);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your tryToWipeResults function is very similar to the processResults function: in both functions

  1. first the PQconsumeInput function is called
  2. then there is a loop until PQisBusy
  3. inside the loop the PQgetResult function is called
    Maybe it would be easier to add the pParms->waitMode == DISPATCH_WAIT_CANCEL argument to the processResults function and its processing?

Copy link
Member Author

@bandetto bandetto Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

processResults() is complicated and I'll risk breaking something else when making changes there.

cdbconn_discardResults() is also almost identical to the added function. But tryToWipeResults() was added for a reason and does only what it needs to.

Copy link
Member

@bimboterminator1 bimboterminator1 Aug 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, why we cannot use cdbconn_discardResults()?
And I agree with @RekGRpth about avoiding injecting a special case this way. Are you sure there is not any approach that looks more organic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, why we cannot use cdbconn_discardResults()

Because it's used in other context and I'd prefer not to change existing behavior.

Are you sure there is not any approach that looks more organic?

I think the current state of the patch is the most straightforward and safe way to make this change.

bandetto added a commit that referenced this pull request Jul 29, 2025
Previosly, if a query fails under resource group manager due to a lack of
memory, the query is cancelled due to an error.

The coordinator would still try to receive tuples from segments upon
cancellation. Retrieving tuples requires more memory allocations, which may
cause the same OOM error again. The query will get cancelled due to an error,
entering into a recursion and leading to an eventual crash.

This patch implements proper query cancellation by explicitly ignoring any
libpq data from segments without extra memory allocations.

Ticket: ADBDEV-7690
Copy link
Member

@bimboterminator1 bimboterminator1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you write about CdbDispatchCommand in pg_locks_status if it has been removed from there? Write description in way more consistent with current changes , please

* might not be enough memory to discard the result properly.
* Let's get the big guns out.
*/
tryToWipeResults(dispatchResult);
Copy link
Member

@bimboterminator1 bimboterminator1 Aug 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, why we cannot use cdbconn_discardResults()?
And I agree with @RekGRpth about avoiding injecting a special case this way. Are you sure there is not any approach that looks more organic?

When querying pg_locks (or using `pg_lock_status()`), approximately 75% of
backend memory allocations for resulting tuples weren't registered with
Vmtracker or Resource Group Control. This memory would also leak if the query
was cancelled or failed.

This happened because `CdbDispatchCommand()`, which was previously used by
`pg_locks`, called libpq to obtain the results that were allocated as PQresult
structures with bare `malloc()`, even on the server side.

This patch fixes both untracked memory issues by enforcing Vmtracker routines
for `PGresult` allocations on the server-side.

Including `postgres.h` in frontend code causes several errcode-related macro
redefinition warnings. They are now un-definined first.

Please note that the original error from ADBDEV-7145, which is an
`AbortTransaction()` recurstion due to an OOM error while cancelling a query,
is fixed only in the next commit.

Ticket: ADBDEV-7691
Previosly, if a query fails under resource group manager due to a lack of
memory, the query is cancelled due to an error.

The coordinator would still try to receive tuples from segments upon
cancellation. Retrieving tuples requires more memory allocations, which may
cause the same OOM error again. The query will get cancelled due to an error,
entering into a recursion and leading to an eventual crash.

This patch implements proper query cancellation by explicitly ignoring any
libpq data from segments without extra memory allocations.

Ticket: ADBDEV-7690
Copy link
Member

@bimboterminator1 bimboterminator1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Update PR description.

@RekGRpth
Copy link
Member

RekGRpth commented Aug 7, 2025

Still, I suggest separating the patch for correcting the recursive error into a separate PR and accepting it first.

I disagree. The current approach was chosen since the recursive error patch cannot be tested without the first commit.

I seem to have managed to reproduce the bug fixed by the second commit without using the first commit.

diff --git a/src/backend/cdb/dispatcher/cdbdisp_async.c b/src/backend/cdb/dispatcher/cdbdisp_async.c
index c41dbe55a72..c8cea03a508 100644
--- a/src/backend/cdb/dispatcher/cdbdisp_async.c
+++ b/src/backend/cdb/dispatcher/cdbdisp_async.c
@@ -866,6 +866,11 @@ handlePollSuccess(CdbDispatchCmdAsync *pParms,
 		ELOG_DISPATCHER_DEBUG("PQsocket says there are results from %d of %d (%s)",
 							  i + 1, pParms->dispatchCount, segdbDesc->whoami);
 
+		if (pParms->waitMode == DISPATCH_WAIT_CANCEL)
+		{
+			elog(ERROR, "Out of memory was emulated");
+		}
+
 		/*
 		 * Receive and process results from this QE.
 		 */

or

diff --git a/src/backend/cdb/dispatcher/cdbdisp_async.c b/src/backend/cdb/dispatcher/cdbdisp_async.c
index c41dbe55a72..a6fdd0e4a6b 100644
--- a/src/backend/cdb/dispatcher/cdbdisp_async.c
+++ b/src/backend/cdb/dispatcher/cdbdisp_async.c
@@ -1076,6 +1076,11 @@ processResults(CdbDispatchResult *dispatchResult)
 		ELOG_DISPATCHER_DEBUG("PQgetResult");
 		pRes = PQgetResult(segdbDesc->conn);
 
+		if (dispatchResult->wasCanceled)
+		{
+			elog(ERROR, "Out of memory was emulated");
+		}
+
 		/*
 		 * Command is complete when PGgetResult() returns NULL. It is critical
 		 * that for any connection that had an asynchronous command sent thru
CREATE FUNCTION gp_mock_cdbdispatchcommand(amount int) RETURNS SETOF text AS '/home/gpadmin/src/gpdb6/src/test/isolation2/../regress/regress.so', 'gp_mock_cdbdispatchcommand' LANGUAGE C;

SELECT count(*) FROM gp_mock_cdbdispatchcommand(10000000);
^CCancel request sent
ERROR:  canceling statement due to user request
ERROR:  Out of memory was emulated (cdbdisp_async.c:871)
ERROR:  Out of memory was emulated (cdbdisp_async.c:871)
ERROR:  Out of memory was emulated (cdbdisp_async.c:871)
ERROR:  Out of memory was emulated (cdbdisp_async.c:871)
ERROR:  Out of memory was emulated (cdbdisp_async.c:871)
ERROR:  Out of memory was emulated (cdbdisp_async.c:871)
ERROR:  Out of memory was emulated (cdbdisp_async.c:871)
ERROR:  Out of memory was emulated (cdbdisp_async.c:871)
ERROR:  Out of memory was emulated (cdbdisp_async.c:871)
PANIC:  ERRORDATA_STACK_SIZE exceeded (elog.c:1661)
server closed the connection unexpectedly
	This probably means the server terminated abnormally
	before or while processing the request.
The connection to the server was lost. Attempting reset: Failed.
!> 

@bandetto
Copy link
Member Author

bandetto commented Aug 8, 2025

I seem to have managed to reproduce the bug fixed by the second commit without using the first commit.

Injecting an error into the middle of a function does not really check the fundamental issue behind the task, which are failed memory allocations. Your solution checks only for re-entrance into processResults() and handlePollSuccess().

@bandetto
Copy link
Member Author

Evil PR counterpart from Greengage: GreengageDB/greengage#45

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants