diff options
| author | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-08 00:41:02 +0000 |
|---|---|---|
| committer | DanConwayDev <DanConwayDev@protonmail.com> | 2026-01-08 00:41:02 +0000 |
| commit | 5833c9bdf815699838a0445f750b99b26fd4a3bd (patch) | |
| tree | bd148e548e5621872615627cdbd88ba577d072ce /src/main.rs | |
| parent | ac3e00a7e102d7ae341f554563646e05aed7edac (diff) | |
feat(purgatory): track expired events to prevent infinite re-sync loops
Adds expired event tracking to prevent proactive sync from repeatedly
fetching and re-adding events that expired from purgatory without
finding git data.
Key features:
- Track expired events for 7 days to prevent re-sync loops
- Distinguish synced vs user-submitted events (via socket address)
- Allow users to retry expired events (git data might now be available)
- Reject synced expired events (prevents infinite loop)
- Daily cleanup of expired event records older than 7 days
Implementation:
- Added expired_events: DashMap<EventId, Instant> to Purgatory
- Updated event_ids() to include both purgatory + expired events
- Added is_expired(), mark_expired(), cleanup_expired_events()
- Updated cleanup() to mark expired events automatically
- Added is_synced detection in WritePolicy (localhost:0 = synced)
- Policy layer checks is_synced && is_expired() before rejecting
Behavior:
- Negentropy: Filters expired events before fetching (optimal)
- REQ+EOSE: Rejects synced expired events at policy layer
- User submissions: Always allowed to retry (skip expired check)
Testing:
- Added 5 new tests for expired event tracking
- All 222 tests passing
Fixes the infinite re-sync loop where events without git data would
expire, get synced again, expire again, repeat forever.
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 22 |
1 files changed, 21 insertions, 1 deletions
diff --git a/src/main.rs b/src/main.rs index b4a42af..8b870dc 100644 --- a/src/main.rs +++ b/src/main.rs | |||
| @@ -94,7 +94,7 @@ async fn main() -> Result<()> { | |||
| 94 | sync_manager.run().await; | 94 | sync_manager.run().await; |
| 95 | }); | 95 | }); |
| 96 | 96 | ||
| 97 | // Spawn background cleanup task | 97 | // Spawn background cleanup task for purgatory entries (60s interval) |
| 98 | let cleanup_purgatory = purgatory.clone(); | 98 | let cleanup_purgatory = purgatory.clone(); |
| 99 | tokio::spawn(async move { | 99 | tokio::spawn(async move { |
| 100 | let mut interval = tokio::time::interval(Duration::from_secs(60)); | 100 | let mut interval = tokio::time::interval(Duration::from_secs(60)); |
| @@ -111,6 +111,26 @@ async fn main() -> Result<()> { | |||
| 111 | }); | 111 | }); |
| 112 | info!("Purgatory cleanup task started (60s interval)"); | 112 | info!("Purgatory cleanup task started (60s interval)"); |
| 113 | 113 | ||
| 114 | // Spawn daily cleanup task for old expired event records (prevent unbounded growth) | ||
| 115 | let expired_cleanup_purgatory = purgatory.clone(); | ||
| 116 | tokio::spawn(async move { | ||
| 117 | // Run immediately on startup, then every 24 hours | ||
| 118 | let mut interval = tokio::time::interval(Duration::from_secs(24 * 3600)); | ||
| 119 | loop { | ||
| 120 | interval.tick().await; | ||
| 121 | // Remove expired event records older than 7 days | ||
| 122 | let removed = expired_cleanup_purgatory | ||
| 123 | .cleanup_expired_events(Duration::from_secs(7 * 24 * 3600)); | ||
| 124 | if removed > 0 { | ||
| 125 | info!( | ||
| 126 | "Expired event cleanup: removed {} old expired event records (>7 days)", | ||
| 127 | removed | ||
| 128 | ); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | }); | ||
| 132 | info!("Expired event cleanup task started (24h interval, keeps 7 days)"); | ||
| 133 | |||
| 114 | // Start purgatory sync loop for background git data fetching | 134 | // Start purgatory sync loop for background git data fetching |
| 115 | let sync_ctx = Arc::new(RealSyncContext::new( | 135 | let sync_ctx = Arc::new(RealSyncContext::new( |
| 116 | purgatory.clone(), | 136 | purgatory.clone(), |