lightweight com.atproto.sync.listReposByCollection
45
fork

Configure Feed

Select the types of activity you want to include in your feed.

identity resolution negative cache

phil 0951af44 e53a4977

+39 -1
+39 -1
src/identity.rs
··· 72 72 /// before this TTL expires in practice. 73 73 const CACHE_TTL: Duration = Duration::from_secs(24 * 60 * 60); 74 74 75 + /// How long a failed resolution is cached before retrying. 76 + /// 77 + /// Short enough that transient failures recover quickly, long enough to avoid 78 + /// hammering the identity service for consistently-broken DIDs. 79 + const NEGATIVE_CACHE_TTL: Duration = Duration::from_secs(5 * 60); 80 + 75 81 /// Maximum retries per identity backend on HTTP 429 responses. 76 82 const RATE_LIMIT_RETRIES: u32 = 3; 77 83 ··· 155 161 } 156 162 157 163 /// Capacity-bounded, TTL-expiring cache from DID strings to resolved identities. 164 + /// 165 + /// Successful resolutions are cached for [`CACHE_TTL`] (24 h). Failed 166 + /// resolutions are negative-cached for [`NEGATIVE_CACHE_TTL`] (5 min) so that 167 + /// consistently-broken DIDs don't hammer the identity service on every event. 158 168 struct IdentityCache { 159 169 entries: Cache<Did<'static>, Arc<CachedIdentity>>, 170 + /// DIDs whose last resolution failed. Presence means "don't retry yet". 171 + negative: Cache<Did<'static>, ()>, 160 172 interner: UrlInterner, 161 173 } 162 174 ··· 167 179 .max_capacity(capacity) 168 180 .time_to_live(CACHE_TTL) 169 181 .build(), 182 + negative: Cache::builder() 183 + .max_capacity(capacity / 10) 184 + .time_to_live(NEGATIVE_CACHE_TTL) 185 + .build(), 170 186 interner: UrlInterner::new(), 171 187 } 172 188 } ··· 175 191 self.entries.get(&did.clone().into_static()) 176 192 } 177 193 194 + /// Returns `true` if this DID recently failed resolution. 195 + fn is_negative(&self, did: &Did<'_>) -> bool { 196 + self.negative.contains_key(&did.clone().into_static()) 197 + } 198 + 178 199 fn insert( 179 200 &self, 180 201 did: Did<'static>, ··· 183 204 ) -> Arc<CachedIdentity> { 184 205 let pds = self.interner.intern(pds); 185 206 let identity = Arc::new(CachedIdentity { pds, pubkey }); 207 + self.negative.invalidate(&did); 186 208 self.entries.insert(did, Arc::clone(&identity)); 187 209 identity 188 210 } 189 211 212 + fn insert_negative(&self, did: &Did<'_>) { 213 + self.negative.insert(did.clone().into_static(), ()); 214 + } 215 + 190 216 fn invalidate(&self, did: &Did<'_>) { 191 - self.entries.invalidate(&did.clone().into_static()); 217 + let owned = did.clone().into_static(); 218 + self.entries.invalidate(&owned); 219 + self.negative.invalidate(&owned); 192 220 } 193 221 } 194 222 ··· 266 294 return Ok(cached); 267 295 } 268 296 297 + if self.cache.is_negative(did) { 298 + metrics::counter!("lightrail_identity_resolution_total", "outcome" => "negative_hit") 299 + .increment(1); 300 + return Err(IdentityError::Identity(format!( 301 + "recently failed for {}", 302 + did.as_str() 303 + ))); 304 + } 305 + 269 306 if firehose { 270 307 self.throttle_notify(); 271 308 } else { ··· 281 318 Err(e) => { 282 319 metrics::counter!("lightrail_identity_resolution_total", "outcome" => "error") 283 320 .increment(1); 321 + self.cache.insert_negative(did); 284 322 Err(e) 285 323 } 286 324 }