Re: Add tracking of backend memory allocated to pg_stat_activity - Mailing list pgsql-hackers

From Melanie Plageman
Subject Re: Add tracking of backend memory allocated to pg_stat_activity
Date
Msg-id 20221107211747.eixqqirowxjlidsb@liskov
Whole thread Raw
In response to Re: Add tracking of backend memory allocated to pg_stat_activity  (Reid Thompson <reid.thompson@crunchydata.com>)
Responses Re: Add tracking of backend memory allocated to pg_stat_activity
List pgsql-hackers
On Fri, Nov 04, 2022 at 08:56:13AM -0400, Reid Thompson wrote:
> From a8de5d29c0c6f10962181926a49ad4fec1e52bd1 Mon Sep 17 00:00:00 2001
> From: Reid Thompson <jreidthompson@nc.rr.com>
> Date: Thu, 11 Aug 2022 12:01:25 -0400
> Subject: [PATCH] Add tracking of backend memory allocated to pg_stat_activity
> 
> This new field displays the current bytes of memory allocated to the
> backend process. It is updated as memory for the process is
> malloc'd/free'd. Memory allocated to items on the freelist is included in
> the displayed value.  Dynamic shared memory allocations are included
> only in the value displayed for the backend that created them, they are
> not included in the value for backends that are attached to them to
> avoid double counting. On occasion, orphaned memory segments may be
> cleaned up on postmaster startup. This may result in decreasing the sum
> without a prior increment. We limit the floor of backend_mem_allocated
> to zero. Updated pg_stat_activity documentation for the new column.
> ---
>  doc/src/sgml/monitoring.sgml                |  12 +++
>  src/backend/catalog/system_views.sql        |   1 +
>  src/backend/storage/ipc/dsm_impl.c          |  81 +++++++++++++++
>  src/backend/utils/activity/backend_status.c | 105 ++++++++++++++++++++
>  src/backend/utils/adt/pgstatfuncs.c         |   4 +-
>  src/backend/utils/mmgr/aset.c               |  18 ++++
>  src/backend/utils/mmgr/generation.c         |  15 +++
>  src/backend/utils/mmgr/slab.c               |  21 ++++
>  src/include/catalog/pg_proc.dat             |   6 +-
>  src/include/utils/backend_status.h          |   7 +-
>  src/test/regress/expected/rules.out         |   9 +-
>  11 files changed, 270 insertions(+), 9 deletions(-)
> 
> diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
> index e5d622d514..972805b85a 100644
> --- a/doc/src/sgml/monitoring.sgml
> +++ b/doc/src/sgml/monitoring.sgml
> @@ -947,6 +947,18 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
>       </para></entry>
>      </row>
>  
> +    <row>
> +     <entry role="catalog_table_entry"><para role="column_definition">
> +      <structfield>backend_allocated_bytes</structfield> <type>bigint</type>
> +     </para>
> +     <para>
> +      The byte count of memory allocated to this backend. Dynamic shared memory
> +      allocations are included only in the value displayed for the backend that
> +      created them, they are not included in the value for backends that are
> +      attached to them to avoid double counting.
> +     </para></entry>
> +    </row>
> +

It doesn't seem like you need the backend_ prefix in the view since that
is implied by it being in pg_stat_activity.

For the wording on the description, I find "memory allocated to this
backend" a bit confusing. Perhaps you could reword it to make clear you
mean that the number represents the balance of allocations by this
backend. Something like:

    Memory currently allocated to this backend in bytes. This is the
    balance of bytes allocated and freed by this backend.

I would also link to the system administration function pg_size_pretty()
so users know how to easily convert the value.

If you end up removing shared memory as Andres suggests in [1], I would link
pg_shmem_allocations view here and point out that shared memory allocations can
be viewed there instead (and why).

You could instead add dynamic shared memory allocation to the
pg_shmem_allocations view as suggested as follow-on work by the commit which
introduced it, ed10f32e3.

> +/* --------
> + * pgstat_report_backend_allocated_bytes_increase() -
> + *
> + * Called to report increase in memory allocated for this backend
> + * --------
> + */

It seems like you could combine the
pgstat_report_backend_allocated_bytes_decrease/increase() by either using a
signed integer to represent the allocation/deallocation or passing in a
"direction" that is just a positive or negative multiplier enum.

Especially if you don't use the write barriers, I think you could
simplify the logic in the two functions.

If you do combine them, you might shorten the name to
pgstat_report_backend_allocation() or pgstat_report_allocation().

