-
Notifications
You must be signed in to change notification settings - Fork 3.8k
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
sql: prevent unbounded growth of conn.writerState.buf
and conn.msgBuilder
#80497
Comments
I'm not sure exactly how this would work. What pool of memory would these buffers consume? What would happen as they consumed more and more memory? Would the query have less memory available for execution, causing memory errors? Would we prevent new connections from being created? |
A different approach to prevent these buffers from growing past |
Maybe @rafiss would know. |
I think it would consume from the root SQL memory monitor (i.e.
Hm, if a memory budget error is encountered when composing a PGWire message, then the connection would probably become toast. We need to think more about this one.
Any other consumer of the memory from our accounting system would be more likely to run into "budget exceeded" errors, queries issued by other connections including. I think this is expected and acceptable though.
Probably no. Imagining a scenario when a particular connection has accumulated a very large If, however, we got a situation when a newly-established connection runs into the same error when it itself doesn't have a large |
@mgartner says this isn't terribly urgent. |
I think it would be allowed in the protocol. We should be able to write tests for it. |
Doesn't each session have its own monitor, would that make more sense? I've seen session-root and session monitors,not sure what the diff is. Also even if we flush couldn't we have a large column value that defeated mid-row flushing? |
Yeah, there is one. Eventually everything would still consume from the root SQL monitor (i.e. against |
I'd argue that this wouldn't be acceptable. There would still be a "poisoning" effect where a previous query makes future queries on the connection more likely to hit memory errors. You could imagine a situation where the buffers grow so large that there are only a few bytes left in the budget to process a query and so every query results in "budget exceeded". I think the best options, in order, would be:
|
To be clear, I think memory accounting of the buffers could be useful to prevent other types of OOMs. But I think we need something more than that to prevent these buffers from growing past |
conn.writeState.buf
and writeBuffer.wrapped
conn.writeState.buf
and writeBuffer.wrapped
conn.writeState.buf
and writeBuffer.wrapped
conn.writeState.buf
and writeBuffer.wrapped
I'm curious why both these buffers exist. It looks like we stage results in |
conn.writeState.buf
and writeBuffer.wrapped
conn.writerState.buf
and conn.writeBuffer.wrapped
conn.writerState.buf
and conn.writeBuffer.wrapped
conn.writerState.buf
and conn.msgBuilder
@andreimatei it's been awhile, but your name is featured prominently in the git history of |
Is |
Thanks @andreimatei!
A section of the
I think this rules out my suggestion above:
So this is the best option I can think of at the moment:
|
@cucaroach I've unassigned you because I believe you've had other things on your plate. If you're still making progress on this, feel free to reassign yourself, otherwise, don't worry about it for now. |
There are two buffers in `pgwire.conn` that are used when sending results to the client. They have to buffer each row in its entirety before flushing, so wide rows can cause the capacity of these buffers to exceed the limit. Previously, the buffers were never reallocated even when they exceeded the size limit. This could cause a long-running session to hold on to large amounts of memory until the session was ended. This increased the risk of OOM, in addition to being inefficient. This commit adds a method `maybeReallocate` to `pgwire.conn`, which reallocates each buffer if it has reached the size limit. `maybeReallocate` is called upon closing `Sync` and `Flush` command results. These are natural places to reallocate the buffers, since they already flush the buffers to the client and are called roughly at the statement granularity, rather than for every row. This allows long-running sessions to bound their memory usage without causing reallocation on every wide row that is sent to the client. Informs cockroachdb#80497 Release note (performance improvement): Long-running sql sessions are now less likely to maintain large allocations for long periods of time, which decreases the risk of OOM and improves memory utilization.
There are two buffers in `pgwire.conn` that are used when sending results to the client. They have to buffer each row in its entirety before flushing, so wide rows can cause the capacity of these buffers to exceed the limit. Previously, the buffers were never reallocated even when they exceeded the size limit. This could cause a long-running session to hold on to large amounts of memory until the session was ended. This increased the risk of OOM, in addition to being inefficient. This commit adds a method `maybeReallocate` to `pgwire.conn`, which reallocates each buffer if it has reached the size limit. `maybeReallocate` is called upon closing `Sync` and `Flush` command results. These are natural places to reallocate the buffers, since they already flush the buffers to the client and are called roughly at the statement granularity, rather than for every row. This allows long-running sessions to bound their memory usage without causing reallocation on every wide row that is sent to the client. Informs cockroachdb#80497 Release note (performance improvement): Long-running sql sessions are now less likely to maintain large allocations for long periods of time, which decreases the risk of OOM and improves memory utilization.
There are two buffers in `pgwire.conn` that are used when sending results to the client. They have to buffer each row in its entirety before flushing, so wide rows can cause the capacity of these buffers to exceed the limit. Previously, the buffers were never reallocated even when they exceeded the size limit. This could cause a long-running session to hold on to large amounts of memory until the session was ended. This increased the risk of OOM, in addition to being inefficient. This commit adds a method `maybeReallocate` to `pgwire.conn`, which reallocates each buffer if it has reached the size limit. `maybeReallocate` is called upon closing `Sync` and `Flush` command results. These are natural places to reallocate the buffers, since they already flush the buffers to the client and are called roughly at the statement granularity, rather than for every row. This allows long-running sessions to bound their memory usage without causing reallocation on every wide row that is sent to the client. Informs cockroachdb#80497 Release note (performance improvement): Long-running sql sessions are now less likely to maintain large allocations for long periods of time, which decreases the risk of OOM and improves memory utilization.
There are two buffers in `pgwire.conn` that are used when sending results to the client. They have to buffer each row in its entirety before flushing, so wide rows can cause the capacity of these buffers to exceed the limit. Previously, the buffers were never reallocated even when they exceeded the size limit. This could cause a long-running session to hold on to large amounts of memory until the session was ended. This increased the risk of OOM, in addition to being inefficient. This commit adds a method `maybeReallocate` to `pgwire.conn`, which reallocates each buffer if it has reached the size limit. `maybeReallocate` is called upon closing `Sync` and `Flush` command results. These are natural places to reallocate the buffers, since they already flush the buffers to the client and are called roughly at the statement granularity, rather than for every row. This allows long-running sessions to bound their memory usage without causing reallocation on every wide row that is sent to the client. Informs cockroachdb#80497 Release note (performance improvement): Long-running sql sessions are now less likely to maintain large allocations for long periods of time, which decreases the risk of OOM and improves memory utilization. Release justification: bug fix to reduce likelihood of OOM
85949: sql: reset conn buffers after each query r=DrewKimball a=DrewKimball **sql: reset conn buffers after each query** There are two buffers in `pgwire.conn` that are used when sending results to the client. They have to buffer each row in its entirety before flushing, so wide rows can cause the capacity of these buffers to exceed the limit. Previously, the buffers were never reallocated even when they exceeded the size limit. This could cause a long-running session to hold on to large amounts of memory until the session was ended. This increased the risk of OOM, in addition to being inefficient. This commit adds a method `maybeReallocate` to `pgwire.conn`, which reallocates each buffer if it has reached the size limit. `maybeReallocate` is called upon closing `Sync` and `Flush` command results. These are natural places to reallocate the buffers, since they already flush the buffers to the client and are called roughly at the statement granularity, rather than for every row. This allows long-running sessions to bound their memory usage without causing reallocation on every wide row that is sent to the client. Informs #80497 Release note (performance improvement): Long-running sql sessions are now less likely to maintain large allocations for long periods of time, which decreases the risk of OOM and improves memory utilization. Release justification: bug fix to reduce likelihood of OOM **sql: remove unnecessary maybeFlush call** This commit fixes an oversight of #83870 that added a redundant call to `maybeAppend` in `limitedCommandResult.AddRow`. Release note: None Release justification: low-risk fix for oversight in previous bug fix Co-authored-by: DrewKimball <[email protected]>
I'm leaving this open and moving to the backlog, since it sounds like with some more significant changes we could ensure the buffers never exceed the limit (e.g. we could flush in the middle of a row if we know the length before buffering). |
There are two buffers in `pgwire.conn` that are used when sending results to the client. They have to buffer each row in its entirety before flushing, so wide rows can cause the capacity of these buffers to exceed the limit. Previously, the buffers were never reallocated even when they exceeded the size limit. This could cause a long-running session to hold on to large amounts of memory until the session was ended. This increased the risk of OOM, in addition to being inefficient. This commit adds a method `maybeReallocate` to `pgwire.conn`, which reallocates each buffer if it has reached the size limit. `maybeReallocate` is called upon closing `Sync` and `Flush` command results. These are natural places to reallocate the buffers, since they already flush the buffers to the client and are called roughly at the statement granularity, rather than for every row. This allows long-running sessions to bound their memory usage without causing reallocation on every wide row that is sent to the client. Informs #80497 Release note (performance improvement): Long-running sql sessions are now less likely to maintain large allocations for long periods of time, which decreases the risk of OOM and improves memory utilization. Release justification: bug fix to reduce likelihood of OOM
There are two buffers in `pgwire.conn` that are used when sending results to the client. They have to buffer each row in its entirety before flushing, so wide rows can cause the capacity of these buffers to exceed the limit. Previously, the buffers were never reallocated even when they exceeded the size limit. This could cause a long-running session to hold on to large amounts of memory until the session was ended. This increased the risk of OOM, in addition to being inefficient. This commit adds a method `maybeReallocate` to `pgwire.conn`, which reallocates each buffer if it has reached the size limit. `maybeReallocate` is called upon closing `Sync` and `Flush` command results. These are natural places to reallocate the buffers, since they already flush the buffers to the client and are called roughly at the statement granularity, rather than for every row. This allows long-running sessions to bound their memory usage without causing reallocation on every wide row that is sent to the client. Informs #80497 Release note (performance improvement): Long-running sql sessions are now less likely to maintain large allocations for long periods of time, which decreases the risk of OOM and improves memory utilization. Release justification: bug fix to reduce likelihood of OOM
A wide row returned to the client can cause the following buffers in
pgwire
to grow to a large size. The backing array is not GC'd until the session is terminated. This can cause memory to grow indefinitely as more and more connections grow these buffers.conn.writeState.buf
:cockroach/pkg/sql/pgwire/conn.go
Line 86 in d7f3d51
writeBuffer.wrapped
(writeBuffer
is the type ofconn.msgBuilder
):cockroach/pkg/sql/pgwire/write_buffer.go
Line 30 in c097a16
Some extra information from @yuzefovich:
Jira issue: CRDB-15591
The text was updated successfully, but these errors were encountered: