Thread: Our naming of wait events is a disaster.

Our naming of wait events is a disaster.

From
Tom Lane
Date:
I've been trying to reformat table 27.4 (wait events) to fit
into PDF output, which has caused me to study its contents
more than I ever had before.  The lack of consistency, or
even any weak attempt at consistency, is not just glaring;
it's embarrassing.

We have a lot of wait event names like these:

    XidGenLock
    ProcArrayLock
    SInvalReadLock
    SInvalWriteLock
    WALBufMappingLock
    WALWriteLock

which are more or less fine; maybe one could wish for having
just one way of capitalizing acronyms not two, but I'll let
that pass.  But could we be satisfied with handling all multi
word names in that style?  Nope:

    commit_timestamp
    multixact_offset
    multixact_member
    wal_insert

(and in case you are wondering, yes, "WAL" is also spelled "Wal"
in yet other places.)

And then somebody else, unwilling to use either of those styles,
thought it would be cute to do

    Hash/Batch/Allocating
    Hash/Batch/Electing
    Hash/Batch/Loading
    Hash/GrowBatches/Allocating

and all alone in the remotest stretch of left field, we've got

    speculative token

(yes, with a space in it).

Also, while the average length of these names exceeds 16 characters,
with such gems as SerializablePredicateLockListLock, think not that
prolixity is the uniform rule:

    oldserxid
    proc
    tbm

Is it unreasonable of me to think that there should be *some*
amount of editorial control over these user-visible names?
At the rock bottom minimum, shouldn't we insist that they all
be legal identifiers?

I'm not sure what our stance is on version-to-version consistency
of these names, but I'd like to think that we are not stuck for
all time with the results of these random coin tosses.

My inclination is to propose that we settle on the first style
shown above, which is the majority case now, and rename the
other events to fit that.  As long as we're breaking compatibility
anyway, I'd also like to shorten one or two of the very longest
names, because they're just giving me fits in fixing the PDF
rendering.  (They would make a mess of the display of
pg_stat_activity, too, anytime they come up in the field.)

Thoughts?

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
"Jonah H. Harris"
Date:
On Tue, May 12, 2020 at 11:16 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
My inclination is to propose that we settle on the first style
shown above, which is the majority case now, and rename the
other events to fit that.  As long as we're breaking compatibility
anyway, I'd also like to shorten one or two of the very longest
names, because they're just giving me fits in fixing the PDF
rendering.  (They would make a mess of the display of
pg_stat_activity, too, anytime they come up in the field.)

Thoughts?

+1

--
Jonah H. Harris

Re: Our naming of wait events is a disaster.

From
"Andrey M. Borodin"
Date:

> 12 мая 2020 г., в 20:16, Tom Lane <tgl@sss.pgh.pa.us> написал(а):
>
> Thoughts?
>

I've been coping with cognitive load of these names recently. 2 cents of my impressions:
1. Names are somewhat recognisable and seem to have some meaning. But there is not so much information about them in
theInternet. But I did not try to Google them all, just a small subset. 
2. Anyway, names should be grepable and googlable, i.e. unique amid identifiers.
3. I think names observed in wait_event and wait_event_type should not duplicate information. i.e. "XidGenLock" is
already"LWLock". 
4. It's hard to tell the difference between "buffer_content", "buffer_io", "buffer_mapping", "BufferPin",
"BufFileRead","BufFileWrite" and some others. "CLogControlLock" vs "clog"? I'm not sure good DBA can tell the
differencewithout looking up into the code. 
I hope some thoughts will be useful.

Best regards, Andrey Borodin.


Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
"Andrey M. Borodin" <x4mmm@yandex-team.ru> writes:
> 3. I think names observed in wait_event and wait_event_type should not duplicate information. i.e. "XidGenLock" is
already"LWLock". 

Yeah, I'd been wondering about that too: we could strip the "Lock" suffix
from all the names in the LWLock category, and make pg_stat_activity
output a bit narrower.

There are a lot of other things that seem inconsistent, but I'm not sure
how much patience people would have for judgment-call renamings.  An
example is that "ProcSignalBarrier" is under IO, but why?  Shouldn't it
be reclassified as IPC?  Other than that, *almost* all the IO events
are named SomethingRead, SomethingWrite, or SomethingSync, which
makes sense to me ... should we insist they all follow that pattern?

Anyway, I was just throwing this idea out to see if there would be
howls of "you can't rename anything" anguish.  Since there haven't
been so far, I'll spend a bit more time and try to create a concrete
list of possible changes.

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Simon Riggs
Date:
On Tue, 12 May 2020 at 19:11, Tom Lane <tgl@sss.pgh.pa.us> wrote:
 
Anyway, I was just throwing this idea out to see if there would be
howls of "you can't rename anything" anguish.  Since there haven't
been so far, I'll spend a bit more time and try to create a concrete
list of possible changes.

If we add in extensions and lwlocks, will they show up as well?

--
Simon Riggs                http://www.2ndQuadrant.com/
Mission Critical Databases

Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
Simon Riggs <simon@2ndquadrant.com> writes:
> On Tue, 12 May 2020 at 19:11, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> Anyway, I was just throwing this idea out to see if there would be
>> howls of "you can't rename anything" anguish.  Since there haven't
>> been so far, I'll spend a bit more time and try to create a concrete
>> list of possible changes.

> If we add in extensions and lwlocks, will they show up as well?

Yeah, I was just looking into that.  Part of the reason for the
inconsistency is that we've exposed names that are passed to,
eg, SimpleLruInit that previously were strictly internal debugging
identifiers, so that approximately zero thought was put into them.

We're going to have to document SimpleLruInit and similar functions
along the lines of "The name you give here will be user-visible as
a wait event.  Choose it with an eye to consistency with existing
wait event names, and add it to the user-facing documentation."
But that requirement isn't something I just invented, it was
effectively created by whoever implemented things this way.

Said user-facing documentation largely fails to explain that the
set of wait events can be enlarged by extensions; that needs to
be fixed, too.

There isn't a lot we can do to force extensions to pick consistent
names, but on the other hand we won't be documenting such names
anyway, so for my immediate purposes it doesn't matter ;-)

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Robert Haas
Date:
> There are a lot of other things that seem inconsistent, but I'm not sure
> how much patience people would have for judgment-call renamings.  An
> example is that "ProcSignalBarrier" is under IO, but why?  Shouldn't it
> be reclassified as IPC?

Hmm, that seems like a goof.

> Other than that, *almost* all the IO events
> are named SomethingRead, SomethingWrite, or SomethingSync, which
> makes sense to me ... should we insist they all follow that pattern?

Maybe, but sometimes module X does more than one kind of
read/write/sync, and I'm not necessarily keen on merging things
together. The whole point of this is to be able to tell where you're
stuck in the code, and the more you merge related things together, the
less you can actually tell that.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



Re: Our naming of wait events is a disaster.

From
Robert Haas
Date:
On Tue, May 12, 2020 at 4:00 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Said user-facing documentation largely fails to explain that the
> set of wait events can be enlarged by extensions; that needs to
> be fixed, too.

Is that true? How can they do that? I thought they were stuck with
PG_WAIT_EXTENSION.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



Re: Our naming of wait events is a disaster.

From
Robert Haas
Date:
On Tue, May 12, 2020 at 11:16 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> I've been trying to reformat table 27.4 (wait events) to fit
> into PDF output, which has caused me to study its contents
> more than I ever had before.

That reminds me that it might be easier to maintain that table if we
broke it up into one table per major category - that is, one table for
lwlocks, one table for IPC, one table for IO, etc. - instead of a
single table with a row-span number that is large and frequently
updated incorrectly.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



Re: Our naming of wait events is a disaster.

From
Alvaro Herrera
Date:
On 2020-May-12, Robert Haas wrote:

> That reminds me that it might be easier to maintain that table if we
> broke it up into one table per major category - that is, one table for
> lwlocks, one table for IPC, one table for IO, etc. - instead of a
> single table with a row-span number that is large and frequently
> updated incorrectly.

