forked from paul/sinoptik
Implement caching for provider get requests (closes: #2)
* Also cache address geocoding requests to OSM Nomatim! * Use the `cached` crate for an easy implementation * Add the `cache_key` helper function to deal with floats being annoying * Cache Buienradar get request for 5 minutes (per position/metric) * Cache Luchtmeetnet get request for 5 minutes (per position/metric) * Note the `Item` structs need to implement `Clone` now because the cache will own them and Rocket will want a copy too
This commit is contained in:
parent
927cb0ad92
commit
8d19dbb517
|
@ -41,6 +41,25 @@ dependencies = [
|
|||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-mutex"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-rwlock"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "261803dcc39ba9e72760ba6e16d0199b1eef9fc44e81bffabbebb9f5aea3906c"
|
||||
dependencies = [
|
||||
"async-mutex",
|
||||
"event-listener",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-stream"
|
||||
version = "0.3.2"
|
||||
|
@ -186,6 +205,40 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
|
||||
|
||||
[[package]]
|
||||
name = "cached"
|
||||
version = "0.30.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af4dfac631a8e77b2f327f7852bb6172771f5279c4512efe79fad6067b37be3d"
|
||||
dependencies = [
|
||||
"async-mutex",
|
||||
"async-rwlock",
|
||||
"async-trait",
|
||||
"cached_proc_macro",
|
||||
"cached_proc_macro_types",
|
||||
"futures",
|
||||
"hashbrown",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cached_proc_macro"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "725f434d6da2814b989bd905c62ca28a9383feff7440210dc279665fbbbc9511"
|
||||
dependencies = [
|
||||
"cached_proc_macro_types",
|
||||
"darling",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cached_proc_macro_types"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.72"
|
||||
|
@ -381,6 +434,41 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "1.0.0"
|
||||
|
@ -444,6 +532,12 @@ dependencies = [
|
|||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.4.1"
|
||||
|
@ -912,6 +1006,12 @@ dependencies = [
|
|||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.3"
|
||||
|
@ -2042,6 +2142,7 @@ dependencies = [
|
|||
name = "sinoptik"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cached",
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"color-eyre",
|
||||
|
@ -2176,6 +2277,12 @@ version = "0.1.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.86"
|
||||
|
|
|
@ -4,6 +4,7 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cached = { version = "0.30.0", features = ["async"] }
|
||||
csv = "1.1.6"
|
||||
chrono = "0.4.19"
|
||||
chrono-tz = "0.6.1"
|
||||
|
|
14
src/main.rs
14
src/main.rs
|
@ -13,6 +13,7 @@
|
|||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use cached::proc_macro::cached;
|
||||
use color_eyre::Result;
|
||||
use geocoding::{Forward, Openstreetmap, Point};
|
||||
use rocket::serde::json::Json;
|
||||
|
@ -27,6 +28,17 @@ use self::providers::luchtmeetnet::Item as LuchtmeetnetItem;
|
|||
pub(crate) mod maps;
|
||||
pub(crate) mod providers;
|
||||
|
||||
/// Caching key helper function that can be used by providers.
|
||||
///
|
||||
/// This is necessary because `f64` does not implement `Eq` nor `Hash`, which is required by
|
||||
/// the caching implementation.
|
||||
fn cache_key(lat: f64, lon: f64, metric: Metric) -> (i32, i32, Metric) {
|
||||
let lat_key = (lat * 10_000.0) as i32;
|
||||
let lon_key = (lon * 10_000.0) as i32;
|
||||
|
||||
(lat_key, lon_key, metric)
|
||||
}
|
||||
|
||||
/// The current for a specific location.
|
||||
///
|
||||
/// Only the metrics asked for are included as well as the position and current time.
|
||||
|
@ -94,7 +106,7 @@ impl Forecast {
|
|||
///
|
||||
/// This is used for selecting which metrics should be calculated & returned.
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, FromFormField)]
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, FromFormField)]
|
||||
enum Metric {
|
||||
/// All metrics.
|
||||
#[field(value = "all")]
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//! For more information about Buienradar, see: <https://www.buienradar.nl/overbuienradar/contact>
|
||||
//! and <https://www.buienradar.nl/overbuienradar/gratis-weerdata>.
|
||||
|
||||
use cached::proc_macro::cached;
|
||||
use chrono::offset::TimeZone;
|
||||
use chrono::serde::ts_seconds;
|
||||
use chrono::{DateTime, Local, NaiveTime, ParseError, Utc};
|
||||
|
@ -11,7 +12,7 @@ use csv::ReaderBuilder;
|
|||
use reqwest::Url;
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Metric;
|
||||
use crate::{cache_key, Metric};
|
||||
|
||||
/// The base URL for the Buienradar API.
|
||||
const BUIENRADAR_BASE_URL: &str = "https://gpsgadget.buienradar.nl/data/raintext";
|
||||
|
@ -30,7 +31,7 @@ struct Row {
|
|||
}
|
||||
|
||||
/// The Buienradar API precipitation data item.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(crate = "rocket::serde", try_from = "Row")]
|
||||
pub(crate) struct Item {
|
||||
/// The time(stamp) of the forecast.
|
||||
|
@ -89,6 +90,12 @@ fn convert_value(v: u16) -> f32 {
|
|||
///
|
||||
/// Returns [`None`] if retrieval or deserialization fails, or if the metric is not supported by
|
||||
/// this provider.
|
||||
#[cached(
|
||||
time = 300,
|
||||
convert = "{ cache_key(lat, lon, metric) }",
|
||||
key = "(i32, i32, Metric)",
|
||||
option = true
|
||||
)]
|
||||
pub(crate) async fn get(lat: f64, lon: f64, metric: Metric) -> Option<Vec<Item>> {
|
||||
if metric != Metric::Precipitation {
|
||||
return None;
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
//!
|
||||
//! For more information about Luchtmeetnet, see: <https://www.luchtmeetnet.nl/contact>.
|
||||
|
||||
use cached::proc_macro::cached;
|
||||
use chrono::serde::ts_seconds;
|
||||
use chrono::{DateTime, Utc};
|
||||
use reqwest::Url;
|
||||
use rocket::serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::Metric;
|
||||
use crate::{cache_key, Metric};
|
||||
|
||||
/// The base URL for the Luchtmeetnet API.
|
||||
const LUCHTMEETNET_BASE_URL: &str = "https://api.luchtmeetnet.nl/open_api/concentrations";
|
||||
|
@ -22,7 +23,7 @@ struct Container {
|
|||
}
|
||||
|
||||
/// The Luchtmeetnet API data item.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub(crate) struct Item {
|
||||
/// The time(stamp) of the forecast.
|
||||
|
@ -48,6 +49,12 @@ pub(crate) struct Item {
|
|||
///
|
||||
/// Returns [`None`] if retrieval or deserialization fails, or if the metric is not supported by
|
||||
/// this provider.
|
||||
#[cached(
|
||||
time = 300,
|
||||
convert = "{ cache_key(lat, lon, metric) }",
|
||||
key = "(i32, i32, Metric)",
|
||||
option = true
|
||||
)]
|
||||
pub(crate) async fn get(lat: f64, lon: f64, metric: Metric) -> Option<Vec<Item>> {
|
||||
let formula = match metric {
|
||||
Metric::AQI => "lki",
|
||||
|
|
Loading…
Reference in New Issue