(&self, currency: &str)
| 341 | } |
| 342 | |
| 343 | async fn refresh_currency(&self, currency: &str) -> Result<(), anyhow::Error> { |
| 344 | let mut inner = self.inner.lock().await; |
| 345 | let now = Instant::now(); |
| 346 | let mut start_background_refresh = false; |
| 347 | |
| 348 | let mut source_candidates: Vec<String> = inner |
| 349 | .sources |
| 350 | .iter() |
| 351 | .filter(|(_, s)| now >= s.backoff_until) |
| 352 | .map(|(name, _)| name.clone()) |
| 353 | .collect(); |
| 354 | |
| 355 | if source_candidates.is_empty() { |
| 356 | log::warn!("all sources have failed recently, trying them immediately again"); |
| 357 | source_candidates = inner.sources.keys().cloned().collect(); |
| 358 | } |
| 359 | |
| 360 | let currency_cache = if let Some(c) = inner.currencies.get(currency) { |
| 361 | c |
| 362 | } else { |
| 363 | inner |
| 364 | .currencies |
| 365 | .insert(currency.to_owned(), CurrencyCache::new()); |
| 366 | start_background_refresh = true; |
| 367 | inner.currencies.get(currency).unwrap() |
| 368 | }; |
| 369 | source_candidates.retain(|c| { |
| 370 | currency_cache |
| 371 | .prices |
| 372 | .get(c) |
| 373 | .is_none_or(|p| p.timestamp + SERVE_TTL <= now) |
| 374 | }); |
| 375 | |
| 376 | drop(inner); |
| 377 | |
| 378 | let futures = source_candidates.iter().map(|source_name| { |
| 379 | let inner = self.inner.clone(); |
| 380 | let client = self.client.clone(); |
| 381 | let source_name = source_name.clone(); |
| 382 | |
| 383 | async move { |
| 384 | let source = { |
| 385 | let inner = inner.lock().await; |
| 386 | inner.sources.get(&source_name).unwrap().source.clone() |
| 387 | }; |
| 388 | |
| 389 | let rate_result = source.get_rate(&client, currency).await; |
| 390 | |
| 391 | let mut inner = inner.lock().await; |
| 392 | |
| 393 | let source_health = inner.sources.get_mut(&source_name).unwrap(); |
| 394 | |
| 395 | match rate_result { |
| 396 | Ok(price) => { |
| 397 | source_health.mark_success(); |
| 398 | |
| 399 | let cache = inner.currencies.get_mut(currency).unwrap(); |
| 400 | cache.prices.insert( |
no test coverage detected