mirror of
https://github.com/Horizontal42/summarum.git
synced 2026-11-10 19:11:58 +03:00
⚡ Optimize historical rates batch fetching with API range query
Replaced individual Frankfurter API requests with a single batched range query in fetch_historical_rates_batch. Added fallback logic to handle missing weekend/holiday dates. Co-authored-by: Horizontal42 <75383519+Horizontal42@users.noreply.github.com>
This commit is contained in:
parent
37cfd2144b
commit
845bc3d484
2 changed files with 56 additions and 29 deletions
1
src-tauri/src/bench_batch.rs
Normal file
1
src-tauri/src/bench_batch.rs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
// Wait, we can just write a quick script to benchmark it from JS or Rust
|
||||||
|
|
@ -628,42 +628,68 @@ async fn fetch_historical_rates_batch(
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut futures = Vec::new();
|
let mut parsed_dates: Vec<chrono::NaiveDate> = missing_dates
|
||||||
|
.iter()
|
||||||
|
.filter_map(|d| chrono::NaiveDate::parse_from_str(d, "%Y-%m-%d").ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
for date in missing_dates {
|
if parsed_dates.is_empty() {
|
||||||
let url = format!("https://api.frankfurter.dev/v1/{}?from=USD", date);
|
return Ok(results);
|
||||||
let client_clone = client.clone();
|
}
|
||||||
let app_clone = app.clone();
|
|
||||||
|
|
||||||
futures.push(async move {
|
parsed_dates.sort();
|
||||||
let mut rate_map = None;
|
|
||||||
if let Ok(resp) = client_clone.get(&url).send().await {
|
let min_date = parsed_dates.first().unwrap();
|
||||||
if let Ok(bytes) = read_body_capped(resp, RATES_RESPONSE_LIMIT).await {
|
let max_date = parsed_dates.last().unwrap();
|
||||||
if let Ok(body) = serde_json::from_slice::<serde_json::Value>(&bytes) {
|
|
||||||
if let Some(rates_obj) = body["rates"].as_object() {
|
// Request from min_date - 7 days to cover weekends/holidays before the earliest requested date
|
||||||
let daily_rates = parse_rate_map(rates_obj);
|
let start_date = *min_date - chrono::Duration::days(7);
|
||||||
let cache_path =
|
|
||||||
data_dir(&app_clone).join(format!("rates-{}.json", date));
|
let url = format!(
|
||||||
if let Ok(serialized) = serde_json::to_string(&daily_rates) {
|
"https://api.frankfurter.dev/v1/{}..{}?from=USD",
|
||||||
write_atomic_async(cache_path, serialized).await;
|
start_date.format("%Y-%m-%d"),
|
||||||
|
max_date.format("%Y-%m-%d")
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Ok(resp) = client.get(&url).send().await {
|
||||||
|
if let Ok(bytes) = read_body_capped(resp, RATES_RESPONSE_LIMIT * 50).await {
|
||||||
|
if let Ok(body) = serde_json::from_slice::<serde_json::Value>(&bytes) {
|
||||||
|
if let Some(rates_obj) = body["rates"].as_object() {
|
||||||
|
let mut available_dates: Vec<chrono::NaiveDate> = rates_obj
|
||||||
|
.keys()
|
||||||
|
.filter_map(|d| chrono::NaiveDate::parse_from_str(d, "%Y-%m-%d").ok())
|
||||||
|
.collect();
|
||||||
|
available_dates.sort();
|
||||||
|
|
||||||
|
for req_date_str in missing_dates {
|
||||||
|
if let Ok(req_date) = chrono::NaiveDate::parse_from_str(&req_date_str, "%Y-%m-%d") {
|
||||||
|
// Find the exact date or the closest preceding date
|
||||||
|
let matched_date_str = match available_dates.binary_search(&req_date) {
|
||||||
|
Ok(idx) => Some(available_dates[idx].format("%Y-%m-%d").to_string()),
|
||||||
|
Err(idx) => {
|
||||||
|
if idx > 0 {
|
||||||
|
Some(available_dates[idx - 1].format("%Y-%m-%d").to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(matched_date) = matched_date_str {
|
||||||
|
if let Some(matched_rates_obj) = rates_obj.get(&matched_date).and_then(|r| r.as_object()) {
|
||||||
|
let daily_rates = parse_rate_map(matched_rates_obj);
|
||||||
|
let cache_path = data_dir(&app).join(format!("rates-{}.json", req_date_str));
|
||||||
|
if let Ok(serialized) = serde_json::to_string(&daily_rates) {
|
||||||
|
write_atomic_async(cache_path, serialized).await;
|
||||||
|
}
|
||||||
|
results.insert(req_date_str, daily_rates);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rate_map = Some((date, daily_rates));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rate_map
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
use futures::stream::StreamExt;
|
|
||||||
let fetched_results = futures::stream::iter(futures)
|
|
||||||
.buffer_unordered(5)
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
for (date, rates) in fetched_results.into_iter().flatten() {
|
|
||||||
results.insert(date, rates);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(results)
|
Ok(results)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue