From ee4157034aa359ca90e95b69b772ccfb70a633d6 Mon Sep 17 00:00:00 2001 From: John Tennant Date: Mon, 31 Aug 2026 22:34:20 -0400 Subject: [PATCH] fix(projects): serialize channel state authority Signed-off-by: John Tennant --- crates/buzz-db/src/store/project_revision.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/buzz-db/src/store/project_revision.rs b/crates/buzz-db/src/store/project_revision.rs index 34a4c91eab..ba10af9d79 100644 --- a/crates/buzz-db/src/store/project_revision.rs +++ b/crates/buzz-db/src/store/project_revision.rs @@ -167,16 +167,18 @@ impl Db { let role = if actor.as_slice() == owner.as_slice() { None } else if let Some(home_channel_id) = home { - // Serialize the authorization read with roster mutations. Without - // the shared membership lock, an admin removal or demotion could - // commit while this transaction still acts on the stale role. + // Serialize the authorization read with roster mutations. The + // channel-row share lock also serializes archive/deletion. Without + // both, a revocation could commit while this transaction still + // acts on stale authority. acquire_channel_membership_lock(&mut tx, community_id, home_channel_id).await?; sqlx::query_scalar::<_, String>( "SELECT cm.role::text FROM channel_members cm \ JOIN channels c ON c.community_id=cm.community_id AND c.id=cm.channel_id \ WHERE cm.community_id=$1 AND cm.channel_id=$2 AND cm.pubkey=$3 \ AND cm.removed_at IS NULL AND c.deleted_at IS NULL \ - AND c.archived_at IS NULL", + AND c.archived_at IS NULL \ + FOR SHARE OF c", ) .bind(community_id.as_uuid()) .bind(home_channel_id)