Commit e61b7505 by Calen Pennington

Merge pull request #8678 from cpennington/improve-structure-cache-metrics

Improve structure cache metrics
parents ba971db1 67783f8d
...@@ -21,7 +21,6 @@ from mongodb_proxy import autoretry_read, MongoProxy ...@@ -21,7 +21,6 @@ from mongodb_proxy import autoretry_read, MongoProxy
from xmodule.exceptions import HeartbeatFailure from xmodule.exceptions import HeartbeatFailure
from xmodule.modulestore import BlockData from xmodule.modulestore import BlockData
from xmodule.modulestore.split_mongo import BlockKey from xmodule.modulestore.split_mongo import BlockKey
import dogstats_wrapper as dog_stats_api
new_contract('BlockData', BlockData) new_contract('BlockData', BlockData)
...@@ -42,9 +41,10 @@ class Tagger(object): ...@@ -42,9 +41,10 @@ class Tagger(object):
An object used by :class:`QueryTimer` to allow timed code blocks An object used by :class:`QueryTimer` to allow timed code blocks
to add measurements and tags to the timer. to add measurements and tags to the timer.
""" """
def __init__(self): def __init__(self, default_sample_rate):
self.added_tags = [] self.added_tags = []
self.measures = [] self.measures = []
self.sample_rate = default_sample_rate
def measure(self, name, size): def measure(self, name, size):
""" """
...@@ -109,7 +109,7 @@ class QueryTimer(object): ...@@ -109,7 +109,7 @@ class QueryTimer(object):
metric_name: The name used to aggregate all of these metrics. metric_name: The name used to aggregate all of these metrics.
course_context: The course which the query is being made for. course_context: The course which the query is being made for.
""" """
tagger = Tagger() tagger = Tagger(self._sample_rate)
metric_name = "{}.{}".format(self._metric_base, metric_name) metric_name = "{}.{}".format(self._metric_base, metric_name)
start = time() start = time()
...@@ -125,24 +125,24 @@ class QueryTimer(object): ...@@ -125,24 +125,24 @@ class QueryTimer(object):
size, size,
timestamp=end, timestamp=end,
tags=[tag for tag in tags if not tag.startswith('{}:'.format(metric_name))], tags=[tag for tag in tags if not tag.startswith('{}:'.format(metric_name))],
sample_rate=self._sample_rate, sample_rate=tagger.sample_rate,
) )
dog_stats_api.histogram( dog_stats_api.histogram(
'{}.duration'.format(metric_name), '{}.duration'.format(metric_name),
end - start, end - start,
timestamp=end, timestamp=end,
tags=tags, tags=tags,
sample_rate=self._sample_rate, sample_rate=tagger.sample_rate,
) )
dog_stats_api.increment( dog_stats_api.increment(
metric_name, metric_name,
timestamp=end, timestamp=end,
tags=tags, tags=tags,
sample_rate=self._sample_rate, sample_rate=tagger.sample_rate,
) )
TIMER = QueryTimer(__name__, 0.001) TIMER = QueryTimer(__name__, 0.01)
def structure_from_mongo(structure, course_context=None): def structure_from_mongo(structure, course_context=None):
...@@ -222,33 +222,42 @@ class CourseStructureCache(object): ...@@ -222,33 +222,42 @@ class CourseStructureCache(object):
except InvalidCacheBackendError: except InvalidCacheBackendError:
self.no_cache_found = True self.no_cache_found = True
def get(self, key): def get(self, key, course_context=None):
"""Pull the compressed, pickled struct data from cache and deserialize.""" """Pull the compressed, pickled struct data from cache and deserialize."""
if self.no_cache_found: if self.no_cache_found:
return None return None
compressed_pickled_data = self.cache.get(key) with TIMER.timer("CourseStructureCache.get", course_context) as tagger:
if compressed_pickled_data is None: compressed_pickled_data = self.cache.get(key)
return None tagger.tag(from_cache=str(compressed_pickled_data is not None).lower())
return pickle.loads(zlib.decompress(compressed_pickled_data))
if compressed_pickled_data is None:
# Always log cache misses, because they are unexpected
tagger.sample_rate = 1
return None
tagger.measure('compressed_size', len(compressed_pickled_data))
pickled_data = zlib.decompress(compressed_pickled_data)
tagger.measure('uncompressed_size', len(pickled_data))
def set(self, key, structure): return pickle.loads(pickled_data)
def set(self, key, structure, course_context=None):
"""Given a structure, will pickle, compress, and write to cache.""" """Given a structure, will pickle, compress, and write to cache."""
if self.no_cache_found: if self.no_cache_found:
return None return None
pickled_data = pickle.dumps(structure, pickle.HIGHEST_PROTOCOL) with TIMER.timer("CourseStructureCache.set", course_context) as tagger:
# 1 = Fastest (slightly larger results) pickled_data = pickle.dumps(structure, pickle.HIGHEST_PROTOCOL)
compressed_pickled_data = zlib.compress(pickled_data, 1) tagger.measure('uncompressed_size', len(pickled_data))
# record compressed course structure sizes # 1 = Fastest (slightly larger results)
dog_stats_api.histogram( compressed_pickled_data = zlib.compress(pickled_data, 1)
'compressed_course_structure.size', tagger.measure('compressed_size', len(compressed_pickled_data))
len(compressed_pickled_data),
tags=[key] # Stuctures are immutable, so we set a timeout of "never"
) self.cache.set(key, compressed_pickled_data, None)
# Stuctures are immutable, so we set a timeout of "never"
self.cache.set(key, compressed_pickled_data, None)
class MongoConnection(object): class MongoConnection(object):
...@@ -311,14 +320,19 @@ class MongoConnection(object): ...@@ -311,14 +320,19 @@ class MongoConnection(object):
with TIMER.timer("get_structure", course_context) as tagger_get_structure: with TIMER.timer("get_structure", course_context) as tagger_get_structure:
cache = CourseStructureCache() cache = CourseStructureCache()
structure = cache.get(key) structure = cache.get(key, course_context)
tagger_get_structure.tag(from_cache='true' if structure else 'false') tagger_get_structure.tag(from_cache=str(bool(structure)).lower())
if not structure: if not structure:
# Always log cache misses, because they are unexpected
tagger_get_structure.sample_rate = 1
with TIMER.timer("get_structure.find_one", course_context) as tagger_find_one: with TIMER.timer("get_structure.find_one", course_context) as tagger_find_one:
doc = self.structures.find_one({'_id': key}) doc = self.structures.find_one({'_id': key})
tagger_find_one.measure("blocks", len(doc['blocks'])) tagger_find_one.measure("blocks", len(doc['blocks']))
structure = structure_from_mongo(doc, course_context) structure = structure_from_mongo(doc, course_context)
cache.set(key, structure) tagger_find_one.sample_rate = 1
cache.set(key, structure, course_context)
return structure return structure
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment