web: introduce a cache for loaded epochs

This commit is contained in:
Vincent Breitmoser 2021-07-20 12:10:57 +02:00
parent e4696e5f1d
commit 9965e61108
2 changed files with 49 additions and 14 deletions

View File

@ -1,4 +1,3 @@
use rocket;
use rocket::http::{Header, Status};
use rocket::request;
use rocket::outcome::Outcome;
@ -30,6 +29,8 @@ use crate::database::{Database, KeyDatabase, Query};
use crate::database::types::Fingerprint;
use crate::Result;
use crate::web::vks_updates::UpdateEpochCache;
use std::convert::TryInto;
mod hkp;
@ -38,6 +39,7 @@ mod maintenance;
mod vks;
mod vks_web;
mod vks_api;
mod vks_updates;
mod debug_web;
use crate::web::maintenance::MaintenanceMode;
@ -417,6 +419,8 @@ fn rocket_factory(mut rocket: rocket::Rocket) -> Result<rocket::Rocket> {
vks_web::verify_confirm_form,
vks_web::quick_upload,
vks_web::quick_upload_proceed,
// Update Manifests
vks_updates::get_update_manifest,
// Debug
debug_web::debug_info,
// HKP
@ -443,6 +447,7 @@ fn rocket_factory(mut rocket: rocket::Rocket) -> Result<rocket::Rocket> {
let rate_limiter = configure_rate_limiter(rocket.config())?;
let maintenance_mode = configure_maintenance_mode(rocket.config())?;
let localized_template_list = configure_localized_template_list(rocket.config())?;
let update_cache = UpdateEpochCache::new();
println!("{:?}", localized_template_list);
let prometheus = configure_prometheus(rocket.config());
@ -462,6 +467,7 @@ fn rocket_factory(mut rocket: rocket::Rocket) -> Result<rocket::Rocket> {
.manage(db_service)
.manage(rate_limiter)
.manage(localized_template_list)
.manage(update_cache)
.mount("/", routes);
if let Some(prometheus) = prometheus {
@ -568,7 +574,7 @@ pub mod tests {
use std::io::Write;
use std::path::Path;
use tempfile::{tempdir, TempDir};
use super::rocket;
use rocket;
use rocket::local::{Client, LocalResponse};
use rocket::http::Status;
use rocket::http::ContentType;

View File

@ -1,4 +1,4 @@
use std::io::{BufRead, BufReader};
use std::{collections::HashMap, sync::RwLock};
use rocket::http::hyper::header::{Expires, HttpDate};
@ -8,6 +8,14 @@ use crate::Result;
const EPOCH_SERVE_LIMIT: u32 = 120;
pub struct UpdateEpochCache(RwLock<HashMap<Epoch, Vec<u32>>>);
impl UpdateEpochCache {
pub fn new() -> Self {
UpdateEpochCache(RwLock::new(HashMap::new()))
}
}
#[derive(Responder)]
pub enum ManifestUpdateResponse {
#[response(status = 200, content_type = "application/pgp-keystore-update-manifest")]
@ -21,6 +29,7 @@ pub enum ManifestUpdateResponse {
#[get("/vks/v1/updates/<epoch>")]
pub fn get_update_manifest(
db: rocket::State<KeyDatabase>,
cache: rocket::State<UpdateEpochCache>,
epoch: u32,
) -> ManifestUpdateResponse {
let epoch_now = Epoch::current().unwrap();
@ -33,19 +42,15 @@ pub fn get_update_manifest(
format!("Updafe manifest data only available for the last {} epochs", EPOCH_SERVE_LIMIT));
}
if let Err(e) = provision_cache_since(&db, &cache, &epoch_since) {
return ManifestUpdateResponse::NotFound(e.to_string());
}
let cache_lock = cache.0.read().expect("lock can't be poisoned");
let mut epoch_data: Vec<&[u32]> = Vec::with_capacity((epoch_now - epoch_since) as usize);
for e in epoch_since.until(epoch_now).expect("epoch_since is before epoch_now") {
match db.read_log_epoch(e) {
Err(e) => {
eprintln!("Error reading epoch: {:?}", e);
return ManifestUpdateResponse::NotFound(
"No data found for requested update epoch".to_owned());
}
Ok(None) => {
return ManifestUpdateResponse::NotFound(
"No data found for requested update epoch".to_owned());
}
Ok(Some(v)) => epoch_data.push(&v),
if let Some(v) = cache_lock.get(&e) {
epoch_data.push(&v);
}
}
@ -54,3 +59,27 @@ pub fn get_update_manifest(
let expires = Expires(HttpDate(epoch_now.succ().expect("We're not at the end of time").into()));
ManifestUpdateResponse::Binary(manifest.to_vec(), expires)
}
fn provision_cache_since(db: &KeyDatabase, cache: &UpdateEpochCache, epoch_since: &Epoch) -> Result<()> {
let epoch_now = Epoch::current().expect("not the end of time");
let mut cache_lock = cache.0.write().expect("lock can't be poisoned");
for epoch in epoch_since.until(epoch_now).expect("epoch_since is before epoch_now") {
if cache_lock.contains_key(&epoch) {
continue;
}
match db.read_log_epoch(epoch) {
Err(e) => {
eprintln!("{:?}", e);
Err(anyhow!("No update manifest data available for requested epoch"))?
},
Ok(None) => Err(anyhow!("No update manifest data available for requested epoch"))?,
Ok(Some(prefixes)) => cache_lock.insert(epoch, prefixes),
};
}
let ref epoch_earliest = epoch_now - EPOCH_SERVE_LIMIT;
cache_lock.retain(|k, _| k > epoch_earliest);
Ok(())
}