Beispiel #1
0
    async def wrapper(*args, **kwargs):
        if not configuration.configured():
            raise NotConfiguredCacheCalledException()

        configuration_snapshot = MutableCacheConfiguration.initialized_with(configuration)

        force_refresh = kwargs.pop('force_refresh_memoized', False)
        key = configuration_snapshot.key_extractor().format_key(method, args, kwargs)

        current_entry = await configuration_snapshot.storage().get(key)  # type: Optional[CacheEntry]
        if current_entry is not None:
            configuration_snapshot.eviction_strategy().mark_read(key)

        now = datetime.datetime.utcnow()

        def value_future_provider():
            return _apply_timeout(configuration_snapshot.method_timeout(), method(*args, **kwargs))

        if current_entry is None:
            logger.debug('Creating (blocking) entry for key %s', key)
            result = await refresh(current_entry, key, value_future_provider, configuration_snapshot)
        elif force_refresh:
            logger.debug('Forced entry update (blocking) for key %s', key)
            result = await refresh(current_entry, key, value_future_provider, configuration_snapshot)
        elif current_entry.expires_after <= now:
            logger.debug('Entry expiration reached - entry update (blocking) for key %s', key)
            result = await refresh(None, key, value_future_provider, configuration_snapshot)
        elif current_entry.update_after <= now:
            logger.debug('Entry update point expired - entry update (async - current entry returned) for key %s', key)
            _call_soon(refresh, current_entry, key, value_future_provider, configuration_snapshot)
            result = current_entry
        else:
            result = current_entry

        return result.value
Beispiel #2
0
    async def refresh(actual_entry: Optional[CacheEntry], key: CacheKey,
                      value_future_provider: Callable[[], asyncio.Future],
                      configuration_snapshot: CacheConfiguration):
        if actual_entry is None and update_statuses.is_being_updated(key):
            logger.debug(
                'As entry expired, waiting for results of concurrent refresh %s',
                key)
            entry = await update_statuses.await_updated(key)
            if entry is None:
                raise CachedMethodFailedException(
                    'Concurrent refresh failed to complete')
            return entry
        elif actual_entry is not None and update_statuses.is_being_updated(
                key):
            logger.debug(
                'As update point reached but concurrent update already in progress, '
                'relying on concurrent refresh to finish %s', key)
            return actual_entry
        elif not update_statuses.is_being_updated(key):
            update_statuses.mark_being_updated(key)
            try:
                value_future = value_future_provider()
                value = await value_future
                offered_entry = configuration_snapshot.entry_builder().build(
                    key, value)
                await configuration_snapshot.storage().offer(
                    key, offered_entry)
                update_statuses.mark_updated(key, offered_entry)
                logger.debug('Successfully refreshed cache for key %s', key)

                eviction_strategy = configuration_snapshot.eviction_strategy()
                eviction_strategy.mark_written(key, offered_entry)
                to_release = eviction_strategy.next_to_release()
                if to_release is not None:
                    _call_soon(try_release, to_release, configuration_snapshot)

                return offered_entry
            except _timeout_error_type() as e:
                logger.debug('Timeout for %s: %s', key, e)
                update_statuses.mark_update_aborted(key)
                raise CachedMethodFailedException('Refresh timed out')
            except Exception as e:
                logger.debug('Error while refreshing cache for %s: %s', key, e)
                update_statuses.mark_update_aborted(key)
                raise CachedMethodFailedException('Refresh failed to complete',
                                                  e)