<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Vignesh Kumar A · sudohulk · #postgresql</title><description>How PostgreSQL works inside: storage, WAL, transactions, the planner, and the code paths behind them.</description><link>https://sudohulk.com</link><language>en</language><managingEditor>vignesh@sudohulk.com (Vignesh Kumar A)</managingEditor><atom:link href="https://sudohulk.com/tags/postgresql/rss.xml" rel="self" type="application/rss+xml"/><image><url>https://sudohulk.com/icons/icon-144.png</url><title>Vignesh Kumar A · sudohulk · #postgresql</title><link>https://sudohulk.com/</link><width>144</width><height>144</height></image><item><title>How PostgreSQL tells every connection its cache is stale</title><link>https://sudohulk.com/writing/postgres-cache-invalidation</link><guid isPermaLink="true">https://sudohulk.com/writing/postgres-cache-invalidation</guid><description>Each PostgreSQL connection caches the catalog for itself. How a change is announced to all of them at commit through a shared ring, and what happens to a connection that falls behind.</description><pubDate>Sat, 17 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2 id=&quot;what-happened&quot;&gt;What happened&lt;/h2&gt;
&lt;p id=&quot;p-r5r2uo&quot;&gt;I once worked on a cache that sat in front of a database. It answered quickly and sometimes wrongly.&lt;/p&gt;
&lt;p id=&quot;p-03xo40&quot;&gt;I redesigned it as three isolated segments with &lt;strong&gt;version-based invalidation&lt;/strong&gt;. Each entry remembers the version of the data it came from, and once the data has a newer version, the old entry stops counting. The hit rate went from 84.3% to 98.1%, and the database answered about 131 million fewer queries a week.&lt;/p&gt;
&lt;p id=&quot;p-lz95do&quot;&gt;Looking back, storing things was the easy part of that cache. Most of the effort went into deciding when to throw an entry away.&lt;/p&gt;
&lt;p id=&quot;p-2lt4bp&quot;&gt;Later, when I started reading PostgreSQL’s source, I found the same problem inside every connection.&lt;/p&gt;
&lt;p id=&quot;p-xeo799&quot;&gt;&lt;strong&gt;Each PostgreSQL connection keeps its own copy of the catalog. A change is announced at commit through one shared ring of messages, and each connection reads the ring before it trusts its copy.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;why-every-connection-keeps-its-own-cache&quot;&gt;Why every connection keeps its own cache&lt;/h2&gt;
&lt;p id=&quot;p-qy4zur&quot;&gt;Think of a library where every reader keeps a notebook of the facts they look up often, so they don’t have to walk back to the shelves each time. PostgreSQL connections work the same way.&lt;/p&gt;
&lt;p id=&quot;p-3ja4oe&quot;&gt;Each connection gets its own server process, a &lt;strong&gt;backend&lt;/strong&gt;, with private memory. What the database knows about itself is in the &lt;strong&gt;system catalog&lt;/strong&gt;, a set of ordinary tables. &lt;code&gt;pg_class&lt;/code&gt; has a row per table, index or view, and &lt;code&gt;pg_attribute&lt;/code&gt; has a row per column.&lt;/p&gt;
&lt;p id=&quot;p-uq2n7r&quot;&gt;To run even &lt;code&gt;SELECT * FROM orders&lt;/code&gt;, a backend has to know what &lt;code&gt;orders&lt;/code&gt; is: its columns, their types, its indexes. It could read all of that from the catalog tables for every query, but that would be far too slow.&lt;/p&gt;
&lt;p id=&quot;p-45vwg2&quot;&gt;Instead, each backend keeps three caches of its own:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;strong&gt;catalog cache&lt;/strong&gt; (catcache) holds single catalog rows looked up by key, such as “the &lt;code&gt;pg_class&lt;/code&gt; row of table 16384”. Most code gets to it through a layer called the &lt;strong&gt;syscache&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;relation cache&lt;/strong&gt; (relcache) holds one assembled description per table or index, with everything needed to open it.&lt;/li&gt;
&lt;li&gt;the &lt;strong&gt;plan cache&lt;/strong&gt; holds the saved plans of prepared statements, which depend on the tables they read.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;p-bghwrr&quot;&gt;None of this is shared. When one connection changes the catalog, every other connection’s caches are wrong, and nothing has told them yet.&lt;/p&gt;
&lt;h2 id=&quot;collecting-changes-until-commit&quot;&gt;Collecting changes until commit&lt;/h2&gt;
&lt;p id=&quot;p-ykl59x&quot;&gt;When a backend inserts, updates or deletes a catalog row, it doesn’t flush anything straight away. Under PostgreSQL’s rules, the old row stays valid until the current command ends.&lt;/p&gt;
&lt;p id=&quot;p-teusio&quot;&gt;What the backend does instead is record an &lt;strong&gt;invalidation message&lt;/strong&gt; for each cache that could hold the row. It keeps that list until the change is final.&lt;/p&gt;
&lt;p id=&quot;p-wqm7g1&quot;&gt;An update counts as a delete plus an insert, and the two fold into one message when the cache’s key didn’t change. A change to a row of &lt;code&gt;pg_class&lt;/code&gt;, &lt;code&gt;pg_attribute&lt;/code&gt; or &lt;code&gt;pg_index&lt;/code&gt;, or to a foreign key’s row in &lt;code&gt;pg_constraint&lt;/code&gt;, also records a relcache message for the table that row describes. A table gets at most one of those per command.&lt;/p&gt;
&lt;p id=&quot;p-e6mrle&quot;&gt;What happens to the list depends on how things end:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A command ends.&lt;/strong&gt; The backend applies that command’s messages to its own caches and nobody else’s (&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L1408&quot; title=&quot;CommandEndInvalidationMessages, in src/backend/utils/cache/inval.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;CommandEndInvalidationMessages&lt;/a&gt;). That’s why your next statement already sees the column you added. Other backends can’t see your uncommitted rows, so there’s nothing to tell them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The transaction commits.&lt;/strong&gt; The commit is recorded first, then every message goes into the shared queue (&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L1198&quot; title=&quot;AtEOXact_Inval, in src/backend/utils/cache/inval.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;AtEOXact_Inval&lt;/a&gt;), and only after that are the locks released.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The transaction aborts.&lt;/strong&gt; Nothing is sent, and the backend undoes its own cache changes. An aborted subtransaction does the same for its part, and a committed one hands its list to its parent.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;p-z64soh&quot;&gt;The order at commit protects both sides. If the messages went out before the commit was recorded, another backend could reload the catalog and still find the old rows. If they went out after the locks were released, a backend waiting for the table could get them only after it had used its stale entry.&lt;/p&gt;
&lt;p id=&quot;p-z94zz1&quot;&gt;The commit’s WAL record carries the same messages, so a standby replays them for its own connections.&lt;/p&gt;
&lt;h2 id=&quot;the-ring-of-messages&quot;&gt;The ring of messages&lt;/h2&gt;
&lt;p id=&quot;p-oqug2s&quot;&gt;The messages go into the &lt;strong&gt;shared invalidation queue&lt;/strong&gt;, sinval for short, in shared memory. There’s one for the whole server, covering every database. Each message carries a database id, and a backend skips messages for other databases.&lt;/p&gt;
&lt;p id=&quot;p-cvo5ib&quot;&gt;The code describes it as an infinite list of numbered messages. In memory it’s a ring of 4,096 slots (&lt;code&gt;MAXNUMMESSAGES&lt;/code&gt;), and message &lt;em&gt;n&lt;/em&gt; sits in slot &lt;em&gt;n&lt;/em&gt; modulo 4,096. A message is 16 bytes, so the whole ring is about 64 KB.&lt;/p&gt;
&lt;p id=&quot;p-lzmho7&quot;&gt;Three numbers keep track of it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;maxMsgNum&lt;/code&gt;, the write position, where the next message goes;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nextMsgNum&lt;/code&gt;, one per backend, the next message that backend hasn’t read;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;minMsgNum&lt;/code&gt;, the oldest message anyone still needs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;p-ofma1w&quot;&gt;A new connection starts with its read position at the write position. Its caches are empty, so it has nothing to catch up on.&lt;/p&gt;
&lt;figure class=&quot;figure&quot;&gt;&lt;img alt=&quot;A ring of 24 slots standing in for 4,096. The writer, W, is at the next free slot. Readers B1 and B2 are a little behind it. B3 is far behind, with most of the ring still unread.&quot; loading=&quot;eager&quot; fetchpriority=&quot;high&quot; decoding=&quot;async&quot; width=&quot;360&quot; height=&quot;520&quot; src=&quot;https://sudohulk.com/_astro/sinval-ring.BXghl_4U_2dzIV9.svg&quot;&gt;&lt;figcaption&gt;The ring drawn with 24 slots. W writes clockwise, and each reader has its own position.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p id=&quot;p-oz5lne&quot;&gt;The main kinds of message are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;catcache&lt;/strong&gt;: rows with this hash may be stale in this cache;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;catalog&lt;/strong&gt;: forget everything cached from this whole catalog;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;relcache&lt;/strong&gt;: this table’s description is stale, or every table’s when the id is 0;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;smgr&lt;/strong&gt;: close your file handles for this table; this kind is never skipped by database;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;relmap&lt;/strong&gt; and &lt;strong&gt;snapshot&lt;/strong&gt;: smaller kinds, for the map of a few catalogs’ files and for the snapshot used to read the catalog.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;p-tftu7d&quot;&gt;A catcache message doesn’t carry the key, only a &lt;strong&gt;hash&lt;/strong&gt; of it, because a table name wouldn’t fit in 16 bytes. The receiver removes every entry in that cache with the same hash. Once in a while that catches an unrelated row whose key hashes the same, a false positive that costs one reload.&lt;/p&gt;
&lt;p id=&quot;p-mcup9p&quot;&gt;Writers add messages under a lock, at most 64 at a time (&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/ipc/sinvaladt.c#L369&quot; title=&quot;SIInsertDataEntries, in src/backend/storage/ipc/sinvaladt.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;SIInsertDataEntries&lt;/a&gt;), so even a commit with thousands of messages never holds the lock for long. After each chunk, the writer sets a “you have messages” flag on every backend.&lt;/p&gt;
&lt;h2 id=&quot;reading-the-ring-before-trusting-the-cache&quot;&gt;Reading the ring before trusting the cache&lt;/h2&gt;
&lt;p id=&quot;p-mlu0v6&quot;&gt;A backend has to read the ring before it trusts its caches.&lt;/p&gt;
&lt;p id=&quot;p-4emxx1&quot;&gt;Reading the ring is one function, &lt;code&gt;AcceptInvalidationMessages()&lt;/code&gt; (&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L929&quot; title=&quot;AcceptInvalidationMessages, in src/backend/utils/cache/inval.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;inval.c:929&lt;/code&gt;&lt;/a&gt;). It calls &lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/ipc/sinval.c#L68&quot; title=&quot;ReceiveSharedInvalidMessages, in src/backend/storage/ipc/sinval.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;ReceiveSharedInvalidMessages&lt;/a&gt;, which pulls messages 32 at a time from &lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/ipc/sinvaladt.c#L472&quot; title=&quot;SIGetDataEntries, in src/backend/storage/ipc/sinvaladt.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;SIGetDataEntries&lt;/a&gt; and applies each one to the local caches.&lt;/p&gt;
&lt;p id=&quot;p-jh73tu&quot;&gt;A backend reads the ring at two moments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;when a transaction starts&lt;/strong&gt;, so every transaction begins with checked caches;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;when it locks a table it did not already hold in that mode&lt;/strong&gt;, in &lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/lmgr/lmgr.c#L106&quot; title=&quot;LockRelationOid, in src/backend/storage/lmgr/lmgr.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;LockRelationOid&lt;/a&gt; and its siblings in &lt;code&gt;lmgr.c&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;p-r6nowm&quot;&gt;&lt;code&gt;ALTER TABLE ... ADD COLUMN&lt;/code&gt; shows why the second one matters. It takes an &lt;code&gt;ACCESS EXCLUSIVE&lt;/code&gt; lock, and the committing backend writes its messages before it lets go of that lock.&lt;/p&gt;
&lt;p id=&quot;p-qfbtm1&quot;&gt;Whoever gets a lock on the table next reads the ring right after, and finds the messages already there. Once a backend holds the lock, it can trust its cache for that table.&lt;/p&gt;
&lt;p id=&quot;p-av8o9h&quot;&gt;If the backend already held that kind of lock, it skips the read. Nobody could have changed the table in a conflicting way while it held the lock.&lt;/p&gt;
&lt;p id=&quot;p-rw14of&quot;&gt;Usually there’s nothing new, and the read costs almost nothing. &lt;code&gt;SIGetDataEntries&lt;/code&gt; first looks at the backend’s own “has messages” flag without taking a lock, and returns at once when the flag isn’t set.&lt;/p&gt;
&lt;p id=&quot;p-m1q2qr&quot;&gt;When there are messages, each one is applied in turn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A catcache message removes the matching rows, plus every cached list in that cache. A row in use at that moment is marked dead, and removed once it’s released.&lt;/li&gt;
&lt;li&gt;A relcache message drops the table’s description if nobody has it open, and rebuilds it if someone does.&lt;/li&gt;
&lt;li&gt;Each message also runs the &lt;strong&gt;callbacks&lt;/strong&gt; registered for it. The plan cache registers some, so a relcache message marks every saved plan that reads the table as invalid. The plan is built again the next time it’s used.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;p-f8pdd9&quot;&gt;Within one transaction’s messages, the catcache ones come first. That way a table’s description is never rebuilt from catalog rows that are about to be thrown away.&lt;/p&gt;
&lt;h2 id=&quot;when-the-ring-fills-up&quot;&gt;When the ring fills up&lt;/h2&gt;
&lt;p id=&quot;p-0hm49m&quot;&gt;A writer never waits for a slow reader, or one idle connection could hold up every DDL on the server. Instead, PostgreSQL cleans up the ring and nudges the reader furthest behind. A reader that falls too far behind gets reset.&lt;/p&gt;
&lt;p id=&quot;p-0wtf46&quot;&gt;Cleanup (&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/ipc/sinvaladt.c#L576&quot; title=&quot;SICleanupQueue, in src/backend/storage/ipc/sinvaladt.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;SICleanupQueue&lt;/a&gt;) runs when the ring holds 2,048 messages, again every 256 after that, and whenever a write would not fit. It does three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It finds the oldest message anyone still needs, which frees the slots before it.&lt;/li&gt;
&lt;li&gt;It marks for &lt;strong&gt;reset&lt;/strong&gt; any backend so far behind that the writer is about to overwrite a message it hasn’t read.&lt;/li&gt;
&lt;li&gt;It signals at most one backend: of those more than 2,048 messages behind and not yet signalled, the one furthest behind. The signal asks it to &lt;strong&gt;catch up&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;em&gt;Diagram 1 is drawn on the page: &lt;a href=&quot;https://sudohulk.com/writing/postgres-cache-invalidation&quot;&gt;see it in the post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p id=&quot;p-5edbyj&quot;&gt;The signalled backend deals with it while it waits for its client. If it’s inside an open transaction, it calls &lt;code&gt;AcceptInvalidationMessages()&lt;/code&gt;. If not, it starts and commits an empty transaction, which reads the ring on the way in.&lt;/p&gt;
&lt;p id=&quot;p-cv99hq&quot;&gt;Once it has caught up, it runs cleanup itself, and that cleanup signals the next backend furthest behind. The source calls this a &lt;strong&gt;daisy chain&lt;/strong&gt;. Usually only one signal is in flight at a time, so idle connections wake one after another and not all at once.&lt;/p&gt;
&lt;p id=&quot;p-w2v7ai&quot;&gt;A reader that stays stuck after its signal is passed over. Later cleanups signal the next reader that hasn’t had one.&lt;/p&gt;
&lt;p id=&quot;p-450mjc&quot;&gt;The last resort is the reset. A backend whose unread message has been overwritten can’t know what that message said. The only safe thing is to throw away its whole cache and reload entries from the catalog as they’re needed.&lt;/p&gt;
&lt;figure class=&quot;figure&quot;&gt;&lt;img alt=&quot;The same ring after the writer has come all the way round. W has written into the slot B3 had not read, shown in red, so B3 is marked for reset. B1 and B2 are not affected.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;360&quot; height=&quot;480&quot; src=&quot;https://sudohulk.com/_astro/sinval-reset.DfUPHrcm_ZH7clu.svg&quot;&gt;&lt;figcaption&gt;The writer laps B3. B3 can&apos;t know what it missed, so it starts over.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p id=&quot;p-b8ridu&quot;&gt;At its next read, a backend marked for reset gets no messages. It jumps to the write position and resets instead, through &lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L784&quot; title=&quot;InvalidateSystemCachesExtended, in src/backend/utils/cache/inval.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;InvalidateSystemCachesExtended&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;every catalog cache is emptied;&lt;/li&gt;
&lt;li&gt;every table description nobody has open is dropped, and the open ones are rebuilt;&lt;/li&gt;
&lt;li&gt;every catcache callback runs with hash 0, and every relcache callback with &lt;code&gt;InvalidOid&lt;/code&gt;, both meaning “everything”.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;p-ac8mtz&quot;&gt;A reset is always correct, but for a while it’s slow. Every lookup in that connection goes back to the catalog tables until the caches fill up again.&lt;/p&gt;
&lt;h2 id=&quot;keeping-your-own-cache-honest&quot;&gt;Keeping your own cache honest&lt;/h2&gt;
&lt;p id=&quot;p-sxnuk7&quot;&gt;An extension may keep a cache of its own, with things like options worked out for a table, a parsed setting or a connection to another server. PostgreSQL doesn’t know about that cache, so it can’t flush it, but you can register a function for it to call when it applies a message.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CacheRegisterSyscacheCallback(cacheid, func, arg)&lt;/code&gt; (&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L1815&quot; title=&quot;CacheRegisterSyscacheCallback, in src/backend/utils/cache/inval.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;inval.c:1815&lt;/code&gt;&lt;/a&gt;) runs &lt;code&gt;func&lt;/code&gt; for each catcache message for that cache, with the hash.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CacheRegisterRelcacheCallback(func, arg)&lt;/code&gt; (&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L1857&quot; title=&quot;CacheRegisterRelcacheCallback, in src/backend/utils/cache/inval.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;inval.c:1857&lt;/code&gt;&lt;/a&gt;) runs &lt;code&gt;func&lt;/code&gt; for each relcache message, with the table’s OID.&lt;/li&gt;
&lt;/ul&gt;
&lt;p id=&quot;p-8ydicl&quot;&gt;The rules I follow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Register once per backend.&lt;/strong&gt; There’s no function to unregister. The slots are fixed: 64 catcache callbacks and 10 relcache callbacks per backend, shared with PostgreSQL’s own caches and every other extension that’s loaded. Running out is &lt;code&gt;FATAL&lt;/code&gt;, and the connection ends.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treat 0 and &lt;code&gt;InvalidOid&lt;/code&gt; as “drop everything”.&lt;/strong&gt; That’s what a reset sends.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In the callback, only mark entries stale.&lt;/strong&gt; Don’t read the catalogs there; rebuild on the next lookup. The callback runs whenever the backend reads the ring, and that can be in the middle of your own catalog read.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A catcache callback gets a hash, not a key.&lt;/strong&gt; Store each key’s hash when you build an entry, or flush everything for that cache.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Watch for entries born stale.&lt;/strong&gt; Building an entry reads the catalogs, reading takes locks, and a lock reads the ring. That can run your callback for the very entry you’re building. PostgreSQL’s relcache builds again when that happens; most other caches rely on DDL taking an &lt;code&gt;ACCESS EXCLUSIVE&lt;/code&gt; lock.&lt;/li&gt;
&lt;/ol&gt;
&lt;p id=&quot;p-2k48wt&quot;&gt;The sketch below shows the pattern. The &lt;code&gt;my_&lt;/code&gt; names are made up, and I haven’t compiled this exact code.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;typedef&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; MyEntry&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;Oid     relid;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;          /* hash key: the table */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    valid;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;     setting;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;        /* worked out from the catalogs */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;} MyEntry;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;static&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; HTAB &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;my_cache &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;NULL&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;static&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; uint64 my_inval_count &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;static&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;void&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;my_relcache_callback&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(Datum &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;arg&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;, Oid &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;relid&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;HASH_SEQ_STATUS status;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;MyEntry    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;my_inval_count&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;++&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (relid &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; InvalidOid)&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;    /* a reset: anything may be stale */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;hash_seq_init&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;status, my_cache);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;while&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; ((entry &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;hash_seq_search&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;status)) &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;NULL&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry-&amp;gt;valid &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; ((entry &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;hash_search&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(my_cache, &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;relid, HASH_FIND, &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;NULL&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)) &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;NULL&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry-&amp;gt;valid &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;   /* only mark it: no catalog reads here */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;static&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; MyEntry &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;my_lookup&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(Oid &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;relid&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;MyEntry    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;bool&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;        found;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (my_cache &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;NULL&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;HASHCTL ctl &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; {.keysize &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;sizeof&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(Oid), .entrysize &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;sizeof&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(MyEntry)};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;my_cache &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;hash_create&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&quot;my cache&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;64&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;ctl, HASH_ELEM &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; HASH_BLOBS);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;        &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;CacheRegisterRelcacheCallback&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(my_relcache_callback, (Datum) &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;hash_search&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(my_cache, &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;relid, HASH_ENTER, &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;found);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;found)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry-&amp;gt;valid &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;while&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry-&amp;gt;valid)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;uint64  before &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; my_inval_count;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry-&amp;gt;setting &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;my_compute_setting&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(relid);&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;  /* reads the catalogs */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry-&amp;gt;valid &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (my_inval_count &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; before);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; entry;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p id=&quot;p-uzx05v&quot;&gt;The counter guards against an entry born stale: if any callback ran during the build, the loop builds it again. If the build fails with an error, the entry stays invalid and the next lookup tries again.&lt;/p&gt;
&lt;p id=&quot;p-6wdr7y&quot;&gt;&lt;code&gt;postgres_fdw&lt;/code&gt; is a real example. Its connection cache registers catcache callbacks for &lt;code&gt;pg_foreign_server&lt;/code&gt; and &lt;code&gt;pg_user_mapping&lt;/code&gt; once, when it creates its hash table. Each connection it opens remembers the hash of its server’s key and of its user mapping’s key.&lt;/p&gt;
&lt;p id=&quot;p-qjyglv&quot;&gt;Its callback (&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/contrib/postgres_fdw/connection.c#L1295&quot; title=&quot;pgfdw_inval_callback, in contrib/postgres_fdw/connection.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;connection.c:1295&lt;/code&gt;&lt;/a&gt;) treats hash 0 as “all”, and otherwise compares hashes.&lt;/p&gt;
&lt;p id=&quot;p-9rq2hd&quot;&gt;A connection not used in the current transaction is closed at once. A connection in use is only marked, and closed when the transaction ends, because you can’t pull a connection out from under a running query.&lt;/p&gt;
&lt;p id=&quot;p-cfuv2q&quot;&gt;Another cache in the same extension, the one that remembers which objects are safe to send to the remote server, does the simple thing. It empties itself whenever a foreign server’s row changes (&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/contrib/postgres_fdw/shippable.c#L64&quot; title=&quot;InvalidateShippableCacheCallback, in contrib/postgres_fdw/shippable.c at REL_18_3&quot; class=&quot;pgsrc external&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;shippable.c:64&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;
&lt;h2 id=&quot;a-worked-example-one-alter-table&quot;&gt;A worked example: one ALTER TABLE&lt;/h2&gt;
&lt;p id=&quot;p-ft2f5e&quot;&gt;Take two connections to the same database. Session B has been using &lt;code&gt;orders&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;PREPARE get_order(&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;AS&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;SELECT&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; id, total &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;FROM&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; orders &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;WHERE&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; id &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; $&lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;EXECUTE&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; get_order(&lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;7&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p id=&quot;p-lmfp73&quot;&gt;B now holds the &lt;code&gt;pg_class&lt;/code&gt; row of &lt;code&gt;orders&lt;/code&gt;, a relcache entry for it, and a saved plan for &lt;code&gt;get_order&lt;/code&gt;.&lt;/p&gt;
&lt;p id=&quot;p-3sol1j&quot;&gt;Session A runs one statement:&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;sql&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;ALTER&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;TABLE&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; orders &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;ADD&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; COLUMN discount &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;integer&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p id=&quot;p-r3hoiq&quot;&gt;Inside A:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A takes an &lt;code&gt;ACCESS EXCLUSIVE&lt;/code&gt; lock on &lt;code&gt;orders&lt;/code&gt;. If B were inside a transaction that had read &lt;code&gt;orders&lt;/code&gt;, A would wait here until it ended.&lt;/li&gt;
&lt;li&gt;A inserts a &lt;code&gt;pg_attribute&lt;/code&gt; row for &lt;code&gt;discount&lt;/code&gt;. &lt;code&gt;pg_attribute&lt;/code&gt; has two caches, by column name and by column number, so that’s two catcache messages. The row belongs to &lt;code&gt;orders&lt;/code&gt;, so there’s one relcache message too.&lt;/li&gt;
&lt;li&gt;A updates the &lt;code&gt;pg_class&lt;/code&gt; row of &lt;code&gt;orders&lt;/code&gt;, whose column count goes up by one. &lt;code&gt;pg_class&lt;/code&gt; has two caches, by OID and by name. Neither key changed, so that’s one message each, and &lt;code&gt;orders&lt;/code&gt; already has its relcache message.&lt;/li&gt;
&lt;li&gt;A ends an internal command and applies the messages to its own caches. The rest of the statement sees the new column.&lt;/li&gt;
&lt;li&gt;A commits. The commit is recorded, the messages go into the ring, and the lock is released.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;table-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Table 1&quot;&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Kind&lt;/th&gt;
&lt;th&gt;Cache or table&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;catcache&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pg_attribute&lt;/code&gt; by name&lt;/td&gt;
&lt;td&gt;the new column’s row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;catcache&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pg_attribute&lt;/code&gt; by number&lt;/td&gt;
&lt;td&gt;the same row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;catcache&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pg_class&lt;/code&gt; by OID&lt;/td&gt;
&lt;td&gt;the column count changed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;catcache&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pg_class&lt;/code&gt; by name&lt;/td&gt;
&lt;td&gt;the same row&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;relcache&lt;/td&gt;
&lt;td&gt;&lt;code&gt;orders&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;its rows changed; listed once&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p id=&quot;p-m9wi8i&quot;&gt;By my reading of the source, five is the floor. A default, a constraint or an index touches rows in more catalogs, and each of those rows brings its own messages. I’d rather give you the kinds than a total I can’t defend.&lt;/p&gt;
&lt;p id=&quot;p-e5vzu8&quot;&gt;Then B runs &lt;code&gt;EXECUTE get_order(7);&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;B starts a transaction and reads the ring, which now holds A’s messages.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;pg_class&lt;/code&gt; messages remove B’s cached row of &lt;code&gt;orders&lt;/code&gt;. The &lt;code&gt;pg_attribute&lt;/code&gt; messages name the new column, which B never cached. B’s rows for the old columns didn’t change, so they stay.&lt;/li&gt;
&lt;li&gt;The relcache message drops B’s description of &lt;code&gt;orders&lt;/code&gt;, which nobody has open between transactions. The plan cache’s callback marks &lt;code&gt;get_order&lt;/code&gt; invalid.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXECUTE&lt;/code&gt; finds the plan invalid. It locks &lt;code&gt;orders&lt;/code&gt;, reads the ring again and finds nothing new, reloads &lt;code&gt;orders&lt;/code&gt; from the catalog, and plans again.&lt;/li&gt;
&lt;li&gt;B gets the same two columns, and it never runs the old plan against the changed table.&lt;/li&gt;
&lt;/ol&gt;
&lt;p id=&quot;p-6nz4vg&quot;&gt;The same steps as a sequence diagram. In it, B reads the ring when it takes its lock; in a new transaction it has already read it once at the start.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Diagram 2 is drawn on the page: &lt;a href=&quot;https://sudohulk.com/writing/postgres-cache-invalidation&quot;&gt;see it in the post&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;gotchas&quot;&gt;Gotchas&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A catcache message carries a hash, not a key.&lt;/strong&gt; Unrelated rows with the same hash are flushed too, which costs a reload and nothing else. Your own catcache callback gets the hash and no OID, so compare stored hashes or flush everything.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Any message to a cache drops every list in it.&lt;/strong&gt; Some lookups ask a cache for all rows that match part of a key, and the answer is cached as a list; finding functions by name works this way. A list can’t be checked against one hash, so it goes. A burst of &lt;code&gt;CREATE OR REPLACE FUNCTION&lt;/code&gt; empties those lists in every connection.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;An idle connection lags.&lt;/strong&gt; It reads the ring at its next transaction or new lock, or when it’s the one signalled. It acts on a signal only while it waits for its client, so a connection busy with one long query reads the ring only when it takes a new lock.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;One huge DDL can reset many connections at once.&lt;/strong&gt; Dropping a schema full of tables changes thousands of catalog rows, and the commit sends all their messages together. Connections that haven’t read along by the time the writer comes round can be reset, and then they all reload their caches at about the same time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;debug_discard_caches&lt;/code&gt; works only in assert builds.&lt;/strong&gt; It throws caches away at every chance, to catch code that holds on to an entry too long. At 1, the regression tests run about 100 times slower. It works in builds with assertions, or with &lt;code&gt;DISCARD_CACHES_ENABLED&lt;/code&gt; defined; anywhere else it stays 0, and setting it to anything else fails.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A prepared &lt;code&gt;SELECT *&lt;/code&gt; fails after &lt;code&gt;ADD COLUMN&lt;/code&gt;, on purpose.&lt;/strong&gt; The new plan would return one more column, and PostgreSQL won’t change a prepared statement’s result shape. Name the columns, or prepare the statement again.&lt;/p&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;ERROR:  cached plan must not change result type&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-whole-loop-in-one-picture&quot;&gt;The whole loop in one picture&lt;/h2&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;session A, the writer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;change a catalog row&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;-&amp;gt; one message per cache that could hold it&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;end of each command&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;-&amp;gt; apply them to A&apos;s own caches&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;COMMIT recorded&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;-&amp;gt; all messages into the ring&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;-&amp;gt; locks released&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;the ring: one per server, 4,096 slots&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;one write position&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;one read position per backend&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;session B, a reader&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;transaction start, or a new lock&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;-&amp;gt; read the ring&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;-&amp;gt; drop stale rows, descriptions, plans&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;next use&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;-&amp;gt; reload from the catalog tables&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;ring too full&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;-&amp;gt; signal the furthest-behind reader not yet signalled&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#e6edf3;--1:#24292e&quot;&gt;-&amp;gt; a full ring behind: reset, reload all&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;h2 id=&quot;why-this-matters&quot;&gt;Why this matters&lt;/h2&gt;
&lt;p id=&quot;p-knumps&quot;&gt;If you write an extension, PostgreSQL can’t see any cache you keep, so follow the five rules in &lt;a href=&quot;#keeping-your-own-cache-honest&quot;&gt;Keeping your own cache honest&lt;/a&gt;.&lt;/p&gt;
&lt;p id=&quot;p-qa277j&quot;&gt;If you run many connections, each one keeps its own caches and pays for its own reloads. After a wave of resets, every one of them reads the same catalog rows again.&lt;/p&gt;
&lt;p id=&quot;p-mgavji&quot;&gt;Huge catalog changes go down better at quiet times, or split into smaller transactions, so connections get a chance to catch up between commits. A connection pooler helps as well, since fewer backends means fewer caches to keep up to date.&lt;/p&gt;
&lt;h2 id=&quot;dig-deeper&quot;&gt;Dig deeper&lt;/h2&gt;
&lt;p id=&quot;p-x3menf&quot;&gt;The long comment that opens &lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;inval.c&lt;/code&gt;&lt;/a&gt; is the best explanation of all this that I know. &lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/ipc/sinvaladt.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;sinvaladt.c&lt;/code&gt;&lt;/a&gt; opens with the design of the ring. Both links, and every trail below, are at PostgreSQL 18.3.&lt;/p&gt;
&lt;div class=&quot;trail&quot;&gt;
&lt;p class=&quot;trail__title&quot;&gt;&lt;span class=&quot;trail__kind&quot;&gt;Code trail&lt;/span&gt; From a commit to the next query&lt;/p&gt;
&lt;ol class=&quot;trail__hops&quot;&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;CommandEndInvalidationMessages&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;inval.c:1408&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (transInvalInfo &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;NULL&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#674e34&quot;&gt;ProcessInvalidationMessages&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#ff7f76;--1:#982934&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;transInvalInfo&lt;/span&gt;&lt;span style=&quot;--0:#ff7f76;--1:#982934&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;ii.CurrentCmdInvalidMsgs,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;              &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;LocalExecuteInvalidationMessage);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;/* WAL Log per-command invalidation messages for wal_level=logical */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;XLogLogicalInfoActive&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;LogLogicalInvalidations&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;AppendInvalidationMessages&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;transInvalInfo&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;PriorCmdInvalidMsgs&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;               &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;transInvalInfo&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;ii.CurrentCmdInvalidMsgs);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L1416-L1427&quot; title=&quot;src/backend/utils/cache/inval.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;inval.c, lines 1416–1427&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;At the end of each command, the backend applies that command’s messages to its own caches only. Nothing reaches the ring yet.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;CommitTransaction&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;xact.c:2227&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; Make catalog changes visible to all backends.  This has to happen after&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; relcache references are &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;dropped&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (see comments &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;for&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;AtEOXact_RelationCache&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;), but before locks are &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;released&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; anyone is&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; waiting &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; lock on a relation we&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&apos;ve modified, we want them to know&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;* about the catalog change before they start using the relation).&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;AtEOXact_Inval(true);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;AtEOXact_MultiXact();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;ResourceOwnerRelease(TopTransactionResourceOwner,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;           &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;RESOURCE_RELEASE_LOCKS,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/access/transam/xact.c#L2427-L2438&quot; title=&quot;src/backend/access/transam/xact.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;xact.c, lines 2427–2438&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;After the commit is recorded (&lt;code&gt;RecordTransactionCommit&lt;/code&gt;, earlier in this function), the messages go out, and the comment explains why this happens before the locks are released.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;AtEOXact_Inval&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;inval.c:1198&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;AppendInvalidationMessages&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;transInvalInfo&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;PriorCmdInvalidMsgs&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;                 &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;transInvalInfo&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;ii.CurrentCmdInvalidMsgs);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#674e34&quot;&gt;ProcessInvalidationMessagesMulti&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#ff7f76;--1:#982934&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;transInvalInfo&lt;/span&gt;&lt;span style=&quot;--0:#ff7f76;--1:#982934&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#8b3c06&quot;&gt;PriorCmdInvalidMsgs&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;                   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;SendSharedInvalidMessages);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (transInvalInfo&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;ii.RelcacheInitFileInval)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;RelationCacheInitFilePostInvalidate&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;ProcessInvalidationMessages&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;transInvalInfo-&amp;gt;PriorCmdInvalidMsgs,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;LocalExecuteInvalidationMessage);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L1222-L1234&quot; title=&quot;src/backend/utils/cache/inval.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;inval.c, lines 1222–1234&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;On commit, every message goes to the shared ring. On abort, the &lt;code&gt;else&lt;/code&gt; branch only undoes this backend’s own caches.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;SIInsertDataEntries&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;sinvaladt.c:369&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; If the buffer is full, we &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;must&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; acquire some space.  Clean the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; queue and reset anyone who is preventing space from being freed.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; Otherwise, clean the queue only when it&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&apos;s exceeded the next&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;* fullness threshold.  We have to loop and recheck the buffer state&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;* after any call of SICleanupQueue.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;for (;;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;numMsgs = segP-&amp;gt;maxMsgNum - segP-&amp;gt;minMsgNum;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;if (numMsgs + nthistime &amp;gt; MAXNUMMESSAGES ||&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;numMsgs &amp;gt;= segP-&amp;gt;nextThreshold)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;SICleanupQueue(true, nthistime);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;break;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/ipc/sinvaladt.c#L395-L408&quot; title=&quot;src/backend/storage/ipc/sinvaladt.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;sinvaladt.c, lines 395–408&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;Before each chunk of at most 64 messages, the writer cleans the ring if it is full or past its next threshold.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;AtStart_Cache&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;xact.c:1166&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;static&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;void&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;AtStart_Cache&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;AcceptInvalidationMessages&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/access/transam/xact.c#L1166-L1170&quot; title=&quot;src/backend/access/transam/xact.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;xact.c, lines 1166–1170&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;Every transaction starts by reading the ring. This is the whole function.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;LockRelationOid&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;lmgr.c:106&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; Now that we have the lock, check &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; invalidation messages, so that we&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; will update or flush any stale relcache entry before we try to use it.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;RangeVarGetRelid&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;() specifically relies on us &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; this.  We can skip&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; this in the not&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;uncommon case that we already had the same type of lock&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; being requested, since then no one &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; could have modified the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; relcache entry in an undesirable way.  (In the case where our own xact&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; modifies the rel, the relcache update happens via&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; CommandCounterIncrement, not here.)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; However, in corner cases where code acts on &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;tables&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (usually &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;catalogs&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; recursively, we might get here &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;while&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; still processing invalidation&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; messages in some outer execution of this function or a sibling.  The&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&quot;cleared&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; status of the lock tells us whether we really are done&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; absorbing relevant inval messages.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (res &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;!=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; LOCKACQUIRE_ALREADY_CLEAR)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;AcceptInvalidationMessages&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;MarkLockClear&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(locallock);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/lmgr/lmgr.c#L119-L137&quot; title=&quot;src/backend/storage/lmgr/lmgr.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;lmgr.c, lines 119–137&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;The other moment: right after a lock on a table is granted, unless the backend already held one like it.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;AcceptInvalidationMessages&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;inval.c:929&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#674e34&quot;&gt;ReceiveSharedInvalidMessages&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(LocalExecuteInvalidationMessage,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;InvalidateSystemCaches);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;/*----------&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* Test code to force cache flushes anytime a flush could happen.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* This helps detect intermittent faults caused by code that reads a cache&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* entry and then performs an action that could invalidate the entry, but&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* rarely actually does so.  This can spot issues that would otherwise&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* only arise with badly timed concurrent DDL, for example.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* The default debug_discard_caches = 0 does no forced cache flushes.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L938-L949&quot; title=&quot;src/backend/utils/cache/inval.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;inval.c, lines 938–949&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;Reading the ring is one call, handed two functions: one applies a message, the other does a full reset.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;SIGetDataEntries&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;sinvaladt.c:472&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (stateP&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;resetState)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;/*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* Force reset.  We can say we have dealt with any messages added&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* since the reset, as well; and that means we should clear the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* signaled flag, too.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;stateP-&amp;gt;nextMsgNum &lt;/span&gt;&lt;span style=&quot;--0:#ff7f76;--1:#982934&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; max;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;stateP-&amp;gt;resetState &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;stateP-&amp;gt;signaled &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;LWLockRelease&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(SInvalReadLock);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#ff7f76;--1:#982934&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#ff7f76;--1:#982934&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#004fa9&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/ipc/sinvaladt.c#L515-L526&quot; title=&quot;src/backend/storage/ipc/sinvaladt.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;sinvaladt.c, lines 515–526&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;A backend marked for reset jumps to the write position and returns -1, which its caller turns into a reset.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p class=&quot;trail__licence&quot;&gt;Code from PostgreSQL 18.3. Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group; Portions Copyright (c) 1994, The Regents of the University of California. Quoted under the &lt;a href=&quot;https://www.postgresql.org/about/licence/&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;PostgreSQL Licence&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;trail&quot;&gt;
&lt;p class=&quot;trail__title&quot;&gt;&lt;span class=&quot;trail__kind&quot;&gt;Code trail&lt;/span&gt; When the ring fills up&lt;/p&gt;
&lt;ol class=&quot;trail__hops&quot;&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;SICleanupQueue&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;sinvaladt.c:576&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (n &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; lowbound)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;stateP-&amp;gt;resetState &lt;/span&gt;&lt;span style=&quot;--0:#ff7f76;--1:#982934&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#004fa9&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;/* no point in signaling him ... */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;continue&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;/* Track the global minimum nextMsgNum */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (n &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; min)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;min &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; n;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;/* Also see who&apos;s furthest back of the unsignaled backends */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (n &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; minsig &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;!&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;stateP&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;signaled)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;minsig &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; n;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;needSig &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; stateP;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/ipc/sinvaladt.c#L617-L632&quot; title=&quot;src/backend/storage/ipc/sinvaladt.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;sinvaladt.c, lines 617–632&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;For each backend: mark it for reset if it blocks the space a writer needs, and otherwise note the furthest behind that has not been signalled.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;ProcessCatchupInterrupt&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;sinval.c:173&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;IsTransactionOrTransactionBlock&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;())&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;elog&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(DEBUG4, &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&quot;ProcessCatchupEvent inside transaction&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;AcceptInvalidationMessages&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;elog&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(DEBUG4, &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&quot;ProcessCatchupEvent outside transaction&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;StartTransactionCommand&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;CommitTransactionCommand&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/ipc/sinval.c#L191-L200&quot; title=&quot;src/backend/storage/ipc/sinval.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;sinval.c, lines 191–200&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;How a signalled, idle backend catches up: read the ring inside its transaction, or run an empty one.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;ReceiveSharedInvalidMessages&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;sinval.c:68&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; We are now caught up.  If we received a catchup signal, reset that&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; flag, and call &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;SICleanupQueue&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;().  This is not so much because we need&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; to flush dead messages right now, as that we want to pass on the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; catchup signal to the next slowest backend.  &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&quot;Daisy chaining&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; the&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; catchup signal this way avoids creating spikes in system load &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; what&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; should be just a background maintenance activity.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (catchupInterruptPending)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;catchupInterruptPending &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;elog&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(DEBUG4, &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&quot;sinval catchup complete, cleaning queue&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;SICleanupQueue&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/storage/ipc/sinval.c#L128-L139&quot; title=&quot;src/backend/storage/ipc/sinval.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;sinval.c, lines 128–139&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;The daisy chain: once caught up, run cleanup, which signals the next backend furthest behind.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;InvalidateSystemCachesExtended&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;inval.c:784&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;InvalidateCatalogSnapshot&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#674e34&quot;&gt;ResetCatalogCachesExt&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(debug_discard);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#674e34&quot;&gt;RelationCacheInvalidate&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(debug_discard);&lt;/span&gt;&lt;span style=&quot;--0:#9fa6af;--1:#4d545b&quot;&gt; /* gets smgr and relmap too */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (i &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;; i &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; syscache_callback_count; i&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;++&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; SYSCACHECALLBACK &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;ccitem &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; syscache_callback_list &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; i;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;ccitem-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(ccitem-&amp;gt;arg, ccitem-&amp;gt;id, &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;for&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (i &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;; i &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; relcache_callback_count; i&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;++&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;struct&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; RELCACHECALLBACK &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;ccitem &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; relcache_callback_list &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; i;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;ccitem-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(ccitem-&amp;gt;arg, InvalidOid);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L789-L804&quot; title=&quot;src/backend/utils/cache/inval.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;inval.c, lines 789–804&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;The reset: every catalog cache and table description, then every callback with 0 or &lt;code&gt;InvalidOid&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p class=&quot;trail__licence&quot;&gt;Code from PostgreSQL 18.3. Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group; Portions Copyright (c) 1994, The Regents of the University of California. Quoted under the &lt;a href=&quot;https://www.postgresql.org/about/licence/&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;PostgreSQL Licence&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;trail&quot;&gt;
&lt;p class=&quot;trail__title&quot;&gt;&lt;span class=&quot;trail__kind&quot;&gt;Code trail&lt;/span&gt; A callback of your own&lt;/p&gt;
&lt;ol class=&quot;trail__hops&quot;&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;CacheRegisterSyscacheCallback&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;inval.c:1815&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#ff7f76;--1:#982934&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (syscache_callback_count &lt;/span&gt;&lt;span style=&quot;--0:#ff7f76;--1:#982934&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; MAX_SYSCACHE_CALLBACKS)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line highlight mark&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#674e34&quot;&gt;elog&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(FATAL, &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&quot;out of syscache_callback_list slots&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;syscache_callback_links&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;[cacheid] &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;/* first callback for this cache */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;syscache_callback_links&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;[cacheid] &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; syscache_callback_count &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;/* add to end of chain, so that older callbacks are called first */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;int&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;      i &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;syscache_callback_links&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;[cacheid] &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L1822-L1833&quot; title=&quot;src/backend/utils/cache/inval.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;inval.c, lines 1822–1833&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;A fixed array, &lt;code&gt;FATAL&lt;/code&gt; when it is full, and no way to take an entry out again.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;CacheRegisterRelcacheCallback&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;inval.c:1857&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;void&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;CacheRegisterRelcacheCallback&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(RelcacheCallbackFunction &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;func&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;Datum &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;arg&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (relcache_callback_count &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; MAX_RELCACHE_CALLBACKS)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;elog&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(FATAL, &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&quot;out of relcache_callback_list slots&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;relcache_callback_list&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;[relcache_callback_count].function &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; func;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FFA657;--1:#AE4B07&quot;&gt;relcache_callback_list&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;[relcache_callback_count].arg &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; arg;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;++&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;relcache_callback_count;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/src/backend/utils/cache/inval.c#L1857-L1868&quot; title=&quot;src/backend/utils/cache/inval.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;inval.c, lines 1857–1868&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;Ten slots per backend, shared with PostgreSQL’s own caches.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;li class=&quot;trail__hop&quot;&gt;
&lt;details&gt;
&lt;summary class=&quot;trail__summary&quot;&gt;&lt;code&gt;pgfdw_inval_callback&lt;/code&gt; &lt;span class=&quot;trail__where&quot;&gt;connection.c:1295&lt;/span&gt;&lt;/summary&gt;
&lt;div class=&quot;expressive-code&quot;&gt;&lt;figure class=&quot;frame&quot;&gt;&lt;figcaption class=&quot;header&quot;&gt;&lt;/figcaption&gt;&lt;pre data-language=&quot;c&quot;&gt;&lt;code&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;/* hashvalue == 0 means a cache reset, must clear all state */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (hashvalue &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;||&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(cacheid &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; FOREIGNSERVEROID &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;server_hashvalue &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; hashvalue) &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;||&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(cacheid &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; USERMAPPINGOID &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry&lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;mapping_hashvalue &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; hashvalue))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;/*&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* Close the connection immediately if it&apos;s not used yet in this&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* transaction. Otherwise mark it as invalid so that&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* pgfdw_xact_callback() can close it at the end of this&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;* transaction.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#8B949E;--1:#616972&quot;&gt;*/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; (entry-&amp;gt;xact_depth &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;==&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;elog&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(DEBUG3, &lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&quot;discarding connection &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#005CC5&quot;&gt;%p&lt;/span&gt;&lt;span style=&quot;--0:#A5D6FF;--1:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;, entry-&amp;gt;conn);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;    &lt;/span&gt;&lt;span style=&quot;--0:#CFAE8A;--1:#7A5C3E&quot;&gt;disconnect_pg_server&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;(entry);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;  &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;ec-line&quot;&gt;&lt;div class=&quot;code&quot;&gt;&lt;span class=&quot;indent&quot;&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;entry-&amp;gt;invalidated &lt;/span&gt;&lt;span style=&quot;--0:#FF7B72;--1:#BF3441&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt; &lt;/span&gt;&lt;span style=&quot;--0:#79C0FF;--1:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--0:#E6EDF3;--1:#24292E&quot;&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;copy&quot;&gt;&lt;div aria-live=&quot;polite&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p class=&quot;trail__src&quot;&gt;&lt;a href=&quot;https://github.com/postgres/postgres/blob/REL_18_3/contrib/postgres_fdw/connection.c#L1311-L1330&quot; title=&quot;contrib/postgres_fdw/connection.c&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;connection.c, lines 1311–1330&lt;/a&gt; at REL_18_3&lt;/p&gt;
&lt;div class=&quot;trail__note&quot;&gt;
&lt;p&gt;Hash 0 means everything; otherwise match the stored hashes. An idle connection closes now, a busy one at the end of its transaction.&lt;/p&gt;
&lt;/div&gt;
&lt;/details&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p class=&quot;trail__licence&quot;&gt;Code from PostgreSQL 18.3. Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group; Portions Copyright (c) 1994, The Regents of the University of California. Quoted under the &lt;a href=&quot;https://www.postgresql.org/about/licence/&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;PostgreSQL Licence&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;aside class=&quot;callout callout-unsure&quot; data-callout=&quot;unsure&quot; data-label=&quot;What I&amp;#x27;m still unsure about&quot;&gt;&lt;p&gt;&lt;strong&gt;What I&amp;#x27;m still unsure about&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt;
&lt;li id=&quot;sinval-reset-count&quot; class=&quot;question&quot;&gt;How often resets happen on a busy server. I know of no statistics view that counts them; the only sign I have found is a &lt;code&gt;DEBUG4&lt;/code&gt; log line, “cache state reset”.&lt;/li&gt;
&lt;li id=&quot;why-4096-messages&quot; class=&quot;question&quot;&gt;Why the ring holds 4,096 messages, and not a number sized from &lt;code&gt;max_connections&lt;/code&gt; or a setting. I have not found the discussion that chose it.&lt;/li&gt;
&lt;li id=&quot;repeatable-read-ddl&quot; class=&quot;question&quot;&gt;What a long &lt;code&gt;REPEATABLE READ&lt;/code&gt; transaction sees when another session adds a column before the first one touches the table. Its data snapshot is fixed but its catalog reads are not, and I want to trace that case end to end.&lt;/li&gt;
&lt;/ul&gt;&lt;/aside&gt;
</content:encoded><category>postgresql</category><category>databases</category><category>performance</category><author>vignesh@sudohulk.com (Vignesh Kumar A)</author></item><item><title>Full-page writes and torn pages</title><link>https://sudohulk.com/writing/full-page-writes-and-torn-pages</link><guid isPermaLink="true">https://sudohulk.com/writing/full-page-writes-and-torn-pages</guid><description>Why the first change to a page after a checkpoint carries the whole 8 KB page in WAL.</description><pubDate>Tue, 22 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p id=&quot;p-22pkhs&quot;&gt;A &lt;strong&gt;torn page&lt;/strong&gt; is what you get when the operating system writes only part of an 8 KB PostgreSQL page before power is lost: the disk sector size is smaller than the page, so half the page can be new and half old. Replaying a normal WAL record against such a page would corrupt it further, because the record assumes the page looked exactly like it did before the change.&lt;/p&gt;
&lt;p id=&quot;p-yhre84&quot;&gt;PostgreSQL’s answer is the &lt;strong&gt;full-page write&lt;/strong&gt; (also called a full-page image, FPI): the first time a page is modified after a checkpoint, the WAL record carries the entire page, not just the delta. On recovery, that copy simply overwrites whatever is on disk, torn or not. Later changes to the same page before the next checkpoint carry only deltas, because the full copy in the log has already made the page’s starting state safe.&lt;/p&gt;
&lt;aside class=&quot;callout callout-gotcha&quot; data-callout=&quot;gotcha&quot; data-label=&quot;Gotcha&quot;&gt;&lt;p&gt;&lt;strong&gt;Gotcha&lt;/strong&gt;&lt;/p&gt;&lt;p id=&quot;p-2bf7q4&quot;&gt;This is why WAL volume spikes right after a checkpoint, and why spreading checkpoints out (&lt;code&gt;checkpoint_completion_target&lt;/code&gt;) or checkpointing less often reduces WAL, at the cost of longer recovery.&lt;/p&gt;&lt;/aside&gt;
&lt;p id=&quot;p-equzfm&quot;&gt;&lt;code&gt;full_page_writes&lt;/code&gt; can be turned off on storage that guarantees atomic page writes. Most people should not.&lt;/p&gt;
&lt;p id=&quot;p-l60mzs&quot;&gt;Related: &lt;a href=&quot;https://sudohulk.com/writing/pd-lsn-vs-record-lsn&quot; class=&quot;wikilink&quot; data-wikilink=&quot;pd-lsn-vs-record-lsn&quot;&gt;pd_lsn vs record LSN&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>postgresql</category><category>databases</category><author>vignesh@sudohulk.com (Vignesh Kumar A)</author></item><item><title>Lazy transaction id assignment</title><link>https://sudohulk.com/writing/lazy-xid-assignment</link><guid isPermaLink="true">https://sudohulk.com/writing/lazy-xid-assignment</guid><description>A transaction does not get an xid at BEGIN; it gets one the first time it writes.</description><pubDate>Tue, 22 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p id=&quot;p-o9cdys&quot;&gt;&lt;code&gt;BEGIN&lt;/code&gt; does not hand out a &lt;strong&gt;transaction id&lt;/strong&gt; (xid). A transaction that only reads never gets one; it runs under a lightweight &lt;strong&gt;virtual transaction id&lt;/strong&gt; made of the backend id and a per-backend counter. The real 32-bit xid is assigned lazily, on the first operation that needs to mark a tuple: an &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, or a lock that has to be recorded.&lt;/p&gt;
&lt;p id=&quot;p-ydyt4k&quot;&gt;Xids are scarce, and they wrap around. Every assigned xid must eventually be frozen by vacuum to avoid wraparound, so handing them to read-only transactions would burn through the space and create work for no reason.&lt;/p&gt;
&lt;aside class=&quot;callout callout-gotcha&quot; data-callout=&quot;gotcha&quot; data-label=&quot;Gotcha&quot;&gt;&lt;p&gt;&lt;strong&gt;Gotcha&lt;/strong&gt;&lt;/p&gt;&lt;p id=&quot;p-2hra14&quot;&gt;&lt;code&gt;txid_current()&lt;/code&gt; forces an xid to be assigned. &lt;code&gt;pg_current_xact_id_if_assigned()&lt;/code&gt; (PG 13+) tells you whether one exists without creating it.&lt;/p&gt;&lt;/aside&gt;
&lt;p id=&quot;p-sy8v2c&quot;&gt;Related: &lt;a href=&quot;https://sudohulk.com/writing/pd-lsn-vs-record-lsn&quot; class=&quot;wikilink&quot; data-wikilink=&quot;pd-lsn-vs-record-lsn&quot;&gt;pd_lsn vs record LSN&lt;/a&gt;, and a post on what happens between BEGIN and COMMIT once it is written.&lt;/p&gt;
</content:encoded><category>postgresql</category><author>vignesh@sudohulk.com (Vignesh Kumar A)</author></item><item><title>pd_lsn vs record LSN</title><link>https://sudohulk.com/writing/pd-lsn-vs-record-lsn</link><guid isPermaLink="true">https://sudohulk.com/writing/pd-lsn-vs-record-lsn</guid><description>Two LSNs that are easy to mix up, and the rule recovery uses to compare them.</description><pubDate>Mon, 21 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p id=&quot;p-fhgxjp&quot;&gt;Every WAL record has an address in the log stream: its &lt;strong&gt;record LSN&lt;/strong&gt;. Every 8 KB data page also carries an LSN in its header, &lt;code&gt;pd_lsn&lt;/code&gt;: the &lt;strong&gt;page LSN&lt;/strong&gt;, which is the address just past the most recent WAL record already applied to that page.&lt;/p&gt;
&lt;p id=&quot;p-sq1ept&quot;&gt;The rule that makes crash recovery idempotent: when replaying a record for a page, PostgreSQL applies it only if &lt;code&gt;pd_lsn&lt;/code&gt; is older than the record’s LSN. If the page already shows a newer LSN, that change reached disk before the crash and is skipped.&lt;/p&gt;
&lt;aside class=&quot;callout callout-gotcha&quot; data-callout=&quot;gotcha&quot; data-label=&quot;Gotcha&quot;&gt;&lt;p&gt;&lt;strong&gt;Gotcha&lt;/strong&gt;&lt;/p&gt;&lt;p id=&quot;p-t18o0o&quot;&gt;An LSN is meaningful only inside one server. Two nodes’ &lt;code&gt;1/2D3E&lt;/code&gt; have nothing to do with each other. Never compare LSNs across machines.&lt;/p&gt;&lt;/aside&gt;
&lt;p id=&quot;p-hwzjco&quot;&gt;Related: &lt;a href=&quot;https://sudohulk.com/writing/full-page-writes-and-torn-pages&quot; class=&quot;wikilink&quot; data-wikilink=&quot;full-page-writes-and-torn-pages&quot;&gt;full-page-writes-and-torn-pages&lt;/a&gt; (once written).&lt;/p&gt;
</content:encoded><category>postgresql</category><author>vignesh@sudohulk.com (Vignesh Kumar A)</author></item></channel></rss>