From 60644db1e8f6e6169b7d281a1c8e825bfcbefe58 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Sun, 17 May 2026 17:38:47 -0500 Subject: [PATCH] Make schema-creation DDL concurrent-safe against pg_namespace race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The migrator's WriteSql block emitted "IF NOT EXISTS(information_schema.schemata) THEN EXECUTE 'CREATE SCHEMA …'", which is not atomic against concurrent sessions: two sessions can both pass the existence check, then race on the insert into pg_namespace and one loses with "23505 duplicate key value violates unique constraint pg_namespace_nspname_index". PostgreSQL's own "CREATE SCHEMA IF NOT EXISTS" has the same window. Wrap the create in a plpgsql sub-block that catches duplicate_schema (42P06) and unique_violation (23505) specifically, so the losing session treats the race as benign; any other error still propagates. Tracked downstream as Marten flake: JasperFx/marten#4445. --- src/Weasel.Postgresql/PostgresqlMigrator.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Weasel.Postgresql/PostgresqlMigrator.cs b/src/Weasel.Postgresql/PostgresqlMigrator.cs index 48525eca..06feb08e 100644 --- a/src/Weasel.Postgresql/PostgresqlMigrator.cs +++ b/src/Weasel.Postgresql/PostgresqlMigrator.cs @@ -94,16 +94,20 @@ public override void WriteSchemaDropSql(IEnumerable schemaNames, TextWri private static void WriteSql(string databaseSchemaName, TextWriter writer) { + // Neither the "IF NOT EXISTS(information_schema.schemata) THEN EXECUTE 'CREATE SCHEMA'" + // pattern nor PostgreSQL's own "CREATE SCHEMA IF NOT EXISTS" is concurrent-safe — both + // can have two sessions pass the existence check then race on the insert into pg_namespace, + // surfacing as "23505 duplicate key value violates unique constraint pg_namespace_nspname_index" + // / "42P06 schema X already exists". Wrap the create in a sub-block that swallows those two + // race exceptions specifically; any other error still propagates. writer.WriteLine( $""" - IF NOT EXISTS( - SELECT schema_name - FROM information_schema.schemata - WHERE schema_name = '{databaseSchemaName}' - ) - THEN - EXECUTE 'CREATE SCHEMA {PostgresqlProvider.Instance.ToQualifiedName(databaseSchemaName)}'; - END IF; + BEGIN + EXECUTE 'CREATE SCHEMA IF NOT EXISTS {PostgresqlProvider.Instance.ToQualifiedName(databaseSchemaName)}'; + EXCEPTION + WHEN duplicate_schema THEN NULL; + WHEN unique_violation THEN NULL; + END; """); }