Added at_idmapper_flush to allow objects more control of their flush characteristics.
This commit is contained in:
parent
cd616caf29
commit
15025c0586
6 changed files with 74 additions and 33 deletions
|
|
@ -51,10 +51,10 @@ class ContentsHandler(object):
|
||||||
Re-initialize the content cache
|
Re-initialize the content cache
|
||||||
|
|
||||||
"""
|
"""
|
||||||
print "__init__", self.obj, calledby()
|
#print "__init__", self.obj, calledby()
|
||||||
self._pkcache.update(dict((obj.pk, None) for obj in
|
self._pkcache.update(dict((obj.pk, None) for obj in
|
||||||
ObjectDB.objects.filter(db_location=self.obj) if obj.pk))
|
ObjectDB.objects.filter(db_location=self.obj) if obj.pk))
|
||||||
print "init contentscache: self._pkcache:", self.obj, self._pkcache
|
#print "init contentscache: self._pkcache:", self.obj, self._pkcache, id(self._pkcache), id(self)
|
||||||
|
|
||||||
def get(self, exclude=None):
|
def get(self, exclude=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -70,15 +70,15 @@ class ContentsHandler(object):
|
||||||
if exclude:
|
if exclude:
|
||||||
pks = [pk for pk in self._pkcache if pk not in [excl.pk for excl in make_iter(exclude)]]
|
pks = [pk for pk in self._pkcache if pk not in [excl.pk for excl in make_iter(exclude)]]
|
||||||
else:
|
else:
|
||||||
print calledby()
|
#print calledby()
|
||||||
print "get: self._pkcache", self.obj, self._pkcache
|
#print "get: self._pkcache", self.obj, self._pkcache, id(self._pkcache), id(self)
|
||||||
pks = self._pkcache
|
pks = self._pkcache
|
||||||
try:
|
try:
|
||||||
return [self._idcache[pk] for pk in pks]
|
return [self._idcache[pk] for pk in pks]
|
||||||
except KeyError, err:
|
except KeyError, err:
|
||||||
# this can happen if the idmapper cache was cleared for an object
|
# this can happen if the idmapper cache was cleared for an object
|
||||||
# in the contents cache. If so we need to re-initialize and try again.
|
# in the contents cache. If so we need to re-initialize and try again.
|
||||||
print "content_cache.get keyerror:", err, self._pkcache, self._idcache
|
#print "content_cache.get keyerror:", err, self._pkcache, self._idcache
|
||||||
self.init()
|
self.init()
|
||||||
try:
|
try:
|
||||||
return [self._idcache[pk] for pk in pks]
|
return [self._idcache[pk] for pk in pks]
|
||||||
|
|
@ -96,7 +96,7 @@ class ContentsHandler(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self._pkcache[obj.pk] = None
|
self._pkcache[obj.pk] = None
|
||||||
print "add self._pkcache:", self.obj, obj, obj.pk, self._pkcache
|
#print "add self._pkcache:", self.obj, obj, obj.pk, self._pkcache
|
||||||
|
|
||||||
def remove(self, obj):
|
def remove(self, obj):
|
||||||
"""
|
"""
|
||||||
|
|
@ -107,7 +107,7 @@ class ContentsHandler(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self._pkcache.pop(obj.pk, None)
|
self._pkcache.pop(obj.pk, None)
|
||||||
print "remove self._pkcache", self.obj, obj, obj.pk, self._pkcache
|
#print "remove self._pkcache", self.obj, obj, obj.pk, self._pkcache
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -116,7 +116,7 @@ class ContentsHandler(object):
|
||||||
"""
|
"""
|
||||||
self._pkcache = {}
|
self._pkcache = {}
|
||||||
self.init()
|
self.init()
|
||||||
print "clear _pkcache", self.obj, self._pkcache
|
#print "clear _pkcache", self.obj, self._pkcache
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -272,12 +272,12 @@ class ObjectDB(TypedObject):
|
||||||
# remove the safe flag
|
# remove the safe flag
|
||||||
del self._safe_contents_update
|
del self._safe_contents_update
|
||||||
|
|
||||||
print "location_set:", self.key, old_location, self.db_location
|
#print "location_set:", self.key, old_location, self.db_location
|
||||||
# update the contents cache
|
# update the contents cache
|
||||||
if old_location:
|
if old_location:
|
||||||
old_location.contents_cache.remove(self)
|
old_location.contents_cache.remove(self)
|
||||||
if self.db_location:
|
if self.db_location:
|
||||||
print "cache add:", self.db_location, self
|
#print "cache add:", self.db_location, self
|
||||||
self.db_location.contents_cache.add(self)
|
self.db_location.contents_cache.add(self)
|
||||||
|
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
con = self.contents_cache.get(exclude=exclude)
|
con = self.contents_cache.get(exclude=exclude)
|
||||||
print "contents_get:", self, con, calledby()
|
#print "contents_get:", self, con, id(self), calledby()
|
||||||
return con
|
return con
|
||||||
contents = property(contents_get)
|
contents = property(contents_get)
|
||||||
|
|
||||||
|
|
@ -267,7 +267,6 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
return "{}(#{})".format(self.name, self.id)
|
return "{}(#{})".format(self.name, self.id)
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
def search(self, searchdata,
|
def search(self, searchdata,
|
||||||
global_search=False,
|
global_search=False,
|
||||||
use_nicks=True, # should this default to off?
|
use_nicks=True, # should this default to off?
|
||||||
|
|
@ -1134,7 +1133,6 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
# hooks called when moving the object
|
# hooks called when moving the object
|
||||||
|
|
||||||
def at_before_move(self, destination):
|
def at_before_move(self, destination):
|
||||||
|
|
|
||||||
|
|
@ -325,6 +325,14 @@ class AttributeHandler(object):
|
||||||
self._catcache.pop(catkey, None)
|
self._catcache.pop(catkey, None)
|
||||||
self._cache_complete = False
|
self._cache_complete = False
|
||||||
|
|
||||||
|
def reset_cache(self):
|
||||||
|
"""
|
||||||
|
Reset cache from the outside.
|
||||||
|
"""
|
||||||
|
self._cache_complete = False
|
||||||
|
self._cache = {}
|
||||||
|
self._catcache = {}
|
||||||
|
|
||||||
def has(self, key=None, category=None):
|
def has(self, key=None, category=None):
|
||||||
"""
|
"""
|
||||||
Checks if the given Attribute (or list of Attributes) exists on
|
Checks if the given Attribute (or list of Attributes) exists on
|
||||||
|
|
@ -910,7 +918,6 @@ class NAttributeHandler(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self._store[key] = value
|
self._store[key] = value
|
||||||
self.obj.set_recache_protection()
|
|
||||||
|
|
||||||
def remove(self, key):
|
def remove(self, key):
|
||||||
"""
|
"""
|
||||||
|
|
@ -922,7 +929,6 @@ class NAttributeHandler(object):
|
||||||
"""
|
"""
|
||||||
if key in self._store:
|
if key in self._store:
|
||||||
del self._store[key]
|
del self._store[key]
|
||||||
self.obj.set_recache_protection(self._store)
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -376,6 +376,27 @@ class TypedObject(SharedMemoryModel):
|
||||||
raise Exception("dbref cannot be deleted!")
|
raise Exception("dbref cannot be deleted!")
|
||||||
dbref = property(__dbref_get, __dbref_set, __dbref_del)
|
dbref = property(__dbref_get, __dbref_set, __dbref_del)
|
||||||
|
|
||||||
|
def at_idmapper_flush(self):
|
||||||
|
"""
|
||||||
|
This is called when the idmapper cache is flushed and
|
||||||
|
allows customized actions when this happens.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
do_flush (bool): If True, flush this object as normal. If
|
||||||
|
False, don't flush and expect this object to handle
|
||||||
|
the flushing on its own.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if self.nattributes.all():
|
||||||
|
# we can't flush this object if we have non-persistent
|
||||||
|
# attributes stored - those would get lost! Nevertheless
|
||||||
|
# we try to flush as many references as we can.
|
||||||
|
self.attributes.reset_cache()
|
||||||
|
self.tags.reset_cache()
|
||||||
|
return False
|
||||||
|
# a normal flush
|
||||||
|
return True
|
||||||
|
|
||||||
#
|
#
|
||||||
# Object manipulation methods
|
# Object manipulation methods
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,13 @@ class TagHandler(object):
|
||||||
self._catcache.pop(catkey, None)
|
self._catcache.pop(catkey, None)
|
||||||
self._cache_complete = False
|
self._cache_complete = False
|
||||||
|
|
||||||
|
def reset_cache(self):
|
||||||
|
"""
|
||||||
|
Reset the cache from the outside.
|
||||||
|
"""
|
||||||
|
self._cache_complete = False
|
||||||
|
self._cache = {}
|
||||||
|
self._catcache = {}
|
||||||
|
|
||||||
def add(self, tag=None, category=None, data=None):
|
def add(self, tag=None, category=None, data=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,6 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
# the dbmodel is either the proxy base or ourselves
|
# the dbmodel is either the proxy base or ourselves
|
||||||
dbmodel = cls._meta.proxy_for_model if cls._meta.proxy else cls
|
dbmodel = cls._meta.proxy_for_model if cls._meta.proxy else cls
|
||||||
cls.__dbclass__ = dbmodel
|
cls.__dbclass__ = dbmodel
|
||||||
dbmodel._idmapper_recache_protection = False
|
|
||||||
if not hasattr(dbmodel, "__instance_cache__"):
|
if not hasattr(dbmodel, "__instance_cache__"):
|
||||||
# we store __instance_cache__ only on the dbmodel base
|
# we store __instance_cache__ only on the dbmodel base
|
||||||
dbmodel.__instance_cache__ = {}
|
dbmodel.__instance_cache__ = {}
|
||||||
|
|
@ -291,8 +290,10 @@ class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if force or not cls._idmapper_recache_protection:
|
if force or cls.at_idmapper_flush():
|
||||||
del cls.__dbclass__.__instance_cache__[key]
|
del cls.__dbclass__.__instance_cache__[key]
|
||||||
|
else:
|
||||||
|
cls._dbclass__.__instance_cache__[key].refresh_from_db()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -319,27 +320,37 @@ class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)):
|
||||||
cls.__dbclass__.__instance_cache__ = {}
|
cls.__dbclass__.__instance_cache__ = {}
|
||||||
else:
|
else:
|
||||||
cls.__dbclass__.__instance_cache__ = dict((key, obj) for key, obj in cls.__dbclass__.__instance_cache__.items()
|
cls.__dbclass__.__instance_cache__ = dict((key, obj) for key, obj in cls.__dbclass__.__instance_cache__.items()
|
||||||
if obj._idmapper_recache_protection)
|
if not obj.at_idmapper_flush())
|
||||||
|
for ins in cls.__dbclass__.__instance_cache__.itervalues():
|
||||||
|
ins.refresh_from_db()
|
||||||
#flush_instance_cache = classmethod(flush_instance_cache)
|
#flush_instance_cache = classmethod(flush_instance_cache)
|
||||||
|
|
||||||
# per-instance methods
|
# per-instance methods
|
||||||
|
|
||||||
|
def at_idmapper_flush(self):
|
||||||
|
"""
|
||||||
|
This is called when the idmapper cache is flushed and
|
||||||
|
allows customized actions when this happens.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
do_flush (bool): If True, flush this object as normal. If
|
||||||
|
False, don't flush and expect this object to handle
|
||||||
|
the flushing on its own.
|
||||||
|
"""
|
||||||
|
return True
|
||||||
|
|
||||||
def flush_from_cache(self, force=False):
|
def flush_from_cache(self, force=False):
|
||||||
"""
|
"""
|
||||||
Flush this instance from the instance cache. Use
|
Flush this instance from the instance cache. Use
|
||||||
`force` to override recache_protection for the object.
|
`force` to override the result of at_idmapper_flush() for the object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
pk = self._get_pk_val()
|
pk = self._get_pk_val()
|
||||||
if pk and (force or not self._idmapper_recache_protection):
|
if pk:
|
||||||
|
if force or self.at_idmapper_flush():
|
||||||
self.__class__.__dbclass__.__instance_cache__.pop(pk, None)
|
self.__class__.__dbclass__.__instance_cache__.pop(pk, None)
|
||||||
|
else:
|
||||||
def set_recache_protection(self, mode=True):
|
self.refresh_from_db()
|
||||||
"""
|
|
||||||
Set if this instance should be allowed to be recached.
|
|
||||||
|
|
||||||
"""
|
|
||||||
self._idmapper_recache_protection = bool(mode)
|
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -415,7 +426,6 @@ class WeakSharedMemoryModelBase(SharedMemoryModelBase):
|
||||||
def _prepare(cls):
|
def _prepare(cls):
|
||||||
super(WeakSharedMemoryModelBase, cls)._prepare()
|
super(WeakSharedMemoryModelBase, cls)._prepare()
|
||||||
cls.__dbclass__.__instance_cache__ = WeakValueDictionary()
|
cls.__dbclass__.__instance_cache__ = WeakValueDictionary()
|
||||||
cls._idmapper_recache_protection = False
|
|
||||||
|
|
||||||
|
|
||||||
class WeakSharedMemoryModel(with_metaclass(WeakSharedMemoryModelBase, SharedMemoryModel)):
|
class WeakSharedMemoryModel(with_metaclass(WeakSharedMemoryModelBase, SharedMemoryModel)):
|
||||||
|
|
@ -429,10 +439,9 @@ class WeakSharedMemoryModel(with_metaclass(WeakSharedMemoryModelBase, SharedMemo
|
||||||
|
|
||||||
def flush_cache(**kwargs):
|
def flush_cache(**kwargs):
|
||||||
"""
|
"""
|
||||||
Flush idmapper cache. When doing so the cache will
|
Flush idmapper cache. When doing so the cache will fire the
|
||||||
look for a property `_idmapper_cache_flush_safe` on the
|
at_idmapper_flush hook to allow the object to optionally handle
|
||||||
class/subclass instance and only flush if this
|
its own flushing.
|
||||||
is `True`.
|
|
||||||
|
|
||||||
Uses a signal so we make sure to catch cascades.
|
Uses a signal so we make sure to catch cascades.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue