Re: shared-memory based stats collector

From: Kyotaro Horiguchi <horikyota(dot)ntt(at)gmail(dot)com>
To: andres(at)anarazel(dot)de
Cc: masao(dot)fujii(at)oss(dot)nttdata(dot)com, gkokolatos(at)protonmail(dot)com, pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: shared-memory based stats collector
Date: 2021-03-23 02:20:52
Message-ID: 20210323.112052.1952896078786703992.horikyota.ntt@gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

At Fri, 19 Mar 2021 14:27:38 -0700, Andres Freund <andres(at)anarazel(dot)de> wrote in
> Hi,
>
> On 2021-03-10 20:26:56 -0800, Andres Freund wrote:
> > > + * We expose this shared entry now. You might think that the entry
> > > + * can be removed by a concurrent backend, but since we are creating
> > > + * an stats entry, the object actually exists and used in the upper
> > > + * layer. Such an object cannot be dropped until the first vacuum
> > > + * after the current transaction ends.
> > > + */
> > > + dshash_release_lock(pgStatSharedHash, shhashent);
> >
> > I don't think you can safely release the lock before you incremented the
> > refcount? What if, once the lock is released, somebody looks up that
> > entry, increments the refcount, and decrements it again? It'll see a
> > refcount of 0 at the end and decide to free the memory. Then the code
> > below will access already freed / reused memory, no?
>
> Yep, it's not even particularly hard to hit:
>
> S0: CREATE TABLE a_table();
> S0: INSERT INTO a_table();
> S0: disconnect
> S1: set a breakpoint to just after the dshash_release_lock(), with an
> if objid == a_table_oid
> S1: SELECT pg_stat_get_live_tuples('a_table'::regclass);
> (will break at above breakpoint, without having incremented the
> refcount yet)
> S2: DROP TABLE a_table;
> S2: VACUUM pg_class;
>
> At that point S2's call to pgstat_vacuum_stat() will find the shared
> stats entry for a_table, delete the entry from the shared hash table,
> see that the stats data has a zero refcount, and free it. Once S1 wakes
> up it'll use already freed (and potentially since reused) memory.

Sorry for the delay. You're right. I actually see permanent-block when
continue running S1 after the vacuum. That happens at LWLockRelease
on freed block.

Moving the refcount bumping before the dshash_release_lock call fixes
that. One issue doing that *was* get_stat_entry() had the path for
the case pgStatCacheContext is not available, which is already
dead. After the early lock releasing is removed, the comment is no
longer needed, too.

While working on this, I noticed that the previous diff
v56-57-func-diff.txt was slightly stale (missing a later bug fix). So
attached contains a fix on the amendment path.

Please find the attached.

regards.

--
Kyotaro Horiguchi
NTT Open Source Software Center

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Fujii Masao 2021-03-23 02:40:26 Re: make the stats collector shutdown without writing the statsfiles if the immediate shutdown is requested.
Previous Message Bruce Momjian 2021-03-23 02:19:40 Re: Key management with tests