Пример #1
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)
Пример #2
0
    def test_should_throw_exception_on_wrapped_method_failure(self):
        # given
        @memoize()
        @gen.coroutine
        def get_value(arg, kwarg=None):
            raise ValueError("Get lost")

        # when
        with self.assertRaises(Exception) as context:
            yield get_value('test1', kwarg='args1')

        # then
        expected = CachedMethodFailedException('Refresh failed to complete', ValueError('Get lost', ))
        self.assertEqual(str(expected), str(context.exception))  # ToDo: consider better comparision
Пример #3
0
    async def test_should_throw_exception_on_wrapped_method_failure(self):
        # given
        async def get_value(arg, kwarg=None):
            raise ValueError("Get lost")

        get_value_cached = memoize(method=get_value, configuration=DefaultInMemoryCacheConfiguration())

        # when
        with self.assertRaises(Exception) as context:
            await get_value_cached('test1', kwarg='args1')

        # then
        expected = CachedMethodFailedException('Refresh failed to complete', ValueError('Get lost', ))
        self.assertEqual(str(expected), str(context.exception))  # ToDo: consider better comparision
Пример #4
0
    async def test_should_throw_exception_on_refresh_timeout(self):
        # given
        async def get_value(arg, kwarg=None):
            await _ensure_asyncio_background_tasks_finished()
            time.sleep(.200)
            await _ensure_asyncio_background_tasks_finished()
            return 0

        get_value_cached = memoize(
            method=get_value,
            configuration=DefaultInMemoryCacheConfiguration(method_timeout=timedelta(milliseconds=100)))

        # when
        with self.assertRaises(Exception) as context:
            await get_value_cached('test1', kwarg='args1')

        # then
        expected = CachedMethodFailedException('Refresh timed out')
        self.assertEqual(str(expected), str(context.exception))  # ToDo: consider better comparision
Пример #5
0
    def test_should_throw_exception_on_refresh_timeout(self):
        # given

        @memoize(
            configuration=MutableCacheConfiguration
                .initialized_with(DefaultInMemoryCacheConfiguration())
                .set_method_timeout(timedelta(milliseconds=100))
        )
        @gen.coroutine
        def get_value(arg, kwarg=None):
            yield _ensure_background_tasks_finished()
            time.sleep(.200)
            yield _ensure_background_tasks_finished()
            return 0

        # when
        with self.assertRaises(Exception) as context:
            yield get_value('test1', kwarg='args1')

        # then
        expected = CachedMethodFailedException('Refresh timed out')
        self.assertEqual(str(expected), str(context.exception))  # ToDo: consider better comparision
Пример #6
0
    async def test_complex_showcase(self):
        # given
        UPDATE_MS = 400.0
        UPDATE_S = UPDATE_MS / 1000
        EXPIRE_MS = 800.0
        EXPIRE_S = EXPIRE_MS / 1000

        @memoize(configuration=DefaultInMemoryCacheConfiguration(
            update_after=timedelta(milliseconds=UPDATE_MS),
            expire_after=timedelta(milliseconds=EXPIRE_MS)))
        async def get_value_or_throw(arg, kwarg=None):
            if should_throw:
                raise ValueError(value)
            else:
                return value

        # when #1: initial call
        value, should_throw = 'ok #1', False
        res1 = await get_value_or_throw('test', kwarg='args')

        # when #2: background refresh - returns stale
        time.sleep(UPDATE_S)
        await _ensure_asyncio_background_tasks_finished()
        value, should_throw = 'ok #2', False
        res2 = await get_value_or_throw('test', kwarg='args')

        # when #3: no refresh (cache up-to-date)
        time.sleep(.10)
        await _ensure_asyncio_background_tasks_finished()
        value, should_throw = 'ok #3', False
        res3 = await get_value_or_throw('test', kwarg='args')

        # when #4: no refresh (cache up-to-date) but starts throwing
        time.sleep(.10)
        await _ensure_asyncio_background_tasks_finished()
        value, should_throw = 'throws #1', True
        res4 = await get_value_or_throw('test', kwarg='args')

        # when #5: background refresh while throwing - returns stale
        time.sleep(UPDATE_S)
        await _ensure_asyncio_background_tasks_finished()
        value, should_throw = 'throws #2', True
        res5 = await get_value_or_throw('test', kwarg='args')

        # when #6: stale value from cache as method raised during background refresh at #5
        time.sleep(.10)
        await _ensure_asyncio_background_tasks_finished()
        value, should_throw = 'throws #3', True
        res6 = await get_value_or_throw('test', kwarg='args')

        # when #7: stale expired - cache throws synchronously
        time.sleep(EXPIRE_S)
        await _ensure_asyncio_background_tasks_finished()
        value, should_throw = 'throws #4', True
        with self.assertRaises(Exception) as context:
            await get_value_or_throw('test', kwarg='args')

        # then
        self.assertEqual('ok #1', res1)
        self.assertEqual('ok #1',
                         res2)  # previous value - refresh in background
        self.assertEqual('ok #2', res3)  # value from cache - still relevant
        self.assertEqual('ok #2', res4)  # value from cache - still relevant
        self.assertEqual('ok #2',
                         res5)  # stale from cache - refresh in background
        self.assertEqual(
            'ok #2',
            res6)  # stale from cache - should be updated but method throws
        expected = CachedMethodFailedException('Refresh failed to complete',
                                               ValueError('throws #4', ))
        self.assertEqual(str(expected), str(
            context.exception))  # ToDo: consider better comparision