From 85d621c791efaad1245c1aec8e5185a1eb78c7b9 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Wed, 18 Feb 2026 09:24:10 +0000 Subject: fix: break circular deadlock in sync loop by including purgatory in URL lookup The sync loop calls fetch_repository_data() to get clone URLs so it knows where to fetch git data from. Previously this only queried the database, which means an announcement still in purgatory (no git data yet) would return no clone URLs, so the sync loop could never fetch the git data needed to promote the announcement - a circular deadlock. Fix by switching to fetch_repository_data_with_purgatory() which combines database announcements with purgatory announcements. Update the trait method's doc comment to document this behaviour. The mock implementation in tests is unaffected since it returns pre-configured data rather than delegating to either function. --- src/purgatory/sync/context.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/purgatory/sync/context.rs b/src/purgatory/sync/context.rs index 33c2d12..3568e89 100644 --- a/src/purgatory/sync/context.rs +++ b/src/purgatory/sync/context.rs @@ -75,7 +75,12 @@ pub trait SyncContext: Send + Sync { /// # Returns /// Set of clone URLs from PR events in purgatory for this identifier fn collect_pr_clone_urls(&self, identifier: &str) -> HashSet; - /// Get repository data (announcements, clone URLs, etc.) from the database. + /// Get repository data (announcements, clone URLs, etc.) from the database and purgatory. + /// + /// Checks both the database (promoted announcements) and purgatory (announcements + /// awaiting git data). This is necessary to obtain clone URLs when an announcement + /// has not yet been promoted - without purgatory data, the sync loop would have no + /// URLs to fetch from and the announcement could never be promoted (circular deadlock). /// /// # Arguments /// * `identifier` - The repository identifier (d-tag value) @@ -279,7 +284,16 @@ impl SyncContext for RealSyncContext { } async fn fetch_repository_data(&self, identifier: &str) -> Result { - crate::git::authorization::fetch_repository_data(&self.database, identifier).await + // Use the purgatory-aware variant so that clone URLs from announcements still + // in purgatory (not yet promoted) are available. Without this, the sync loop + // would find no URLs to fetch from and the announcement could never be promoted + // (circular deadlock: can't promote without git data, can't get git data without URLs). + crate::git::authorization::fetch_repository_data_with_purgatory( + &self.database, + &self.purgatory, + identifier, + ) + .await } fn collect_needed_oids(&self, identifier: &str) -> HashSet { -- cgit v1.2.3