async def invalidate(self, *, data=None, sender=None): if isinstance(data, (bytes, str)): try: data = serialize.loads(data) except (TypeError, pickle.UnpicklingError): logger.warning("Invalid message") return assert isinstance(data, dict) assert "tid" in data assert "keys" in data if data["tid"] in self._ignored_tids: # on the same thread, ignore this sucker... self._ignored_tids.remove(data["tid"]) return for key in data["keys"]: if key in self._memory_cache: del self._memory_cache[key] push = data.get("push", {}) if isinstance(push, dict): for cache_key, ob in push.items(): self._memory_cache.set(cache_key, ob, self.get_size(ob)) # clean up possible memory leak while len(self._ignored_tids) > 100: self._ignored_tids.pop(0)
async def invalidate(self, *, data=None, sender=None): if isinstance(data, (bytes, str)): try: data = serialize.loads(data) except (TypeError, pickle.UnpicklingError): logger.warning("Invalid message") return assert isinstance(data, dict) assert 'tid' in data assert 'keys' in data if data['tid'] in self._ignored_tids: # on the same thread, ignore this sucker... self._ignored_tids.remove(data['tid']) return for key in data['keys']: if key in self._memory_cache: del self._memory_cache[key] for cache_key, ob in data.get('push', {}).items(): print(f'PUSH {cache_key}') self._memory_cache[cache_key] = ob # clean up possible memory leak while len(self._ignored_tids) > 100: self._ignored_tids.pop(0)
async def get(self, key): try: if key in self._memory_cache: logger.info('Retrieved {} from memory cache'.format(key)) return self._memory_cache[key] if self._obj_driver is not None: val = await self._obj_driver.get(CACHE_PREFIX + key) if val is not None: logger.info('Retrieved {} from redis cache'.format(key)) val = serialize.loads(val) self._memory_cache[key] = val except Exception: logger.warning('Error getting cache value', exc_info=True)
async def get(self, key): try: if key in self._memory_cache: logger.debug("Retrieved {} from memory cache".format(key)) return self._memory_cache[key] if self._obj_driver is not None: val = await self._obj_driver.get(CACHE_PREFIX + key) if val is not None: logger.debug("Retrieved {} from redis cache".format(key)) val = serialize.loads(val) size = self.get_size(val) self._memory_cache.set(key, val, size) return val except Exception: logger.warning("Error getting cache value", exc_info=True)
async def test_subscriber_ignores_trsn_on_invalidate(redis_container, guillotina_main): util = get_utility(ICacheUtility) await util.initialize() assert util.initialized assert util._obj_driver is not None assert util._subscriber is not None trns = mocks.MockTransaction(mocks.MockTransactionManager()) trns.added = trns.deleted = {} content = create_content() trns.modified = {content.__uuid__: content} rcache = BasicCache(trns) await rcache.clear() driver = await resolve_dotted_name("guillotina.contrib.redis").get_driver() await rcache.set("foobar", oid=content.__uuid__) assert serialize.loads(await driver.get(CACHE_PREFIX + "root-" + content.__uuid__)) == "foobar" assert util._memory_cache.get("root-" + content.__uuid__) == "foobar" assert await rcache.get(oid=content.__uuid__) == "foobar" assert "root-" + content.__uuid__ in util._memory_cache util.ignore_tid(5555) await driver.publish( app_settings["cache"]["updates_channel"], pickle.dumps({ "data": serialize.dumps({ "tid": 5555, "keys": ["root-" + content.__uuid__] }), "ruid": "nonce" }), ) await asyncio.sleep(1) # should be enough for pub/sub to finish # should still be there because we set to ignore this tid assert "root-" + content.__uuid__ in util._memory_cache # tid should also now be removed from ignored list assert 5555 not in util._ignored_tids
async def test_cache_set(redis_container, guillotina_main): util = get_utility(ICacheUtility) await util.initialize() assert util.initialized assert util._obj_driver is not None trns = mocks.MockTransaction(mocks.MockTransactionManager()) trns.added = trns.deleted = {} rcache = BasicCache(trns) await rcache.clear() await rcache.set("bar", oid="foo") # make sure it is in redis driver = await resolve_dotted_name("guillotina.contrib.redis").get_driver() val = await driver.get(CACHE_PREFIX + "root-foo") assert serialize.loads(val) == "bar" # but also in memory assert util._memory_cache.get("root-foo") == "bar" # and api matches.. assert await rcache.get(oid="foo") == "bar" await util.finalize(None)
async def test_subscriber_invalidates(redis_container, guillotina_main, loop): util = get_utility(ICacheUtility) await util.initialize() assert util.initialized assert util._obj_driver is not None assert util._subscriber is not None trns = mocks.MockTransaction(mocks.MockTransactionManager()) trns.added = trns.deleted = {} content = create_content() trns.modified = {content.__uuid__: content} rcache = BasicCache(trns) await rcache.clear() await rcache.set('foobar', oid=content.__uuid__) driver = await resolve_dotted_name('guillotina.contrib.redis').get_driver() assert serialize.loads(await driver.get(CACHE_PREFIX + 'root-' + content.__uuid__)) == "foobar" assert util._memory_cache.get('root-' + content.__uuid__) == 'foobar' assert await rcache.get(oid=content.__uuid__) == 'foobar' assert 'root-' + content.__uuid__ in util._memory_cache await driver.publish( app_settings['cache']['updates_channel'], pickle.dumps({ 'data': serialize.dumps({ 'tid': 32423, 'keys': ['root-' + content.__uuid__] }), 'ruid': 'nonce' })) await asyncio.sleep(1) # should be enough for pub/sub to finish assert 'root-' + content.__uuid__ not in util._memory_cache
async def test_invalidate_object(redis_container, guillotina_main, loop): util = get_utility(ICacheUtility) await util.initialize() assert util.initialized assert util._obj_driver is not None assert util._subscriber is not None trns = mocks.MockTransaction(mocks.MockTransactionManager()) trns.added = trns.deleted = {} content = create_content() trns.modified = {content.__uuid__: content} rcache = BasicCache(trns) await rcache.clear() await rcache.set('foobar', oid=content.__uuid__) driver = await resolve_dotted_name('guillotina.contrib.redis').get_driver() assert serialize.loads(await driver.get(CACHE_PREFIX + 'root-' + content.__uuid__)) == "foobar" assert util._memory_cache.get('root-' + content.__uuid__) == 'foobar' assert await rcache.get(oid=content.__uuid__) == 'foobar' await rcache.close(invalidate=True) assert await rcache.get(oid=content.__uuid__) is None
async def test_cache_clear(redis_container, guillotina_main, loop): util = get_utility(ICacheUtility) await util.initialize() assert util.initialized assert util._obj_driver is not None trns = mocks.MockTransaction(mocks.MockTransactionManager()) trns.added = trns.deleted = {} rcache = BasicCache(trns) await rcache.clear() await rcache.set('bar', oid='foo') # make sure it is in redis driver = await resolve_dotted_name('guillotina.contrib.redis').get_driver() assert serialize.loads(await driver.get(CACHE_PREFIX + 'root-foo')) == "bar" assert util._memory_cache.get('root-foo') == 'bar' assert await rcache.get(oid='foo') == 'bar' await rcache.clear() assert await rcache.get(oid='foo') is None await util.finalize(None)