(Didn't we have a patch to generate the table programmatically?)

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



Re: Our naming of wait events is a disaster.

From
Peter Geoghegan
Date:
On Tue, May 12, 2020 at 8:16 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> I'm not sure what our stance is on version-to-version consistency
> of these names, but I'd like to think that we are not stuck for
> all time with the results of these random coin tosses.

These names are fundamentally implementation details, and
implementation details are subject to change without too much warning.
I think it's okay to change the names for consistency along the lines
you propose. ISTM that it's worth going to a little bit of effort to
preserve any existing names. But not too much.

-- 
Peter Geoghegan



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
Robert Haas <robertmhaas@gmail.com> writes:
> On Tue, May 12, 2020 at 11:16 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> I've been trying to reformat table 27.4 (wait events) to fit
>> into PDF output, which has caused me to study its contents
>> more than I ever had before.

> That reminds me that it might be easier to maintain that table if we
> broke it up into one table per major category - that is, one table for
> lwlocks, one table for IPC, one table for IO, etc. - instead of a
> single table with a row-span number that is large and frequently
> updated incorrectly.

Yeah, see my last attempt at

https://www.postgresql.org/message-id/26961.1589260206%40sss.pgh.pa.us

I'm probably going to go with that, but as given that patch conflicts
with my other pending patch to change the catalog description tables,
so I want to push that other one first and then clean up the wait-
event one.  In the meantime, I'm going to look at these naming issues,
which will also be changing that patch ...

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
Robert Haas <robertmhaas@gmail.com> writes:
> On Tue, May 12, 2020 at 4:00 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> Said user-facing documentation largely fails to explain that the
>> set of wait events can be enlarged by extensions; that needs to
>> be fixed, too.

> Is that true? How can they do that? I thought they were stuck with
> PG_WAIT_EXTENSION.

Extensions can definitely add new LWLock tranches, and thereby
enlarge the set of names in that category.  I haven't figured out
whether there are other avenues.

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Simon Riggs
Date:
On Tue, 12 May 2020 at 21:00, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Simon Riggs <simon@2ndquadrant.com> writes:
> On Tue, 12 May 2020 at 19:11, Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> Anyway, I was just throwing this idea out to see if there would be
>> howls of "you can't rename anything" anguish.  Since there haven't
>> been so far, I'll spend a bit more time and try to create a concrete
>> list of possible changes.

> If we add in extensions and lwlocks, will they show up as well?

Yeah, I was just looking into that.  Part of the reason for the
inconsistency is that we've exposed names that are passed to,
eg, SimpleLruInit that previously were strictly internal debugging
identifiers, so that approximately zero thought was put into them.

We're going to have to document SimpleLruInit and similar functions
along the lines of "The name you give here will be user-visible as
a wait event.  Choose it with an eye to consistency with existing
wait event names, and add it to the user-facing documentation."
But that requirement isn't something I just invented, it was
effectively created by whoever implemented things this way.

Said user-facing documentation largely fails to explain that the
set of wait events can be enlarged by extensions; that needs to
be fixed, too.

There isn't a lot we can do to force extensions to pick consistent
names, but on the other hand we won't be documenting such names
anyway, so for my immediate purposes it doesn't matter ;-)

 I think we need to plan the namespace with extensions in mind.

There are now dozens; some of them even help you view wait events...

We don't want the equivalent of the Dewey decimal system: 300 categories of Exaggeration and one small corner for Science.

--
Simon Riggs                http://www.2ndQuadrant.com/
Mission Critical Databases

Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> (Didn't we have a patch to generate the table programmatically?)

Having now looked around a bit at where the names come from, I think
such a patch would be impossible as things stand, which is a pity
because as-things-stand is a totally unmaintainable situation.
Anybody at all can call LWLockRegisterTranche and thereby create a new
name that ought to be listed in the SGML table.  Apparently, somebody
grepped for such calls and put all the ones that existed at the time
into the table; unsurprisingly, the results are already out of date.
Several of the hard-wired calls in RegisterLWLockTranches() are not
reflected in the SGML table AFAICS.

Or, if you don't want to call LWLockRegisterTranche, you can instead
call RequestNamedLWLockTranche.  Whee.  At least there are none of
those in the core code.  However, we do have both autoprewarm and
pg_stat_statements calling these things from contrib.

That raises a policy question, or really two of them: should contrib
code be held to the standards we're going to set forth for tranche
names chosen by core code?  And should contrib-created tranche names
be listed in chapter 27's table?  (If not, should the individual
contrib modules document their additions?)

We could make things a little better perhaps if we got rid of all the
cowboy calls to LWLockRegisterTranche and had RegisterLWLockTranches
make all of them using a single table of names, as it already does
with MainLWLockNames[] but not the other ones.  Then it'd be possible
to have documentation beside that table warning people to add entries
to the SGML docs; and even for the people who can't be bothered to
read comments, at least they'd be adding names to a list of names that
would give them some precedent and context for how to choose a new name.
I think 50% of the problem right now is that if you just write a
random new call to LWLockRegisterTranche in a random new place, you
have no context about what the tranche name should look like.

Even with all the names declared in some reasonably centralized
place(s), we'd be a long way from making the SGML tables programmatically,
because we'd not have text descriptions for the wait events.  I can
imagine extending the source-code conventions a bit to include those
strings there, but I'm doubtful that it's worth the trouble.

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
For the specific category of the heavyweight lock types, I'm
now thinking that we can't change the event names very much, because
those are also exposed in pg_locks' locktype column.  We can be
darn certain, for example, that changing the spelling of "relation"
in that column would break a lot of user queries.  Conceivably we
could decouple the wait event names from the locktype column, but
on the whole that doesn't seem like a great plan.

However, having said that, I remain on the warpath about "speculative
token".  That's an utterly horrid choice for both locktype and wait
event.  I also notice, with no amusement, that "speculative token"
is not documented in the pg_locks documentation.  So I think we should
change it ... but to what, exactly?  Looking at the other existing names:

const char *const LockTagTypeNames[] = {
    "relation",
    "extend",
    "page",
    "tuple",
    "transactionid",
    "virtualxid",
    "speculative token",
    "object",
    "userlock",
    "advisory"
};

I'm inclined to propose "spectoken".  I'd be okay with "spec_token" as
well, but there are not underscores in the longer-established names.

(Needless to say, this array is going to gain a comment noting that
there are two places to document any changes.  Also, if we split up
the wait_event table as discussed earlier, it might make sense for
pg_locks' documentation to cross-reference the sub-table for heavyweight
lock events, since that has some explanation of what the codes mean.)

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Isaac Morland
Date:
On Tue, 12 May 2020 at 18:11, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Alvaro Herrera <alvherre@2ndquadrant.com> writes:
> (Didn't we have a patch to generate the table programmatically?)

Having now looked around a bit at where the names come from, I think
such a patch would be impossible as things stand, which is a pity
because as-things-stand is a totally unmaintainable situation.
Anybody at all can call LWLockRegisterTranche and thereby create a new
name that ought to be listed in the SGML table.  Apparently, somebody
grepped for such calls and put all the ones that existed at the time
into the table; unsurprisingly, the results are already out of date.
Several of the hard-wired calls in RegisterLWLockTranches() are not
reflected in the SGML table AFAICS.

I expect there is a reason why this hasn’t been suggested, but just in case it is at all helpful:

When do these names get created? That is, do all the wait types get created and registered on startup, or is it more like whenever something needs to do something the name gets passed in ad hoc? I'm wondering because it seems like it might be helpful to have a system view which gives all the wait event types, names, and descriptions. Maybe even add a column for which extension (or core) it came from. The documentation could then just explain the general situation and point people at the system view to see exactly which wait types exist in their system. This would require every instance where a type is registered to pass an additional parameter — the description, as currently seen in the table in the documentation.

Of course if the names get passed in ad hoc then such a view could only show the types that happen to have been created up to the moment it is queried, which would defeat the purpose. And I can think of a few potential reasons why this might not work at all, starting with the need to re-write every example of registering a new type to provide the documentation string for the view.

Inspiration due to pg_setting, pg_config, pg_available_extensions and pg_get_keywords ().

Re: Our naming of wait events is a disaster.

From
Thomas Munro
Date:
On Wed, May 13, 2020 at 3:16 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> And then somebody else, unwilling to use either of those styles,
> thought it would be cute to do
>
>         Hash/Batch/Allocating
>         Hash/Batch/Electing
>         Hash/Batch/Loading
>         Hash/GrowBatches/Allocating

Perhaps we should also drop the 'ing' from the verbs, to be more like
...Read etc.



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
Thomas Munro <thomas.munro@gmail.com> writes:
> On Wed, May 13, 2020 at 3:16 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> Hash/Batch/Allocating
>> Hash/Batch/Electing
>> Hash/Batch/Loading
>> Hash/GrowBatches/Allocating

> Perhaps we should also drop the 'ing' from the verbs, to be more like
> ...Read etc.

Yeah, that aspect was bothering me too.  Comparing these to other
wait event names, you could make a case for either "Allocate" or
"Allocation"; but there are no other names with -ing.

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
Isaac Morland <isaac.morland@gmail.com> writes:
> ...  I'm wondering because it seems
> like it might be helpful to have a system view which gives all the wait
> event types, names, and descriptions. Maybe even add a column for which
> extension (or core) it came from. The documentation could then just explain
> the general situation and point people at the system view to see exactly
> which wait types exist in their system.

There's certainly an argument for doing things like that, but I think it'd
be a net negative in terms of quality and consistency of documentation.
We'd basically be deciding those are non-goals.

Of course, ripping out table 27.4 altogether would be a simple solution
to the formatting problem I started with ;-).  But it doesn't really
seem like the answer we want.

> Of course if the names get passed in ad hoc then such a view could only
> show the types that happen to have been created up to the moment it is
> queried, which would defeat the purpose.

Yes, exactly.

I don't actually understand why the LWLock tranche mechanism is designed
the way it is.  It seems to be intended to support different backends
having different sets of LWLocks, but I fail to see why that's a good idea,
or even useful at all.  In any case, dynamically-created LWLocks are
clearly out of scope for the documentation.  The problem that I'm trying
to deal with right now is that even LWLocks that are hard-wired into the
backend code are difficult to enumerate.  That wasn't a problem before
we decided we needed to expose them all to user view; but now it is.

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
I looked through the naming situation for LWLocks, and SLRUs which turn
out to be intimately connected to that, because most of the dubious
LWLock names we're currently exposing actually are derived from SLRUs.
Here's some ideas (not quite a full proposal yet) for changing that.

Note that because of the SLRU connection, I feel justified in treating
this business as an open item for v13, not something to fix later.
Whatever your position on the mutability of wait event names, we have
as of v13 exposed SLRU names in other places besides that: the
pg_stat_slru view shows them, and the pg_stat_reset_slru() function
acccepts them as arguments.  So there will certainly be a larger
compatibility penalty to be paid if we don't fix this now.

For each SLRU, there is a named "control lock" that guards the SLRU's
control info, and a tranche of per-buffer locks.  The control locks
are mostly named as the SLRU name followed by "ControlLock", though
it should surprise nobody to hear that we haven't quite been 100% on
that.  As of right now, the per-buffer tranche just has the same name
as the SLRU, which is not great, not least because it gives the wrong
impression about the scopes of those locks.

Digging through the existing callers of SimpleLruInit, we have

name                    control lock                    subdir

"async"                 AsyncCtlLock                    "pg_notify"

"clog"                  CLogControlLock                 "pg_xact"

"commit_timestamp"      CommitTsControlLock             "pg_commit_ts"

"multixact_member"      MultiXactMemberControlLock      "pg_multixact/members"

"multixact_offset"      MultiXactOffsetControlLock      "pg_multixact/offsets"

"oldserxid"             OldSerXidLock                   "pg_serial"

"subtrans"              SubtransControlLock             "pg_subtrans"

After studying that list for awhile, it seems to me that we could
do worse than to name the SLRUs to match their on-disk subdirectories,
which are names that are already user-visible.  So I propose these
base names for the SLRUs:

Notify
Xact
CommitTs
MultiXactMember  (or MultiXactMembers)
MultiXactOffset  (or MultiXactOffsets)
Serial
Subtrans

I could go either way on whether to include "s" in the two mxact SLRU
names --- using "s" matches the on-disk names, but the other SLRU
names are not pluralized.

I think we should expose exactly these names in the pg_stat_slru view
and as pg_stat_reset_slru() arguments.  (Maybe pg_stat_reset_slru
should match its argument case-insensitively ... it does not now.)

As for the control locks, they should all be named in a directly
predictable way from their SLRUs.  We could stick with the
"ControlLock" suffix, or we could change to a less generic term
like "SLRULock".  There are currently two locks that are named
somethingControlLock but are not SLRU guards:

DynamicSharedMemoryControlLock
ReplicationSlotControlLock

so I'd kind of like to either rename those two, or stop using
"ControlLock" as the SLRU suffix, or arguably both, because "Control"
is practically a noise word in this context.  (Any renaming here will
imply source code adjustments, but I don't think any of these locks
are touched widely enough for that to be problematic.)

As for the per-buffer locks, maybe name those tranches as SLRU name
plus "BufferLock"?  Or "BufferLocks", to emphasize that there's not
just one?

Moving on to the other tranches that don't correspond to single
predefined locks, I propose renaming as follows:

existing name           proposed name

buffer_content          BufferContent
buffer_io               BufferIO
buffer_mapping          BufferMapping
lock_manager            LockManager
parallel_append         ParallelAppend
parallel_hash_join      ParallelHashJoin
parallel_query_dsa      ParallelQueryDSA
predicate_lock_manager  PredicateLockManager
proc                    FastPath  (see below)
replication_origin      ReplicationOrigin
replication_slot_io     ReplicationSlotIO
serializable_xact       PerXactPredicateList  (see below)
session_dsa             PerSessionDSA
session_record_table    PerSessionRecordType
session_typmod_table    PerSessionRecordTypmod
shared_tuplestore       SharedTupleStore
tbm                     SharedTidBitmap
wal_insert              WALInsert

These are mostly just adjusting the names to correspond to the new
rule about spelling of multi-word names, but there are two that
perhaps require extra discussion:

"proc": it hardly needs pointing out that this name utterly sucks.
I looked into the code, and the tranche corresponds to the PGPROC
"backendLock" fields; that name also fails to explain anything
at all, as does its comment.  Further research shows that what those
locks actually guard is the "fast path lock" data within each PGPROC,
that is the "fpXXX" fields.  I propose renaming the PGPROC fields to
"fpInfoLock" and the tranche to FastPath.

"serializable_xact": this is pretty awful as well, seeing that we
have half a dozen other kinds of locks related to the serializable
machinery, and these locks are not nearly as widely scoped as
the name might make you think.  In reality, per predicate.c:

 *  SERIALIZABLEXACT's member 'predicateLockListLock'
 *      - Protects the linked list of locks held by a transaction.  Only
 *          needed for parallel mode, where multiple backends share the
 *          same SERIALIZABLEXACT object.  Not needed if
 *          SerializablePredicateLockListLock is held exclusively.

So my tentative proposal for the tranche name is PerXactPredicateList,
and the field member ought to get some name derived from that.  It
might be better if this name included something about "Parallel", but
I couldn't squeeze that in without making the name longer than I'd like.

Finally, of the individually-named lwlocks (see lwlocknames.txt),
the only ones not related to SLRUs that I feel a need to rename
are

AsyncQueueLock => NotifyQueueLock for consistency with SLRU rename
CLogTruncationLock => XactTruncationLock for consistency with SLRU
SerializablePredicateLockListLock => shorten to SerializablePredicateListLock
DynamicSharedMemoryControlLock => drop "Control"?
ReplicationSlotControlLock => drop "Control"?

Lastly there's the issue of whether we want to drop the "Lock" suffix
in the names of these locks as presented in the wait_event data.
I'm kind of inclined to do so, just for brevity.  Also, if we don't
do that, then it seems like the tranche names for the
not-individually-named locks ought to gain a suffix, like "Locks".

Comments?

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
The attached patch doesn't actually change any LWLock names, but it
is a useful start on that project.  What it does is to get rid of the
current scheme of dynamically registering the names of built-in
LWLocks in favor of having a constant array of those names.  It's
completely foolish to expend process-startup cycles on constructing
an array of constant data; moreover, the way things are done now
results in the tranche names being defined all over creation.  I draw
a short straight line between that technique and the lack of consistency
in the tranche names.  Given that we have an enum in lwlock.h enumerating
the built-in tranches, there's certainly no expectation that somebody's
going to create a new one without letting the lwlock module know about
it, so this gives up no flexibility.  In fact, it adds some, because
we can now name an SLRU's buffer-locks tranche whatever we want ---
it's not hard-wired as being the same as the SLRU's base name.

The dynamic registration mechanism is still there, but it's now
*only* used if you load an extension that creates dynamic LWLocks.

At some point it might be interesting to generate the enum
BuiltinTrancheIds and the BuiltinTrancheNames array from a common
source file, as we do for lwlocknames.h/.c.  I didn't feel a need
to make that happen today, though.

            regards, tom lane

diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index 3ba9fc9..3572b01 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -235,9 +235,6 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
     else
         Assert(found);

-    /* Register SLRU tranche in the main tranches array */
-    LWLockRegisterTranche(tranche_id, name);
-
     /*
      * Initialize the unshared control struct, including directory path. We
      * assume caller set PagePrecedes.
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index a53e6d9..4284659 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5116,10 +5116,8 @@ XLOGShmemInit(void)
         /* both should be present or neither */
         Assert(foundCFile && foundXLog);

-        /* Initialize local copy of WALInsertLocks and register the tranche */
+        /* Initialize local copy of WALInsertLocks */
         WALInsertLocks = XLogCtl->Insert.WALInsertLocks;
-        LWLockRegisterTranche(LWTRANCHE_WAL_INSERT,
-                              "wal_insert");

         if (localControlFile)
             pfree(localControlFile);
@@ -5155,7 +5153,6 @@ XLOGShmemInit(void)
         (WALInsertLockPadded *) allocptr;
     allocptr += sizeof(WALInsertLockPadded) * NUM_XLOGINSERT_LOCKS;

-    LWLockRegisterTranche(LWTRANCHE_WAL_INSERT, "wal_insert");
     for (i = 0; i < NUM_XLOGINSERT_LOCKS; i++)
     {
         LWLockInitialize(&WALInsertLocks[i].l.lock, LWTRANCHE_WAL_INSERT);
diff --git a/src/backend/replication/logical/origin.c b/src/backend/replication/logical/origin.c
index d0d2b46..923ea3f 100644
--- a/src/backend/replication/logical/origin.c
+++ b/src/backend/replication/logical/origin.c
@@ -517,9 +517,6 @@ ReplicationOriginShmemInit(void)
             ConditionVariableInit(&replication_states[i].origin_cv);
         }
     }
