diff options
| -rw-r--r-- | src/purgatory/sync/context.rs | 7 | ||||
| -rw-r--r-- | src/sync/mod.rs | 68 | ||||
| -rw-r--r-- | tests/archive_read_only.rs | 1 |
3 files changed, 4 insertions, 72 deletions
diff --git a/src/purgatory/sync/context.rs b/src/purgatory/sync/context.rs index 778cdb8..33c2d12 100644 --- a/src/purgatory/sync/context.rs +++ b/src/purgatory/sync/context.rs | |||
| @@ -279,12 +279,7 @@ impl SyncContext for RealSyncContext { | |||
| 279 | } | 279 | } |
| 280 | 280 | ||
| 281 | async fn fetch_repository_data(&self, identifier: &str) -> Result<RepositoryData> { | 281 | async fn fetch_repository_data(&self, identifier: &str) -> Result<RepositoryData> { |
| 282 | crate::git::authorization::fetch_repository_data_with_purgatory( | 282 | crate::git::authorization::fetch_repository_data(&self.database, identifier).await |
| 283 | &self.database, | ||
| 284 | &self.purgatory, | ||
| 285 | identifier, | ||
| 286 | ) | ||
| 287 | .await | ||
| 288 | } | 283 | } |
| 289 | 284 | ||
| 290 | fn collect_needed_oids(&self, identifier: &str) -> HashSet<String> { | 285 | fn collect_needed_oids(&self, identifier: &str) -> HashSet<String> { |
diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 872df66..1ee1872 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs | |||
| @@ -1719,50 +1719,8 @@ impl SyncManager { | |||
| 1719 | // For sync-triggered events that go to purgatory, trigger immediate sync | 1719 | // For sync-triggered events that go to purgatory, trigger immediate sync |
| 1720 | // (instead of the default 3-minute delay for user-submitted events) | 1720 | // (instead of the default 3-minute delay for user-submitted events) |
| 1721 | if result == ProcessResult::Purgatory { | 1721 | if result == ProcessResult::Purgatory { |
| 1722 | // Announcements (kind 30617) - re-process rejected state events | ||
| 1723 | // When an announcement goes to purgatory, state events that were | ||
| 1724 | // previously rejected ("no announcement exists") can now be authorized | ||
| 1725 | // via fetch_repository_data_with_purgatory. | ||
| 1726 | if event.kind == Kind::GitRepoAnnouncement { | ||
| 1727 | use crate::nostr::events::RepositoryAnnouncement; | ||
| 1728 | |||
| 1729 | if let Ok(announcement) = RepositoryAnnouncement::from_event((*event).clone()) { | ||
| 1730 | // Re-process rejected state events for this announcement | ||
| 1731 | let (removed, hot_events) = rejected_events_index.invalidate_and_get( | ||
| 1732 | &event.pubkey, | ||
| 1733 | &announcement.identifier, | ||
| 1734 | Some(rejected_index::EventType::State), | ||
| 1735 | ); | ||
| 1736 | |||
| 1737 | if removed > 0 { | ||
| 1738 | tracing::info!( | ||
| 1739 | pubkey = %event.pubkey, | ||
| 1740 | identifier = %announcement.identifier, | ||
| 1741 | removed_from_cold_index = removed, | ||
| 1742 | hot_cache_events = hot_events.len(), | ||
| 1743 | "Invalidated rejected state events (announcement now in purgatory)" | ||
| 1744 | ); | ||
| 1745 | } | ||
| 1746 | |||
| 1747 | // Re-process state events from hot cache immediately | ||
| 1748 | if !hot_events.is_empty() { | ||
| 1749 | let _stats = Self::reprocess_events_from_hot_cache( | ||
| 1750 | hot_events, | ||
| 1751 | "state event (announcement in purgatory)", | ||
| 1752 | &event.pubkey, | ||
| 1753 | &announcement.identifier, | ||
| 1754 | &relay_url_clone, | ||
| 1755 | &database, | ||
| 1756 | &write_policy, | ||
| 1757 | &local_relay, | ||
| 1758 | &rejected_events_index, | ||
| 1759 | ) | ||
| 1760 | .await; | ||
| 1761 | } | ||
| 1762 | } | ||
| 1763 | } | ||
| 1764 | // State events (kind 30618) - extract identifier and trigger immediate sync | 1722 | // State events (kind 30618) - extract identifier and trigger immediate sync |
| 1765 | else if event.kind.as_u16() == 30618 { | 1723 | if event.kind.as_u16() == 30618 { |
| 1766 | if let Some(identifier) = event.tags.iter().find_map(|tag| { | 1724 | if let Some(identifier) = event.tags.iter().find_map(|tag| { |
| 1767 | let tag_vec = tag.clone().to_vec(); | 1725 | let tag_vec = tag.clone().to_vec(); |
| 1768 | if tag_vec.len() >= 2 && tag_vec[0] == "d" { | 1726 | if tag_vec.len() >= 2 && tag_vec[0] == "d" { |
| @@ -1796,9 +1754,7 @@ impl SyncManager { | |||
| 1796 | 1754 | ||
| 1797 | // Track pagination state for this subscription (REQ+EOSE) | 1755 | // Track pagination state for this subscription (REQ+EOSE) |
| 1798 | // and received event IDs for negentropy batches | 1756 | // and received event IDs for negentropy batches |
| 1799 | // Include Purgatory results so announcements in purgatory still trigger | 1757 | if result == ProcessResult::Saved || result == ProcessResult::Duplicate { |
| 1800 | // per-repo sync (state events, PR events) from the source relay. | ||
| 1801 | if result == ProcessResult::Saved || result == ProcessResult::Duplicate || result == ProcessResult::Purgatory { | ||
| 1802 | let mut pending = pending_sync_index.write().await; | 1758 | let mut pending = pending_sync_index.write().await; |
| 1803 | if let Some(batches) = pending.get_mut(&relay_url_clone) { | 1759 | if let Some(batches) = pending.get_mut(&relay_url_clone) { |
| 1804 | for batch in batches.iter_mut() { | 1760 | for batch in batches.iter_mut() { |
| @@ -2550,26 +2506,6 @@ impl SyncManager { | |||
| 2550 | "{} added to purgatory (waiting for git data)", | 2506 | "{} added to purgatory (waiting for git data)", |
| 2551 | context | 2507 | context |
| 2552 | ); | 2508 | ); |
| 2553 | // Trigger immediate sync for re-processed events that go to purgatory | ||
| 2554 | // (same as sync-triggered events in the main event loop) | ||
| 2555 | if event.kind.as_u16() == 30618 { | ||
| 2556 | // State event - extract identifier from 'd' tag | ||
| 2557 | if let Some(id) = event.tags.iter().find_map(|tag| { | ||
| 2558 | let tag_vec = tag.clone().to_vec(); | ||
| 2559 | if tag_vec.len() >= 2 && tag_vec[0] == "d" { | ||
| 2560 | Some(tag_vec[1].clone()) | ||
| 2561 | } else { | ||
| 2562 | None | ||
| 2563 | } | ||
| 2564 | }) { | ||
| 2565 | write_policy.purgatory().enqueue_sync_immediate(&id); | ||
| 2566 | } | ||
| 2567 | } else if event.kind.as_u16() == 1617 || event.kind.as_u16() == 1618 { | ||
| 2568 | // PR event - extract identifier from 'a' tag | ||
| 2569 | if let Some(id) = crate::git::sync::extract_identifier_from_pr_event(&event) { | ||
| 2570 | write_policy.purgatory().enqueue_sync_immediate(&id); | ||
| 2571 | } | ||
| 2572 | } | ||
| 2573 | } | 2509 | } |
| 2574 | ProcessResult::Rejected => { | 2510 | ProcessResult::Rejected => { |
| 2575 | stats.rejected += 1; | 2511 | stats.rejected += 1; |
diff --git a/tests/archive_read_only.rs b/tests/archive_read_only.rs index e39b4b2..e388ae5 100644 --- a/tests/archive_read_only.rs +++ b/tests/archive_read_only.rs | |||
| @@ -55,6 +55,7 @@ use std::time::Duration; | |||
| 55 | /// 5. Verify bare repository is created and git data is synced | 55 | /// 5. Verify bare repository is created and git data is synced |
| 56 | /// 6. Verify git pushes are rejected (read-only mode) | 56 | /// 6. Verify git pushes are rejected (read-only mode) |
| 57 | #[tokio::test] | 57 | #[tokio::test] |
| 58 | #[ignore] // Requires SyncLevel implementation (Phase 3) - purgatory announcements don't trigger per-repo sync yet | ||
| 58 | async fn test_archive_read_only_creates_bare_repo() { | 59 | async fn test_archive_read_only_creates_bare_repo() { |
| 59 | // 1. Start source relay | 60 | // 1. Start source relay |
| 60 | let source_relay = TestRelay::start().await; | 61 | let source_relay = TestRelay::start().await; |