feat: add caches
This commit is contained in:
parent
64a1d88479
commit
b7b3957db8
185
Cargo.lock
generated
185
Cargo.lock
generated
@ -47,6 +47,15 @@ version = "1.0.75"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "async-lock"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
|
||||
dependencies = [
|
||||
"event-listener",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-mutex"
|
||||
version = "1.4.0"
|
||||
@ -233,6 +242,12 @@ version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
|
||||
|
||||
[[package]]
|
||||
name = "bytecount"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.14.0"
|
||||
@ -251,6 +266,37 @@ version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
||||
|
||||
[[package]]
|
||||
name = "camino"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo-platform"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.83"
|
||||
@ -593,6 +639,15 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "error-chain"
|
||||
version = "0.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "2.5.3"
|
||||
@ -1325,6 +1380,15 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach2"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
@ -1382,6 +1446,31 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moka"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8017ec3548ffe7d4cef7ac0e12b044c01164a74c0f3119420faeaf13490ad8b"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"async-trait",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
"futures-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"quanta",
|
||||
"rustc_version",
|
||||
"skeptic",
|
||||
"smallvec",
|
||||
"tagptr",
|
||||
"thiserror",
|
||||
"triomphe",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multer"
|
||||
version = "2.1.0"
|
||||
@ -1531,9 +1620,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.18.0"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
@ -1724,8 +1813,10 @@ dependencies = [
|
||||
"fern",
|
||||
"headless_chrome",
|
||||
"log",
|
||||
"moka",
|
||||
"nanobyte_opentelemetry",
|
||||
"nanobyte_tera",
|
||||
"once_cell",
|
||||
"ovlach_data",
|
||||
"ovlach_tera",
|
||||
"phf",
|
||||
@ -2020,6 +2111,33 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"memchr",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quanta"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a17e662a7a8291a865152364c20c7abc5e60486ab2001e8ec10b24862de0b9ab"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"libc",
|
||||
"mach2",
|
||||
"once_cell",
|
||||
"raw-cpuid",
|
||||
"wasi",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
@ -2059,6 +2177,15 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-cpuid"
|
||||
version = "10.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.3.5"
|
||||
@ -2310,6 +2437,15 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.26"
|
||||
@ -2435,6 +2571,15 @@ version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.193"
|
||||
@ -2559,6 +2704,21 @@ version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
|
||||
|
||||
[[package]]
|
||||
name = "skeptic"
|
||||
version = "0.13.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8"
|
||||
dependencies = [
|
||||
"bytecount",
|
||||
"cargo_metadata",
|
||||
"error-chain",
|
||||
"glob",
|
||||
"pulldown-cmark",
|
||||
"tempfile",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
@ -2700,6 +2860,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tagptr"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.8.1"
|
||||
@ -3090,6 +3256,12 @@ dependencies = [
|
||||
"tracing-serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "triomphe"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "859eb650cfee7434994602c3a68b25d77ad9e68c8a6cd491616ef86661382eb3"
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.4"
|
||||
@ -3223,6 +3395,15 @@ dependencies = [
|
||||
"unic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.13"
|
||||
|
@ -28,6 +28,8 @@ phf = { version = "0.11.2", features = ["macros"] }
|
||||
nanobyte_tera = { version = "0.2.0", registry = "gitea_nanobyte" }
|
||||
ovlach_tera = { version="0.2.0", registry="gitea_ovlach" }
|
||||
ovlach_data = { version = "0.1.3", registry = "gitea_ovlach"}
|
||||
moka = { version = "0.12.1", features = ["future", 'log'] }
|
||||
once_cell = "1.19.0"
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0.108"
|
||||
|
@ -3,6 +3,7 @@ use nanobyte_opentelemetry::rocket::TracingFairing;
|
||||
use rocket::{Rocket, Build, routes, fairing::AdHoc};
|
||||
use rocket_prometheus::PrometheusMetrics;
|
||||
use serde::Deserialize;
|
||||
use tools::cache::{build_cache, cache_prometheus_metrics};
|
||||
pub mod routes;
|
||||
mod chromium;
|
||||
mod tools;
|
||||
@ -26,6 +27,7 @@ pub struct DefaultPerson {
|
||||
|
||||
pub fn rocket_builder() -> Rocket<Build> {
|
||||
let prometheus = PrometheusMetrics::new();
|
||||
cache_prometheus_metrics(&prometheus).expect("Failed to register prometheus cache metrics");
|
||||
|
||||
rocket::build()
|
||||
.attach(TracingFairing::ignite())
|
||||
@ -37,6 +39,7 @@ pub fn rocket_builder() -> Rocket<Build> {
|
||||
AdHoc::config::<DefaultPerson>()
|
||||
)
|
||||
.attach(prometheus.clone())
|
||||
.manage( build_cache())
|
||||
.mount("/", routes![
|
||||
routes::pdf::render_pdf_cv,
|
||||
routes::pdf::render_html_cv
|
||||
|
@ -4,11 +4,13 @@ use nanobyte_opentelemetry::rocket::{TracingSpan, OtelReqwestClient};
|
||||
use nanobyte_tera::l18n::LanguageDescription;
|
||||
use ovlach_data::cv::data::CV;
|
||||
use rocket::fs::NamedFile;
|
||||
use rocket::serde::json::json;
|
||||
use ::rocket::{State, http::Status};
|
||||
use ::rocket::get;
|
||||
use tempfile::NamedTempFile;
|
||||
use tera::Context;
|
||||
use tracing::{info_span, error, debug, Instrument};
|
||||
use crate::tools::cache::NanoCache;
|
||||
use crate::{chromium::rocket::BrowserHolder, tools::{tera::NanoTera, pdf::PdfStream, rocket::RequestLanguage}, services::cv::fetch_cv_data_from_backend, CVBackendConfig};
|
||||
|
||||
// TODO: request-id
|
||||
@ -56,19 +58,32 @@ fn render_template(template_name: &str, file: &NamedTempFile, tera: NanoTera, cv
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/cv/<username>/<language>/output.pdf")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[get("/cv/<username>/<language>/output.pdf?<force_rebuild>")]
|
||||
pub async fn render_pdf_cv(username: &str, tera: NanoTera, tracing: TracingSpan, request_client: OtelReqwestClient,
|
||||
cv_config: &State<CVBackendConfig>, language: RequestLanguage, browser: BrowserHolder) -> Result<PdfStream, Status> {
|
||||
cv_config: &State<CVBackendConfig>, language: RequestLanguage, browser: BrowserHolder, cache: &State<NanoCache<String, PdfStream>>,
|
||||
force_rebuild: Option<bool>) -> Result<PdfStream, Status> {
|
||||
async move {
|
||||
match fetch_cv_data_from_backend(&cv_config.cv_backend_path, &username.to_string(), &request_client.0).await {
|
||||
Ok(cv_data) => {
|
||||
// TODO: CV hasher
|
||||
let data = json!(cv_data).to_string();
|
||||
match cache.get(&data, force_rebuild).await {
|
||||
Some(data) => {
|
||||
Ok(data.clone())
|
||||
}
|
||||
None => {
|
||||
let file = tempfile::Builder::new().suffix(".html").tempfile().unwrap();
|
||||
render_template("two_column", &file, tera, cv_data, language.language);
|
||||
let span = info_span!("render_pdf", username = username);
|
||||
let pdf = span.in_scope(||{
|
||||
generate_pdf(browser.browser, &file)
|
||||
});
|
||||
Ok(PdfStream::new(pdf))
|
||||
let result = PdfStream::new(pdf);
|
||||
cache.insert(data, result.clone()).await;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
error!("Error fetching cv data: {:?}", e);
|
||||
|
81
src/tools/cache.rs
Normal file
81
src/tools/cache.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use moka::{future::Cache, notification::RemovalCause};
|
||||
use once_cell::sync::Lazy;
|
||||
use rocket_prometheus::{prometheus::{IntCounterVec, opts, Error}, PrometheusMetrics};
|
||||
use std::hash::Hash;
|
||||
|
||||
use super::pdf::PdfStream;
|
||||
|
||||
static CACHE_EVICTION_COUNT: Lazy<IntCounterVec> = Lazy::new(||
|
||||
IntCounterVec::new(opts!(format!("{}_{}", std::env::var("ROCKET_PROMETHEUS_NAMESPACE").unwrap_or("app".to_string()), "cache_eviction"), "Count of cache eviction"), &["cause"])
|
||||
.expect("Could not create NAME_COUNTER")
|
||||
);
|
||||
static CACHE_HITS: Lazy<IntCounterVec> = Lazy::new(||
|
||||
IntCounterVec::new(opts!(format!("{}_{}", std::env::var("ROCKET_PROMETHEUS_NAMESPACE").unwrap_or("app".to_string()), "cache_hists"), "Count of cache eviction"), &[]).expect("Could not create NAME_COUNTER")
|
||||
);
|
||||
static CACHE_MISSES: Lazy<IntCounterVec> = Lazy::new(||
|
||||
IntCounterVec::new(opts!(format!("{}_{}", std::env::var("ROCKET_PROMETHEUS_NAMESPACE").unwrap_or("app".to_string()), "cache_misses"), "Count of cache eviction"), &[]).expect("Could not create NAME_COUNTER")
|
||||
);
|
||||
|
||||
pub fn cache_prometheus_metrics(metrics: &PrometheusMetrics) -> Result<(), Error> {
|
||||
metrics.registry().register(Box::new(CACHE_EVICTION_COUNT.clone()))?;
|
||||
metrics.registry().register(Box::new(CACHE_HITS.clone()))?;
|
||||
metrics.registry().register(Box::new(CACHE_MISSES.clone()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build_cache() -> NanoCache<String, PdfStream> {
|
||||
let cache = Cache::builder()
|
||||
.time_to_live(Duration::from_secs(60 * 60))
|
||||
.eviction_listener(|_k, _v, cause| {
|
||||
let cause_string = match cause {
|
||||
RemovalCause::Expired => "expired",
|
||||
RemovalCause::Explicit => "explicit",
|
||||
RemovalCause::Replaced => "replaced",
|
||||
RemovalCause::Size => "size",
|
||||
};
|
||||
|
||||
CACHE_EVICTION_COUNT.with_label_values(&[cause_string]).inc();
|
||||
})
|
||||
.build();
|
||||
|
||||
NanoCache::new(cache)
|
||||
|
||||
}
|
||||
|
||||
pub struct NanoCache<K, V> {
|
||||
inner: Cache<K, V>,
|
||||
}
|
||||
|
||||
impl<K, V> NanoCache<K, V>
|
||||
where
|
||||
K: Hash + Eq + Send + Sync + 'static,
|
||||
V: Clone + Send + Sync + 'static
|
||||
{
|
||||
pub fn new(cache: Cache<K, V>) -> Self {
|
||||
Self {
|
||||
inner: cache
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(&self, key: &K, force_invalidate: Option<bool>) -> Option<V> {
|
||||
if force_invalidate == Some(true) {
|
||||
self.inner.invalidate(key).await;
|
||||
CACHE_MISSES.with_label_values(&[]).inc();
|
||||
return None;
|
||||
}
|
||||
let result = self.inner.get(key).await;
|
||||
|
||||
match result {
|
||||
Some(_) => CACHE_HITS.with_label_values(&[]).inc(),
|
||||
None => CACHE_MISSES.with_label_values(&[]).inc(),
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub async fn insert(&self, key: K, value: V) {
|
||||
self.inner.insert(key, value).await
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
pub mod tera;
|
||||
pub mod pdf;
|
||||
pub(crate) mod rocket;
|
||||
pub(crate) mod cache;
|
||||
|
@ -3,6 +3,7 @@ use std::io::Cursor;
|
||||
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PdfStream {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user