From 6eb6ab1a1edd9fd809cff1ede374b6b17c791c78 Mon Sep 17 00:00:00 2001 From: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 Date: Sun, 12 Jul 2026 15:58:01 -0400 Subject: [PATCH] test(search): serialize pgcrypto install to fix parallel-setup race fts_integration.rs setup() applies migration 0001, which contains CREATE EXTENSION IF NOT EXISTS pgcrypto. pg_extension is database- global with a unique index on extname; IF NOT EXISTS is a check-then- insert, not an atomic upsert. When 18 tests run in parallel, the first wave all see the extension as absent, all attempt the insert, and all but one hit SQLSTATE 23505 (duplicate key on pg_extension_name_index). Fix: take a session-level pg_advisory_lock on the admin connection before the extension create, release it after. This serializes the install across all parallel test workers so exactly one does the real CREATE; the rest treat it as a no-op on lock acquisition. The advisory lock is released automatically when the session ends. Co-authored-by: Will Pfleger Signed-off-by: Will Pfleger --- crates/buzz-search/tests/fts_integration.rs | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/buzz-search/tests/fts_integration.rs b/crates/buzz-search/tests/fts_integration.rs index 03052c2a5..55d698abf 100644 --- a/crates/buzz-search/tests/fts_integration.rs +++ b/crates/buzz-search/tests/fts_integration.rs @@ -38,6 +38,31 @@ async fn setup() -> (PgPool, String) { .connect(&url) .await .expect("connect"); + + // Serialize the pgcrypto extension install across all parallel test workers. + // + // `CREATE EXTENSION IF NOT EXISTS pgcrypto` targets `pg_extension`, which is + // database-global with a unique index on `extname`. PostgreSQL's IF NOT EXISTS + // is a check-then-insert, not an atomic upsert: when many test workers run + // concurrently they all see the extension as absent, all attempt the insert, + // and all but one hit SQLSTATE 23505 (unique violation). A session-level + // advisory lock (arbitrary but stable key) serializes the install so that + // exactly one worker does the real CREATE; the rest treat it as a no-op when + // they enter the lock. The lock is released automatically when the session + // ends, so there is no risk of wedging subsequent runs. + sqlx::query("SELECT pg_advisory_lock(3723742987654321)") + .execute(&admin_pool) + .await + .expect("acquire pgcrypto advisory lock"); + sqlx::query("CREATE EXTENSION IF NOT EXISTS pgcrypto") + .execute(&admin_pool) + .await + .expect("install pgcrypto extension"); + sqlx::query("SELECT pg_advisory_unlock(3723742987654321)") + .execute(&admin_pool) + .await + .expect("release pgcrypto advisory lock"); + let create_sql = format!("CREATE SCHEMA \"{schema}\""); sqlx::query(sqlx::AssertSqlSafe(create_sql)) .execute(&admin_pool)