-
-    LWLockRegisterTranche(replication_states_ctl->tranche_id,
-                          "replication_origin");
 }

 /* ---------------------------------------------------------------------------
diff --git a/src/backend/replication/slot.c b/src/backend/replication/slot.c
index abae74c..d3d1033 100644
--- a/src/backend/replication/slot.c
+++ b/src/backend/replication/slot.c
@@ -140,9 +140,6 @@ ReplicationSlotsShmemInit(void)
         ShmemInitStruct("ReplicationSlot Ctl", ReplicationSlotsShmemSize(),
                         &found);

-    LWLockRegisterTranche(LWTRANCHE_REPLICATION_SLOT_IO_IN_PROGRESS,
-                          "replication_slot_io");
-
     if (!found)
     {
         int            i;
diff --git a/src/backend/storage/buffer/buf_init.c b/src/backend/storage/buffer/buf_init.c
index af62d48..8954856 100644
--- a/src/backend/storage/buffer/buf_init.c
+++ b/src/backend/storage/buffer/buf_init.c
@@ -87,9 +87,6 @@ InitBufferPool(void)
                         NBuffers * (Size) sizeof(LWLockMinimallyPadded),
                         &foundIOLocks);

-    LWLockRegisterTranche(LWTRANCHE_BUFFER_IO_IN_PROGRESS, "buffer_io");
-    LWLockRegisterTranche(LWTRANCHE_BUFFER_CONTENT, "buffer_content");
-
     /*
      * The array used to sort to-be-checkpointed buffer ids is located in
      * shared memory, to avoid having to allocate significant amounts of
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 3630006..6a94448 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -267,8 +267,6 @@ CreateSharedProcArray(void)
                             mul_size(sizeof(bool), TOTAL_MAX_CACHED_SUBXIDS),
                             &found);
     }
-
-    LWLockRegisterTranche(LWTRANCHE_PROC, "proc");
 }

 /*
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 4c14e51..a4bc47a 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -108,14 +108,85 @@ extern slock_t *ShmemLock;
 #define LW_SHARED_MASK                ((uint32) ((1 << 24)-1))

 /*
- * This is indexed by tranche ID and stores the names of all tranches known
- * to the current backend.
+ * There are three sorts of LWLock "tranches":
+ *
+ * 1. The individually-named locks defined in lwlocknames.h each have their
+ * own tranche.  The names of these tranches appear in MainLWLockNames[]
+ * in lwlocknames.c.
+ *
+ * 2. There are some predefined tranches for built-in groups of locks.
+ * These are listed in enum BuiltinTrancheIds in lwlock.h, and their names
+ * appear in BuiltinTrancheNames[] below.
+ *
+ * 3. Extensions can create new tranches, via either RequestNamedLWLockTranche
+ * or LWLockRegisterTranche.  The names of these that are known in the current
+ * process appear in LWLockTrancheNames[].
  */
-static const char **LWLockTrancheArray = NULL;
-static int    LWLockTranchesAllocated = 0;

