-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add more metrics #130
Changes from 3 commits
55ecc36
d187ffb
ce3074c
f8787df
2b67729
2f0e11e
b17867e
63781b7
de0e2fd
5d1518c
4bcc41a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,17 @@ | ||
//! Tile cache manager | ||
use std::{collections::HashMap, fmt::Debug, ops::Deref, sync::Arc, time::Duration}; | ||
use std::{ | ||
collections::HashMap, | ||
fmt::Debug, | ||
ops::Deref, | ||
sync::Arc, | ||
time::{Duration, SystemTime}, | ||
}; | ||
|
||
use cadence::Counted; | ||
use tokio::sync::RwLock; | ||
|
||
use crate::{ | ||
adm, | ||
metrics::Metrics, | ||
server::location::LocationResult, | ||
server::ServerState, | ||
tags::Tags, | ||
|
@@ -32,6 +38,7 @@ pub struct AudienceKey { | |
#[derive(Debug)] | ||
pub struct Tiles { | ||
pub json: String, | ||
pub ttl: SystemTime, | ||
} | ||
|
||
/// The simple tile Cache | ||
|
@@ -71,14 +78,25 @@ async fn tile_cache_updater(state: &ServerState) { | |
tiles_cache, | ||
reqwest_client, | ||
adm_endpoint_url, | ||
metrics, | ||
settings, | ||
.. | ||
} = state; | ||
|
||
trace!("tile_cache_updater.."); | ||
let keys: Vec<_> = tiles_cache.read().await.keys().cloned().collect(); | ||
let tiles = tiles_cache.read().await; | ||
jrconlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
let keys: Vec<_> = tiles.keys().cloned().collect(); | ||
let mut cache_size = 0; | ||
let mut cache_count: i64 = 0; | ||
for key in keys { | ||
// proactively remove expired tiles from the cache, since we only | ||
// write new ones (or ones which return a value) | ||
if let Some(tile) = tiles.get(&key) { | ||
if tile.ttl <= SystemTime::now() { | ||
tiles_cache.write().await.remove(&key); | ||
} | ||
} | ||
let mut tags = Tags::default(); | ||
let metrics = Metrics::from(state); | ||
let result = adm::get_tiles( | ||
reqwest_client, | ||
adm_endpoint_url, | ||
|
@@ -92,6 +110,7 @@ async fn tile_cache_updater(state: &ServerState) { | |
key.form_factor, | ||
state, | ||
&mut tags, | ||
&metrics, | ||
None, | ||
) | ||
.await; | ||
|
@@ -103,10 +122,12 @@ async fn tile_cache_updater(state: &ServerState) { | |
Ok(tiles) => tiles, | ||
Err(e) => { | ||
error!("tile_cache_updater: response error {}", e); | ||
metrics.incr_with_tags("tile_cache_updater.error").send(); | ||
metrics.incr_with_tags("tile_cache_updater.error", Some(&tags)); | ||
continue; | ||
} | ||
}; | ||
cache_size += tiles.len(); | ||
cache_count += 1; | ||
// XXX: not a great comparison (comparing json Strings).. | ||
let new_tiles = { | ||
tiles_cache | ||
|
@@ -117,14 +138,23 @@ async fn tile_cache_updater(state: &ServerState) { | |
}; | ||
if new_tiles { | ||
trace!("tile_cache_updater updating: {:?}", &key); | ||
tiles_cache.write().await.insert(key, Tiles { json: tiles }); | ||
metrics.incr_with_tags("tile_cache_updater.update").send(); | ||
tiles_cache.write().await.insert( | ||
key, | ||
Tiles { | ||
json: tiles, | ||
ttl: SystemTime::now() + Duration::from_secs(settings.tiles_ttl as u64), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm debating if we should make this TTL * 2, to cover potential issues with ADM returning nothing, or an error. To be fair, we should probably also exit if adM returns anything other than a 200, and assume that they're having problems. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Initially I leaned towards hiding things from the client like this but #92 suggests the client wants more feedback from the other end. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, if a background refresh fails, how should we notify the client? |
||
}, | ||
); | ||
metrics.incr_with_tags("tile_cache_updater.update", Some(&tags)); | ||
} | ||
} | ||
Err(e) => { | ||
error!("tile_cache_updater error: {}", e); | ||
metrics.incr_with_tags("tile_cache_updater.error").send(); | ||
metrics.incr_with_tags("tile_cache_updater.error", Some(&tags)); | ||
} | ||
} | ||
} | ||
let metrics = Metrics::from(state); | ||
metrics.count("tile_cache_updater.size", cache_size as i64); | ||
metrics.count("tile_cache_updater.count", cache_count); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,9 @@ use maxminddb::{self, geoip2::City, MaxMindDBError}; | |
use serde::{self, Serialize}; | ||
|
||
use crate::error::{HandlerErrorKind, HandlerResult}; | ||
use crate::metrics::Metrics; | ||
use crate::settings::Settings; | ||
use crate::tags::Tags; | ||
|
||
const GOOG_LOC_HEADER: &str = "x-client-geo-location"; | ||
|
||
|
@@ -255,6 +257,7 @@ impl Location { | |
&self, | ||
ip_addr: IpAddr, | ||
preferred_languages: &[String], | ||
metrics: &Metrics, | ||
) -> HandlerResult<Option<LocationResult>> { | ||
if self.iploc.is_none() { | ||
return Ok(None); | ||
|
@@ -302,9 +305,19 @@ impl Location { | |
Ok(location) => { | ||
if let Some(names) = location.city.and_then(|c| c.names) { | ||
result.city = get_preferred_language_element(&preferred_languages, &names) | ||
} else { | ||
metrics.incr_with_tags( | ||
"location.unknown.city", | ||
Some(&Tags::from_extra(vec![("ip", ip_addr.to_string())])), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. extras are only sent to sentry, so all these one offs in this module are unnecessary for metrics calls (I guess that applies to the new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I debated adding these as proper tags, but I figured we'd hit cardinality issues pretty fast. I'll remove the tag from this for now. As for keeping the |
||
); | ||
}; | ||
if let Some(names) = location.country.and_then(|c| c.names) { | ||
result.country = get_preferred_language_element(&preferred_languages, &names) | ||
} else { | ||
jrconlin marked this conversation as resolved.
Show resolved
Hide resolved
|
||
metrics.incr_with_tags( | ||
"location.unknown.country", | ||
Some(&Tags::from_extra(vec![("ip", ip_addr.to_string())])), | ||
); | ||
}; | ||
if let Some(divs) = location.subdivisions { | ||
if let Some(subdivision) = divs.get(0) { | ||
|
@@ -313,6 +326,11 @@ impl Location { | |
get_preferred_language_element(&preferred_languages, names); | ||
} | ||
} | ||
} else { | ||
metrics.incr_with_tags( | ||
"location.unknown.subdivision", | ||
Some(&Tags::from_extra(vec![("ip", ip_addr.to_string())])), | ||
) | ||
} | ||
if let Some(location) = location.location { | ||
result.dma = location.metro_code; | ||
|
@@ -396,9 +414,13 @@ mod test { | |
..Default::default() | ||
}; | ||
let location = Location::from(&settings); | ||
let metrics = Metrics::noop(); | ||
if location.is_available() { | ||
// TODO: either mock maxminddb::Reader or pass it in as a wrapped impl | ||
let result = location.mmdb_locate(test_ip, &langs).await?.unwrap(); | ||
let result = location | ||
.mmdb_locate(test_ip, &langs, &metrics) | ||
.await? | ||
.unwrap(); | ||
assert_eq!(result.city, Some("Milton".to_owned())); | ||
assert_eq!(result.subdivision, Some("Washington".to_owned())); | ||
assert_eq!(result.country, Some("United States".to_owned())); | ||
|
@@ -417,8 +439,9 @@ mod test { | |
..Default::default() | ||
}; | ||
let location = Location::from(&settings); | ||
let metrics = Metrics::noop(); | ||
if location.is_available() { | ||
let result = location.mmdb_locate(test_ip, &langs).await?; | ||
let result = location.mmdb_locate(test_ip, &langs, &metrics).await?; | ||
assert!(result.is_none()); | ||
} else { | ||
println!("⚠Location Database not found, cannot test location, skipping"); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does "tag" in this name mean?
Also related, dunno if you saw this comment: #130 (review)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, I think that was the product of a bad replace.
I'll correct the names (and I'll also add
adm
)