Thread: Memory leak with CALL to Procedure with COMMIT.

Memory leak with CALL to Procedure with COMMIT.

From
Prabhat Sahu
Date:
Hi Hackers,

While testing with PG procedure, I found a memory leak on HEAD, with below steps:

postgres=# CREATE OR REPLACE PROCEDURE proc1(v1 INOUT INT)
AS $$ 
BEGIN
 commit; 
END; $$ LANGUAGE plpgsql;
CREATE PROCEDURE

postgres=# call proc1(10);
WARNING:  Snapshot reference leak: Snapshot 0x23678e8 still referenced
 v1 
----
 10
(1 row)

--

With Regards,

Prabhat Kumar Sahu
Skype ID: prabhat.sahu1984
EnterpriseDB Corporation

The Postgres Database Company

Re: Memory leak with CALL to Procedure with COMMIT.

From
Michael Paquier
Date:
On Mon, Jul 23, 2018 at 12:19:12PM +0530, Prabhat Sahu wrote:
> While testing with PG procedure, I found a memory leak on HEAD, with below
> steps:
>
> postgres=# CREATE OR REPLACE PROCEDURE proc1(v1 INOUT INT)
> AS $$
> BEGIN
>  commit;
> END; $$ LANGUAGE plpgsql;
> CREATE PROCEDURE
>
> postgres=# call proc1(10);
> WARNING:  Snapshot reference leak: Snapshot 0x23678e8 still referenced
>  v1
> ----
>  10
> (1 row)

I can reproduce this issue on HEAD and v11, so an open item is added.
Peter, could you look at it?
--
Michael

Attachment

Re: Memory leak with CALL to Procedure with COMMIT.

From
"Jonathan S. Katz"
Date:
> On Jul 23, 2018, at 3:06 AM, Michael Paquier <michael@paquier.xyz> wrote:
>
> On Mon, Jul 23, 2018 at 12:19:12PM +0530, Prabhat Sahu wrote:
>> While testing with PG procedure, I found a memory leak on HEAD, with below
>> steps:
>>
>> postgres=# CREATE OR REPLACE PROCEDURE proc1(v1 INOUT INT)
>> AS $$
>> BEGIN
>> commit;
>> END; $$ LANGUAGE plpgsql;
>> CREATE PROCEDURE
>>
>> postgres=# call proc1(10);
>> WARNING:  Snapshot reference leak: Snapshot 0x23678e8 still referenced
>> v1
>> ----
>> 10
>> (1 row)
>
> I can reproduce this issue on HEAD and v11, so an open item is added.
> Peter, could you look at it?

I tested and was able to reproduce this on head. I also tried a few other other
and was able to reproduce it when the procedure contained a few read-only
statements prior to commit, where the argument passed in was designated "INOUT."

Scenarios 1 & 2 show the leak whereas 3 & 4 do not.

    /** Scenario 1: Original scenario */
    CREATE OR REPLACE PROCEDURE proc1(v1 INOUT INT)
    AS $$
    BEGIN
     commit;
    END; $$ LANGUAGE plpgsql;

    CALL proc1(10);

    WARNING:  Snapshot reference leak: Snapshot 0x7f9519826d18 still referenced
    CONTEXT:  PL/pgSQL function proc1(integer) line 3 at COMMIT
     v1
    ----
     10
    (1 row)

    /** Scenario 2: call "perform" prior to the commit */
    CREATE OR REPLACE PROCEDURE proc2(v1 INOUT INT)
    AS $$
    BEGIN
        PERFORM v1;
        COMMIT;
    END; $$ LANGUAGE plpgsql;

    CALL proc2(10);
    WARNING:  Snapshot reference leak: Snapshot 0x7f9519826d18 still referenced
    CONTEXT:  PL/pgSQL function proc2(integer) line 4 at COMMIT
     v1
    ----
     10
    (1 row)


    /** Scenario 3: argument is only IN */
    CREATE OR REPLACE PROCEDURE proc3(v1 IN INT)
    AS $$
    BEGIN
        PERFORM v1;
        COMMIT;
    END; $$ LANGUAGE plpgsql;

    CALL proc3(10);
    CALL

    /** Scenario 4: Same as #2 but with a ROLLBACK  */
    CREATE OR REPLACE PROCEDURE proc4(v1 INOUT INT)
    AS $$
    BEGIN
        PERFORM v1;
        ROLLBACK;
    END; $$ LANGUAGE plpgsql;

    CALL proc4(10);
    CALL

Jonathan

Attachment

Re: Memory leak with CALL to Procedure with COMMIT.

From
Peter Eisentraut
Date:
The commit that started this is

commit 59a85323d9d5927a852939fa93f09d142c72c91a
Author: Peter Eisentraut <peter_e@gmx.net>
Date:   Mon Jul 9 13:58:08 2018

    Add UtilityReturnsTuples() support for CALL

    This ensures that prepared statements for CALL can return tuples.

The change whether a statement returns tuples or not causes some
different paths being taking deep in the portal code that set snapshot
in different ways.  I haven't fully understood them yet.  I plan to post
more tomorrow.

-- 
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services