-#define T_NAME(lock) \
-    (LWLockTrancheArray[(lock)->tranche])
+static const char *const BuiltinTrancheNames[] = {
+    /* LWTRANCHE_CLOG_BUFFERS: */
+    "clog",
+    /* LWTRANCHE_COMMITTS_BUFFERS: */
+    "commit_timestamp",
+    /* LWTRANCHE_SUBTRANS_BUFFERS: */
+    "subtrans",
+    /* LWTRANCHE_MXACTOFFSET_BUFFERS: */
+    "multixact_offset",
+    /* LWTRANCHE_MXACTMEMBER_BUFFERS: */
+    "multixact_member",
+    /* LWTRANCHE_ASYNC_BUFFERS: */
+    "async",
+    /* LWTRANCHE_OLDSERXID_BUFFERS: */
+    "oldserxid",
+    /* LWTRANCHE_WAL_INSERT: */
+    "wal_insert",
+    /* LWTRANCHE_BUFFER_CONTENT: */
+    "buffer_content",
+    /* LWTRANCHE_BUFFER_IO_IN_PROGRESS: */
+    "buffer_io",
+    /* LWTRANCHE_REPLICATION_ORIGIN: */
+    "replication_origin",
+    /* LWTRANCHE_REPLICATION_SLOT_IO_IN_PROGRESS: */
+    "replication_slot_io",
+    /* LWTRANCHE_PROC: */
+    "proc",
+    /* LWTRANCHE_BUFFER_MAPPING: */
+    "buffer_mapping",
+    /* LWTRANCHE_LOCK_MANAGER: */
+    "lock_manager",
+    /* LWTRANCHE_PREDICATE_LOCK_MANAGER: */
+    "predicate_lock_manager",
+    /* LWTRANCHE_PARALLEL_HASH_JOIN: */
+    "parallel_hash_join",
+    /* LWTRANCHE_PARALLEL_QUERY_DSA: */
+    "parallel_query_dsa",
+    /* LWTRANCHE_SESSION_DSA: */
+    "session_dsa",
+    /* LWTRANCHE_SESSION_RECORD_TABLE: */
+    "session_record_table",
+    /* LWTRANCHE_SESSION_TYPMOD_TABLE: */
+    "session_typmod_table",
+    /* LWTRANCHE_SHARED_TUPLESTORE: */
+    "shared_tuplestore",
+    /* LWTRANCHE_TBM: */
+    "tbm",
+    /* LWTRANCHE_PARALLEL_APPEND: */
+    "parallel_append",
+    /* LWTRANCHE_SXACT: */
+    "serializable_xact"
+};
+
+StaticAssertDecl(lengthof(BuiltinTrancheNames) ==
+                 LWTRANCHE_FIRST_USER_DEFINED - NUM_INDIVIDUAL_LWLOCKS,
+                 "missing entries in BuiltinTrancheNames[]");
+
+/*
+ * This is indexed by tranche ID minus LWTRANCHE_FIRST_USER_DEFINED, and
+ * stores the names of all dynamically-created tranches known to the current
+ * process.  Any unused entries in the array will contain NULL.
+ */
+static const char **LWLockTrancheNames = NULL;
+static int    LWLockTrancheNamesAllocated = 0;

 /*
  * This points to the main array of LWLocks in shared memory.  Backends inherit
@@ -158,10 +229,13 @@ NamedLWLockTranche *NamedLWLockTrancheArray = NULL;
 static bool lock_named_request_allowed = true;

 static void InitializeLWLocks(void);
-static void RegisterLWLockTranches(void);

 static inline void LWLockReportWaitStart(LWLock *lock);
 static inline void LWLockReportWaitEnd(void);
+static const char *GetLWTrancheName(uint16 trancheId);
+
+#define T_NAME(lock) \
+    GetLWTrancheName((lock)->tranche)

 #ifdef LWLOCK_STATS
 typedef struct lwlock_stats_key
@@ -332,7 +406,7 @@ get_lwlock_stats_entry(LWLock *lock)
  * allocated in the main array.
  */
 static int
-NumLWLocksByNamedTranches(void)
+NumLWLocksForNamedTranches(void)
 {
     int            numLocks = 0;
     int            i;
@@ -353,7 +427,8 @@ LWLockShmemSize(void)
     int            i;
     int            numLocks = NUM_FIXED_LWLOCKS;

-    numLocks += NumLWLocksByNamedTranches();
+    /* Calculate total number of locks needed in the main array. */
+    numLocks += NumLWLocksForNamedTranches();

     /* Space for the LWLock array. */
     size = mul_size(numLocks, sizeof(LWLockPadded));
@@ -368,7 +443,7 @@ LWLockShmemSize(void)
     for (i = 0; i < NamedLWLockTrancheRequests; i++)
         size = add_size(size, strlen(NamedLWLockTrancheRequestArray[i].tranche_name) + 1);

-    /* Disallow named LWLocks' requests after startup */
+    /* Disallow adding any more named tranches. */
     lock_named_request_allowed = false;

     return size;
@@ -376,7 +451,7 @@ LWLockShmemSize(void)

 /*
  * Allocate shmem space for the main LWLock array and all tranches and
- * initialize it.  We also register all the LWLock tranches here.
+ * initialize it.  We also register extension LWLock tranches here.
  */
 void
 CreateLWLocks(void)
@@ -416,8 +491,10 @@ CreateLWLocks(void)
         InitializeLWLocks();
     }

-    /* Register all LWLock tranches */
-    RegisterLWLockTranches();
+    /* Register named extension LWLock tranches in the current process. */
+    for (int i = 0; i < NamedLWLockTrancheRequests; i++)
+        LWLockRegisterTranche(NamedLWLockTrancheArray[i].trancheId,
+                              NamedLWLockTrancheArray[i].trancheName);
 }

 /*
@@ -426,7 +503,7 @@ CreateLWLocks(void)
 static void
 InitializeLWLocks(void)
 {
-    int            numNamedLocks = NumLWLocksByNamedTranches();
+    int            numNamedLocks = NumLWLocksForNamedTranches();
     int            id;
     int            i;
     int            j;
@@ -452,7 +529,10 @@ InitializeLWLocks(void)
     for (id = 0; id < NUM_PREDICATELOCK_PARTITIONS; id++, lock++)
         LWLockInitialize(&lock->lock, LWTRANCHE_PREDICATE_LOCK_MANAGER);

-    /* Initialize named tranches. */
+    /*
+     * Copy the info about any named tranches into shared memory (so that
+     * other processes can see it), and initialize the requested LWLocks.
+     */
     if (NamedLWLockTrancheRequests > 0)
     {
         char       *trancheNames;
@@ -486,51 +566,6 @@ InitializeLWLocks(void)
 }

 /*
- * Register named tranches and tranches for fixed LWLocks.
- */
-static void
-RegisterLWLockTranches(void)
-{
-    int            i;
-
-    if (LWLockTrancheArray == NULL)
-    {
-        LWLockTranchesAllocated = 128;
-        LWLockTrancheArray = (const char **)
-            MemoryContextAllocZero(TopMemoryContext,
-                                   LWLockTranchesAllocated * sizeof(char *));
-        Assert(LWLockTranchesAllocated >= LWTRANCHE_FIRST_USER_DEFINED);
-    }
-
-    for (i = 0; i < NUM_INDIVIDUAL_LWLOCKS; ++i)
-        LWLockRegisterTranche(i, MainLWLockNames[i]);
-
-    LWLockRegisterTranche(LWTRANCHE_BUFFER_MAPPING, "buffer_mapping");
-    LWLockRegisterTranche(LWTRANCHE_LOCK_MANAGER, "lock_manager");
-    LWLockRegisterTranche(LWTRANCHE_PREDICATE_LOCK_MANAGER,
-                          "predicate_lock_manager");
-    LWLockRegisterTranche(LWTRANCHE_PARALLEL_QUERY_DSA,
-                          "parallel_query_dsa");
-    LWLockRegisterTranche(LWTRANCHE_SESSION_DSA,
-                          "session_dsa");
-    LWLockRegisterTranche(LWTRANCHE_SESSION_RECORD_TABLE,
-                          "session_record_table");
-    LWLockRegisterTranche(LWTRANCHE_SESSION_TYPMOD_TABLE,
-                          "session_typmod_table");
-    LWLockRegisterTranche(LWTRANCHE_SHARED_TUPLESTORE,
-                          "shared_tuplestore");
-    LWLockRegisterTranche(LWTRANCHE_TBM, "tbm");
-    LWLockRegisterTranche(LWTRANCHE_PARALLEL_APPEND, "parallel_append");
-    LWLockRegisterTranche(LWTRANCHE_PARALLEL_HASH_JOIN, "parallel_hash_join");
-    LWLockRegisterTranche(LWTRANCHE_SXACT, "serializable_xact");
-
-    /* Register named tranches. */
-    for (i = 0; i < NamedLWLockTrancheRequests; i++)
-        LWLockRegisterTranche(NamedLWLockTrancheArray[i].trancheId,
-                              NamedLWLockTrancheArray[i].trancheName);
-}
-
-/*
  * InitLWLockAccess - initialize backend-local state needed to hold LWLocks
  */
 void
