Do not allocate anything in subtransaction memory context (#12176)

## Problem

See https://github.com/neondatabase/neon/issues/12173

## Summary of changes

Allocate table in TopTransactionMemoryContext

---------

Co-authored-by: Konstantin Knizhnik <knizhnik@neon.tech>
This commit is contained in:
Konstantin Knizhnik
2025-07-04 13:24:39 +03:00
committed by GitHub
parent cc699f6f85
commit 436a117c15
4 changed files with 80 additions and 12 deletions

View File

@@ -98,12 +98,14 @@ typedef struct
typedef struct DdlHashTable
{
struct DdlHashTable *prev_table;
size_t subtrans_level;
HTAB *db_table;
HTAB *role_table;
} DdlHashTable;
static DdlHashTable RootTable;
static DdlHashTable *CurrentDdlTable = &RootTable;
static int SubtransLevel; /* current nesting level of subtransactions */
static void
PushKeyValue(JsonbParseState **state, char *key, char *value)
@@ -332,9 +334,25 @@ SendDeltasToControlPlane()
}
}
static void
InitCurrentDdlTableIfNeeded()
{
/* Lazy construction of DllHashTable chain */
if (SubtransLevel > CurrentDdlTable->subtrans_level)
{
DdlHashTable *new_table = MemoryContextAlloc(CurTransactionContext, sizeof(DdlHashTable));
new_table->prev_table = CurrentDdlTable;
new_table->subtrans_level = SubtransLevel;
new_table->role_table = NULL;
new_table->db_table = NULL;
CurrentDdlTable = new_table;
}
}
static void
InitDbTableIfNeeded()
{
InitCurrentDdlTableIfNeeded();
if (!CurrentDdlTable->db_table)
{
HASHCTL db_ctl = {};
@@ -353,6 +371,7 @@ InitDbTableIfNeeded()
static void
InitRoleTableIfNeeded()
{
InitCurrentDdlTableIfNeeded();
if (!CurrentDdlTable->role_table)
{
HASHCTL role_ctl = {};
@@ -371,19 +390,21 @@ InitRoleTableIfNeeded()
static void
PushTable()
{
DdlHashTable *new_table = MemoryContextAlloc(CurTransactionContext, sizeof(DdlHashTable));
new_table->prev_table = CurrentDdlTable;
new_table->role_table = NULL;
new_table->db_table = NULL;
CurrentDdlTable = new_table;
SubtransLevel += 1;
}
static void
MergeTable()
{
DdlHashTable *old_table = CurrentDdlTable;
DdlHashTable *old_table;
Assert(SubtransLevel >= CurrentDdlTable->subtrans_level);
if (--SubtransLevel >= CurrentDdlTable->subtrans_level)
{
return;
}
old_table = CurrentDdlTable;
CurrentDdlTable = old_table->prev_table;
if (old_table->db_table)
@@ -476,11 +497,15 @@ MergeTable()
static void
PopTable()
{
/*
* Current table gets freed because it is allocated in aborted
* subtransaction's memory context.
*/
CurrentDdlTable = CurrentDdlTable->prev_table;
Assert(SubtransLevel >= CurrentDdlTable->subtrans_level);
if (--SubtransLevel < CurrentDdlTable->subtrans_level)
{
/*
* Current table gets freed because it is allocated in aborted
* subtransaction's memory context.
*/
CurrentDdlTable = CurrentDdlTable->prev_table;
}
}
static void

View File

@@ -0,0 +1,21 @@
DO $$
DECLARE
i numeric;
BEGIN
create role somebody;
FOR i IN 1..1000000 LOOP
BEGIN
IF i % 1000 = 0 THEN
alter role somebody password 'welcome';
ELSE
PERFORM 1;
END IF;
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'error';
END;
IF I = 1000000 THEN
PERFORM pg_log_backend_memory_contexts(pg_backend_pid());
END IF;
END LOOP;
END;
$$;

View File

@@ -10,3 +10,4 @@ test: neon-clog
test: neon-test-utils
test: neon-vacuum-full
test: neon-event-triggers
test: neon-subxacts

View File

@@ -0,0 +1,21 @@
DO $$
DECLARE
i numeric;
BEGIN
create role somebody;
FOR i IN 1..1000000 LOOP
BEGIN
IF i % 1000 = 0 THEN
alter role somebody password 'welcome';
ELSE
PERFORM 1;
END IF;
EXCEPTION WHEN OTHERS THEN
RAISE WARNING 'error';
END;
IF I = 1000000 THEN
PERFORM pg_log_backend_memory_contexts(pg_backend_pid());
END IF;
END LOOP;
END;
$$;