def generate_course_expired_fragment_from_key(user, course_key): """ Like `generate_course_expired_fragment`, but using a CourseKey instead of a CourseOverview and using request-level caching. Either returns WebFragment to inject XBlock content into, or None if we shouldn't show a course expired message for this user. """ request_cache = RequestCache('generate_course_expired_fragment_from_key') cache_key = u'message:{},{}'.format(user.id, course_key) cache_response = request_cache.get_cached_response(cache_key) if cache_response.is_found: cached_message = cache_response.value # In this case, there is no message to display. if cached_message is None: return None return generate_fragment_from_message(cached_message) course = CourseOverview.get_from_id(course_key) message = generate_course_expired_message(user, course) request_cache.set(cache_key, message) if message is None: return None return generate_fragment_from_message(message)
def _decorator(*args, **kwargs): """ Arguments: args, kwargs: values passed into the wrapped function """ # Check to see if we have a result in cache. If not, invoke our wrapped # function. Cache and return the result to the caller. if request_cache_getter: request_cache = request_cache_getter(args, kwargs) else: request_cache = RequestCache(namespace) if request_cache: cache_key = _func_call_cache_key(f, arg_map_function, *args, **kwargs) cached_response = request_cache.get_cached_response(cache_key) if cached_response.is_found: return cached_response.value result = f(*args, **kwargs) if request_cache: request_cache.set(cache_key, result) return result
def render(self, context=None, request=None): """ This takes a render call with a context (from Django) and translates it to a render call on the mako template. When rendering a large sequence of XBlocks, we may end up rendering hundreds of small templates. Even if context processors aren't very expensive individually, they will quickly add up in that situation. To help guard against this, we do context processing once for a given request and then cache it. """ context_object = self._get_context_object(request) request_cache = RequestCache('context_processors') cache_response = request_cache.get_cached_response('cp_output') if cache_response.is_found: context_dictionary = dict(cache_response.value) else: context_dictionary = self._get_context_processors_output_dict(context_object) # The context_dictionary is later updated with template specific # variables. There are potentially hundreds of calls to templates # rendering and we don't want them to interfere with each other, so # we make a copy from the output of the context processors and then # recreate a new dict every time we pull from the cache. request_cache.set('cp_output', dict(context_dictionary)) if isinstance(context, Context): context_dictionary.update(context.flatten()) elif context is not None: context_dictionary.update(context) self._add_core_context(context_dictionary) self._evaluate_lazy_csrf_tokens(context_dictionary) return self.mako_template.render_unicode(**context_dictionary)
def inner_wrapper(*args, **kwargs): """ Wrapper function to decorate with. """ # Check to see if we have a result in cache. If not, invoke our wrapped # function. Cache and return the result to the caller. request_cache = RequestCache(namespace) cache_key = _func_call_cache_key(f, *args, **kwargs) cached_response = request_cache.get_cached_response(cache_key) if cached_response.is_found: return cached_response.value result = f(*args, **kwargs) request_cache.set(cache_key, result) return result
def get_first_purchase_offer_banner_fragment_from_key(user, course_key): """ Like `get_first_purchase_offer_banner_fragment`, but using a CourseKey instead of a CourseOverview and using request-level caching. Either returns WebFragment to inject XBlock content into, or None if we shouldn't show a first purchase offer message for this user. """ request_cache = RequestCache('get_first_purchase_offer_banner_fragment_from_key') cache_key = u'html:{},{}'.format(user.id, course_key) cache_response = request_cache.get_cached_response(cache_key) if cache_response.is_found: cached_html = cache_response.value if cached_html is None: return None return Fragment(cached_html) course = CourseOverview.get_from_id(course_key) offer_html = generate_offer_html(user, course) request_cache.set(cache_key, offer_html) return Fragment(offer_html)
def _cache_bucket(self, key, value): request_cache = RequestCache('experiments') request_cache.set(key, value) return value
def _log_and_monitor_expected_errors(request, exception, caller): """ Adds logging and monitoring for expected errors as needed. Arguments: request: The request exception: The exception caller: Either 'middleware' or 'drf` """ expected_error_settings_dict = _get_expected_error_settings_dict() if not expected_error_settings_dict: return # 'module.Class', for example, 'django.core.exceptions.PermissionDenied' # Note: `Exception` itself doesn't have a module. exception_module = getattr(exception, '__module__', '') separator = '.' if exception_module else '' module_and_class = f'{exception_module}{separator}{exception.__class__.__name__}' # Set checked_error_expected_from custom attribute to potentially help find issues where errors are never processed. set_custom_attribute('checked_error_expected_from', caller) # check if we already added logging/monitoring from a different caller request_cache = RequestCache('openedx.core.lib.request_utils') cached_handled_exception = request_cache.get_cached_response( 'handled_exception') if cached_handled_exception.is_found: cached_module_and_class = cached_handled_exception.value # exception was already processed by a different caller if cached_handled_exception.value == module_and_class: set_custom_attribute('checked_error_expected_from', 'multiple') return # We have confirmed using monitoring that it is very rare that middleware and drf handle different uncaught exceptions. # We will leave this attribute in place, but it is not worth investing in a workaround, especially given that # New Relic now offers its own expected error functionality, and this functionality may be simplified or removed. set_custom_attribute('unexpected_multiple_exceptions', cached_module_and_class) log.warning( "Unexpected scenario where different exceptions are handled by _log_and_monitor_expected_errors. " "See 'unexpected_multiple_exceptions' custom attribute. Skipping exception for %s.", module_and_class, ) return request_cache.set('handled_exception', module_and_class) if module_and_class not in expected_error_settings_dict: return exception_message = str(exception) set_custom_attribute('error_expected', True) expected_error_settings = expected_error_settings_dict[module_and_class] if expected_error_settings['is_ignored']: # Additional error details are needed for ignored errors, because they are otherwise # not available by our monitoring system, because they have been ignored. set_custom_attribute('error_ignored_class', module_and_class) set_custom_attribute('error_ignored_message', exception_message) if expected_error_settings['log_error']: exc_info = exception if expected_error_settings[ 'log_stack_trace'] else None request_path = getattr(request, 'path', 'request-path-unknown') log.info( 'Expected error %s: %s: seen for path %s', module_and_class, exception_message, request_path, exc_info=exc_info, )