@@ -595,32 +630,47 @@ LWLockNewTrancheId(void)
 }

 /*
- * Register a tranche ID in the lookup table for the current process.  This
- * routine will save a pointer to the tranche name passed as an argument,
+ * Register a dynamic tranche name in the lookup table of the current process.
+ *
+ * This routine will save a pointer to the tranche name passed as an argument,
  * so the name should be allocated in a backend-lifetime context
- * (TopMemoryContext, static variable, or similar).
+ * (TopMemoryContext, static constant, or similar).
  */
 void
 LWLockRegisterTranche(int tranche_id, const char *tranche_name)
 {
-    Assert(LWLockTrancheArray != NULL);
+    /* This should only be called for user-defined tranches. */
+    if (tranche_id < LWTRANCHE_FIRST_USER_DEFINED)
+        return;
+
+    /* Convert to array index. */
+    tranche_id -= LWTRANCHE_FIRST_USER_DEFINED;

-    if (tranche_id >= LWLockTranchesAllocated)
+    /* If necessary, create or enlarge array. */
+    if (tranche_id >= LWLockTrancheNamesAllocated)
     {
-        int            i = LWLockTranchesAllocated;
-        int            j = LWLockTranchesAllocated;
+        int            newalloc;

-        while (i <= tranche_id)
-            i *= 2;
+        newalloc = Max(LWLockTrancheNamesAllocated, 8);
+        while (newalloc <= tranche_id)
+            newalloc *= 2;

-        LWLockTrancheArray = (const char **)
-            repalloc(LWLockTrancheArray, i * sizeof(char *));
-        LWLockTranchesAllocated = i;
-        while (j < LWLockTranchesAllocated)
-            LWLockTrancheArray[j++] = NULL;
+        if (LWLockTrancheNames == NULL)
+            LWLockTrancheNames = (const char **)
+                MemoryContextAllocZero(TopMemoryContext,
+                                       newalloc * sizeof(char *));
+        else
+        {
+            LWLockTrancheNames = (const char **)
+                repalloc(LWLockTrancheNames, newalloc * sizeof(char *));
+            memset(LWLockTrancheNames + LWLockTrancheNamesAllocated,
+                   0,
+                   (newalloc - LWLockTrancheNamesAllocated) * sizeof(char *));
+        }
+        LWLockTrancheNamesAllocated = newalloc;
     }

-    LWLockTrancheArray[tranche_id] = tranche_name;
+    LWLockTrancheNames[tranche_id] = tranche_name;
 }

 /*
@@ -667,7 +717,7 @@ RequestNamedLWLockTranche(const char *tranche_name, int num_lwlocks)

     request = &NamedLWLockTrancheRequestArray[NamedLWLockTrancheRequests];
     Assert(strlen(tranche_name) + 1 < NAMEDATALEN);
-    StrNCpy(request->tranche_name, tranche_name, NAMEDATALEN);
+    strlcpy(request->tranche_name, tranche_name, NAMEDATALEN);
     request->num_lwlocks = num_lwlocks;
     NamedLWLockTrancheRequests++;
 }
@@ -709,23 +759,42 @@ LWLockReportWaitEnd(void)
 }

 /*
- * Return an identifier for an LWLock based on the wait class and event.
+ * Return the name of an LWLock tranche.
  */
-const char *
-GetLWLockIdentifier(uint32 classId, uint16 eventId)
+static const char *
+GetLWTrancheName(uint16 trancheId)
 {
-    Assert(classId == PG_WAIT_LWLOCK);
+    /* Individual LWLock? */
+    if (trancheId < NUM_INDIVIDUAL_LWLOCKS)
+        return MainLWLockNames[trancheId];
+
+    /* Built-in tranche? */
+    if (trancheId < LWTRANCHE_FIRST_USER_DEFINED)
+        return BuiltinTrancheNames[trancheId - NUM_INDIVIDUAL_LWLOCKS];

     /*
-     * It is quite possible that user has registered tranche in one of the
-     * backends (e.g. by allocating lwlocks in dynamic shared memory) but not
-     * all of them, so we can't assume the tranche is registered here.
+     * It's an extension tranche, so look in LWLockTrancheNames[].  However,
+     * it's possible that the tranche has never been registered in the current
+     * process, in which case give up and return "extension".
      */
-    if (eventId >= LWLockTranchesAllocated ||
-        LWLockTrancheArray[eventId] == NULL)
+    trancheId -= LWTRANCHE_FIRST_USER_DEFINED;
+
+    if (trancheId >= LWLockTrancheNamesAllocated ||
+        LWLockTrancheNames[trancheId] == NULL)
         return "extension";

-    return LWLockTrancheArray[eventId];
+    return LWLockTrancheNames[trancheId];
+}
+
+/*
+ * Return an identifier for an LWLock based on the wait class and event.
+ */
+const char *
+GetLWLockIdentifier(uint32 classId, uint16 eventId)
+{
+    Assert(classId == PG_WAIT_LWLOCK);
+    /* The event IDs are just tranche numbers. */
+    return GetLWTrancheName(eventId);
 }

 /*

Re: Our naming of wait events is a disaster.

From
Robert Haas
Date:
On Tue, May 12, 2020 at 10:54 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> I don't actually understand why the LWLock tranche mechanism is designed
> the way it is.  It seems to be intended to support different backends
> having different sets of LWLocks, but I fail to see why that's a good idea,
> or even useful at all.  In any case, dynamically-created LWLocks are
> clearly out of scope for the documentation.  The problem that I'm trying
> to deal with right now is that even LWLocks that are hard-wired into the
> backend code are difficult to enumerate.  That wasn't a problem before
> we decided we needed to expose them all to user view; but now it is.

If you are using parallel query, your backend might have a DSM segment
that contains LWLocks. Anyone who is not attached to that DSM segment
won't know about them, though possibly they have a different DSM
segment containing a tranche with the same name.

Also, if you are using extensions that use LWLocks, a particular
extension may be loaded into backend A but not backend B. Suppose
backend A has an extension loaded but backend B does not. Then suppose
that A waits for an LWLock and B meanwhile examines pg_stat_activity.

It's hard to come up with totally satisfying solutions to problems
like these, but I think the important thing to remember is that, at
least in the extension case, this is really an obscure corner case.
Normally the same extensions will be loaded everywhere and anything
known to one backend will be known also the others. However, it's not
guaranteed.

I tend to prefer that modules register their own tranches rather than
having a central table someplace, because I like the idea that the
things that a particular module knows about are contained within its
own source files and not spread all over the code base. I think that
we've done rather badly with this in a number of places, lwlock.c
among them, and I don't like it much. It tends to lead to layering
violations, and it also tends to create interfaces that extensions
can't really use. I admit that it's not ideal as far as this
particular problem is concerned, but I don't think that makes it a bad
idea in general. In some sense, the lack of naming consistency here is
a manifestation of an underlying chaos in the code: we've created many
different ways of waiting for things with many different
characteristics and little thought to consistency, and this mechanism
has exposed that underlying problem. The wait state interface is
surely not to blame for the fact that LWLock names and heavyweight
lock types are capitalized inconsistently. In fact, there seem to be
few things that PostgreSQL hackers love more than inconsistent
capitalization, and this is just one of a whole lot of instances of
it.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
Robert Haas <robertmhaas@gmail.com> writes:
> I tend to prefer that modules register their own tranches rather than
> having a central table someplace, because I like the idea that the
> things that a particular module knows about are contained within its
> own source files and not spread all over the code base. I think that
> we've done rather badly with this in a number of places, lwlock.c
> among them, and I don't like it much.

Well, we could solve this problem very easily by ripping out everything
having to do with wait-state monitoring ... and personally I'd be a lot
in favor of that, because I haven't seen anything about either the
design or documentation of the feature that I thought was very well
done.  However, if you'd like to have wait-state monitoring, and you'd
like the documentation for it to be more useful than "go read the code",
then I don't see any way around the conclusion that there are going to
be centralized lists of the possible wait states.

That being the case, refusing to use a centralized list in the
implementation seems rather pointless; and having some aspects of the
implementation use centralized lists (see the enums in lwlock.h and
elsewhere) while other aspects don't is just schizophrenic.

> In some sense, the lack of naming consistency here is
> a manifestation of an underlying chaos in the code: we've created many
> different ways of waiting for things with many different
> characteristics and little thought to consistency, and this mechanism
> has exposed that underlying problem.

Yeah, agreed.  Nonetheless, now we have a problem.

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Robert Haas
Date:
On Thu, May 14, 2020 at 2:54 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Well, we could solve this problem very easily by ripping out everything
> having to do with wait-state monitoring ... and personally I'd be a lot
> in favor of that, because I haven't seen anything about either the
> design or documentation of the feature that I thought was very well
> done.

Well, I'm going to disagree with that, but opinions can vary. If I'd
tried to create naming consistency here when I created this stuff, I
would've had to rename things in existing systems rather than just
expose what was already there, and that wasn't the goal of the patch,
and I don't see a very good reason why it should have been. Providing
information is a separate project from cleaning up naming. And, while
I don't love the fact that people have added new things without trying
very hard to be consistent with existing things all that much, I still
don't think inconsistent naming rises to the level of a disaster.

> However, if you'd like to have wait-state monitoring, and you'd
> like the documentation for it to be more useful than "go read the code",
> then I don't see any way around the conclusion that there are going to
> be centralized lists of the possible wait states.
>
> That being the case, refusing to use a centralized list in the
> implementation seems rather pointless; and having some aspects of the
> implementation use centralized lists (see the enums in lwlock.h and
> elsewhere) while other aspects don't is just schizophrenic.

There's something to that argument, especially it enable us to
auto-generate the documentation tables.

That being said, my view of this system is that it's good to document
the wait events that we have, but also that there are almost certainly
going to be cases where we can't say a whole lot more than "go read
the code," or at least not without an awful lot of work. I think
there's a reasonable chance that someone who sees a lot of ClientRead
or DataFileWrite wait events will have some idea what kind of problem
is indicated, even without consulting the documentation and even
moreso if we have some good documentation which they can consult. But
I don't know what anybody's going to do if they see a lot of
OldSerXidLock or AddinShmemInitLock contention. Presumably those are
cases that the developers thought were unlikely, or they'd have chosen
a different locking regimen. If they were wrong, I think it's a good
thing for users to have a relatively easy way to find that out, but
I'm not sure what anybody's going to do be able to do about it without
patching the code, or at least looking at it.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
Robert Haas <robertmhaas@gmail.com> writes:
> That being said, my view of this system is that it's good to document
> the wait events that we have, but also that there are almost certainly
> going to be cases where we can't say a whole lot more than "go read
> the code," or at least not without an awful lot of work.

Can't disagree with that.

> I think
> there's a reasonable chance that someone who sees a lot of ClientRead
> or DataFileWrite wait events will have some idea what kind of problem
> is indicated, even without consulting the documentation and even
> moreso if we have some good documentation which they can consult. But
> I don't know what anybody's going to do if they see a lot of
> OldSerXidLock or AddinShmemInitLock contention.

I submit that at least part of the problem is precisely one of crappy
naming.  I didn't know what OldSerXidLock did either, until yesterday
when I dug into the code to find out.  If it's named something like
"SerialSLRULock", then at least somebody who has heard of SLRUs will
have an idea of what is indicated.  And we are exposing the notion
of SLRUs pretty prominently in the monitoring docs as of v13, so that's
not an unreasonable presumption.

            regards, tom lane



Re: Our naming of wait events is a disaster.

From
Robert Haas
Date:
On Thu, May 14, 2020 at 3:58 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> I submit that at least part of the problem is precisely one of crappy
> naming.  I didn't know what OldSerXidLock did either, until yesterday
> when I dug into the code to find out.  If it's named something like
> "SerialSLRULock", then at least somebody who has heard of SLRUs will
> have an idea of what is indicated.  And we are exposing the notion
> of SLRUs pretty prominently in the monitoring docs as of v13, so that's
> not an unreasonable presumption.

To the extent that exposing some of this information causes us to
think more carefully about the naming, I think that's all to the good.
I don't expect such measures to solve all of our problems in this
area, but the idea that we can choose names with no consideration to
whether anybody can understand what they mean is wrong even when the
audience is only developers.

-- 
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
I wrote:
> Digging through the existing callers of SimpleLruInit, we have
> name                    control lock                    subdir
> "async"                 AsyncCtlLock                    "pg_notify"
> "clog"                  CLogControlLock                 "pg_xact"
> "commit_timestamp"      CommitTsControlLock             "pg_commit_ts"
> "multixact_member"      MultiXactMemberControlLock      "pg_multixact/members"
> "multixact_offset"      MultiXactOffsetControlLock      "pg_multixact/offsets"
> "oldserxid"             OldSerXidLock                   "pg_serial"
> "subtrans"              SubtransControlLock             "pg_subtrans"

> After studying that list for awhile, it seems to me that we could
> do worse than to name the SLRUs to match their on-disk subdirectories,
> which are names that are already user-visible.  So I propose these
> base names for the SLRUs:

> Notify
> Xact
> CommitTs
> MultiXactMember  (or MultiXactMembers)
> MultiXactOffset  (or MultiXactOffsets)
> Serial
> Subtrans

As POC for this, here's a draft patch to rename the "async" SLRU and
associated locks.  If people are good with this then I'll go through
and do similarly for the other SLRUs.

A case could be made for doing s/async/notify/ more widely in async.c;
for instance it's odd that the struct protected by NotifyQueueLock
didn't get renamed to NotifyQueueControl.  But that seems a bit out
of scope for the immediate problem, and anyway I'm not sure how far to
take it.  I don't really want to rename async.c's externally-visible
functions, for instance.  For the moment I just renamed symbols used
in the SimpleLruInit() call.

            regards, tom lane

diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 87502a4..27e7591 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1754,12 +1754,12 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <entry>Waiting to manage space allocation in shared memory.</entry>
      </row>
      <row>
-      <entry><literal>AsyncCtlLock</literal></entry>
-      <entry>Waiting to read or update shared notification state.</entry>
+      <entry><literal>NotifySLRULock</literal></entry>
+      <entry>Waiting to read or update state of the Notify SLRU cache.</entry>
      </row>
      <row>
-      <entry><literal>AsyncQueueLock</literal></entry>
-      <entry>Waiting to read or update notification messages.</entry>
+      <entry><literal>NotifyQueueLock</literal></entry>
+      <entry>Waiting to read or update <command>NOTIFY</command> messages.</entry>
      </row>
      <row>
       <entry><literal>AutoFileLock</literal></entry>
@@ -1941,8 +1941,8 @@ postgres   27093  0.0  0.0  30096  2752 ?        Ss   11:34   0:00 postgres: ser
       <entry>Waiting to allocate or assign a transaction id.</entry>
      </row>
      <row>
-      <entry><literal>async</literal></entry>
-      <entry>Waiting for I/O on an async (notify) buffer.</entry>
+      <entry><literal>NotifyBuffer</literal></entry>
+      <entry>Waiting for I/O on a Notify SLRU buffer.</entry>
      </row>
      <row>
       <entry><literal>buffer_content</literal></entry>
@@ -4484,7 +4484,7 @@ SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event i
         Resets statistics to zero for a single SLRU cache, or for all SLRUs in
         the cluster.  If the argument is NULL, all counters shown in
         the <structname>pg_stat_slru</structname> view for all SLRU caches are
-        reset.  The argument can be one of <literal>async</literal>,
+        reset.  The argument can be one of <literal>Notify</literal>,
         <literal>clog</literal>, <literal>commit_timestamp</literal>,
         <literal>multixact_offset</literal>,
         <literal>multixact_member</literal>, <literal>oldserxid</literal>, or
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 4613bd1..a3ba88d 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -107,7 +107,7 @@
  * frontend during startup.)  The above design guarantees that notifies from
  * other backends will never be missed by ignoring self-notifies.
  *
- * The amount of shared memory used for notify management (NUM_ASYNC_BUFFERS)
+ * The amount of shared memory used for notify management (NUM_NOTIFY_BUFFERS)
  * can be varied without affecting anything but performance.  The maximum
  * amount of notification data that can be queued at one time is determined
  * by slru.c's wraparound limit; see QUEUE_MAX_PAGE below.
@@ -225,7 +225,7 @@ typedef struct QueuePosition
  *
  * Resist the temptation to make this really large.  While that would save
  * work in some places, it would add cost in others.  In particular, this
- * should likely be less than NUM_ASYNC_BUFFERS, to ensure that backends
+ * should likely be less than NUM_NOTIFY_BUFFERS, to ensure that backends
  * catch up before the pages they'll need to read fall out of SLRU cache.
  */
 #define QUEUE_CLEANUP_DELAY 4