Re: Memory leak with CALL to Procedure with COMMIT.

From
Peter Eisentraut
Date:
On 14/08/2018 15:35, Peter Eisentraut wrote:
> The commit that started this is
> 
> commit 59a85323d9d5927a852939fa93f09d142c72c91a
> Author: Peter Eisentraut <peter_e@gmx.net>
> Date:   Mon Jul 9 13:58:08 2018
> 
>     Add UtilityReturnsTuples() support for CALL
> 
>     This ensures that prepared statements for CALL can return tuples.
> 
> The change whether a statement returns tuples or not causes some
> different paths being taking deep in the portal code that set snapshot
> in different ways.  I haven't fully understood them yet.

The problem arises with the combination of CALL with output parameters
and doing a COMMIT inside the procedure.

When a CALL has output parameters, the portal uses the strategy
PORTAL_UTIL_SELECT instead of PORTAL_MULTI_QUERY.  Using
PORTAL_UTIL_SELECT causes the portal's snapshot to be registered with
the current resource owner (portal->holdSnapshot).  I'm not sure why
this is done for one kind of portal strategy but not the other.

Then, when the COMMIT inside the procedure is run, the current resource
owner is released and it complains that a snapshot is still registered
there.

There are, technically, three ways to fix this: silence the warning,
unregister the snapshot explicitly, or don't register the snapshot to
begin with.