> +void
> +pgstat_report_backend_allocated_bytes_increase(uint64 allocation)
> +{
> +    volatile PgBackendStatus *beentry = MyBEEntry;
> +
> +    if (!beentry || !pgstat_track_activities)
> +    {
> +        /*
> +         * Account for memory before pgstats is initialized. This will be
> +         * migrated to pgstats on initialization.
> +         */
> +        backend_allocated_bytes += allocation;
> +
> +        return;
> +    }
> +
> +    /*
> +     * Update my status entry, following the protocol of bumping
> +     * st_changecount before and after.  We use a volatile pointer here to
> +     * ensure the compiler doesn't try to get cute.
> +     */
> +    PGSTAT_BEGIN_WRITE_ACTIVITY(beentry);
> +    beentry->backend_allocated_bytes += allocation;
> +    PGSTAT_END_WRITE_ACTIVITY(beentry);
> +}
> +
> +/* --------
> + * pgstat_report_backend_allocated_bytes_decrease() -
> + *
> + * Called to report decrease in memory allocated for this backend
> + * --------
> + */
> +void
> +pgstat_report_backend_allocated_bytes_decrease(uint64 deallocation)
> +{
> +    volatile PgBackendStatus *beentry = MyBEEntry;
> +
> +    /*
> +     * Cases may occur where shared memory from a previous postmaster
> +     * invocation still exist. These are cleaned up at startup by
> +     * dsm_cleanup_using_control_segment. Limit decreasing memory allocated to
> +     * zero in case no corresponding prior increase exists or decrease has
> +     * already been accounted for.
> +     */
> +
> +    if (!beentry || !pgstat_track_activities)
> +    {
> +        /*
> +         * Account for memory before pgstats is initialized. This will be
> +         * migrated to pgstats on initialization. Do not allow
> +         * backend_allocated_bytes to go below zero. If pgstats has not been
> +         * initialized, we are in startup and we set backend_allocated_bytes
> +         * to zero in cases where it would go negative and skip generating an
> +         * ereport.
> +         */
> +        if (deallocation > backend_allocated_bytes)
> +            backend_allocated_bytes = 0;
> +        else
> +            backend_allocated_bytes -= deallocation;
> +
> +        return;
> +    }
> +
> +    /*
> +     * Do not allow backend_allocated_bytes to go below zero. ereport if we
> +     * would have. There's no need for a lock around the read here as it's
> +     * being referenced from the same backend which means that there shouldn't
> +     * be concurrent writes. We want to generate an ereport in these cases.
> +     */
> +    if (deallocation > beentry->backend_allocated_bytes)
> +    {
> +        ereport(LOG, errmsg("decrease reduces reported backend memory allocated below zero; setting reported to
0"));
> +

I also think it would be nice to include the deallocation amount and
backend_allocated_bytes amount in the log message.
It also might be nice to start the message with something more clear
than "decrease".
For example, I would find this clear as a user:

    Backend [backend_type or pid] deallocated [deallocation number] bytes,
    [backend_allocated_bytes - deallocation number] more than this backend
    has reported allocating.

> diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
> index 96bffc0f2a..b6d135ad2f 100644
> --- a/src/backend/utils/adt/pgstatfuncs.c
> +++ b/src/backend/utils/adt/pgstatfuncs.c
> @@ -553,7 +553,7 @@ pg_stat_get_progress_info(PG_FUNCTION_ARGS)
>  Datum
>  pg_stat_get_activity(PG_FUNCTION_ARGS)
>  {
> -#define PG_STAT_GET_ACTIVITY_COLS    30
> +#define PG_STAT_GET_ACTIVITY_COLS    31
>      int            num_backends = pgstat_fetch_stat_numbackends();
>      int            curr_backend;
>      int            pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
> @@ -609,6 +609,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
>          else
>              nulls[16] = true;
>  
> +        values[30] = UInt64GetDatum(beentry->backend_allocated_bytes);

Though not the fault of this patch, it is becoming very difficult to
keep the columns straight in pg_stat_get_activity(). Perhaps you could
add a separate commit to add an enum for the columns so the function is
easier to understand.

> diff --git a/src/include/utils/backend_status.h b/src/include/utils/backend_status.h
> index b582b46e9f..75d87e8308 100644
> --- a/src/include/utils/backend_status.h
> +++ b/src/include/utils/backend_status.h
> @@ -169,6 +169,9 @@ typedef struct PgBackendStatus
>  
>      /* query identifier, optionally computed using post_parse_analyze_hook */
>      uint64        st_query_id;
> +
> +    /* Current memory allocated to this backend */
> +    uint64        backend_allocated_bytes;
>  } PgBackendStatus;

I don't think you need the backend_ prefix here since it is in
PgBackendStatus.

> @@ -313,7 +316,9 @@ extern const char *pgstat_get_backend_current_activity(int pid, bool checkUser);
>  extern const char *pgstat_get_crashed_backend_activity(int pid, char *buffer,
>                                                         int buflen);
>  extern uint64 pgstat_get_my_query_id(void);
> -
> +extern void pgstat_report_backend_allocated_bytes_increase(uint64 allocation);
> +extern void pgstat_report_backend_allocated_bytes_decrease(uint64 deallocation);
> +extern uint64 pgstat_get_all_backend_memory_allocated(void);
>  
>  /* ----------
>   * Support functions for the SQL-callable functions to
> diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out
> index 624d0e5aae..ba9f494806 100644
> --- a/src/test/regress/expected/rules.out
> +++ b/src/test/regress/expected/rules.out
> @@ -1753,10 +1753,11 @@ pg_stat_activity| SELECT s.datid,
>      s.state,
>      s.backend_xid,
>      s.backend_xmin,
> +    s.backend_allocated_bytes,
>      s.query_id,
>      s.query,
>      s.backend_type

Seems like it would be possible to add a functional test to stats.sql.

- Melanie

[1] https://www.postgresql.org/message-id/20221105024146.xxlbtsxh2niyz2fu%40awork3.anarazel.de



pgsql-hackers by date:

Previous
From: Nathan Bossart
Date:
Subject: Re: archive modules
Next
From: Nathan Bossart
Date:
Subject: Re: thinko in basic_archive.c