@@ -244,7 +244,7 @@ typedef struct QueueBackendStatus
 /*
  * Shared memory state for LISTEN/NOTIFY (excluding its SLRU stuff)
  *
- * The AsyncQueueControl structure is protected by the AsyncQueueLock.
+ * The AsyncQueueControl structure is protected by the NotifyQueueLock.
  *
  * When holding the lock in SHARED mode, backends may only inspect their own
  * entries as well as the head and tail pointers. Consequently we can allow a
@@ -254,9 +254,9 @@ typedef struct QueueBackendStatus
  * When holding the lock in EXCLUSIVE mode, backends can inspect the entries
  * of other backends and also change the head and tail pointers.
  *
- * AsyncCtlLock is used as the control lock for the pg_notify SLRU buffers.
+ * NotifySLRULock is used as the control lock for the pg_notify SLRU buffers.
  * In order to avoid deadlocks, whenever we need both locks, we always first
- * get AsyncQueueLock and then AsyncCtlLock.
+ * get NotifyQueueLock and then NotifySLRULock.
  *
  * Each backend uses the backend[] array entry with index equal to its
  * BackendId (which can range from 1 to MaxBackends).  We rely on this to make
@@ -292,9 +292,9 @@ static AsyncQueueControl *asyncQueueControl;
 /*
  * The SLRU buffer area through which we access the notification queue
  */
-static SlruCtlData AsyncCtlData;
+static SlruCtlData NotifyCtlData;

-#define AsyncCtl                    (&AsyncCtlData)
+#define NotifyCtl                    (&NotifyCtlData)
 #define QUEUE_PAGESIZE                BLCKSZ
 #define QUEUE_FULL_WARN_INTERVAL    5000    /* warn at most once every 5s */

@@ -506,7 +506,7 @@ AsyncShmemSize(void)
     size = mul_size(MaxBackends + 1, sizeof(QueueBackendStatus));
     size = add_size(size, offsetof(AsyncQueueControl, backend));

-    size = add_size(size, SimpleLruShmemSize(NUM_ASYNC_BUFFERS, 0));
+    size = add_size(size, SimpleLruShmemSize(NUM_NOTIFY_BUFFERS, 0));

     return size;
 }
@@ -552,18 +552,18 @@ AsyncShmemInit(void)
     /*
      * Set up SLRU management of the pg_notify data.
      */
-    AsyncCtl->PagePrecedes = asyncQueuePagePrecedes;
-    SimpleLruInit(AsyncCtl, "async", NUM_ASYNC_BUFFERS, 0,
-                  AsyncCtlLock, "pg_notify", LWTRANCHE_ASYNC_BUFFERS);
+    NotifyCtl->PagePrecedes = asyncQueuePagePrecedes;
+    SimpleLruInit(NotifyCtl, "Notify", NUM_NOTIFY_BUFFERS, 0,
+                  NotifySLRULock, "pg_notify", LWTRANCHE_NOTIFY_BUFFER);
     /* Override default assumption that writes should be fsync'd */
-    AsyncCtl->do_fsync = false;
+    NotifyCtl->do_fsync = false;

     if (!found)
     {
         /*
          * During start or reboot, clean out the pg_notify directory.
          */
-        (void) SlruScanDirectory(AsyncCtl, SlruScanDirCbDeleteAll, NULL);
+        (void) SlruScanDirectory(NotifyCtl, SlruScanDirCbDeleteAll, NULL);
     }
 }

@@ -918,7 +918,7 @@ PreCommit_Notify(void)
          * Make sure that we have an XID assigned to the current transaction.
          * GetCurrentTransactionId is cheap if we already have an XID, but not
          * so cheap if we don't, and we'd prefer not to do that work while
-         * holding AsyncQueueLock.
+         * holding NotifyQueueLock.
          */
         (void) GetCurrentTransactionId();

