Merge pull request #9155 from edx/jeskew/csm_api_events_to_datadog
Add DataDog histogram events to DjangoXBlockUserStateClient class.
This commit is contained in:
@@ -5,20 +5,18 @@ data in a Django ORM model.
|
||||
|
||||
import itertools
|
||||
from operator import attrgetter
|
||||
from time import time
|
||||
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
|
||||
import dogstats_wrapper as dog_stats_api
|
||||
from django.contrib.auth.models import User
|
||||
from xblock.fields import Scope, ScopeBase
|
||||
from edx_user_state_client.interface import XBlockUserStateClient, XBlockUserState
|
||||
from courseware.models import StudentModule, StudentModuleHistory
|
||||
from contracts import contract, new_contract
|
||||
from opaque_keys.edx.keys import UsageKey
|
||||
|
||||
new_contract('UsageKey', UsageKey)
|
||||
from edx_user_state_client.interface import XBlockUserStateClient, XBlockUserState
|
||||
|
||||
|
||||
class DjangoXBlockUserStateClient(XBlockUserStateClient):
|
||||
@@ -41,6 +39,9 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
|
||||
even though the actual stored state in the database will be ``"{}"``).
|
||||
"""
|
||||
|
||||
# Use this sample rate for DataDog events.
|
||||
API_DATADOG_SAMPLE_RATE = 0.01
|
||||
|
||||
class ServiceUnavailable(XBlockUserStateClient.ServiceUnavailable):
|
||||
"""
|
||||
This error is raised if the service backing this client is currently unavailable.
|
||||
@@ -94,6 +95,27 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
|
||||
usage_key = student_module.module_state_key.map_into_course(student_module.course_id)
|
||||
yield (student_module, usage_key)
|
||||
|
||||
def _ddog_increment(self, evt_time, evt_name):
|
||||
"""
|
||||
DataDog increment method.
|
||||
"""
|
||||
dog_stats_api.increment(
|
||||
'DjangoXBlockUserStateClient.{}'.format(evt_name),
|
||||
timestamp=evt_time,
|
||||
sample_rate=self.API_DATADOG_SAMPLE_RATE,
|
||||
)
|
||||
|
||||
def _ddog_histogram(self, evt_time, evt_name, value):
|
||||
"""
|
||||
DataDog histogram method.
|
||||
"""
|
||||
dog_stats_api.histogram(
|
||||
'DjangoXBlockUserStateClient.{}'.format(evt_name),
|
||||
value,
|
||||
timestamp=evt_time,
|
||||
sample_rate=self.API_DATADOG_SAMPLE_RATE,
|
||||
)
|
||||
|
||||
def get_many(self, username, block_keys, scope=Scope.user_state, fields=None):
|
||||
"""
|
||||
Retrieve the stored XBlock state for the specified XBlock usages.
|
||||
@@ -111,12 +133,16 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
|
||||
if scope != Scope.user_state:
|
||||
raise ValueError("Only Scope.user_state is supported, not {}".format(scope))
|
||||
|
||||
block_count = state_length = 0
|
||||
evt_time = time()
|
||||
|
||||
modules = self._get_student_modules(username, block_keys)
|
||||
for module, usage_key in modules:
|
||||
if module.state is None:
|
||||
continue
|
||||
|
||||
state = json.loads(module.state)
|
||||
state_length += len(module.state)
|
||||
|
||||
# If the state is the empty dict, then it has been deleted, and so
|
||||
# conformant UserStateClients should treat it as if it doesn't exist.
|
||||
@@ -129,8 +155,14 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
|
||||
for field in fields
|
||||
if field in state
|
||||
}
|
||||
block_count += 1
|
||||
yield XBlockUserState(username, usage_key, state, module.modified, scope)
|
||||
|
||||
# The rest of this method exists only to submit DataDog events.
|
||||
# Remove it once we're no longer interested in the data.
|
||||
self._ddog_histogram(evt_time, 'get_many.blks_out', block_count)
|
||||
self._ddog_histogram(evt_time, 'get_many.blks_size', state_length)
|
||||
|
||||
def set_many(self, username, block_keys_to_state, scope=Scope.user_state):
|
||||
"""
|
||||
Set fields for a particular XBlock.
|
||||
@@ -155,6 +187,8 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
|
||||
else:
|
||||
user = User.objects.get(username=username)
|
||||
|
||||
evt_time = time()
|
||||
|
||||
for usage_key, state in block_keys_to_state.items():
|
||||
student_module, created = StudentModule.objects.get_or_create(
|
||||
student=user,
|
||||
@@ -166,16 +200,43 @@ class DjangoXBlockUserStateClient(XBlockUserStateClient):
|
||||
},
|
||||
)
|
||||
|
||||
num_fields_before = num_fields_after = num_new_fields_set = len(state)
|
||||
num_fields_updated = 0
|
||||
if not created:
|
||||
if student_module.state is None:
|
||||
current_state = {}
|
||||
else:
|
||||
current_state = json.loads(student_module.state)
|
||||
num_fields_before = len(current_state)
|
||||
current_state.update(state)
|
||||
num_fields_after = len(current_state)
|
||||
student_module.state = json.dumps(current_state)
|
||||
# We just read this object, so we know that we can do an update
|
||||
student_module.save(force_update=True)
|
||||
|
||||
# The rest of this method exists only to submit DataDog events.
|
||||
# Remove it once we're no longer interested in the data.
|
||||
#
|
||||
# Record whether a state row has been created or updated.
|
||||
if created:
|
||||
self._ddog_increment(evt_time, 'set_many.state_created')
|
||||
else:
|
||||
self._ddog_increment(evt_time, 'set_many.state_updated')
|
||||
|
||||
# Event to record number of fields sent in to set/set_many.
|
||||
self._ddog_histogram(evt_time, 'set_many.fields_in', len(state))
|
||||
|
||||
# Event to record number of new fields set in set/set_many.
|
||||
num_new_fields_set = num_fields_after - num_fields_before
|
||||
self._ddog_histogram(evt_time, 'set_many.fields_set', num_new_fields_set)
|
||||
|
||||
# Event to record number of existing fields updated in set/set_many.
|
||||
num_fields_updated = max(0, len(state) - num_new_fields_set)
|
||||
self._ddog_histogram(evt_time, 'set_many.fields_updated', num_fields_updated)
|
||||
|
||||
# Event for the entire set_many call.
|
||||
self._ddog_histogram(evt_time, 'set_many.blks_updated', len(block_keys_to_state))
|
||||
|
||||
def delete_many(self, username, block_keys, scope=Scope.user_state, fields=None):
|
||||
"""
|
||||
Delete the stored XBlock state for a many xblock usages.
|
||||
|
||||
Reference in New Issue
Block a user