# HG changeset patch # User Christophe de Vienne # Date 1422291550 -3600 # Node ID 38ed4c3ac3de5819dd6ab7b52ed0f6be1b1054b5 # Parent 48a952dc108febf6a996d10cc00cbdf69aad818b [tools] Provide a faster build_user The main trick is to use a cache of user entities. To do so, a few tools are needed since the entities are not supposed to be copied around between connexions. Related to #4870347 diff -r 48a952dc108f -r 38ed4c3ac3de pyramid_cubicweb/__init__.py --- a/pyramid_cubicweb/__init__.py Fri Jan 23 14:00:02 2015 +0100 +++ b/pyramid_cubicweb/__init__.py Mon Jan 26 17:59:10 2015 +0100 @@ -54,6 +54,7 @@ for name in aslist(config.registry.settings.get('cubicweb.includes', [])): config.include(name) + config.include('pyramid_cubicweb.tools') config.include('pyramid_cubicweb.core') if asbool(config.registry.settings.get('cubicweb.bwcompat', True)): diff -r 48a952dc108f -r 38ed4c3ac3de pyramid_cubicweb/tests/test_tools.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pyramid_cubicweb/tests/test_tools.py Mon Jan 26 17:59:10 2015 +0100 @@ -0,0 +1,26 @@ +from pyramid_cubicweb.tests import PyramidCWTest +from pyramid_cubicweb import tools + + +class ToolsTest(PyramidCWTest): + anonymous_allowed = True + + def test_clone_user(self): + with self.admin_access.repo_cnx() as cnx: + user = cnx.find('CWUser', login='anon').one() + user.login # fill the cache + clone = tools.clone_user(self.repo, user) + + self.assertEqual(clone.eid, user.eid) + self.assertEqual(clone.login, user.login) + + self.assertEqual(clone.cw_rset.rows, user.cw_rset.rows) + self.assertEqual(clone.cw_rset.rql, user.cw_rset.rql) + + def test_cnx_attach_entity(self): + with self.admin_access.repo_cnx() as cnx: + user = cnx.find('CWUser', login='anon').one() + + with self.admin_access.repo_cnx() as cnx: + tools.cnx_attach_entity(cnx, user) + self.assertEqual(user.login, 'anon') diff -r 48a952dc108f -r 38ed4c3ac3de pyramid_cubicweb/tools.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pyramid_cubicweb/tools.py Mon Jan 26 17:59:10 2015 +0100 @@ -0,0 +1,64 @@ +"""Various tools""" + +#: A short-term cache for user clones. +#: used by cached_build_user to speed-up repetitive calls to build_user +#: The expiration is handled in a dumb and brutal way: the whole cache is +#: cleared every 5 minutes. +_user_cache = {} + + +def clone_user(repo, user): + """Clone a CWUser instance. + + .. warning:: + + The returned clone is detached from any cnx. + Before using it in any way, it should be attached to a cnx that has not + this user already loaded. + """ + CWUser = repo.vreg['etypes'].etype_class('CWUser') + clone = CWUser( + None, + rset=user.cw_rset.copy(), + row=user.cw_row, + col=user.cw_col, + groups=set(user._groups) if hasattr(user, '_groups') else None, + properties=dict(user._properties) + if hasattr(user, '_properties') else None) + clone.cw_attr_cache = dict(user.cw_attr_cache) + return clone + + +def cnx_attach_entity(cnx, entity): + """Attach an entity to a cnx.""" + entity._cw = cnx + if entity.cw_rset: + entity.cw_rset.req = cnx + + +def cached_build_user(repo, eid): + """Cached version of + :meth:`cubicweb.server.repository.Repository._build_user` + """ + with repo.internal_cnx() as cnx: + if eid in _user_cache: + entity = clone_user(repo, _user_cache[eid]) + cnx_attach_entity(cnx, entity) + return entity + + user = repo._build_user(cnx, eid) + user.cw_clear_relation_cache() + _user_cache[eid] = clone_user(repo, user) + return user + + +def clear_cache(): + """Clear the user cache""" + _user_cache.clear() + + +def includeme(config): + repo = config.registry['cubicweb.repository'] + interval = int(config.registry.settings.get( + 'cubicweb.usercache.expiration_time', 60*5)) + repo.looping_task(interval, clear_cache)