@@ -949,7 +949,7 @@ PreCommit_Notify(void)
         {
             /*
              * Add the pending notifications to the queue.  We acquire and
-             * release AsyncQueueLock once per page, which might be overkill
+             * release NotifyQueueLock once per page, which might be overkill
              * but it does allow readers to get in while we're doing this.
              *
              * A full queue is very uncommon and should really not happen,
@@ -959,14 +959,14 @@ PreCommit_Notify(void)
              * transaction, but we have not yet committed to clog, so at this
              * point in time we can still roll the transaction back.
              */
-            LWLockAcquire(AsyncQueueLock, LW_EXCLUSIVE);
+            LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
             asyncQueueFillWarning();
             if (asyncQueueIsFull())
                 ereport(ERROR,
                         (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                          errmsg("too many notifications in the NOTIFY queue")));
             nextNotify = asyncQueueAddEntries(nextNotify);
-            LWLockRelease(AsyncQueueLock);
+            LWLockRelease(NotifyQueueLock);
         }
     }
 }
@@ -1075,7 +1075,7 @@ Exec_ListenPreCommit(void)
      * We need exclusive lock here so we can look at other backends' entries
      * and manipulate the list links.
      */
-    LWLockAcquire(AsyncQueueLock, LW_EXCLUSIVE);
+    LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
     head = QUEUE_HEAD;
     max = QUEUE_TAIL;
     prevListener = InvalidBackendId;
@@ -1101,7 +1101,7 @@ Exec_ListenPreCommit(void)
         QUEUE_NEXT_LISTENER(MyBackendId) = QUEUE_FIRST_LISTENER;
         QUEUE_FIRST_LISTENER = MyBackendId;
     }
-    LWLockRelease(AsyncQueueLock);
+    LWLockRelease(NotifyQueueLock);

     /* Now we are listed in the global array, so remember we're listening */
     amRegisteredListener = true;
@@ -1308,7 +1308,7 @@ asyncQueueUnregister(void)
     /*
      * Need exclusive lock here to manipulate list links.
      */
-    LWLockAcquire(AsyncQueueLock, LW_EXCLUSIVE);
+    LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
     /* Mark our entry as invalid */
     QUEUE_BACKEND_PID(MyBackendId) = InvalidPid;
     QUEUE_BACKEND_DBOID(MyBackendId) = InvalidOid;
@@ -1327,7 +1327,7 @@ asyncQueueUnregister(void)
         }
     }
     QUEUE_NEXT_LISTENER(MyBackendId) = InvalidBackendId;
-    LWLockRelease(AsyncQueueLock);
+    LWLockRelease(NotifyQueueLock);

     /* mark ourselves as no longer listed in the global array */
     amRegisteredListener = false;
@@ -1336,7 +1336,7 @@ asyncQueueUnregister(void)
 /*
  * Test whether there is room to insert more notification messages.
  *
- * Caller must hold at least shared AsyncQueueLock.
+ * Caller must hold at least shared NotifyQueueLock.
  */
 static bool
 asyncQueueIsFull(void)
@@ -1437,8 +1437,8 @@ asyncQueueNotificationToEntry(Notification *n, AsyncQueueEntry *qe)
  * notification to write and return the first still-unwritten cell back.
  * Eventually we will return NULL indicating all is done.
  *
- * We are holding AsyncQueueLock already from the caller and grab AsyncCtlLock
- * locally in this function.
+ * We are holding NotifyQueueLock already from the caller and grab
+ * NotifySLRULock locally in this function.
  */
 static ListCell *
 asyncQueueAddEntries(ListCell *nextNotify)
@@ -1449,8 +1449,8 @@ asyncQueueAddEntries(ListCell *nextNotify)
     int            offset;
     int            slotno;

-    /* We hold both AsyncQueueLock and AsyncCtlLock during this operation */
-    LWLockAcquire(AsyncCtlLock, LW_EXCLUSIVE);
+    /* We hold both NotifyQueueLock and NotifySLRULock during this operation */
+    LWLockAcquire(NotifySLRULock, LW_EXCLUSIVE);

     /*
      * We work with a local copy of QUEUE_HEAD, which we write back to shared
@@ -1475,13 +1475,13 @@ asyncQueueAddEntries(ListCell *nextNotify)
      */
     pageno = QUEUE_POS_PAGE(queue_head);
     if (QUEUE_POS_IS_ZERO(queue_head))
-        slotno = SimpleLruZeroPage(AsyncCtl, pageno);
+        slotno = SimpleLruZeroPage(NotifyCtl, pageno);
     else
-        slotno = SimpleLruReadPage(AsyncCtl, pageno, true,
+        slotno = SimpleLruReadPage(NotifyCtl, pageno, true,
                                    InvalidTransactionId);

     /* Note we mark the page dirty before writing in it */
-    AsyncCtl->shared->page_dirty[slotno] = true;
+    NotifyCtl->shared->page_dirty[slotno] = true;

     while (nextNotify != NULL)
     {
@@ -1512,7 +1512,7 @@ asyncQueueAddEntries(ListCell *nextNotify)
         }

         /* Now copy qe into the shared buffer page */
-        memcpy(AsyncCtl->shared->page_buffer[slotno] + offset,
+        memcpy(NotifyCtl->shared->page_buffer[slotno] + offset,
                &qe,
                qe.length);

@@ -1527,7 +1527,7 @@ asyncQueueAddEntries(ListCell *nextNotify)
              * asyncQueueIsFull() ensured that there is room to create this
              * page without overrunning the queue.
              */
-            slotno = SimpleLruZeroPage(AsyncCtl, QUEUE_POS_PAGE(queue_head));
+            slotno = SimpleLruZeroPage(NotifyCtl, QUEUE_POS_PAGE(queue_head));

             /*
              * If the new page address is a multiple of QUEUE_CLEANUP_DELAY,
@@ -1545,7 +1545,7 @@ asyncQueueAddEntries(ListCell *nextNotify)
     /* Success, so update the global QUEUE_HEAD */
     QUEUE_HEAD = queue_head;

-    LWLockRelease(AsyncCtlLock);
+    LWLockRelease(NotifySLRULock);

     return nextNotify;
 }
@@ -1562,9 +1562,9 @@ pg_notification_queue_usage(PG_FUNCTION_ARGS)
     /* Advance the queue tail so we don't report a too-large result */
     asyncQueueAdvanceTail();

-    LWLockAcquire(AsyncQueueLock, LW_SHARED);
+    LWLockAcquire(NotifyQueueLock, LW_SHARED);
     usage = asyncQueueUsage();
-    LWLockRelease(AsyncQueueLock);
+    LWLockRelease(NotifyQueueLock);

     PG_RETURN_FLOAT8(usage);
 }
@@ -1572,7 +1572,7 @@ pg_notification_queue_usage(PG_FUNCTION_ARGS)
 /*
  * Return the fraction of the queue that is currently occupied.
  *
- * The caller must hold AsyncQueueLock in (at least) shared mode.
+ * The caller must hold NotifyQueueLock in (at least) shared mode.
  */
 static double
 asyncQueueUsage(void)
@@ -1601,7 +1601,7 @@ asyncQueueUsage(void)
  * This is unlikely given the size of the queue, but possible.
  * The warnings show up at most once every QUEUE_FULL_WARN_INTERVAL.
  *
- * Caller must hold exclusive AsyncQueueLock.
+ * Caller must hold exclusive NotifyQueueLock.
  */
 static void
 asyncQueueFillWarning(void)
@@ -1665,7 +1665,7 @@ SignalBackends(void)

     /*
      * Identify backends that we need to signal.  We don't want to send
-     * signals while holding the AsyncQueueLock, so this loop just builds a
+     * signals while holding the NotifyQueueLock, so this loop just builds a
      * list of target PIDs.
      *
      * XXX in principle these pallocs could fail, which would be bad. Maybe
@@ -1676,7 +1676,7 @@ SignalBackends(void)
     ids = (BackendId *) palloc(MaxBackends * sizeof(BackendId));
     count = 0;

-    LWLockAcquire(AsyncQueueLock, LW_EXCLUSIVE);
+    LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
     for (BackendId i = QUEUE_FIRST_LISTENER; i > 0; i = QUEUE_NEXT_LISTENER(i))
     {
         int32        pid = QUEUE_BACKEND_PID(i);
@@ -1710,7 +1710,7 @@ SignalBackends(void)
         ids[count] = i;
         count++;
     }
-    LWLockRelease(AsyncQueueLock);
+    LWLockRelease(NotifyQueueLock);

     /* Now send signals */
     for (int i = 0; i < count; i++)
@@ -1720,7 +1720,7 @@ SignalBackends(void)
         /*
          * Note: assuming things aren't broken, a signal failure here could
          * only occur if the target backend exited since we released
-         * AsyncQueueLock; which is unlikely but certainly possible. So we
+         * NotifyQueueLock; which is unlikely but certainly possible. So we
          * just log a low-level debug message if it happens.
          */
         if (SendProcSignal(pid, PROCSIG_NOTIFY_INTERRUPT, ids[i]) < 0)
@@ -1930,12 +1930,12 @@ asyncQueueReadAllNotifications(void)
     }            page_buffer;

     /* Fetch current state */
-    LWLockAcquire(AsyncQueueLock, LW_SHARED);
+    LWLockAcquire(NotifyQueueLock, LW_SHARED);
     /* Assert checks that we have a valid state entry */
     Assert(MyProcPid == QUEUE_BACKEND_PID(MyBackendId));
     pos = oldpos = QUEUE_BACKEND_POS(MyBackendId);
     head = QUEUE_HEAD;