Silencing the warning, other than by just deleting it, might require
passing in some more context information into ResourceOwnerRelease().
(The existing isTopLevel isn't quite the right thing.)

Unregistering the snapshot explicitly looks tricky because in
SPI_commit() or thereabouts we have no context information about which
snapshot might have been registered where.

Not registering the snapshot to begin with seems dubious, but see my
question above about why this is not done in the case of PORTAL_MULTI_QUERY.

Thoughts?

-- 
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services


Re: Memory leak with CALL to Procedure with COMMIT.

From
Alvaro Herrera
Date:
On 2018-Aug-16, Peter Eisentraut wrote:

> There are, technically, three ways to fix this: silence the warning,
> unregister the snapshot explicitly, or don't register the snapshot to
> begin with.
> 
> Silencing the warning, other than by just deleting it, might require
> passing in some more context information into ResourceOwnerRelease().
> (The existing isTopLevel isn't quite the right thing.)
> 
> Unregistering the snapshot explicitly looks tricky because in
> SPI_commit() or thereabouts we have no context information about which
> snapshot might have been registered where.

Hmm, this got me thinking whether the current resource owner setup for a
procedure is appropriate.  Maybe the problem is that resowners are still
thought of in terms of transactions plus portals, so that if
transactions are done then everything is over; maybe we need to teach
them that procedures can outlive transactions, so you'd have a resowner
that's global to the procedure and then each transaction resowner is a
child of that one?

-- 
Álvaro Herrera                https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services


Re: Memory leak with CALL to Procedure with COMMIT.

From
Tom Lane
Date:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> Hmm, this got me thinking whether the current resource owner setup for a
> procedure is appropriate.  Maybe the problem is that resowners are still
> thought of in terms of transactions plus portals, so that if
> transactions are done then everything is over; maybe we need to teach
> them that procedures can outlive transactions, so you'd have a resowner
> that's global to the procedure and then each transaction resowner is a
> child of that one?

The procedure still has to be running inside a query, and therefore
inside a portal, so the portal's resowner ought to be sufficiently
long-lived for any resources that ought to be procedure-lifetime.
So I doubt we need any more resowners.  It's certainly possible that
something somewhere is assigning a particular resource to the wrong
resowner, of course.

            regards, tom lane


Re: Memory leak with CALL to Procedure with COMMIT.

From
Tom Lane
Date:
Peter Eisentraut <peter.eisentraut@2ndquadrant.com> writes:
> The problem arises with the combination of CALL with output parameters
> and doing a COMMIT inside the procedure.

> When a CALL has output parameters, the portal uses the strategy
> PORTAL_UTIL_SELECT instead of PORTAL_MULTI_QUERY.  Using
> PORTAL_UTIL_SELECT causes the portal's snapshot to be registered with
> the current resource owner (portal->holdSnapshot).  I'm not sure why
> this is done for one kind of portal strategy but not the other.

I'm a bit confused by that statement.  AFAICS, for both PortalRunUtility
and PortalRunMulti, setHoldSnapshot is only passed as true by
FillPortalStore, so registering the snapshot should happen (or not) the
same way for either portal execution strategy.  What scenarios are you
comparing here, exactly?

In the long run where we want to think about allowing multiple rowsets to
be returned out of a procedure, it's fairly likely that PORTAL_UTIL_SELECT
isn't going to work anyway.  Maybe we should be thinking about inventing a
different portal execution strategy for CALL.  But I'm not sure we need
that just yet.

            regards, tom lane


Re: Memory leak with CALL to Procedure with COMMIT.

From
Peter Eisentraut
Date:
On 16/08/2018 19:26, Tom Lane wrote:
>> When a CALL has output parameters, the portal uses the strategy
>> PORTAL_UTIL_SELECT instead of PORTAL_MULTI_QUERY.  Using
>> PORTAL_UTIL_SELECT causes the portal's snapshot to be registered with
>> the current resource owner (portal->holdSnapshot).  I'm not sure why
>> this is done for one kind of portal strategy but not the other.

> I'm a bit confused by that statement.  AFAICS, for both PortalRunUtility
> and PortalRunMulti, setHoldSnapshot is only passed as true by
> FillPortalStore, so registering the snapshot should happen (or not) the
> same way for either portal execution strategy.  What scenarios are you
> comparing here, exactly?

The call to PortalRunMulti() in FillPortalStore() is for
PORTAL_ONE_RETURNING and PORTAL_ONE_MOD_WITH, not for
PORTAL_MULTI_QUERY.  For PORTAL_MULTI_QUERY, FillPortalStore() isn't
called; PortalRun() calls PortalRunMulti() directly with setHoldSnapshot
= false.

-- 
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services


Re: Memory leak with CALL to Procedure with COMMIT.

From
Peter Eisentraut
Date:
I think I've found a reasonable fix for this.

The problem arises with the combination of CALL with output parameters
and doing a COMMIT inside the procedure.  When a CALL has output
parameters, the portal uses the strategy PORTAL_UTIL_SELECT instead of
PORTAL_MULTI_QUERY.  Using PORTAL_UTIL_SELECT causes the portal's
snapshot to be registered with the current resource
owner (portal->holdSnapshot); see
9ee1cf04ab6bcefe03a11837b53f29ca9dc24c7a for the reason.

Normally, PortalDrop() unregisters the snapshot.  If not, then
ResourceOwnerRelease() will print a warning about a snapshot leak on
transaction commit.  A transaction commit normally drops all
portals (PreCommit_Portals()), except the active portal.  So in case of
the active portal, we need to manually release the snapshot to avoid the
warning.

PreCommit_Portals() already contains some code that deals specially with
the active portal versus resource owners, so it seems reasonable to add
there.

I think this problem could theoretically apply to any
multiple-transaction utility command.  For example, if we had a variant
of VACUUM that returned tuples, it would produce the same warning.
(This patch would fix that as well.)

-- 
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services

Attachment

Re: Memory leak with CALL to Procedure with COMMIT.

From
"Jonathan S. Katz"
Date:
> On Aug 23, 2018, at 9:34 AM, Peter Eisentraut <peter.eisentraut@2ndquadrant.com> wrote:
>
> I think I've found a reasonable fix for this.
>
> The problem arises with the combination of CALL with output parameters
> and doing a COMMIT inside the procedure.  When a CALL has output
> parameters, the portal uses the strategy PORTAL_UTIL_SELECT instead of
> PORTAL_MULTI_QUERY.  Using PORTAL_UTIL_SELECT causes the portal's
> snapshot to be registered with the current resource
> owner (portal->holdSnapshot); see
> 9ee1cf04ab6bcefe03a11837b53f29ca9dc24c7a for the reason.
>
> Normally, PortalDrop() unregisters the snapshot.  If not, then
> ResourceOwnerRelease() will print a warning about a snapshot leak on
> transaction commit.  A transaction commit normally drops all
> portals (PreCommit_Portals()), except the active portal.  So in case of
> the active portal, we need to manually release the snapshot to avoid the
> warning.
>
> PreCommit_Portals() already contains some code that deals specially with
> the active portal versus resource owners, so it seems reasonable to add
> there.
>
> I think this problem could theoretically apply to any
> multiple-transaction utility command.  For example, if we had a variant
> of VACUUM that returned tuples, it would produce the same warning.
> (This patch would fix that as well.)

Applied the patch against head. make check passed, all the tests I ran
earlier in this thread passed as well.

Reviewed the code - looks to match above reasoning, and comments
are clear.

From my perspective it looks good, but happy to hear from someone
more familiar than me with this area of the code.

Thanks,

Jonathan

Attachment

Re: Memory leak with CALL to Procedure with COMMIT.

From
Peter Eisentraut
Date:
On 23/08/2018 17:35, Jonathan S. Katz wrote:
> Applied the patch against head. make check passed, all the tests I ran
> earlier in this thread passed as well.
> 
> Reviewed the code - looks to match above reasoning, and comments
> are clear.
> 
> From my perspective it looks good, but happy to hear from someone
> more familiar than me with this area of the code.

committed

-- 
Peter Eisentraut              http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services


Re: Memory leak with CALL to Procedure with COMMIT.

From
"Jonathan S. Katz"
Date:
> On Aug 27, 2018, at 5:13 PM, Peter Eisentraut <peter.eisentraut@2ndquadrant.com> wrote:
>
> On 23/08/2018 17:35, Jonathan S. Katz wrote:
>> Applied the patch against head. make check passed, all the tests I ran
>> earlier in this thread passed as well.
>>
>> Reviewed the code - looks to match above reasoning, and comments
>> are clear.
>>
>> From my perspective it looks good, but happy to hear from someone
>> more familiar than me with this area of the code.
>
> committed

Awesome, thank you! I’ve removed this from the Open Items list.

Jonathan



Attachment