diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-13 19:59:36 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-02-13 19:59:36 +0000 |
| commit | e922e14e3ec4b898c111b2100cd63dddbe2fcdb1 (patch) | |
| tree | 8fa2343b4b12ce97108b1e3461410e97a7af8cce /src/sync/mod.rs | |
| parent | 8c903c9449d387c9b0edefa5aa283b176a3ed0cb (diff) | |
feat: add SyncLevel to sync system for purgatory announcement state-only sync
Purgatory announcements need state events (kind 30618) synced from
external relays, but not full L2/L3 events (patches, issues, PRs)
which would be rejected anyway. This implements the SyncLevel concept
from the design doc (decision #6):
- Add SyncLevel enum (Full vs StateOnly) to RepoSyncNeeds
- When announcement enters purgatory during sync, register in
RepoSyncIndex with SyncLevel::StateOnly
- Add build_sync_level_aware_filters() that partitions repos by level:
StateOnly repos only get state event filters (kind 30618)
- Update derive_relay_targets to track state_only_repos separately
- Update compute_actions to handle both repo sets
- SelfSubscriber always uses SyncLevel::Full (promoted repos)
Diffstat (limited to 'src/sync/mod.rs')
| -rw-r--r-- | src/sync/mod.rs | 59 |
1 files changed, 58 insertions, 1 deletions
diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 1ee1872..519017b 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs | |||
| @@ -85,6 +85,19 @@ use rejected_index::RejectedEventsIndex; | |||
| 85 | // Supporting Data Structures | 85 | // Supporting Data Structures |
| 86 | // ============================================================================= | 86 | // ============================================================================= |
| 87 | 87 | ||
| 88 | /// Level of sync needed for a repository | ||
| 89 | /// | ||
| 90 | /// Purgatory announcements only need state events synced (to validate git data). | ||
| 91 | /// Promoted repos need full L2/L3 sync (patches, issues, PRs, etc.). | ||
| 92 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] | ||
| 93 | pub enum SyncLevel { | ||
| 94 | /// Full L2 + L3 sync (promoted repos with git data) | ||
| 95 | #[default] | ||
| 96 | Full, | ||
| 97 | /// Only state events (kind 30618) - for purgatory announcements | ||
| 98 | StateOnly, | ||
| 99 | } | ||
| 100 | |||
| 88 | /// What repos and root events need to be synced | 101 | /// What repos and root events need to be synced |
| 89 | #[derive(Debug, Clone, Default)] | 102 | #[derive(Debug, Clone, Default)] |
| 90 | pub struct RepoSyncNeeds { | 103 | pub struct RepoSyncNeeds { |
| @@ -92,6 +105,8 @@ pub struct RepoSyncNeeds { | |||
| 92 | pub relays: HashSet<String>, | 105 | pub relays: HashSet<String>, |
| 93 | /// Root event IDs - 1617/1618/1621 - that reference this repo | 106 | /// Root event IDs - 1617/1618/1621 - that reference this repo |
| 94 | pub root_events: HashSet<EventId>, | 107 | pub root_events: HashSet<EventId>, |
| 108 | /// Sync level - StateOnly for purgatory, Full for promoted repos | ||
| 109 | pub sync_level: SyncLevel, | ||
| 95 | } | 110 | } |
| 96 | 111 | ||
| 97 | /// Connection status for a relay | 112 | /// Connection status for a relay |
| @@ -1677,6 +1692,7 @@ impl SyncManager { | |||
| 1677 | let eose_tx = self.eose_tx.as_ref().unwrap().clone(); | 1692 | let eose_tx = self.eose_tx.as_ref().unwrap().clone(); |
| 1678 | let metrics_clone = self.metrics.clone(); | 1693 | let metrics_clone = self.metrics.clone(); |
| 1679 | let pending_sync_index = Arc::clone(&self.pending_sync_index); | 1694 | let pending_sync_index = Arc::clone(&self.pending_sync_index); |
| 1695 | let repo_sync_index = Arc::clone(&self.repo_sync_index); | ||
| 1680 | let health_tracker = Arc::clone(&self.health_tracker); | 1696 | let health_tracker = Arc::clone(&self.health_tracker); |
| 1681 | let rejected_events_index = Arc::clone(&self.rejected_events_index); | 1697 | let rejected_events_index = Arc::clone(&self.rejected_events_index); |
| 1682 | 1698 | ||
| @@ -1719,8 +1735,49 @@ impl SyncManager { | |||
| 1719 | // For sync-triggered events that go to purgatory, trigger immediate sync | 1735 | // For sync-triggered events that go to purgatory, trigger immediate sync |
| 1720 | // (instead of the default 3-minute delay for user-submitted events) | 1736 | // (instead of the default 3-minute delay for user-submitted events) |
| 1721 | if result == ProcessResult::Purgatory { | 1737 | if result == ProcessResult::Purgatory { |
| 1738 | // Announcement events (kind 30617) - register in RepoSyncIndex with StateOnly | ||
| 1739 | // so that state events (kind 30618) are synced for this purgatory announcement | ||
| 1740 | if event.kind == Kind::GitRepoAnnouncement { | ||
| 1741 | if let Some(identifier) = event.tags.iter().find_map(|tag| { | ||
| 1742 | let tag_vec = tag.as_slice(); | ||
| 1743 | if tag_vec.len() >= 2 && tag_vec[0] == "d" { | ||
| 1744 | Some(tag_vec[1].to_string()) | ||
| 1745 | } else { | ||
| 1746 | None | ||
| 1747 | } | ||
| 1748 | }) { | ||
| 1749 | let repo_id = format!("30617:{}:{}", event.pubkey, identifier); | ||
| 1750 | |||
| 1751 | // Extract relay URLs from the purgatory entry | ||
| 1752 | let relays = write_policy | ||
| 1753 | .purgatory() | ||
| 1754 | .find_announcement(&event.pubkey, &identifier) | ||
| 1755 | .map(|entry| entry.relays) | ||
| 1756 | .unwrap_or_default(); | ||
| 1757 | |||
| 1758 | tracing::info!( | ||
| 1759 | event_id = %event.id, | ||
| 1760 | repo_id = %repo_id, | ||
| 1761 | relay_count = relays.len(), | ||
| 1762 | "Registering purgatory announcement in RepoSyncIndex with StateOnly level" | ||
| 1763 | ); | ||
| 1764 | |||
| 1765 | // Register in RepoSyncIndex with StateOnly level | ||
| 1766 | let mut index = repo_sync_index.write().await; | ||
| 1767 | let entry = index | ||
| 1768 | .entry(repo_id) | ||
| 1769 | .or_insert_with(|| RepoSyncNeeds { | ||
| 1770 | relays: HashSet::new(), | ||
| 1771 | root_events: HashSet::new(), | ||
| 1772 | sync_level: SyncLevel::StateOnly, | ||
| 1773 | }); | ||
| 1774 | entry.relays.extend(relays); | ||
| 1775 | // Don't upgrade sync_level if already Full | ||
| 1776 | // (e.g., if announcement was promoted before this runs) | ||
| 1777 | } | ||
| 1778 | } | ||
| 1722 | // State events (kind 30618) - extract identifier and trigger immediate sync | 1779 | // State events (kind 30618) - extract identifier and trigger immediate sync |
| 1723 | if event.kind.as_u16() == 30618 { | 1780 | else if event.kind.as_u16() == 30618 { |
| 1724 | if let Some(identifier) = event.tags.iter().find_map(|tag| { | 1781 | if let Some(identifier) = event.tags.iter().find_map(|tag| { |
| 1725 | let tag_vec = tag.clone().to_vec(); | 1782 | let tag_vec = tag.clone().to_vec(); |
| 1726 | if tag_vec.len() >= 2 && tag_vec[0] == "d" { | 1783 | if tag_vec.len() >= 2 && tag_vec[0] == "d" { |