pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Tomas Vondra <tomas.vondra@enterprisedb.com>
To: Justin Pryzby <pryzby@telsasoft.com>
Cc: pgsql-hackers@postgresql.org
Subject: Re: mem context is not reset between extended stats
Date: Tue, 21 Sep 2021 02:15:45 +0200
Message-ID: <cd3af9ee-fc08-c9b0-e7c9-d3eb6fb74df6@enterprisedb.com> (raw)
In-Reply-To: <20210915200928.GP831@telsasoft.com>
References: <20210915200928.GP831@telsasoft.com>


On 9/15/21 10:09 PM, Justin Pryzby wrote:
> Memory allocation appeared be O(1) WRT the number of statistics objects, which
> was not expected to me.  This is true in v13 (and probably back to v10).
> 
> It seems to work fine to reset the memory context within the loop, so long as
> the statslist is allocated in the parent context.
> 

Yeah, and I agree this fix seems reasonable. Thanks for looking!

In principle we don't expect too many extended statistics on a single 
table, but building a single statistics may use quite a bit of memory, 
so it makes sense to release it early ...

But while playing with this a bit more, I discovered a much worse issue. 
Consider this:

   create table t (a text, b text, c text, d text,
                   e text, f text, g text, h text);

   insert into t select x, x, x, x, x, x, x, x from (
     select md5(mod(i,100)::text) as x
       from generate_series(1,30000) s(i)) foo;


   create statistics s (dependencies) on a, b, c, d, e, f, g, h from t;

   analyze t;

This ends up eating insane amounts of memory - on my laptop it eats 
~2.5GB and then crashes with OOM. This happens because each call to 
dependency_degree does build_sorted_items, which detoasts the values. 
And resetting the context can't fix that, because this happens while 
building a single statistics object.

IMHO the right fix is to run dependency_degree in a separate context, 
and reset it after each dependency. This releases the detoasted values, 
which are otherwise hard to deal with.

This does not mean we should not do what your patch does too. That does 
address various other "leaks" (for example MCV calls build_sorted_items 
too, but only once so it does not have this same issue).

These issues exist pretty much since PG10, which is where extended stats 
were introduced, so we'll have to backpatch it. But there's no rush and 
I don't want to interfere with rc1 at the moment.

Attached are two patches - 0001 is your patch (seems fine, but I looked 
only very briefly) and 0002 is the context reset I proposed.

regards

-- 
Tomas Vondra
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

0001-reset-context.patch (text/x-patch, 1.6K) download From 204f4602b218ec13ac1e3fa501a7f94adc8a4ea1 Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Tue, 21 Sep 2021 01:14:11 +0200 Subject: [PATCH 1/3] reset context --- src/backend/statistics/extended_stats.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c index 2e55913bc8..5c18da02c7 100644 --- a/src/backend/statistics/extended_stats.c +++ b/src/backend/statistics/extended_stats.c @@ -125,14 +125,14 @@ BuildRelationExtStatistics(Relation onerel, double totalrows, if (!natts) return; + pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock); + statslist = fetch_statentries_for_relation(pg_stext, RelationGetRelid(onerel)); + cxt = AllocSetContextCreate(CurrentMemoryContext, "BuildRelationExtStatistics", ALLOCSET_DEFAULT_SIZES); oldcxt = MemoryContextSwitchTo(cxt); - pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock); - statslist = fetch_statentries_for_relation(pg_stext, RelationGetRelid(onerel)); - /* report this phase */ if (statslist != NIL) { @@ -234,14 +234,12 @@ BuildRelationExtStatistics(Relation onerel, double totalrows, pgstat_progress_update_param(PROGRESS_ANALYZE_EXT_STATS_COMPUTED, ++ext_cnt); - /* free the build data (allocated as a single chunk) */ - pfree(data); + MemoryContextReset(cxt); } - table_close(pg_stext, RowExclusiveLock); - MemoryContextSwitchTo(oldcxt); MemoryContextDelete(cxt); + table_close(pg_stext, RowExclusiveLock); } /* -- 2.31.1
0002-build-dependencies-in-context.patch (text/x-patch, 1.9K) download From cd0a3398fd6cb586d373bba3d988e564ee7e8c5a Mon Sep 17 00:00:00 2001 From: Tomas Vondra <tomas.vondra@postgresql.org> Date: Tue, 21 Sep 2021 01:13:11 +0200 Subject: [PATCH 2/3] build dependencies in context --- src/backend/statistics/dependencies.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/backend/statistics/dependencies.c b/src/backend/statistics/dependencies.c index d703e9b9ba..4efba06fd6 100644 --- a/src/backend/statistics/dependencies.c +++ b/src/backend/statistics/dependencies.c @@ -30,6 +30,7 @@ #include "utils/fmgroids.h" #include "utils/fmgrprotos.h" #include "utils/lsyscache.h" +#include "utils/memutils.h" #include "utils/selfuncs.h" #include "utils/syscache.h" #include "utils/typcache.h" @@ -325,12 +326,6 @@ dependency_degree(StatsBuildData *data, int k, AttrNumber *dependency) group_size++; } - if (items) - pfree(items); - - pfree(mss); - pfree(attnums_dep); - /* Compute the 'degree of validity' as (supporting/total). */ return (n_supporting_rows * 1.0 / data->numrows); } @@ -359,9 +354,14 @@ statext_dependencies_build(StatsBuildData *data) /* result */ MVDependencies *dependencies = NULL; + MemoryContext cxt; Assert(data->nattnums >= 2); + cxt = AllocSetContextCreate(CurrentMemoryContext, + "sorted items cxt", + ALLOCSET_DEFAULT_SIZES); + /* * We'll try build functional dependencies starting from the smallest ones * covering just 2 columns, to the largest ones, covering all columns @@ -380,10 +380,16 @@ statext_dependencies_build(StatsBuildData *data) { double degree; MVDependency *d; + MemoryContext oldcxt; + + oldcxt = MemoryContextSwitchTo(cxt); /* compute how valid the dependency seems */ degree = dependency_degree(data, k, dependency); + MemoryContextSwitchTo(oldcxt); + MemoryContextReset(cxt); + /* * if the dependency seems entirely invalid, don't store it */ -- 2.31.1

view thread (5+ messages)  latest in thread

Message-ID: <cd3af9ee-fc08-c9b0-e7c9-d3eb6fb74df6@enterprisedb.com>
Permalink:  ../cd3af9ee-fc08-c9b0-e7c9-d3eb6fb74df6@enterprisedb.com/
Also on:    postgresql.org/message-id/cd3af9ee-fc08-c9b0-e7c9-d3eb6fb74df6@enterprisedb.com

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: tomas.vondra@enterprisedb.com, pryzby@telsasoft.com
  Subject: Re: mem context is not reset between extended stats
  In-Reply-To: <cd3af9ee-fc08-c9b0-e7c9-d3eb6fb74df6@enterprisedb.com>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox