On Fri, Jul 17, 2026 at 12:00:01PM +0300, Alexander Lakhin wrote:
> (Assuming the attached OOM injection patch applied.)
> 1) An issue in generate_partition_qual():
> --- a/src/backend/utils/cache/partcache.c
> +++ b/src/backend/utils/cache/partcache.c
> @@ -419,3 +419,5 @@ generate_partition_qual(Relation rel)
>          oldcxt = MemoryContextSwitchTo(rel->rd_partcheckcxt);
> +oom_prob = 0.1;
>          rel->rd_partcheck = copyObject(result);
> +oom_prob = 0;
>          MemoryContextSwitchTo(oldcxt);

This one is again an ordering problem: let's assign rd_partcheckcxt
and rd_partcheck once we are sure that all the allocations have
succeeded, so as we don't finish with a semi-allocated transient
state.

> 2) lookup_type_cache():
> --- a/src/backend/utils/cache/typcache.c
> +++ b/src/backend/utils/cache/typcache.c
> @@ -463,8 +463,10 @@ lookup_type_cache(Oid type_id, int flags)
>      in_progress_list[in_progress_offset] = type_id;
> 
> +oom_prob = 0.1;
>      /* Try to look up an existing entry */
>      typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
>                                                &type_id,
>                                                HASH_FIND, NULL);
> +oom_prob = 0;
> 
> triggers
> TRAP: failed Assert("cache->cc_tupdesc != NULL"), File:
> "catcache.c", Line: 1112, PID: 43932

This one is tricky.  That's obviously due to the fact that we store a
type ID in the in_progress_list and on OOM we finish with a stale
entry in the list, no cleaned up.  My first impression was: could we
delay the registration of the type ID in the in_progress_list?  But
the answer to that is no; we want it from the point where we to the
lookups in the typcache hash table to protect from concurrent
invalidation.  So I am not sure what to do here yet.

> 3) pgstat_prep_pending_entry() / pgstat_write_statsfiles():
> --- a/src/backend/utils/activity/pgstat.c
> +++ b/src/backend/utils/activity/pgstat.c
> @@ -1333,3 +1333,5 @@ pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, 
> uint64 objid, bool *creat
> 
> +oom_prob = 0.005;
>          entry_ref->pending = MemoryContextAllocZero(pgStatPendingContext, 
> entrysize);
> +oom_prob = 0;
>          dlist_push_tail(&pgStatPending, &entry_ref->pending_node);
> 
> triggers
> TRAP: failed Assert("!ps->dropped"), File: "pgstat.c", Line: 1730, PID: 844901
> maybe this Assert can be removed, because there is no corresponding

Yes, perhaps we should do that.  I am not completely sure if this is
the best thing to do, though.

> 4) make_new_heap():
> --- a/src/backend/commands/repack.c
> +++ b/src/backend/commands/repack.c
> @@ -1255,3 +1255,5 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid 
> NewAccessMethod,
>       */
> +oom_prob = 0.01;
>      CommandCounterIncrement();
> +oom_prob = 0;
> 
> triggers:
> TRAP: failed Assert("ItemIdIsNormal(lp)"), File: "heapam.c", Line: 2813, PID: 
> 943626
> also:
> ERROR:  attempted to delete invisible tuple

I'll let Alvaro comment on this one.  It deserves an open item.

For now, I have 1 out of 4, the partcache one.  25% is still better
than 0%.  :p
--
Michael
diff --git a/src/backend/utils/cache/partcache.c 
b/src/backend/utils/cache/partcache.c
index 3107075c9ad6..b25751b3793f 100644
--- a/src/backend/utils/cache/partcache.c
+++ b/src/backend/utils/cache/partcache.c
@@ -404,21 +404,28 @@ generate_partition_qual(Relation rel)
        /*
         * Save a copy in the relcache.  The order of these operations is fairly
         * critical to avoid memory leaks and ensure that we don't leave a 
corrupt
-        * relcache entry if we fail partway through copyObject.
+        * relcache entry if we fail partway through copyObject.  
rd_partcheckcxt
+        * and rd_partcheck are assigned last, after copyObject() succeeds.
         *
         * If, as is definitely possible, the partcheck list is NIL, then we do
         * not need to make a context to hold it.
         */
        if (result != NIL)
        {
-               rel->rd_partcheckcxt = AllocSetContextCreate(CacheMemoryContext,
-                                                                               
                         "partition constraint",
-                                                                               
                         ALLOCSET_SMALL_SIZES);
-               MemoryContextCopyAndSetIdentifier(rel->rd_partcheckcxt,
+               MemoryContext partcheckcxt;
+               List       *partcheck;
+
+               partcheckcxt = AllocSetContextCreate(CacheMemoryContext,
+                                                                               
         "partition constraint",
+                                                                               
         ALLOCSET_SMALL_SIZES);
+               MemoryContextCopyAndSetIdentifier(partcheckcxt,
                                                                                
  RelationGetRelationName(rel));
-               oldcxt = MemoryContextSwitchTo(rel->rd_partcheckcxt);
-               rel->rd_partcheck = copyObject(result);
+               oldcxt = MemoryContextSwitchTo(partcheckcxt);
+               partcheck = copyObject(result);
                MemoryContextSwitchTo(oldcxt);
+
+               rel->rd_partcheckcxt = partcheckcxt;
+               rel->rd_partcheck = partcheck;
        }
        else
                rel->rd_partcheck = NIL;

Attachment: signature.asc
Description: PGP signature

Reply via email to