Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
        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
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
        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
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
        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
Ejemplo n.º 7
0
 def _cache_bucket(self, key, value):
     request_cache = RequestCache('experiments')
     request_cache.set(key, value)
     return value
Ejemplo n.º 8
0
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,
        )