Thread
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Fix catcache invalidation of a list entry that's being built
- f217c410553d 13.19 landed
- 91fc447c21d3 16.7 landed
- 96e61b2792a5 17.3 landed
- fce17c3a53d6 14.16 landed
- ce7c406f0f8d 15.11 landed
- af8cd1639ab2 18.0 landed
-
Cope with inplace update making catcache stale during TOAST fetch.
- 7a21306aee0a 13.16 landed
- 11f3815d6af8 12.20 landed
- af73e37fa181 14.13 landed
- b08a4b6163eb 15.8 landed
- e4afd7153bd8 16.4 landed
- f9f47f0d93d1 17.0 landed
-
Add previous commit to .git-blame-ignore-revs.
- 36578fa04942 17.0 landed
-
Re-pgindent catcache.c after previous commit.
- d41358f4bbc8 15.6 landed
- d29a4fbacfb7 12.18 landed
- 96c019ffa3f8 17.0 landed
- 7ceeb57baddd 14.11 landed
- 56dcd71decb7 16.2 landed
- 475b3ea3c06b 13.14 landed
-
Cope with catcache entries becoming stale during detoasting.
- db122d426a2d 14.11 landed
- ad98fb14226a 17.0 landed
- 98e03f957436 13.14 landed
- 7e2561e1a258 16.2 landed
- 3b4d85cf159c 12.18 landed
- 2a46a0df4793 15.6 landed
-
Re: Recovering from detoast-related catcache invalidations
Xiaoran Wang <fanfuxiaoran@gmail.com> — 2024-01-11T17:52:21Z
Hi, >> BTW, while nosing around I found what seems like a very nasty related >> bug. Suppose that a catalog tuple being loaded into syscache contains >> some toasted fields. CatalogCacheCreateEntry will flatten the tuple, >> involving fetches from toast tables that will certainly cause >> AcceptInvalidationMessages calls. What if one of those should have >> invalidated this tuple? We will not notice, because it's not in >> the hashtable yet. When we do add it, we will mark it not-dead, >> meaning that the stale entry looks fine and could persist for a long >> while. I spent some time trying to understand the bug and finally, I can reproduce it locally with the following steps: step1: create a function called 'test' with a long body that must be stored in a toast table. and put it in schema 'yy' by : "alter function test set schema yy"; step 2: I added a breakpoint at 'toast_flatten_tuple' for session1 , then execute the following SQL: ---------- set search_path='public'; alter function test set schema xx; ---------- step 3: when the session1 stops at the breakpoint, I open session2 and execute ----------- set search_path = 'yy'; alter function test set schema public; ----------- step4: resume the session1 , it reports the error "ERROR: could not find a function named "test"" step 5: continue to execute "alter function test set schema xx;" in session1, but it still can not work and report the above error although the function test already belongs to schema 'public' Obviously, in session 1, the "test" proc tuple in the cache is outdated. >> The detection of "get an invalidation" could be refined: what I did >> here is to check for any advance of SharedInvalidMessageCounter, >> which clearly will have a significant number of false positives. >> However, the only way I see to make that a lot better is to >> temporarily create a placeholder catcache entry (probably a negative >> one) with the same keys, and then see if it got marked dead. >> This seems a little expensive, plus I'm afraid that it'd be actively >> wrong in the recursive-lookup cases that the existing comment in >> SearchCatCacheMiss is talking about (that is, the catcache entry >> might mislead any recursive lookup that happens). I have reviewed your patch, and it looks good. But instead of checking for any advance of SharedInvalidMessageCounter ( if the invalidate message is not related to the current tuple, it is a little expensive) I have another idea: we can recheck the visibility of the tuple with CatalogSnapshot(the CatalogSnapthot must be refreshed if there is any SharedInvalidMessages) if it is not visible, we re-fetch the tuple, otherwise, we can continue to use it as it is not outdated. I added a commit based on your patch and attached it.
-
Re: Recovering from detoast-related catcache invalidations
Tom Lane <tgl@sss.pgh.pa.us> — 2024-01-11T22:21:03Z
Xiaoran Wang <fanfuxiaoran@gmail.com> writes: >>> The detection of "get an invalidation" could be refined: what I did >>> here is to check for any advance of SharedInvalidMessageCounter, >>> which clearly will have a significant number of false positives. > I have reviewed your patch, and it looks good. But instead of checking for > any advance of SharedInvalidMessageCounter ( if the invalidate message is > not related to the current tuple, it is a little expensive) I have another > idea: we can recheck the visibility of the tuple with CatalogSnapshot(the > CatalogSnapthot must be refreshed if there is any SharedInvalidMessages) if > it is not visible, we re-fetch the tuple, otherwise, we can continue to use > it as it is not outdated. Maybe, but that undocumented hack in SetHintBits seems completely unacceptable. Isn't there a cleaner way to make this check? Also, I'm pretty dubious that GetNonHistoricCatalogSnapshot rather than GetCatalogSnapshot is the right thing, because the catcaches use the latter. regards, tom lane
-
Re: Recovering from detoast-related catcache invalidations
Xiaoran Wang <fanfuxiaoran@gmail.com> — 2024-01-12T03:56:32Z
> Also, I'm pretty dubious that GetNonHistoricCatalogSnapshot rather > than GetCatalogSnapshot is the right thing, because the catcaches > use the latter. Yes, you are right, should use GetCatalogSnapshot here. > Maybe, but that undocumented hack in SetHintBits seems completely > unacceptable. Isn't there a cleaner way to make this check? Maybe we don't need to call 'HeapTupleSatisfiesVisibility' to check if the tuple has been deleted. As the tuple's xmin must been committed, so we just need to check if its xmax is committed, like the below: ------------ @@ -1956,9 +1956,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments, */ if (HeapTupleHasExternal(ntp)) { + TransactionId xmax; dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc); - if (!HeapTupleSatisfiesVisibility(ntp, GetNonHistoricCatalogSnapshot(cache->cc_reloid), InvalidBuffer)) + xmax = HeapTupleHeaderGetUpdateXid(ntp->t_data); + if (TransactionIdIsValid(xmax) && TransactionIdDidCommit(xmax)) { heap_freetuple(dtp); return NULL; ------------ I'm not quite sure the code is correct, I cannot clearly understand 'HeapTupleHeaderGetUpdateXid', and I need more time to dive into it. Any thoughts? Tom Lane <tgl@sss.pgh.pa.us> 于2024年1月12日周五 06:21写道: > Xiaoran Wang <fanfuxiaoran@gmail.com> writes: > >>> The detection of "get an invalidation" could be refined: what I did > >>> here is to check for any advance of SharedInvalidMessageCounter, > >>> which clearly will have a significant number of false positives. > > > I have reviewed your patch, and it looks good. But instead of checking > for > > any advance of SharedInvalidMessageCounter ( if the invalidate message is > > not related to the current tuple, it is a little expensive) I have > another > > idea: we can recheck the visibility of the tuple with > CatalogSnapshot(the > > CatalogSnapthot must be refreshed if there is any SharedInvalidMessages) > if > > it is not visible, we re-fetch the tuple, otherwise, we can continue to > use > > it as it is not outdated. > > Maybe, but that undocumented hack in SetHintBits seems completely > unacceptable. Isn't there a cleaner way to make this check? > > Also, I'm pretty dubious that GetNonHistoricCatalogSnapshot rather > than GetCatalogSnapshot is the right thing, because the catcaches > use the latter. > > regards, tom lane > -
Re: Recovering from detoast-related catcache invalidations
Tom Lane <tgl@sss.pgh.pa.us> — 2024-01-12T20:14:12Z
Xiaoran Wang <fanfuxiaoran@gmail.com> writes: >> Maybe, but that undocumented hack in SetHintBits seems completely >> unacceptable. Isn't there a cleaner way to make this check? > Maybe we don't need to call 'HeapTupleSatisfiesVisibility' to check if the > tuple has been deleted. > As the tuple's xmin must been committed, so we just need to check if its > xmax is committed, I'm not super thrilled with that. Something I realized last night is that your proposal only works if "ntp" is pointing directly into the catalog's disk buffers. If something earlier than this code had made a local-memory copy of the catalog tuple, then it's possible that its header fields (particularly xmax) are out of date compared to shared buffers and would fail to tell us that some other process just invalidated the tuple. Now in fact, with the current implementation of syscache_getnext() the result is actually a live tuple and so we can expect to see any relevant updates. But I think we'd better add some Asserts that that's so; and that also provides us with a way to call HeapTupleSatisfiesVisibility fully legally, because we can get the buffer reference out of the scan descriptor too. This is uncomfortably much in bed with the tuple table slot code, perhaps, but I don't see a way to do it more cleanly unless we want to add some new provisions to that API. Andres, do you have any thoughts about that? Anyway, this approach gets rid of false positives, which is great for performance and bad for testing. Code coverage says that now we never hit the failure paths during regression tests, which is unsurprising, but I'm not very comfortable with leaving those paths unexercised. I tried to make an isolation test to exercise them, but there's no good way at the SQL level to get a session to block during the detoast step. LOCK TABLE on some catalog's toast table would do, but we disallow it. I thought about adding a small C function to regress.so to take out such a lock, but we have no infrastructure for referencing regress.so from isolation tests. What I ended up doing is adding a random failure about 0.1% of the time in USE_ASSERT_CHECKING builds --- that's intellectually ugly for sure, but doing better seems like way more work than it's worth. regards, tom lane
-
Re: Recovering from detoast-related catcache invalidations
Tom Lane <tgl@sss.pgh.pa.us> — 2024-01-12T20:47:13Z
I wrote: > This is uncomfortably much in bed with the tuple table slot code, > perhaps, but I don't see a way to do it more cleanly unless we want > to add some new provisions to that API. Andres, do you have any > thoughts about that? Oh! After nosing around a bit more I remembered systable_recheck_tuple, which is meant for exactly this purpose. So v4 attached. regards, tom lane
-
Re: Recovering from detoast-related catcache invalidations
Xiaoran Wang <fanfuxiaoran@gmail.com> — 2024-01-13T05:16:52Z
Great! That's what exactly we need. The patch LGTM, +1 Tom Lane <tgl@sss.pgh.pa.us> 于2024年1月13日周六 04:47写道: > I wrote: > > This is uncomfortably much in bed with the tuple table slot code, > > perhaps, but I don't see a way to do it more cleanly unless we want > > to add some new provisions to that API. Andres, do you have any > > thoughts about that? > > Oh! After nosing around a bit more I remembered systable_recheck_tuple, > which is meant for exactly this purpose. So v4 attached. > > regards, tom lane > >
-
Re: Recovering from detoast-related catcache invalidations
Xiaoran Wang <fanfuxiaoran@gmail.com> — 2024-01-13T09:02:15Z
Hmm, how about first checking if any invalidated shared messages have been accepted, then rechecking the tuple's visibility? If there is no invalidated shared message accepted during 'toast_flatten_tuple', there is no need to do then visibility check, then it can save several CPU cycles. ---- if (inval_count != SharedInvalidMessageCounter && !systable_recheck_tuple(scandesc, ntp)) { heap_freetuple(dtp); return NULL; } ---- Xiaoran Wang <fanfuxiaoran@gmail.com> 于2024年1月13日周六 13:16写道: > Great! That's what exactly we need. > > The patch LGTM, +1 > > > Tom Lane <tgl@sss.pgh.pa.us> 于2024年1月13日周六 04:47写道: > >> I wrote: >> > This is uncomfortably much in bed with the tuple table slot code, >> > perhaps, but I don't see a way to do it more cleanly unless we want >> > to add some new provisions to that API. Andres, do you have any >> > thoughts about that? >> >> Oh! After nosing around a bit more I remembered systable_recheck_tuple, >> which is meant for exactly this purpose. So v4 attached. >> >> regards, tom lane >> >> -
Re: Recovering from detoast-related catcache invalidations
Tom Lane <tgl@sss.pgh.pa.us> — 2024-01-13T17:18:32Z
Xiaoran Wang <fanfuxiaoran@gmail.com> writes: > Hmm, how about first checking if any invalidated shared messages have been > accepted, then rechecking the tuple's visibility? > If there is no invalidated shared message accepted during > 'toast_flatten_tuple', > there is no need to do then visibility check, then it can save several > CPU cycles. Meh, I'd just as soon not add the additional dependency/risk of bugs. This is an expensive and seldom-taken code path, so I don't think shaving a few cycles is really important. regards, tom lane
-
Re: Recovering from detoast-related catcache invalidations
Tom Lane <tgl@sss.pgh.pa.us> — 2024-01-13T19:12:23Z
I wrote: > Xiaoran Wang <fanfuxiaoran@gmail.com> writes: >> Hmm, how about first checking if any invalidated shared messages have been >> accepted, then rechecking the tuple's visibility? >> If there is no invalidated shared message accepted during >> 'toast_flatten_tuple', >> there is no need to do then visibility check, then it can save several >> CPU cycles. > Meh, I'd just as soon not add the additional dependency/risk of bugs. > This is an expensive and seldom-taken code path, so I don't think > shaving a few cycles is really important. It occurred to me that this idea might be more interesting if we could encapsulate it right into systable_recheck_tuple: something like having systable_beginscan capture the current SharedInvalidMessageCounter and save it in the SysScanDesc struct, then compare in systable_recheck_tuple to possibly short-circuit that work. This'd eliminate one of the main bug hazards in the idea, namely that you might capture SharedInvalidMessageCounter too late, after something's already happened. However, the whole idea only works for catalogs that have catcaches, and the other users of systable_recheck_tuple are interested in pg_depend which doesn't. So that put a damper on my enthusiasm for the idea. regards, tom lane
-
Re: Recovering from detoast-related catcache invalidations
Noah Misch <noah@leadboat.com> — 2024-01-14T20:14:11Z
On Fri, Jan 12, 2024 at 03:47:13PM -0500, Tom Lane wrote: > I wrote: > > This is uncomfortably much in bed with the tuple table slot code, > > perhaps, but I don't see a way to do it more cleanly unless we want > > to add some new provisions to that API. Andres, do you have any > > thoughts about that? > > Oh! After nosing around a bit more I remembered systable_recheck_tuple, > which is meant for exactly this purpose. So v4 attached. systable_recheck_tuple() is blind to heap_inplace_update(), so it's not a general proxy for invalidation messages. The commit for $SUBJECT (ad98fb1) doesn't create any new malfunctions, but I expect the systable_recheck_tuple() part will change again before the heap_inplace_update() story is over (https://postgr.es/m/flat/CAMp+ueZQz3yDk7qg42hk6-9gxniYbp-=bG2mgqecErqR5gGGOA@mail.gmail.com).
-
Re: Recovering from detoast-related catcache invalidations
Xiaoran Wang <fanfuxiaoran@gmail.com> — 2024-01-15T03:28:23Z
This is an interesting idea. Although some catalog tables are not in catcaches, such as pg_depend, when scanning them, if there is any SharedInvalidationMessage, the CatalogSnapshot will be invalidated and recreated ("RelationInvalidatesSnapshotsOnly" in syscache.c) Maybe during the system_scan, it receives the SharedInvalidationMessages and returns the tuples which are out of date. systable_recheck_tuple is used in dependency.c for such case. Tom Lane <tgl@sss.pgh.pa.us> 于2024年1月14日周日 03:12写道: > I wrote: > > Xiaoran Wang <fanfuxiaoran@gmail.com> writes: > >> Hmm, how about first checking if any invalidated shared messages have > been > >> accepted, then rechecking the tuple's visibility? > >> If there is no invalidated shared message accepted during > >> 'toast_flatten_tuple', > >> there is no need to do then visibility check, then it can save several > >> CPU cycles. > > > Meh, I'd just as soon not add the additional dependency/risk of bugs. > > This is an expensive and seldom-taken code path, so I don't think > > shaving a few cycles is really important. > > It occurred to me that this idea might be more interesting if we > could encapsulate it right into systable_recheck_tuple: something > like having systable_beginscan capture the current > SharedInvalidMessageCounter and save it in the SysScanDesc struct, > then compare in systable_recheck_tuple to possibly short-circuit > that work. This'd eliminate one of the main bug hazards in the > idea, namely that you might capture SharedInvalidMessageCounter too > late, after something's already happened. However, the whole idea > only works for catalogs that have catcaches, and the other users of > systable_recheck_tuple are interested in pg_depend which doesn't. > So that put a damper on my enthusiasm for the idea. > > regards, tom lane > -
Re: Recovering from detoast-related catcache invalidations
Noah Misch <noah@leadboat.com> — 2024-09-24T21:20:36Z
On Sun, Jan 14, 2024 at 12:14:11PM -0800, Noah Misch wrote: > On Fri, Jan 12, 2024 at 03:47:13PM -0500, Tom Lane wrote: > > Oh! After nosing around a bit more I remembered systable_recheck_tuple, > > which is meant for exactly this purpose. So v4 attached. > > systable_recheck_tuple() is blind to heap_inplace_update(), so it's not a > general proxy for invalidation messages. The commit for $SUBJECT (ad98fb1) > doesn't create any new malfunctions, but I expect the systable_recheck_tuple() > part will change again before the heap_inplace_update() story is over > (https://postgr.es/m/flat/CAMp+ueZQz3yDk7qg42hk6-9gxniYbp-=bG2mgqecErqR5gGGOA@mail.gmail.com). Commit f9f47f0 (2024-06-27) addressed inplace updates here.
-
Re: Recovering from detoast-related catcache invalidations
Heikki Linnakangas <hlinnaka@iki.fi> — 2024-12-13T14:20:10Z
On 25/09/2024 00:20, Noah Misch wrote: > On Sun, Jan 14, 2024 at 12:14:11PM -0800, Noah Misch wrote: >> On Fri, Jan 12, 2024 at 03:47:13PM -0500, Tom Lane wrote: >>> Oh! After nosing around a bit more I remembered systable_recheck_tuple, >>> which is meant for exactly this purpose. So v4 attached. >> >> systable_recheck_tuple() is blind to heap_inplace_update(), so it's not a >> general proxy for invalidation messages. The commit for $SUBJECT (ad98fb1) >> doesn't create any new malfunctions, but I expect the systable_recheck_tuple() >> part will change again before the heap_inplace_update() story is over >> (https://postgr.es/m/flat/CAMp+ueZQz3yDk7qg42hk6-9gxniYbp-=bG2mgqecErqR5gGGOA@mail.gmail.com). > > Commit f9f47f0 (2024-06-27) addressed inplace updates here. I started to wonder if there's still an issue with catcache list entries. The code to build a CatCList looks like this: SearchCatCacheList() systable_beginscan() while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) { ct = CatalogCacheCreateEntry(ntp) if (ct == NULL) { /* 'ntp' was concurrently invalidated, start all over */ } } systable_endscan(); /* create CatCList entry */ CatalogCacheCreateEntry() can accept catcache invalidations when it opens the toast table, and it now has recheck logic to detect the case that the tuple it's processing (ntp) is invalidated. However, isn't it also possible that it accepts an invalidation message for a tuple that we had processed in an earlier iteration of the loop? Or that a new catalog tuple was inserted that should be part of the list we're building? -- Heikki Linnakangas Neon (https://neon.tech) -
Re: Recovering from detoast-related catcache invalidations
Tom Lane <tgl@sss.pgh.pa.us> — 2024-12-13T15:30:22Z
Heikki Linnakangas <hlinnaka@iki.fi> writes: > CatalogCacheCreateEntry() can accept catcache invalidations when it > opens the toast table, and it now has recheck logic to detect the case > that the tuple it's processing (ntp) is invalidated. However, isn't it > also possible that it accepts an invalidation message for a tuple that > we had processed in an earlier iteration of the loop? Or that a new > catalog tuple was inserted that should be part of the list we're building? The expectation is that the list will be built and returned to the caller, but it's already marked as stale so it will be rebuilt on next request. We could consider putting a loop around that, but (a) it might loop a lot of times, and (b) returning a stale list isn't much different from the situation where the list-invalidating event arrives a nanosecond after we finish rather than a nanosecond before. Ultimately it's the caller's responsibility that the returned list be consistent enough for its purposes. It might achieve that by first taking a lock on a related table, for example. regards, tom lane
-
Re: Recovering from detoast-related catcache invalidations
Heikki Linnakangas <hlinnaka@iki.fi> — 2024-12-14T00:06:53Z
On 13/12/2024 17:30, Tom Lane wrote: > Heikki Linnakangas <hlinnaka@iki.fi> writes: >> CatalogCacheCreateEntry() can accept catcache invalidations when it >> opens the toast table, and it now has recheck logic to detect the case >> that the tuple it's processing (ntp) is invalidated. However, isn't it >> also possible that it accepts an invalidation message for a tuple that >> we had processed in an earlier iteration of the loop? Or that a new >> catalog tuple was inserted that should be part of the list we're building? > > The expectation is that the list will be built and returned to the > caller, but it's already marked as stale so it will be rebuilt > on next request. Ah, you mean this at the end: > /* mark list dead if any members already dead */ > if (ct->dead) > cl->dead = true; Ok, I missed that. It does not handle the 2nd scenario though: If a new catalog tuple is concurrently inserted that should be part of the list, it is missed. I was able to reproduce that, by pausing a process with gdb while it's building the list in SearchCatCacheList(): 1. Create a function called foofunc(integer). It must be large so that its pg_proc tuple is toasted. 2. In one backend, run "SELECT foofunc(1)". It calls FuncnameGetCandidates() which calls "SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));". Put a break point in SearchCatCacheList() just after the systable_beginscan(). 3. In another backend, create function foofunc() with no args. 4. continue execution from the breakpoint. 5. Run "SELECT foofunc()" in the first session. It fails to find the function. The error persists, it will fail to find that function if you try again, until the syscache is invalidated again for some reason. Attached is an injection point test case to reproduce that. If you change the test so that the function's body is shorter, so that it's not toasted, the test passes. -- Heikki Linnakangas Neon (https://neon.tech)
-
Re: Recovering from detoast-related catcache invalidations
Heikki Linnakangas <hlinnaka@iki.fi> — 2024-12-23T22:18:09Z
On 14/12/2024 02:06, Heikki Linnakangas wrote: > Ok, I missed that. It does not handle the 2nd scenario though: If a new > catalog tuple is concurrently inserted that should be part of the list, > it is missed. > > I was able to reproduce that, by pausing a process with gdb while it's > building the list in SearchCatCacheList(): > > 1. Create a function called foofunc(integer). It must be large so that > its pg_proc tuple is toasted. > > 2. In one backend, run "SELECT foofunc(1)". It calls > FuncnameGetCandidates() which calls > "SearchSysCacheList1(PROCNAMEARGSNSP, CStringGetDatum(funcname));". Put > a break point in SearchCatCacheList() just after the systable_beginscan(). > > 3. In another backend, create function foofunc() with no args. > > 4. continue execution from the breakpoint. > > 5. Run "SELECT foofunc()" in the first session. It fails to find the > function. The error persists, it will fail to find that function if you > try again, until the syscache is invalidated again for some reason. > > Attached is an injection point test case to reproduce that. If you > change the test so that the function's body is shorter, so that it's not > toasted, the test passes. I'm thinking of the attached to fix this. It changes the strategy for detecting concurrent cache invalidations. Instead of the "recheck" mechanism that was introduced in commit ad98fb1422, keep a stack of "build in-progress" entries, and CatCacheInvalidate() invalidate those "in-progress" entries in addition to the actual CatCTup and CatCList entries. My first attempt was to insert the CatCTup or CatCList entry to the catcache before starting to build it, marked with a flag to indicate that the entry isn't fully built yet. But when I started to write that it got pretty invasive, and it felt easier to add another structure to hold the in-progress entries instead. (I'm not sure I got the 'volatile' markers on the local variable right in this patch; before committing this I'll need to freshen my memory on the rules on PG_TRY() and local variables again. Also, I'm not sure I want to commit the test with the injection point, but it's useful now to demonstrate the bug.) -- Heikki Linnakangas Neon (https://neon.tech)
-
Re: Recovering from detoast-related catcache invalidations
Michael Paquier <michael@paquier.xyz> — 2024-12-24T07:38:51Z
On Tue, Dec 24, 2024 at 12:18:09AM +0200, Heikki Linnakangas wrote: > My first attempt was to insert the CatCTup or CatCList entry to the catcache > before starting to build it, marked with a flag to indicate that the entry > isn't fully built yet. But when I started to write that it got pretty > invasive, and it felt easier to add another structure to hold the > in-progress entries instead. > > From ec248c69cb42a0747ecc6a63ac4e4682cce2ee93 Mon Sep 17 00:00:00 2001 > From: Heikki Linnakangas <heikki.linnakangas@iki.fi> > Date: Fri, 20 Dec 2024 18:37:44 +0200 > Subject: [PATCH 1/1] Don't allow GetTransactionSnapshot() in logical decoding It seems to me that this is not what you intended to attach for the catcache inconsistency fix? -- Michael
-
Re: Recovering from detoast-related catcache invalidations
Heikki Linnakangas <hlinnaka@iki.fi> — 2024-12-25T09:27:38Z
On 24/12/2024 09:38, Michael Paquier wrote: > On Tue, Dec 24, 2024 at 12:18:09AM +0200, Heikki Linnakangas wrote: >> My first attempt was to insert the CatCTup or CatCList entry to the catcache >> before starting to build it, marked with a flag to indicate that the entry >> isn't fully built yet. But when I started to write that it got pretty >> invasive, and it felt easier to add another structure to hold the >> in-progress entries instead. >> >> From ec248c69cb42a0747ecc6a63ac4e4682cce2ee93 Mon Sep 17 00:00:00 2001 >> From: Heikki Linnakangas <heikki.linnakangas@iki.fi> >> Date: Fri, 20 Dec 2024 18:37:44 +0200 >> Subject: [PATCH 1/1] Don't allow GetTransactionSnapshot() in logical decoding > > It seems to me that this is not what you intended to attach for the > catcache inconsistency fix? Right, sorry, here are the correct patches. -- Heikki Linnakangas Neon (https://neon.tech)
-
Re: Recovering from detoast-related catcache invalidations
Noah Misch <noah@leadboat.com> — 2025-01-07T21:56:53Z
On Tue, Dec 24, 2024 at 12:18:09AM +0200, Heikki Linnakangas wrote: > On 14/12/2024 02:06, Heikki Linnakangas wrote: > > Ok, I missed that. It does not handle the 2nd scenario though: If a new > > catalog tuple is concurrently inserted that should be part of the list, > > it is missed. > > Attached is an injection point test case to reproduce that. If you > > change the test so that the function's body is shorter, so that it's not > > toasted, the test passes. Nice discovery. > I'm thinking of the attached to fix this. It changes the strategy for > detecting concurrent cache invalidations. Instead of the "recheck" mechanism > that was introduced in commit ad98fb1422, keep a stack of "build > in-progress" entries, and CatCacheInvalidate() invalidate those > "in-progress" entries in addition to the actual CatCTup and CatCList > entries. > > My first attempt was to insert the CatCTup or CatCList entry to the catcache > before starting to build it, marked with a flag to indicate that the entry > isn't fully built yet. But when I started to write that it got pretty > invasive, and it felt easier to add another structure to hold the > in-progress entries instead. That's similar to how relcache has been doing it (in_progress_list). I see no problem applying that technique here. > not sure I want to > commit the test with the injection point, but it's useful now to demonstrate > the bug.) I'd err on the side of including it. Apart from some copied comments, the test looks ready. On Wed, Dec 25, 2024 at 11:27:38AM +0200, Heikki Linnakangas wrote: > +++ b/src/test/modules/test_misc/t/007_bugs.pl test_misc/t/007_bugs.pl could be home to almost anything. How about naming it 007_catcache_inval.pl? > @@ -744,6 +770,13 @@ ResetCatalogCache(CatCache *cache) > #endif > } > } > + > + /* Also invalidate any entries that are being built */ > + for (CatCCreating *e = catcache_creating_stack; e != NULL; e = e->next) > + { > + if (e->cache == cache) > + e->dead = true; > + } > } With debug_discard_caches=1, "make check" hangs early with some INSERT using 100% CPU. The new test file does likewise. I bet this needs a special case to short-circuit debug_discard_caches, like RelationCacheInvalidate() has. > @@ -1665,6 +1698,8 @@ SearchCatCacheList(CatCache *cache, > HeapTuple ntp; > MemoryContext oldcxt; > int i; > + volatile CatCCreating creating_list; You could drop the volatile by copying catcache_creating_stack to an additional var that you reference after longjmp, instead of referencing creating_list.next after longjmp. Likewise for the instance of volatile in CatalogCacheCreateEntry(). https://pubs.opengroup.org/onlinepubs/9799919799/functions/longjmp.html has the rules. Using volatile is fine, though. > + bool first_iter = true; This is okay as non-volatile, but it could just as easily move inside the PG_TRY. > @@ -2076,38 +2118,33 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, SysScanDesc scandesc, > + PG_TRY(); > { > - matches = equalTuple(before, ntp); > - heap_freetuple(before); > + dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc); gcc 4.8.5 warns: catcache.c: In function ‘CatalogCacheCreateEntry’: catcache.c:2159:29: warning: ‘dtp’ may be used uninitialized in this function [-Wmaybe-uninitialized] ct->tuple.t_tableOid = dtp->t_tableOid; I remember some commit of a gcc 4.x warning fix in a recent year, and we do have buildfarm representation. Consider silencing it. The rest looks good. Thanks. -
Re: Recovering from detoast-related catcache invalidations
Heikki Linnakangas <hlinnaka@iki.fi> — 2025-01-09T09:39:53Z
On 07/01/2025 23:56, Noah Misch wrote: > On Tue, Dec 24, 2024 at 12:18:09AM +0200, Heikki Linnakangas wrote: >> I'm thinking of the attached to fix this. It changes the strategy for >> detecting concurrent cache invalidations. Instead of the "recheck" mechanism >> that was introduced in commit ad98fb1422, keep a stack of "build >> in-progress" entries, and CatCacheInvalidate() invalidate those >> "in-progress" entries in addition to the actual CatCTup and CatCList >> entries. >> >> My first attempt was to insert the CatCTup or CatCList entry to the catcache >> before starting to build it, marked with a flag to indicate that the entry >> isn't fully built yet. But when I started to write that it got pretty >> invasive, and it felt easier to add another structure to hold the >> in-progress entries instead. > > That's similar to how relcache has been doing it (in_progress_list). I see no > problem applying that technique here. Oh thanks, I didn't notice that in relcache.c. I adjusted the naming and comments in the new catcache.c code to be a little closer to the relcache.c version, just to make it a bit more consistent. The main difference is that relcache.c uses an array and an end-of-xact callback to reset the array on error, while my implementation uses a linked list of entries allocated on stack and PG_TRY-CATCH for error cleanup. I considered adopting relcache.c's approach for the sake of consistency, but decided to keep my original approach in the end. Some of the code in catcache.c already had a suitable PG_TRY-CATCH block and I didn't want to add a new end-of-xact callback just for this. >> not sure I want to >> commit the test with the injection point, but it's useful now to demonstrate >> the bug.) > > I'd err on the side of including it. Apart from some copied comments, the > test looks ready. Ok, I cleaned up the comments. > On Wed, Dec 25, 2024 at 11:27:38AM +0200, Heikki Linnakangas wrote: >> +++ b/src/test/modules/test_misc/t/007_bugs.pl > > test_misc/t/007_bugs.pl could be home to almost anything. How about naming it > 007_catcache_inval.pl? Renamed >> @@ -744,6 +770,13 @@ ResetCatalogCache(CatCache *cache) >> #endif >> } >> } >> + >> + /* Also invalidate any entries that are being built */ >> + for (CatCCreating *e = catcache_creating_stack; e != NULL; e = e->next) >> + { >> + if (e->cache == cache) >> + e->dead = true; >> + } >> } > > With debug_discard_caches=1, "make check" hangs early with some INSERT using > 100% CPU. The new test file does likewise. I bet this needs a special case > to short-circuit debug_discard_caches, like RelationCacheInvalidate() has. Added >> @@ -1665,6 +1698,8 @@ SearchCatCacheList(CatCache *cache, >> HeapTuple ntp; >> MemoryContext oldcxt; >> int i; >> + volatile CatCCreating creating_list; > > You could drop the volatile by copying catcache_creating_stack to an > additional var that you reference after longjmp, instead of referencing > creating_list.next after longjmp. Likewise for the instance of volatile in > CatalogCacheCreateEntry(). > https://pubs.opengroup.org/onlinepubs/9799919799/functions/longjmp.html has > the rules. Using volatile is fine, though. Thanks for the tips. I used the additional var, it seems a little nicer. >> + bool first_iter = true; > > This is okay as non-volatile, but it could just as easily move inside the > PG_TRY. Done >> @@ -2076,38 +2118,33 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, SysScanDesc scandesc, > >> + PG_TRY(); >> { >> - matches = equalTuple(before, ntp); >> - heap_freetuple(before); >> + dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc); > > gcc 4.8.5 warns: > > catcache.c: In function ‘CatalogCacheCreateEntry’: > catcache.c:2159:29: warning: ‘dtp’ may be used uninitialized in this function [-Wmaybe-uninitialized] > ct->tuple.t_tableOid = dtp->t_tableOid; > > I remember some commit of a gcc 4.x warning fix in a recent year, and we do > have buildfarm representation. Consider silencing it. Hmm, I guess the compiler doesn't see that it's initialized with all the PG_TRY()/CATCH() stuff. I initialized it to NULL to silence the warning. Review of this new version is much appreciated, but if I don't hear anything I'll commit and backpatch this. -- Heikki Linnakangas Neon (https://neon.tech) -
Re: Recovering from detoast-related catcache invalidations
Noah Misch <noah@leadboat.com> — 2025-01-12T01:26:47Z
On Thu, Jan 09, 2025 at 11:39:53AM +0200, Heikki Linnakangas wrote: > On 07/01/2025 23:56, Noah Misch wrote: > > On Tue, Dec 24, 2024 at 12:18:09AM +0200, Heikki Linnakangas wrote: > > > I'm thinking of the attached to fix this. It changes the strategy for > > > detecting concurrent cache invalidations. Instead of the "recheck" mechanism > > > that was introduced in commit ad98fb1422, keep a stack of "build > > > in-progress" entries, and CatCacheInvalidate() invalidate those > > > "in-progress" entries in addition to the actual CatCTup and CatCList > > > entries. > > > > > > My first attempt was to insert the CatCTup or CatCList entry to the catcache > > > before starting to build it, marked with a flag to indicate that the entry > > > isn't fully built yet. But when I started to write that it got pretty > > > invasive, and it felt easier to add another structure to hold the > > > in-progress entries instead. > > > > That's similar to how relcache has been doing it (in_progress_list). I see no > > problem applying that technique here. > > Oh thanks, I didn't notice that in relcache.c. I adjusted the naming and > comments in the new catcache.c code to be a little closer to the relcache.c > version, just to make it a bit more consistent. > > The main difference is that relcache.c uses an array and an end-of-xact > callback to reset the array on error, while my implementation uses a linked > list of entries allocated on stack and PG_TRY-CATCH for error cleanup. I > considered adopting relcache.c's approach for the sake of consistency, but > decided to keep my original approach in the end. Some of the code in > catcache.c already had a suitable PG_TRY-CATCH block and I didn't want to > add a new end-of-xact callback just for this. That difference is reasonable. catcache will add PG_TRY overhead only in the HEAP_HASEXTERNAL case, which is not a normal benchmark situation. If relcache were to adopt the PG_TRY approach, it would be harder to rule out the significance of the overhead. So a long-term state of using both designs is reasonable. It's not a mere historical accident. > > gcc 4.8.5 warns: > > > > catcache.c: In function ‘CatalogCacheCreateEntry’: > > catcache.c:2159:29: warning: ‘dtp’ may be used uninitialized in this function [-Wmaybe-uninitialized] > > ct->tuple.t_tableOid = dtp->t_tableOid; > > > > I remember some commit of a gcc 4.x warning fix in a recent year, and we do > > have buildfarm representation. Consider silencing it. > > Hmm, I guess the compiler doesn't see that it's initialized with all the > PG_TRY()/CATCH() stuff. I initialized it to NULL to silence the warning. It's warning-free now. > @@ -697,9 +725,14 @@ CreateCacheMemoryContext(void) > * > * This is not very efficient if the target cache is nearly empty. > * However, it shouldn't need to be efficient; we don't invoke it often. > + * > + * If 'debug_discard' is true, we are being called as part of > + * debug_discard_caches. In that case, the cache not reset for correctness, s/cache not/cache is not/ or similar > + PG_TRY(); > { > - matches = equalTuple(before, ntp); > - heap_freetuple(before); > + dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc); > } > - if (!matches || !systable_recheck_tuple(scandesc, ntp)) > + PG_FINALLY(); > { > - heap_freetuple(dtp); Is this an intentional removal of the heap_freetuple(dtp) before return NULL? > - return NULL; > + Assert(catcache_in_progress_stack == &in_progress_ent); > + catcache_in_progress_stack = save_in_progress; > } > + PG_END_TRY(); > + > + if (in_progress_ent.dead) > + return NULL; ... > +$node->append_conf( > + 'postgresql.conf', qq{ > +debug_discard_caches=1 > +}); I'd drop this debug_discard_caches. debug_discard_caches buildfarm runs will still test it, so let's have normal runs test the normal case. -
Re: Recovering from detoast-related catcache invalidations
Heikki Linnakangas <hlinnaka@iki.fi> — 2025-01-14T13:13:21Z
On 12/01/2025 03:26, Noah Misch wrote: > On Thu, Jan 09, 2025 at 11:39:53AM +0200, Heikki Linnakangas wrote: >> On 07/01/2025 23:56, Noah Misch wrote: >> @@ -697,9 +725,14 @@ CreateCacheMemoryContext(void) >> * >> * This is not very efficient if the target cache is nearly empty. >> * However, it shouldn't need to be efficient; we don't invoke it often. >> + * >> + * If 'debug_discard' is true, we are being called as part of >> + * debug_discard_caches. In that case, the cache not reset for correctness, > > s/cache not/cache is not/ or similar fixed >> + PG_TRY(); >> { >> - matches = equalTuple(before, ntp); >> - heap_freetuple(before); >> + dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc); >> } >> - if (!matches || !systable_recheck_tuple(scandesc, ntp)) >> + PG_FINALLY(); >> { >> - heap_freetuple(dtp); > > Is this an intentional removal of the heap_freetuple(dtp) before return NULL? No, that was a mistake. Fixed. >> - return NULL; >> + Assert(catcache_in_progress_stack == &in_progress_ent); >> + catcache_in_progress_stack = save_in_progress; >> } >> + PG_END_TRY(); >> + >> + if (in_progress_ent.dead) >> + return NULL; > ... >> +$node->append_conf( >> + 'postgresql.conf', qq{ >> +debug_discard_caches=1 >> +}); > > I'd drop this debug_discard_caches. debug_discard_caches buildfarm runs will > still test it, so let's have normal runs test the normal case. Ah yes, I didn't mean to include that; removed. Committed with those fixes. Thanks for the review! -- Heikki Linnakangas Neon (https://neon.tech) -
Re: Recovering from detoast-related catcache invalidations
Andres Freund <andres@anarazel.de> — 2025-03-26T11:21:43Z
Hi, On 2025-01-14 15:13:21 +0200, Heikki Linnakangas wrote: > Committed with those fixes. Thanks for the review! The test doesn't seem entirely stable. E.g. https://cirrus-ci.com/task/6166374147424256 failed spuriously: [08:52:06.822](0.002s) # issuing query 1 via background psql: # SELECT injection_points_set_local(); # SELECT injection_points_attach('catcache-list-miss-systable-scan-started', 'wait'); [08:52:06.851](0.029s) # results query 1: # { # 'stderr' => 'background_psql: QUERY_SEPARATOR 1: # ', # 'stdout' => ' # # background_psql: QUERY_SEPARATOR 1: # ' # } [08:52:06.893](0.042s) # issuing query 1 via background psql: # SELECT injection_points_wakeup('catcache-list-miss-systable-scan-started'); # SELECT injection_points_detach('catcache-list-miss-systable-scan-started'); [08:52:06.897](0.004s) # pump_until: process terminated unexpectedly when searching for "(?^:(^|\n)background_psql: QUERY_SEPARATOR 1:\r?\n)" with stream: "" process ended prematurely at /tmp/cirrus-ci-build/src/test/perl/PostgreSQL/Test/Utils.pm line 440. 2025-03-25 08:52:06.896 UTC [34240][client backend] [007_catcache_inval.pl][4/2:0] ERROR: could not find injection point catcache-list-miss-systable-scan-started to wake up 2025-03-25 08:52:06.896 UTC [34240][client backend] [007_catcache_inval.pl][4/2:0] STATEMENT: SELECT injection_points_wakeup('catcache-list-miss-systable-scan-started'); Greetings, Andres Freund -
Re: Recovering from detoast-related catcache invalidations
Andres Freund <andres@anarazel.de> — 2025-09-19T20:11:43Z
On 2025-03-26 07:21:43 -0400, Andres Freund wrote: > On 2025-01-14 15:13:21 +0200, Heikki Linnakangas wrote: > > Committed with those fixes. Thanks for the review! > > The test doesn't seem entirely stable. E.g. > https://cirrus-ci.com/task/6166374147424256 > failed spuriously: > > [08:52:06.822](0.002s) # issuing query 1 via background psql: > # SELECT injection_points_set_local(); > # SELECT injection_points_attach('catcache-list-miss-systable-scan-started', 'wait'); > [08:52:06.851](0.029s) # results query 1: > # { > # 'stderr' => 'background_psql: QUERY_SEPARATOR 1: > # ', > # 'stdout' => ' > # > # background_psql: QUERY_SEPARATOR 1: > # ' > # } > [08:52:06.893](0.042s) # issuing query 1 via background psql: > # SELECT injection_points_wakeup('catcache-list-miss-systable-scan-started'); > # SELECT injection_points_detach('catcache-list-miss-systable-scan-started'); > [08:52:06.897](0.004s) # pump_until: process terminated unexpectedly when searching for "(?^:(^|\n)background_psql: QUERY_SEPARATOR 1:\r?\n)" with stream: "" > process ended prematurely at /tmp/cirrus-ci-build/src/test/perl/PostgreSQL/Test/Utils.pm line 440. > > > 2025-03-25 08:52:06.896 UTC [34240][client backend] [007_catcache_inval.pl][4/2:0] ERROR: could not find injection point catcache-list-miss-systable-scan-started to wake up > 2025-03-25 08:52:06.896 UTC [34240][client backend] [007_catcache_inval.pl][4/2:0] STATEMENT: SELECT injection_points_wakeup('catcache-list-miss-systable-scan-started'); And again: https://cirrus-ci.com/task/6082321633247232 Ping? -
Re: Recovering from detoast-related catcache invalidations
Arseniy Mukhin <arseniy.mukhin.dev@gmail.com> — 2025-09-29T16:34:47Z
Hi, On Fri, Sep 19, 2025 at 11:11 PM Andres Freund <andres@anarazel.de> wrote: > > On 2025-03-26 07:21:43 -0400, Andres Freund wrote: > > On 2025-01-14 15:13:21 +0200, Heikki Linnakangas wrote: > > > Committed with those fixes. Thanks for the review! > > > > The test doesn't seem entirely stable. E.g. > > https://cirrus-ci.com/task/6166374147424256 > > failed spuriously: > > > > [08:52:06.822](0.002s) # issuing query 1 via background psql: > > # SELECT injection_points_set_local(); > > # SELECT injection_points_attach('catcache-list-miss-systable-scan-started', 'wait'); > > [08:52:06.851](0.029s) # results query 1: > > # { > > # 'stderr' => 'background_psql: QUERY_SEPARATOR 1: > > # ', > > # 'stdout' => ' > > # > > # background_psql: QUERY_SEPARATOR 1: > > # ' > > # } > > [08:52:06.893](0.042s) # issuing query 1 via background psql: > > # SELECT injection_points_wakeup('catcache-list-miss-systable-scan-started'); > > # SELECT injection_points_detach('catcache-list-miss-systable-scan-started'); > > [08:52:06.897](0.004s) # pump_until: process terminated unexpectedly when searching for "(?^:(^|\n)background_psql: QUERY_SEPARATOR 1:\r?\n)" with stream: "" > > process ended prematurely at /tmp/cirrus-ci-build/src/test/perl/PostgreSQL/Test/Utils.pm line 440. > > > > > > 2025-03-25 08:52:06.896 UTC [34240][client backend] [007_catcache_inval.pl][4/2:0] ERROR: could not find injection point catcache-list-miss-systable-scan-started to wake up > > 2025-03-25 08:52:06.896 UTC [34240][client backend] [007_catcache_inval.pl][4/2:0] STATEMENT: SELECT injection_points_wakeup('catcache-list-miss-systable-scan-started'); > > And again: https://cirrus-ci.com/task/6082321633247232 > > Ping? > The wait_for_event call, which is typically used with a wait injection point, is missing. Could this be the cause of instability? If this makes sense, please find the attached fix. Best regards, Arseniy Mukhin -
Re: Recovering from detoast-related catcache invalidations
Andres Freund <andres@anarazel.de> — 2025-12-18T19:07:00Z
Hi, On 2025-09-29 19:34:47 +0300, Arseniy Mukhin wrote: > On Fri, Sep 19, 2025 at 11:11 PM Andres Freund <andres@anarazel.de> wrote: > > > > On 2025-03-26 07:21:43 -0400, Andres Freund wrote: > > > On 2025-01-14 15:13:21 +0200, Heikki Linnakangas wrote: > > > > Committed with those fixes. Thanks for the review! > > > > > > The test doesn't seem entirely stable. E.g. > > > https://cirrus-ci.com/task/6166374147424256 > > > failed spuriously: > > > > > > [08:52:06.822](0.002s) # issuing query 1 via background psql: > > > # SELECT injection_points_set_local(); > > > # SELECT injection_points_attach('catcache-list-miss-systable-scan-started', 'wait'); > > > [08:52:06.851](0.029s) # results query 1: > > > # { > > > # 'stderr' => 'background_psql: QUERY_SEPARATOR 1: > > > # ', > > > # 'stdout' => ' > > > # > > > # background_psql: QUERY_SEPARATOR 1: > > > # ' > > > # } > > > [08:52:06.893](0.042s) # issuing query 1 via background psql: > > > # SELECT injection_points_wakeup('catcache-list-miss-systable-scan-started'); > > > # SELECT injection_points_detach('catcache-list-miss-systable-scan-started'); > > > [08:52:06.897](0.004s) # pump_until: process terminated unexpectedly when searching for "(?^:(^|\n)background_psql: QUERY_SEPARATOR 1:\r?\n)" with stream: "" > > > process ended prematurely at /tmp/cirrus-ci-build/src/test/perl/PostgreSQL/Test/Utils.pm line 440. > > > > > > > > > 2025-03-25 08:52:06.896 UTC [34240][client backend] [007_catcache_inval.pl][4/2:0] ERROR: could not find injection point catcache-list-miss-systable-scan-started to wake up > > > 2025-03-25 08:52:06.896 UTC [34240][client backend] [007_catcache_inval.pl][4/2:0] STATEMENT: SELECT injection_points_wakeup('catcache-list-miss-systable-scan-started'); > > > > And again: https://cirrus-ci.com/task/6082321633247232 > > > > Ping? > > > > The wait_for_event call, which is typically used with a wait injection > point, is missing. Could this be the cause of instability? If this > makes sense, please find the attached fix. I was just reminded of this thread because I saw the failure again: https://cirrus-ci.com/task/5859971612540928 (it's unrelated to the patch) I think you might be right - the wait point might not yet have been reached, because the query_until() just waits for "starting_bg_psql" being printed by \echo starting_bg_psql SELECT foofunc(1); while the wait point is only hit during the "SELECT foofunc(1)'. There's no guarantee that we will have reached the wait point by this point. I found that I can reproduce the issue with --- i/src/test/modules/test_misc/t/007_catcache_inval.pl +++ w/src/test/modules/test_misc/t/007_catcache_inval.pl @@ -53,6 +53,7 @@ my $psql_session2 = $node->background_psql('postgres'); # catcache list $psql_session->query_safe( qq[ + SELECT pg_sleep(0.1); SELECT injection_points_set_local(); SELECT injection_points_attach('catcache-list-miss-systable-scan-started', 'wait'); ]); @@ -62,6 +63,7 @@ $psql_session->query_safe( $psql_session->query_until( qr/starting_bg_psql/, q( \echo starting_bg_psql + SELECT pg_sleep(3); SELECT foofunc(1); )); (the first SELECT just is there to later avoid hitting the injection point, by already having loaded the cache entry for pg_sleep). And indeed your patch fixes that. Greetings, Andres Freund