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()), {})
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' }, ), {})
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)
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)
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
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))
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
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)
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()), {})
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