-
Notifications
You must be signed in to change notification settings - Fork 23
ADBDEV-7691, ADBDEV-7690: Track PQresult allocations in server side, Gracefully handle OOM when cancelling a query #1804
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: adb-6.x-dev
Are you sure you want to change the base?
Conversation
ea58a4d to
3e21592
Compare
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
3e21592 to
2afc07d
Compare
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
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
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
9b90def to
8013158
Compare
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
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
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
8013158 to
907999e
Compare
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
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
907999e to
c5f2f45
Compare
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
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
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
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
c61249d to
4709631
Compare
This comment was marked as resolved.
This comment was marked as resolved.
| pqSaveErrorResult(conn); | ||
|
|
||
| /* Make sure PQgetResult() calls are not blocking. */ | ||
| PQconsumeInput(conn); |
There was a problem hiding this comment.
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?!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why?
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see similar function processResults
There was a problem hiding this comment.
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?
src/test/isolation2/input/parallel_retrieve_cursor/fault_inject.source
Outdated
Show resolved
Hide resolved
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
4709631 to
118c338
Compare
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
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
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
118c338 to
6b2fe85
Compare
|
Still, I suggest separating the patch for correcting the recursive error into a separate PR and accepting it first. |
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
6b2fe85 to
6610c93
Compare
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); |
There was a problem hiding this comment.
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
- first the
PQconsumeInputfunction is called - then there is a loop until
PQisBusy - inside the loop the
PQgetResultfunction is called
Maybe it would be easier to add thepParms->waitMode == DISPATCH_WAIT_CANCELargument to theprocessResultsfunction and its processing?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
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
6610c93 to
0d087a1
Compare
bimboterminator1
left a comment
There was a problem hiding this 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); |
There was a problem hiding this comment.
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
0d087a1 to
8d681ef
Compare
There was a problem hiding this 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.
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 thruCREATE 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.
!> |
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 |
|
Evil PR counterpart from Greengage: GreengageDB/greengage#45 |
When querying
pg_locks(or usingpg_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 bypg_locks, internally uses libpq to obtain the results, which were allocated asPQresultstructures with baremalloc(), 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
PGresultallocations 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-devare broken.