Fixed a bug in idmapper cache related to proxies. It caused issuses with cleaning cache from the proxy level. All caching is now done on the base model.
This commit is contained in:
parent
2fc15bbed5
commit
469e9c73c1
1 changed files with 38 additions and 33 deletions
|
|
@ -73,19 +73,13 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
Prepare the cache, making sure that proxies of the same db base
|
Prepare the cache, making sure that proxies of the same db base
|
||||||
share the same cache.
|
share the same cache.
|
||||||
"""
|
"""
|
||||||
def prep(dbmodel):
|
# the dbmodel is either the proxy base or ourselves
|
||||||
if not hasattr(dbmodel, "__instance_cache__"):
|
dbmodel = cls._meta.proxy_for_model if cls._meta.proxy else cls
|
||||||
dbmodel.__instance_cache__ = {}
|
cls.__dbclass__ = dbmodel
|
||||||
dbmodel._idmapper_recache_protection = False
|
dbmodel._idmapper_recache_protection = False
|
||||||
if not cls._meta.proxy:
|
if not hasattr(dbmodel, "__instance_cache__"):
|
||||||
# non-proxy models get the full cache
|
# we store __instance_cache__ only on the dbmodel base
|
||||||
prep(cls)
|
dbmodel.__instance_cache__ = {}
|
||||||
else:
|
|
||||||
# proxies get a reference to the cache
|
|
||||||
dbmodel = cls._meta.proxy_for_model
|
|
||||||
prep(dbmodel)
|
|
||||||
cls.__instance_cache__ = dbmodel.__instance_cache__
|
|
||||||
cls._idmapper_recache_protection = False
|
|
||||||
super(SharedMemoryModelBase, cls)._prepare()
|
super(SharedMemoryModelBase, cls)._prepare()
|
||||||
|
|
||||||
def __new__(cls, name, bases, attrs):
|
def __new__(cls, name, bases, attrs):
|
||||||
|
|
@ -99,6 +93,7 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
|
|
||||||
attrs["typename"] = cls.__name__
|
attrs["typename"] = cls.__name__
|
||||||
attrs["path"] = "%s.%s" % (attrs["__module__"], name)
|
attrs["path"] = "%s.%s" % (attrs["__module__"], name)
|
||||||
|
attrs["_is_deleted"] = False
|
||||||
|
|
||||||
# set up the typeclass handling only if a variable _is_typeclass is set on the class
|
# set up the typeclass handling only if a variable _is_typeclass is set on the class
|
||||||
def create_wrapper(cls, fieldname, wrappername, editable=True, foreignkey=False):
|
def create_wrapper(cls, fieldname, wrappername, editable=True, foreignkey=False):
|
||||||
|
|
@ -113,12 +108,7 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
"Wrapper for returing foreignkey fields"
|
"Wrapper for returing foreignkey fields"
|
||||||
if _GA(cls, "_is_deleted"):
|
if _GA(cls, "_is_deleted"):
|
||||||
raise ObjectDoesNotExist("Cannot access %s: Hosting object was already deleted." % fname)
|
raise ObjectDoesNotExist("Cannot access %s: Hosting object was already deleted." % fname)
|
||||||
value = _GA(cls, fieldname)
|
return _GA(cls, fieldname)
|
||||||
#print "_get_foreign:value:", value
|
|
||||||
try:
|
|
||||||
return _GA(value, "typeclass")
|
|
||||||
except:
|
|
||||||
return value
|
|
||||||
def _set_nonedit(cls, fname, value):
|
def _set_nonedit(cls, fname, value):
|
||||||
"Wrapper for blocking editing of field"
|
"Wrapper for blocking editing of field"
|
||||||
raise FieldError("Field %s cannot be edited." % fname)
|
raise FieldError("Field %s cannot be edited." % fname)
|
||||||
|
|
@ -200,6 +190,9 @@ class SharedMemoryModelBase(ModelBase):
|
||||||
|
|
||||||
|
|
||||||
class SharedMemoryModel(Model):
|
class SharedMemoryModel(Model):
|
||||||
|
"""
|
||||||
|
Base class for idmapped objects. Inherit from this.
|
||||||
|
"""
|
||||||
# CL: setting abstract correctly to allow subclasses to inherit the default
|
# CL: setting abstract correctly to allow subclasses to inherit the default
|
||||||
# manager.
|
# manager.
|
||||||
__metaclass__ = SharedMemoryModelBase
|
__metaclass__ = SharedMemoryModelBase
|
||||||
|
|
@ -209,10 +202,6 @@ class SharedMemoryModel(Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
#def __init__(cls, *args, **kwargs):
|
|
||||||
# super(SharedMemoryModel, cls).__init__(*args, **kwargs)
|
|
||||||
# cls.__idmapper_recache_protection = False
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _get_cache_key(cls, args, kwargs):
|
def _get_cache_key(cls, args, kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -244,6 +233,8 @@ class SharedMemoryModel(Model):
|
||||||
return result
|
return result
|
||||||
#_get_cache_key = classmethod(_get_cache_key)
|
#_get_cache_key = classmethod(_get_cache_key)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_cached_instance(cls, id):
|
def get_cached_instance(cls, id):
|
||||||
"""
|
"""
|
||||||
|
|
@ -251,8 +242,7 @@ class SharedMemoryModel(Model):
|
||||||
(which will always be the case when caching is disabled for this class). Please
|
(which will always be the case when caching is disabled for this class). Please
|
||||||
note that the lookup will be done even when instance caching is disabled.
|
note that the lookup will be done even when instance caching is disabled.
|
||||||
"""
|
"""
|
||||||
return cls.__instance_cache__.get(id)
|
return cls.__dbclass__.__instance_cache__.get(id)
|
||||||
#get_cached_instance = classmethod(get_cached_instance)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def cache_instance(cls, instance):
|
def cache_instance(cls, instance):
|
||||||
|
|
@ -260,21 +250,19 @@ class SharedMemoryModel(Model):
|
||||||
Method to store an instance in the cache.
|
Method to store an instance in the cache.
|
||||||
"""
|
"""
|
||||||
if instance._get_pk_val() is not None:
|
if instance._get_pk_val() is not None:
|
||||||
|
cls.__dbclass__.__instance_cache__[instance._get_pk_val()] = instance
|
||||||
cls.__instance_cache__[instance._get_pk_val()] = instance
|
|
||||||
#cache_instance = classmethod(cache_instance)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_all_cached_instances(cls):
|
def get_all_cached_instances(cls):
|
||||||
"return the objects so far cached by idmapper for this class."
|
"return the objects so far cached by idmapper for this class."
|
||||||
return cls.__instance_cache__.values()
|
return cls.__dbclass__.__instance_cache__.values()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _flush_cached_by_key(cls, key, force=True):
|
def _flush_cached_by_key(cls, key, force=True):
|
||||||
"Remove the cached reference."
|
"Remove the cached reference."
|
||||||
try:
|
try:
|
||||||
if force or not cls._idmapper_recache_protection:
|
if force or not cls._idmapper_recache_protection:
|
||||||
del cls.__instance_cache__[key]
|
del cls.__dbclass__.__instance_cache__[key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
@ -297,18 +285,33 @@ class SharedMemoryModel(Model):
|
||||||
keyword to remove all objects, safe or not.
|
keyword to remove all objects, safe or not.
|
||||||
"""
|
"""
|
||||||
if force:
|
if force:
|
||||||
cls.__instance_cache__ = {}
|
cls.__dbclass__.__instance_cache__ = {}
|
||||||
else:
|
else:
|
||||||
cls.__instance_cache__ = dict((key, obj) for key, obj in cls.__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 obj._idmapper_recache_protection)
|
||||||
#flush_instance_cache = classmethod(flush_instance_cache)
|
#flush_instance_cache = classmethod(flush_instance_cache)
|
||||||
|
|
||||||
# per-instance methods
|
# per-instance methods
|
||||||
|
|
||||||
|
def flush_from_cache(self, force=False):
|
||||||
|
"""
|
||||||
|
Flush this instance from the instance cache. Use
|
||||||
|
force to override recache_protection for the object.
|
||||||
|
"""
|
||||||
|
if self.pk and (force or not self._idmapper_recache_protection):
|
||||||
|
self.__class__.__dbclass__.__instance_cache__.pop(self.pk, None)
|
||||||
|
|
||||||
def set_recache_protection(self, mode=True):
|
def set_recache_protection(self, mode=True):
|
||||||
"set if this instance should be allowed to be recached."
|
"set if this instance should be allowed to be recached."
|
||||||
self._idmapper_recache_protection = bool(mode)
|
self._idmapper_recache_protection = bool(mode)
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Delete the object, clearing cache
|
||||||
|
"""
|
||||||
|
self.flush_from_cache()
|
||||||
|
super(SharedMemoryModel, self).delete(*args, **kwargs)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"save method tracking process/thread issues"
|
"save method tracking process/thread issues"
|
||||||
|
|
||||||
|
|
@ -335,7 +338,7 @@ class WeakSharedMemoryModelBase(SharedMemoryModelBase):
|
||||||
"""
|
"""
|
||||||
def _prepare(cls):
|
def _prepare(cls):
|
||||||
super(WeakSharedMemoryModelBase, cls)._prepare()
|
super(WeakSharedMemoryModelBase, cls)._prepare()
|
||||||
cls.__instance_cache__ = WeakValueDictionary()
|
cls.__dbclass__.__instance_cache__ = WeakValueDictionary()
|
||||||
cls._idmapper_recache_protection = False
|
cls._idmapper_recache_protection = False
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -367,7 +370,9 @@ def flush_cache(**kwargs):
|
||||||
else:
|
else:
|
||||||
yield cls
|
yield cls
|
||||||
|
|
||||||
|
#print "start flush ..."
|
||||||
for cls in class_hierarchy([SharedMemoryModel]):
|
for cls in class_hierarchy([SharedMemoryModel]):
|
||||||
|
#print cls
|
||||||
cls.flush_instance_cache()
|
cls.flush_instance_cache()
|
||||||
# run the python garbage collector
|
# run the python garbage collector
|
||||||
return gc.collect()
|
return gc.collect()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue