예제 #1
0
    def test_should_pass_extracted_key_to_eviction_strategy_on_entry_added_to_cache(
            self):
        # given
        key_extractor = Mock()
        key_extractor.format_key = Mock(return_value='key')

        eviction_strategy = Mock()

        @memoize(configuration=MutableCacheConfiguration.initialized_with(
            DefaultInMemoryCacheConfiguration()).set_eviction_strategy(
                eviction_strategy).set_key_extractor(key_extractor))
        @gen.coroutine
        def sample_method(arg, kwarg=None):
            return arg, kwarg

        # when
        yield sample_method('test', kwarg='args')
        yield _ensure_background_tasks_finished()
        yield sample_method('test', kwarg='args')
        yield _ensure_background_tasks_finished()

        # then
        eviction_strategy.mark_read.assert_called_once_with('key')
        _assert_called_once_with(self, eviction_strategy.mark_written,
                                 ('key', AnyObject()), {})
예제 #2
0
    def test_should_call_key_extractor_on_method_used(self):
        # given
        key_extractor = Mock()
        key_extractor.format_key = Mock(return_value='key')

        @memoize(configuration=MutableCacheConfiguration.initialized_with(
            DefaultInMemoryCacheConfiguration()).set_key_extractor(
                key_extractor))
        @gen.coroutine
        def sample_method(arg, kwarg=None):
            return arg, kwarg

        # when
        yield sample_method('test', kwarg='args')
        yield _ensure_background_tasks_finished()

        # then
        # ToDo: assert wrapped methods match somehow

        _assert_called_once_with(self, key_extractor.format_key, (
            AnyObject(),
            ('test', ),
            {
                'kwarg': 'args'
            },
        ), {})
예제 #3
0
    async def test_should_release_keys_on_caching_multiple_elements(self):
        # given
        value = 0
        storage = LocalInMemoryCacheStorage()
        key_extractor = Mock()
        key_extractor.format_key = Mock(
            side_effect=lambda method, args, kwargs: str(
                (args[0], kwargs.get('kwarg'))))

        @memoize(configuration=MutableCacheConfiguration.initialized_with(
            DefaultInMemoryCacheConfiguration()).set_eviction_strategy(
                LeastRecentlyUpdatedEvictionStrategy(capacity=2)).
                 set_key_extractor(key_extractor).set_storage(storage))
        async def get_value(arg, kwarg=None):
            return value

        # when
        await get_value('test1', kwarg='args1')
        await get_value('test2', kwarg='args2')
        await get_value('test3', kwarg='args3')
        await get_value('test4', kwarg='args4')
        await _ensure_asyncio_background_tasks_finished()

        # then
        s1 = await storage.get("('test1', 'args1')")
        s2 = await storage.get("('test2', 'args2')")
        s3 = await storage.get("('test3', 'args3')")
        s4 = await storage.get("('test4', 'args4')")

        self.assertIsNone(s1)
        self.assertIsNone(s2)
        self.assertIsNotNone(s3)
        self.assertIsNotNone(s4)
예제 #4
0
    async def test_should_return_current_value_on_second_call_after_update_time_reached_but_not_expiration_time(
            self):
        # given
        value = 0

        @memoize(configuration=MutableCacheConfiguration.initialized_with(
            DefaultInMemoryCacheConfiguration()).set_entry_builder(
                ProvidedLifeSpanCacheEntryBuilder(
                    update_after=timedelta(milliseconds=100),
                    expire_after=timedelta(minutes=5))))
        async def get_value(arg, kwarg=None):
            return value

        # when
        res1 = await get_value('test', kwarg='args')
        time.sleep(.200)
        await _ensure_asyncio_background_tasks_finished()
        value = 1
        await get_value('test', kwarg='args')
        await _ensure_asyncio_background_tasks_finished()
        res2 = await get_value('test', kwarg='args')

        # then
        self.assertEqual(0, res1)
        self.assertEqual(1, res2)
