Problem
The current dedup logic uses a two-step check-then-mark pattern (SISMEMBER then SADD) in redis_store.rs. With multiple replicas subscribing to the same relay, both can check before either marks, causing duplicate push notifications.
Fix
1. Atomic check-and-mark
Use SADD atomically as both check and mark — it returns 1 if newly added, 0 if existed:
// Instead of:
let exists = redis.sismember("PROCESSED_EVENTS_SET", event_id).await?;
if exists { return; }
redis.sadd("PROCESSED_EVENTS_SET", event_id).await?;
// Use:
let added = redis.sadd("PROCESSED_EVENTS_SET", event_id).await?;
if added == 0 { return; } // already processed
2. Per-event TTL
EXPIRE is currently set on the entire PROCESSED_EVENTS_SET, not per-event. Every new event resets the TTL for all events.
Consider using per-event keys with individual TTLs instead:
// Instead of a set with global TTL:
let added = redis.set_nx(format!("dedup:{}", event_id), "1").await?;
redis.expire(format!("dedup:{}", event_id), DEDUP_TTL).await?;
Problem
The current dedup logic uses a two-step check-then-mark pattern (
SISMEMBERthenSADD) inredis_store.rs. With multiple replicas subscribing to the same relay, both can check before either marks, causing duplicate push notifications.Fix
1. Atomic check-and-mark
Use
SADDatomically as both check and mark — it returns1if newly added,0if existed:2. Per-event TTL
EXPIREis currently set on the entirePROCESSED_EVENTS_SET, not per-event. Every new event resets the TTL for all events.Consider using per-event keys with individual TTLs instead: