Implement a more throughough cache-flush mechanism. This makes sure to flush foreignkey cache relations for objects not affected by flushing (normally objects with an NAttribute stored in them). Without ForeignKey flushing, the location of such objects would remain cached and drift out of sync, leading to content caches in turn desyncing. Resolves #1064.
This commit is contained in:
parent
15025c0586
commit
2b35140fe3
3 changed files with 15 additions and 15 deletions
|
|
@ -22,7 +22,7 @@ from django.core.exceptions import ObjectDoesNotExist
|
||||||
from evennia.typeclasses.models import TypedObject
|
from evennia.typeclasses.models import TypedObject
|
||||||
from evennia.objects.manager import ObjectDBManager
|
from evennia.objects.manager import ObjectDBManager
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
from evennia.utils.utils import (make_iter, dbref, lazy_property, calledby)
|
from evennia.utils.utils import (make_iter, dbref, lazy_property)
|
||||||
|
|
||||||
|
|
||||||
class ContentsHandler(object):
|
class ContentsHandler(object):
|
||||||
|
|
@ -51,10 +51,8 @@ class ContentsHandler(object):
|
||||||
Re-initialize the content cache
|
Re-initialize the content cache
|
||||||
|
|
||||||
"""
|
"""
|
||||||
#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, id(self._pkcache), id(self)
|
|
||||||
|
|
||||||
def get(self, exclude=None):
|
def get(self, exclude=None):
|
||||||
"""
|
"""
|
||||||
|
|
@ -70,15 +68,12 @@ 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 "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
|
|
||||||
self.init()
|
self.init()
|
||||||
try:
|
try:
|
||||||
return [self._idcache[pk] for pk in pks]
|
return [self._idcache[pk] for pk in pks]
|
||||||
|
|
@ -96,7 +91,6 @@ class ContentsHandler(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self._pkcache[obj.pk] = None
|
self._pkcache[obj.pk] = None
|
||||||
#print "add self._pkcache:", self.obj, obj, obj.pk, self._pkcache
|
|
||||||
|
|
||||||
def remove(self, obj):
|
def remove(self, obj):
|
||||||
"""
|
"""
|
||||||
|
|
@ -107,7 +101,6 @@ 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
|
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -116,7 +109,6 @@ class ContentsHandler(object):
|
||||||
"""
|
"""
|
||||||
self._pkcache = {}
|
self._pkcache = {}
|
||||||
self.init()
|
self.init()
|
||||||
#print "clear _pkcache", self.obj, self._pkcache
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -272,12 +264,10 @@ 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
|
|
||||||
# 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
|
|
||||||
self.db_location.contents_cache.add(self)
|
self.db_location.contents_cache.add(self)
|
||||||
|
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
|
|
|
||||||
|
|
@ -386,6 +386,14 @@ class TypedObject(SharedMemoryModel):
|
||||||
False, don't flush and expect this object to handle
|
False, don't flush and expect this object to handle
|
||||||
the flushing on its own.
|
the flushing on its own.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
The default implementation relies on being able to clear
|
||||||
|
Django's Foreignkey cache on objects not affected by the
|
||||||
|
flush (notably objects with an NAttribute stored). We rely
|
||||||
|
on this cache being stored on the format "_<fieldname>_cache".
|
||||||
|
If Django were to change this name internally, we need to
|
||||||
|
update here (unlikely, but marking just in case).
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if self.nattributes.all():
|
if self.nattributes.all():
|
||||||
# we can't flush this object if we have non-persistent
|
# we can't flush this object if we have non-persistent
|
||||||
|
|
@ -393,6 +401,12 @@ class TypedObject(SharedMemoryModel):
|
||||||
# we try to flush as many references as we can.
|
# we try to flush as many references as we can.
|
||||||
self.attributes.reset_cache()
|
self.attributes.reset_cache()
|
||||||
self.tags.reset_cache()
|
self.tags.reset_cache()
|
||||||
|
# flush caches for all related fields
|
||||||
|
for field in self._meta.fields:
|
||||||
|
name = "_%s_cache" % field.name
|
||||||
|
if field.is_relation and name in self.__dict__:
|
||||||
|
# a foreignkey - remove its cache
|
||||||
|
del self.__dict__[name]
|
||||||
return False
|
return False
|
||||||
# a normal flush
|
# a normal flush
|
||||||
return True
|
return True
|
||||||
|
|
|
||||||
|
|
@ -321,8 +321,6 @@ class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)):
|
||||||
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 not obj.at_idmapper_flush())
|
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
|
||||||
|
|
@ -349,8 +347,6 @@ class SharedMemoryModel(with_metaclass(SharedMemoryModelBase, Model)):
|
||||||
if pk:
|
if pk:
|
||||||
if force or self.at_idmapper_flush():
|
if force or self.at_idmapper_flush():
|
||||||
self.__class__.__dbclass__.__instance_cache__.pop(pk, None)
|
self.__class__.__dbclass__.__instance_cache__.pop(pk, None)
|
||||||
else:
|
|
||||||
self.refresh_from_db()
|
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
def delete(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue