there's a lot of variables to what you're doing, caching is a pretty open ended thing. Interesting here that you're caching in two different ways at the same time, both as a textual column and an in-memory map. I'd probably convert _perms_cache to use a Json column and just skip the extra in-memory dictionary, I'm not sure what you're gaining by caching twice like that.
But, for the general issue of a session-scoped, in-memory cache, this is common. You might want to consider that the problem you're trying to solve is a per-Session cache of permissions. But when you deal with each User object, you're storing a cache locally on each User. Why not just stick the dictionary on the Session itself ?
class User(Base):
# ....
_perms_cache = Column('permscache', Text())
@property
def perms(self):
sess = object_session(self)
if not hasattr(sess, '_perms_cache'):
sess._perms_cache = {}
if self in sess._perms_cache:
return sess._perms_cache[self]
else:
sess._perms_cache[self] = result = self._get_perms()
return result
# ...
def invalidate_user_perm_cache(session, gid):
try:
del session._perms_cache
except AttributeError:
pass
sub = session.query(MapUserAndUserGrp.uid) \
.filter(MapUserAndUserGrp.gid == gid)
session.query(User).filter(User.id.in_(sub)) \
.update({User._perms_cache: None}, synchronize_session = False)