From 345f1dfe36ac735ea047306ccba0619b38883344 Mon Sep 17 00:00:00 2001 From: Rouven Himmelstein Date: Wed, 8 Jun 2022 20:44:09 +0200 Subject: [PATCH] fix: geo location caching --- src/geo_location_cache.rs | 31 +++++++++++++++++++++++++++ src/integration_test_resources_api.rs | 6 +++++- src/main.rs | 5 +++++ src/resource_endpoint.rs | 10 ++++++--- src/resource_processor.rs | 18 ++++++++-------- 5 files changed, 57 insertions(+), 13 deletions(-) create mode 100644 src/geo_location_cache.rs diff --git a/src/geo_location_cache.rs b/src/geo_location_cache.rs new file mode 100644 index 0000000..a37145f --- /dev/null +++ b/src/geo_location_cache.rs @@ -0,0 +1,31 @@ +use evmap::{ReadHandle, WriteHandle}; + +/// Holds the geo location cache reader and writer +pub struct GeoLocationCache { + kv_reader: ReadHandle, + kv_writer: WriteHandle, +} + +/// Initializes the geo location cache +pub fn init() -> GeoLocationCache { + let (kv_reader, kv_writer) = evmap::new::(); + GeoLocationCache { + kv_reader, + kv_writer, + } +} + +impl GeoLocationCache { + pub fn contains_key(&self, key: &str) -> bool { + self.kv_reader.contains_key(key) + } + + pub fn get(&self, key: &str) -> Option { + self.kv_reader.get_one(key).map(|t| t.to_string()) + } + + pub fn insert(&mut self, key: String, value: String) { + self.kv_writer.insert(key, value); + self.kv_writer.refresh(); + } +} diff --git a/src/integration_test_resources_api.rs b/src/integration_test_resources_api.rs index 935547f..e7e5250 100644 --- a/src/integration_test_resources_api.rs +++ b/src/integration_test_resources_api.rs @@ -12,7 +12,9 @@ use rand::Rng; use crate::geo_location::GeoLocation; use crate::resource_reader::{RemoteResource, ResourceReader}; -use crate::{resource_endpoint, resource_processor, resource_reader, scheduler}; +use crate::{ + geo_location_cache, resource_endpoint, resource_processor, resource_reader, scheduler, +}; const TEST_JPEG_EXIF_URL: &str = "https://raw.githubusercontent.com/ianare/exif-samples/master/jpg/gps/DSCN0010.jpg"; @@ -314,10 +316,12 @@ fn build_app( > { scheduler::init(); scheduler::fetch_resources(resource_reader.clone(), kv_writer_mutex.clone()); + let geo_location_cache = Arc::new(Mutex::new(geo_location_cache::init())); App::new() .app_data(web::Data::new(kv_reader)) .app_data(web::Data::new(resource_reader)) .app_data(web::Data::new(kv_writer_mutex)) + .app_data(web::Data::new(geo_location_cache.clone())) .service( web::scope("/api/resources") .service(resource_endpoint::list_all_resources) diff --git a/src/main.rs b/src/main.rs index b1f29c3..7b732dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use actix_web::{middleware, web, App, HttpResponse, HttpServer}; mod config_endpoint; mod exif_reader; mod geo_location; +mod geo_location_cache; mod image_processor; mod resource_endpoint; mod resource_processor; @@ -48,6 +49,9 @@ async fn main() -> std::io::Result<()> { // Fetch resources for the first time scheduler::fetch_resources(resource_reader.clone(), kv_writer_mutex.clone()); + // Initialize geo location cache + let geo_location_cache = Arc::new(Mutex::new(geo_location_cache::init())); + // Run the actual web server and hold the main thread here println!("Launching webserver 🚀"); let http_server_result = HttpServer::new(move || { @@ -55,6 +59,7 @@ async fn main() -> std::io::Result<()> { .app_data(web::Data::new(kv_reader.clone())) .app_data(web::Data::new(resource_reader.clone())) .app_data(web::Data::new(kv_writer_mutex.clone())) + .app_data(web::Data::new(geo_location_cache.clone())) .wrap(middleware::Logger::default()) // enable logger .service( web::scope("/api/resources") diff --git a/src/resource_endpoint.rs b/src/resource_endpoint.rs index 9c92e95..21fe740 100644 --- a/src/resource_endpoint.rs +++ b/src/resource_endpoint.rs @@ -3,8 +3,9 @@ use std::sync::{Arc, Mutex}; use actix_web::get; use actix_web::web; use actix_web::HttpResponse; -use evmap::{ReadHandle, WriteHandle}; +use evmap::ReadHandle; +use crate::geo_location_cache::GeoLocationCache; use crate::resource_reader::{RemoteResource, ResourceReader}; use crate::{image_processor, resource_processor}; @@ -184,7 +185,7 @@ pub async fn get_resource_metadata_by_id( pub async fn get_resource_metadata_description_by_id( resources_id: web::Path, kv_reader: web::Data>, - kv_writer_mutex: web::Data>>>, + geo_location_cache_mutex: web::Data>>, ) -> HttpResponse { let resource = kv_reader .get_one(resources_id.as_str()) @@ -192,7 +193,10 @@ pub async fn get_resource_metadata_description_by_id( .and_then(|resource_json_string| serde_json::from_str(resource_json_string.as_str()).ok()); let display_value = resource.map(|resource| { - resource_processor::build_display_value(resource, kv_writer_mutex.get_ref().clone()) + resource_processor::build_display_value( + resource, + geo_location_cache_mutex.get_ref().clone(), + ) }); if let Some(display_value) = display_value { diff --git a/src/resource_processor.rs b/src/resource_processor.rs index b594f3b..2b82fb4 100644 --- a/src/resource_processor.rs +++ b/src/resource_processor.rs @@ -1,12 +1,13 @@ use std::env; use std::sync::{Arc, Mutex}; -use evmap::{ReadHandle, WriteHandle}; +use evmap::ReadHandle; use rand::prelude::SliceRandom; use rand::Rng; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use crate::geo_location; +use crate::geo_location_cache::GeoLocationCache; use crate::resource_reader::RemoteResource; pub fn md5(string: &str) -> String { @@ -49,7 +50,7 @@ pub fn get_all(kv_reader: &ReadHandle) -> Vec { /// The display value contains the date and location of a resource pub async fn build_display_value( resource: RemoteResource, - kv_writer_mutex: Arc>>, + geo_location_cache_mutex: Arc>, ) -> String { let mut display_value: String = String::new(); @@ -59,7 +60,7 @@ pub async fn build_display_value( } // Append city name - let city_name = get_city_name(resource, kv_writer_mutex.clone()).await; + let city_name = get_city_name(resource, geo_location_cache_mutex.clone()).await; if let Some(city_name) = city_name { display_value.push_str(", "); display_value.push_str(city_name.as_str()); @@ -73,21 +74,21 @@ pub async fn build_display_value( /// If not, the city name is taken from the geo location service async fn get_city_name( resource: RemoteResource, - kv_writer_mutex: Arc>>, + geo_location_cache_mutex: Arc>, ) -> Option { let resource_location = resource.location?; let resource_location_string = resource_location.to_string(); // First check cache - if kv_writer_mutex + if geo_location_cache_mutex .lock() .unwrap() .contains_key(resource_location_string.as_str()) { - kv_writer_mutex + geo_location_cache_mutex .lock() .unwrap() - .get_one(resource_location_string.as_str()) + .get(resource_location_string.as_str()) .map(|city_name| city_name.to_string()) } else { // Get city name @@ -95,9 +96,8 @@ async fn get_city_name( if let Some(city_name) = &city_name { // Write to cache - let mut kv_writer = kv_writer_mutex.lock().unwrap(); + let mut kv_writer = geo_location_cache_mutex.lock().unwrap(); kv_writer.insert(resource_location_string, city_name.clone()); - kv_writer.refresh(); } city_name