diff --git a/pkg/sql/conn_executor.go b/pkg/sql/conn_executor.go index c19f82bc8ba3..9582595ca9cc 100644 --- a/pkg/sql/conn_executor.go +++ b/pkg/sql/conn_executor.go @@ -746,14 +746,20 @@ func (h ConnectionHandler) GetQueryCancelKey() pgwirecancel.BackendKeyData { // embedded in the ConnHandler. // // If not nil, reserved represents memory reserved for the connection. The -// connExecutor takes ownership of this memory. +// connExecutor takes ownership of this memory and will close the account before +// exiting. func (s *Server) ServeConn( ctx context.Context, h ConnectionHandler, reserved *mon.BoundAccount, cancel context.CancelFunc, ) error { - defer func() { + // Make sure to close the reserved account even if closeWrapper below + // panics: so we do it in a defer that is guaranteed to execute. We also + // cannot close it before closeWrapper since we need to close the internal + // monitors of the connExecutor first. + defer reserved.Close(ctx) + defer func(ctx context.Context, h ConnectionHandler) { r := recover() h.ex.closeWrapper(ctx, r) - }() + }(ctx, h) return h.ex.run(ctx, s.pool, reserved, cancel) } diff --git a/pkg/sql/pgwire/conn.go b/pkg/sql/pgwire/conn.go index 65744505e859..3b9f7e551349 100644 --- a/pkg/sql/pgwire/conn.go +++ b/pkg/sql/pgwire/conn.go @@ -368,6 +368,7 @@ func (c *conn) serveImpl( } else { // sqlServer == nil means we are in a local test. In this case // we only need the minimum to make pgx happy. + defer reserved.Close(ctx) var err error for param, value := range testingStatusReportParams { err = c.sendParamStatus(param, value) @@ -376,7 +377,6 @@ func (c *conn) serveImpl( } } if err != nil { - reserved.Close(ctx) return } var ac AuthConn = authPipe @@ -387,7 +387,6 @@ func (c *conn) serveImpl( procCh = dummyCh if err := c.sendReadyForQuery(0 /* queryCancelKey */); err != nil { - reserved.Close(ctx) return } } @@ -621,7 +620,8 @@ func (c *conn) serveImpl( // Args: // ac: An interface used by the authentication process to receive password data // and to ultimately declare the authentication successful. -// reserved: Reserved memory. This method takes ownership. +// reserved: Reserved memory. This method takes ownership and guarantees that it +// will be closed when this function returns. // cancelConn: A function to be called when this goroutine exits. Its goal is to // cancel the connection's context, thus stopping the connection's goroutine. // The returned channel is also closed before this goroutine dies, but the