예제 #5
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
예제 #6
0
    async def test_should_throw_exception_on_configuration_not_ready(self):
        # given
        @memoize(configuration=MutableCacheConfiguration.initialized_with(
            DefaultInMemoryCacheConfiguration()).set_configured(False))
        async def get_value(arg, kwarg=None):
            raise ValueError("Get lost")

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

        # then
        expected = NotConfiguredCacheCalledException()
        self.assertEqual(str(expected), str(context.exception))
예제 #7
0
    async def test_should_throw_exception_on_refresh_timeout(self):
        # given

        @memoize(configuration=MutableCacheConfiguration.initialized_with(
            DefaultInMemoryCacheConfiguration()).set_method_timeout(
                timedelta(milliseconds=100)))
        async def get_value(arg, kwarg=None):
            await _ensure_asyncio_background_tasks_finished()
            time.sleep(.200)
            await _ensure_asyncio_background_tasks_finished()
            return 0

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

        # then
        expected = CachedMethodFailedException('Refresh timed out')
        self.assertEqual(str(expected), str(
            context.exception))  # ToDo: consider better comparision
예제 #8
0
    async def test_should_return_same_value_on_constant_key_function(self):
        # given
        value = 0
        key_extractor = Mock()
        key_extractor.format_key = Mock(return_value='lol')

        @memoize(configuration=MutableCacheConfiguration.initialized_with(
            DefaultInMemoryCacheConfiguration()).set_key_extractor(
                key_extractor))
        async def get_value(arg, kwarg=None):
            return value

        # when
        res1 = await get_value('test1', kwarg='args1')
        time.sleep(.200)
        await _ensure_asyncio_background_tasks_finished()
        value = 1
        res2 = await get_value('test2', kwarg='args2')

        # then
        self.assertEqual(0, res1)
        self.assertEqual(0, res2)
예제 #9
0
    def test_should_pass_extracted_key_to_storage_on_entry_added_to_cache(
            self):
        # given
        key_extractor = Mock()
        key_extractor.format_key = Mock(return_value='key')

        storage = Mock()
        storage.get = Mock(return_value=_as_future(None))
        storage.offer = Mock(return_value=_as_future(None))

        @memoize(configuration=MutableCacheConfiguration.initialized_with(
            DefaultInMemoryCacheConfiguration()).set_key_extractor(
                key_extractor).set_storage(storage))
        @gen.coroutine
        def sample_method(arg, kwarg=None):
            return arg, kwarg

        # when
        yield sample_method('test', kwarg='args')
        yield _ensure_background_tasks_finished()

        # then
        storage.get.assert_called_once_with('key')
        _assert_called_once_with(self, storage.offer, ('key', AnyObject()), {})
예제 #10
0
from memoize.entrybuilder import ProvidedLifeSpanCacheEntryBuilder
from memoize.wrapper import memoize

# scenario configuration
concurrent_requests = 5
request_batches_execution_count = 50
cached_value_ttl_millis = 200
delay_between_request_batches_millis = 70

# results/statistics
unique_calls_under_memoize = 0
unique_calls_under_different_cache = 0


@memoize(configuration=MutableCacheConfiguration.initialized_with(
    DefaultInMemoryCacheConfiguration()).set_entry_builder(
        ProvidedLifeSpanCacheEntryBuilder(update_after=timedelta(
            milliseconds=cached_value_ttl_millis))))
async def cached_with_memoize():
    global unique_calls_under_memoize
    unique_calls_under_memoize += 1
    await asyncio.sleep(0.01)
    return unique_calls_under_memoize


@cached(ttl=cached_value_ttl_millis / 1000, cache=SimpleMemoryCache)
async def cached_with_different_cache():
    global unique_calls_under_different_cache
    unique_calls_under_different_cache += 1
    await asyncio.sleep(0.01)
    return unique_calls_under_different_cache