-    LWLockRelease(AsyncQueueLock);
+    LWLockRelease(NotifyQueueLock);

     if (QUEUE_POS_EQUAL(pos, head))
     {
@@ -1990,7 +1990,7 @@ asyncQueueReadAllNotifications(void)
      * that happens it is critical that we not try to send the same message
      * over and over again.  Therefore, we place a PG_TRY block here that will
      * forcibly advance our queue position before we lose control to an error.
-     * (We could alternatively retake AsyncQueueLock and move the position
+     * (We could alternatively retake NotifyQueueLock and move the position
      * before handling each individual message, but that seems like too much
      * lock traffic.)
      */
@@ -2007,11 +2007,11 @@ asyncQueueReadAllNotifications(void)

             /*
              * We copy the data from SLRU into a local buffer, so as to avoid
-             * holding the AsyncCtlLock while we are examining the entries and
-             * possibly transmitting them to our frontend.  Copy only the part
-             * of the page we will actually inspect.
+             * holding the NotifySLRULock while we are examining the entries
+             * and possibly transmitting them to our frontend.  Copy only the
+             * part of the page we will actually inspect.
              */
-            slotno = SimpleLruReadPage_ReadOnly(AsyncCtl, curpage,
+            slotno = SimpleLruReadPage_ReadOnly(NotifyCtl, curpage,
                                                 InvalidTransactionId);
             if (curpage == QUEUE_POS_PAGE(head))
             {
@@ -2026,10 +2026,10 @@ asyncQueueReadAllNotifications(void)
                 copysize = QUEUE_PAGESIZE - curoffset;
             }
             memcpy(page_buffer.buf + curoffset,
-                   AsyncCtl->shared->page_buffer[slotno] + curoffset,
+                   NotifyCtl->shared->page_buffer[slotno] + curoffset,
                    copysize);
             /* Release lock that we got from SimpleLruReadPage_ReadOnly() */
-            LWLockRelease(AsyncCtlLock);
+            LWLockRelease(NotifySLRULock);

             /*
              * Process messages up to the stop position, end of page, or an
@@ -2040,7 +2040,7 @@ asyncQueueReadAllNotifications(void)
              * But if it has, we will receive (or have already received and
              * queued) another signal and come here again.
              *
-             * We are not holding AsyncQueueLock here! The queue can only
+             * We are not holding NotifyQueueLock here! The queue can only
              * extend beyond the head pointer (see above) and we leave our
              * backend's pointer where it is so nobody will truncate or
              * rewrite pages under us. Especially we don't want to hold a lock
@@ -2054,9 +2054,9 @@ asyncQueueReadAllNotifications(void)
     PG_FINALLY();
     {
         /* Update shared state */
-        LWLockAcquire(AsyncQueueLock, LW_SHARED);
+        LWLockAcquire(NotifyQueueLock, LW_SHARED);
         QUEUE_BACKEND_POS(MyBackendId) = pos;
-        LWLockRelease(AsyncQueueLock);
+        LWLockRelease(NotifyQueueLock);
     }
     PG_END_TRY();

@@ -2070,7 +2070,7 @@ asyncQueueReadAllNotifications(void)
  *
  * The current page must have been fetched into page_buffer from shared
  * memory.  (We could access the page right in shared memory, but that
- * would imply holding the AsyncCtlLock throughout this routine.)
+ * would imply holding the NotifySLRULock throughout this routine.)
  *
  * We stop if we reach the "stop" position, or reach a notification from an
  * uncommitted transaction, or reach the end of the page.
@@ -2177,7 +2177,7 @@ asyncQueueAdvanceTail(void)
     int            newtailpage;
     int            boundary;

-    LWLockAcquire(AsyncQueueLock, LW_EXCLUSIVE);
+    LWLockAcquire(NotifyQueueLock, LW_EXCLUSIVE);
     min = QUEUE_HEAD;
     for (BackendId i = QUEUE_FIRST_LISTENER; i > 0; i = QUEUE_NEXT_LISTENER(i))
     {
@@ -2186,7 +2186,7 @@ asyncQueueAdvanceTail(void)
     }
     oldtailpage = QUEUE_POS_PAGE(QUEUE_TAIL);
     QUEUE_TAIL = min;
-    LWLockRelease(AsyncQueueLock);
+    LWLockRelease(NotifyQueueLock);

     /*
      * We can truncate something if the global tail advanced across an SLRU
@@ -2200,10 +2200,10 @@ asyncQueueAdvanceTail(void)
     if (asyncQueuePagePrecedes(oldtailpage, boundary))
     {
         /*
-         * SimpleLruTruncate() will ask for AsyncCtlLock but will also release
-         * the lock again.
+         * SimpleLruTruncate() will ask for NotifySLRULock but will also
+         * release the lock again.
          */
-        SimpleLruTruncate(AsyncCtl, newtailpage);
+        SimpleLruTruncate(NotifyCtl, newtailpage);
     }
 }

diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c
index e246be3..e5437dd 100644
--- a/src/backend/postmaster/pgstat.c
+++ b/src/backend/postmaster/pgstat.c
@@ -147,7 +147,7 @@ PgStat_MsgBgWriter BgWriterStats;
  * all SLRUs without an explicit entry (e.g. SLRUs in extensions).
  */
 static const char *const slru_names[] = {
-    "async",
+    "Notify",
     "clog",
     "commit_timestamp",
     "multixact_offset",
diff --git a/src/backend/storage/lmgr/lwlock.c b/src/backend/storage/lmgr/lwlock.c
index 61bec10..c2afae3 100644
--- a/src/backend/storage/lmgr/lwlock.c
+++ b/src/backend/storage/lmgr/lwlock.c
@@ -134,8 +134,8 @@ static const char *const BuiltinTrancheNames[] = {
     "multixact_offset",
     /* LWTRANCHE_MXACTMEMBER_BUFFERS: */
     "multixact_member",
-    /* LWTRANCHE_ASYNC_BUFFERS: */
-    "async",
+    /* LWTRANCHE_NOTIFY_BUFFER: */
+    "NotifyBuffer",
     /* LWTRANCHE_OLDSERXID_BUFFERS: */
     "oldserxid",
     /* LWTRANCHE_WAL_INSERT: */
diff --git a/src/backend/storage/lmgr/lwlocknames.txt b/src/backend/storage/lmgr/lwlocknames.txt
index db47843..b0fe6a3 100644
--- a/src/backend/storage/lmgr/lwlocknames.txt
+++ b/src/backend/storage/lmgr/lwlocknames.txt
@@ -30,8 +30,8 @@ AutovacuumLock                        22
 AutovacuumScheduleLock                23
 SyncScanLock                        24
 RelationMappingLock                    25
-AsyncCtlLock                        26
-AsyncQueueLock                        27
+NotifySLRULock                        26
+NotifyQueueLock                        27
 SerializableXactHashLock            28
 SerializableFinishedListLock        29
 SerializablePredicateLockListLock    30
diff --git a/src/include/commands/async.h b/src/include/commands/async.h
index d8814e9..4c35394 100644
--- a/src/include/commands/async.h
+++ b/src/include/commands/async.h
@@ -18,7 +18,7 @@
 /*
  * The number of SLRU page buffers we use for the notification queue.
  */
-#define NUM_ASYNC_BUFFERS    8
+#define NUM_NOTIFY_BUFFERS    8

 extern bool Trace_notify;
 extern volatile sig_atomic_t notifyInterruptPending;
diff --git a/src/include/storage/lwlock.h b/src/include/storage/lwlock.h
index 8fda8e4..4e577b2 100644
--- a/src/include/storage/lwlock.h
+++ b/src/include/storage/lwlock.h
@@ -200,7 +200,7 @@ typedef enum BuiltinTrancheIds
     LWTRANCHE_SUBTRANS_BUFFERS,
     LWTRANCHE_MXACTOFFSET_BUFFERS,
     LWTRANCHE_MXACTMEMBER_BUFFERS,
-    LWTRANCHE_ASYNC_BUFFERS,
+    LWTRANCHE_NOTIFY_BUFFER,
     LWTRANCHE_OLDSERXID_BUFFERS,
     LWTRANCHE_WAL_INSERT,
     LWTRANCHE_BUFFER_CONTENT,

Re: Our naming of wait events is a disaster.

From
Alvaro Herrera
Date:
On 2020-May-14, Tom Lane wrote:

> A case could be made for doing s/async/notify/ more widely in async.c;
> for instance it's odd that the struct protected by NotifyQueueLock
> didn't get renamed to NotifyQueueControl.  But that seems a bit out
> of scope for the immediate problem, and anyway I'm not sure how far to
> take it.  I don't really want to rename async.c's externally-visible
> functions, for instance.  For the moment I just renamed symbols used
> in the SimpleLruInit() call.

That approach seems fine -- we'd only rename those things if and when we
modified them for other reasons; and the file itself, probably not at all.
Much like our renaming of XLOG to WAL, we changed the user-visible term
all at once, but the code kept the original names until changed.


Maybe in N years, when the SCM tooling is much better (so that it
doesn't get confused by us having renamed the file in the newer branches
and back-patching to an older branch), we can rename xlog.c to wal.c and
async.c to notify.c.  Or maybe not.

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



Re: Our naming of wait events is a disaster.

From
Tom Lane
Date:
I wrote:
> Thomas Munro <thomas.munro@gmail.com> writes:
>> On Wed, May 13, 2020 at 3:16 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>>> Hash/Batch/Allocating
>>> Hash/Batch/Electing
>>> Hash/Batch/Loading
>>> Hash/GrowBatches/Allocating

>> Perhaps we should also drop the 'ing' from the verbs, to be more like
>> ...Read etc.

> Yeah, that aspect was bothering me too.  Comparing these to other
> wait event names, you could make a case for either "Allocate" or
> "Allocation"; but there are no other names with -ing.

After contemplating these for a bit, my proposal is to drop the
slashes and convert "verbing" to "verb", giving

HashBatchAllocate
HashBatchElect
HashBatchLoad
HashBuildAllocate
HashBuildElect
HashBuildHashInner
HashBuildHashOuter
HashGrowBatchesAllocate
HashGrowBatchesDecide
HashGrowBatchesElect
HashGrowBatchesFinish
HashGrowBatchesRepartition
HashGrowBucketsAllocate
HashGrowBucketsElect
HashGrowBucketsReinsert

In addition to that, I think the ClogGroupUpdate event needs to be renamed
to XactGroupUpdate, since we changed "clog" to "xact" in the exposed SLRU
and LWLock names.

(There are some other names that I wouldn't have picked in a green field,
but it's probably not worth the churn to change them.)

Also, as previously noted, ProcSignalBarrier should be in the IPC event
class not IO.

Barring objections, I'll make these things happen before beta1.

            regards, tom lane