diff --git a/Cargo.lock b/Cargo.lock index 79af97d0..c5021b09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -305,6 +305,11 @@ dependencies = [ "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lru" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "mapbox_expressions_to_sql" version = "0.1.0" @@ -324,6 +329,7 @@ dependencies = [ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "logger 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lru 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "mapbox_expressions_to_sql 0.1.0 (git+https://github.com/stepankuzmin/rust-mapbox-expressions-to-sql)", "persistent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "postgres 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -933,6 +939,7 @@ dependencies = [ "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" "checksum logger 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6c9172cb4c2f6c52117e25570983edcbb322f130b1031ae5d5d6b1abe7eeb493" +"checksum lru 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f1422de50f19f1e08676a563ba667974c0fe487a5c02eea23890983997c0184a" "checksum mapbox_expressions_to_sql 0.1.0 (git+https://github.com/stepankuzmin/rust-mapbox-expressions-to-sql)" = "" "checksum matches 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "100aabe6b8ff4e4a7e32c1c13523379802df0772b82466207ac25b013f193376" "checksum md5 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b6d9aab58e540f50b59d5cfa7f0da4c3d437476890e1e0b6206e230dce55a23c" diff --git a/Cargo.toml b/Cargo.toml index 5df3739c..ca5bb119 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ iron-test = "0.6.0" lazy_static = "0.2.11" log = "0.4.1" logger = "0.4.0" +lru = "0.1.7" mapbox_expressions_to_sql = { git = "https://github.com/stepankuzmin/rust-mapbox-expressions-to-sql" } persistent = "0.4.0" postgres = { version = "0.15", features = ["with-time", "with-uuid", "with-serde_json"] } diff --git a/src/main.rs b/src/main.rs index 1f45c5ae..1329dd4e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,11 @@ fn main() { let conn_string: String = env::var("DATABASE_URL") .expect("DATABASE_URL must be set"); - let chain = martin_lib::chain(conn_string); + let cache_size = env::var("CACHE_SIZE").ok() + .and_then(|cache_size| cache_size.parse::().ok()) + .unwrap_or(16384); + + let chain = martin_lib::chain(conn_string, cache_size); let port = 3000; let bind_addr = format!("0.0.0.0:{}", port); diff --git a/src/martin/lib.rs b/src/martin/lib.rs index 93726ed4..6c2cda8c 100644 --- a/src/martin/lib.rs +++ b/src/martin/lib.rs @@ -1,6 +1,7 @@ extern crate iron_test; extern crate iron; extern crate logger; +extern crate lru; extern crate mapbox_expressions_to_sql; extern crate persistent; extern crate r2d2_postgres; @@ -16,15 +17,17 @@ extern crate urlencoded; use iron::prelude::Chain; use logger::Logger; -use persistent::Read; +use lru::LruCache; +use persistent::{Read, State}; use rererouter::RouterBuilder; +mod cache; mod cors; mod db; mod routes; mod tileset; -pub fn chain(conn_string: String) -> iron::Chain { +pub fn chain(conn_string: String, cache_size: usize) -> iron::Chain { let mut router_builder = RouterBuilder::new(); router_builder.get(r"/index.json", routes::index); router_builder.get(r"/(?P[\w|\.]*)\.json", routes::tileset); @@ -32,7 +35,7 @@ pub fn chain(conn_string: String) -> iron::Chain { let router = router_builder.finalize(); let mut chain = Chain::new(router); - + let (logger_before, logger_after) = Logger::new(None); chain.link_before(logger_before); @@ -51,6 +54,9 @@ pub fn chain(conn_string: String) -> iron::Chain { } }; + let tile_cache = LruCache::new(cache_size); + chain.link(State::::both(tile_cache)); + chain.link_after(cors::Middleware); chain.link_after(logger_after); diff --git a/src/martin/routes.rs b/src/martin/routes.rs index a8fe122b..8eb81373 100644 --- a/src/martin/routes.rs +++ b/src/martin/routes.rs @@ -1,13 +1,15 @@ use iron::{status, mime, Request, Response, IronResult}; use iron::headers::{Headers, parsing}; use iron::prelude::{Plugin}; +use iron::url::Url; use mapbox_expressions_to_sql; -use persistent::Read; +use persistent::{Read, State}; use regex::{Regex, Captures}; use serde_json; use urlencoded::UrlEncodedQuery; use super::db; +use super::cache; use super::tileset; use tilejson::TileJSONBuilder; @@ -72,6 +74,22 @@ fn get_filter<'a>(req: &'a mut Request) -> Option<&'a String> { } pub fn tile(req: &mut Request, caps: Captures) -> IronResult { + let url: Url = req.url.clone().into(); + let lock = req.get::>().unwrap(); + let cached_tile = lock.write().ok().and_then(|mut guard| + guard.get(&url).cloned() + ); + + let content_type = "application/x-protobuf".parse::().unwrap(); + if let Some(tile) = cached_tile { + debug!("{} hit", url); + return match tile.len() { + 0 => Ok(Response::with((content_type, status::NoContent))), + _ => Ok(Response::with((content_type, status::Ok, tile))) + } + }; + + debug!("{} miss", url); let tilesets = req.get::>().unwrap(); let tileset = match tilesets.get(&caps["tileset"]) { Some(tileset) => tileset, @@ -112,7 +130,9 @@ pub fn tile(req: &mut Request, caps: Captures) -> IronResult { } }; - let content_type = "application/x-protobuf".parse::().unwrap(); + let mut guard = lock.write().unwrap(); + guard.put(req.url.clone().into(), tile.clone()); + match tile.len() { 0 => Ok(Response::with((content_type, status::NoContent))), _ => Ok(Response::with((content_type, status::Ok, tile)))