From 34104993136e1a9593d7dac4b804da171548c171 Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 14 May 2015 17:57:49 +0200 Subject: [PATCH] Made the contents cache mechanism use the idmapper cache directly. This should hopefully avoid isses like #745 in the future. --- evennia/commands/default/building.py | 2 +- evennia/objects/models.py | 23 ++++++++++++------- evennia/objects/objects.py | 8 +++++++ .../server/profiling/dummyrunner_settings.py | 18 +++++++-------- evennia/typeclasses/models.py | 14 +++++------ evennia/utils/idmapper/models.py | 21 +++++++++-------- 6 files changed, 51 insertions(+), 35 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index 8b80a22b2f..2f1bcc4be9 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -1136,7 +1136,7 @@ class CmdName(ObjManipCommand): astring = " (%s)" % (", ".join(aliases)) # fix for exits - we need their exit-command to change name too if obj.destination: - obj.flush_from_cache() + obj.flush_from_cache(force=True) caller.msg("Object's name changed to '%s'%s." % (newname, astring)) diff --git a/evennia/objects/models.py b/evennia/objects/models.py index 16ccdc4472..67c18e0cc0 100644 --- a/evennia/objects/models.py +++ b/evennia/objects/models.py @@ -42,7 +42,8 @@ class ContentsHandler(object): """ self.obj = obj - self._cache = {} + self._pkcache = {} + self._idcache = obj.__class__.__instance_cache__ self.init() def init(self): @@ -50,7 +51,7 @@ class ContentsHandler(object): Re-initialize the content cache """ - self._cache.update(dict((obj.pk, obj) for obj in + self._pkcache.update(dict((obj.pk, None) for obj in ObjectDB.objects.filter(db_location=self.obj))) def get(self, exclude=None): @@ -64,10 +65,16 @@ class ContentsHandler(object): objects (list): the Objects inside this location """ + pks = self._pkcache.keys() if exclude: - exclude = [excl.pk for excl in make_iter(exclude)] - return [obj for key, obj in self._cache.items() if key not in exclude] - return self._cache.values() + pks = [pk for pk in pks if pk not in [excl.pk for excl in make_iter(exclude)]] + try: + return [self._idcache[pk] for pk in pks] + except KeyError: + # 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. + self.init() + return self.get(exclude=exclude) def add(self, obj): """ @@ -77,7 +84,7 @@ class ContentsHandler(object): obj (Object): object to add """ - self._cache[obj.pk] = obj + self._pkcache[obj.pk] = None def remove(self, obj): """ @@ -87,14 +94,14 @@ class ContentsHandler(object): obj (Object): object to remove """ - self._cache.pop(obj.pk, None) + self._pkcache.pop(obj.pk, None) def clear(self): """ Clear the contents cache and re-initialize """ - self._cache = {} + self._pkcache = {} self._init() #------------------------------------------------------------ diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index d12f11a111..e3182200f9 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1524,6 +1524,7 @@ class DefaultExit(DefaultObject): exit_cmdset.add(cmd) return exit_cmdset + # Command hooks def basetype_setup(self): """ @@ -1562,6 +1563,13 @@ class DefaultExit(DefaultObject): # we are resetting, or no exit-cmdset was set. Create one dynamically. self.cmdset.add_default(self.create_exit_cmdset(self), permanent=False) + def at_init(self): + """ + This is called when this objects is re-loaded from cache. When + that happens, we make sure to remove any old _exitset cmdset + (this most commonly occurs when renaming an existing exit) + """ + self.cmdset.remove_default() def at_traverse(self, traversing_object, target_location): """ diff --git a/evennia/server/profiling/dummyrunner_settings.py b/evennia/server/profiling/dummyrunner_settings.py index 4e64e4f894..9b122e4ad5 100644 --- a/evennia/server/profiling/dummyrunner_settings.py +++ b/evennia/server/profiling/dummyrunner_settings.py @@ -245,16 +245,16 @@ def c_moves_s(client): # #(0.1, c_creates_button), # #(0.4, c_moves)) # "inactive player" definition -ACTIONS = (c_login_nodig, - c_logout, - (1.0, c_idles)) +#ACTIONS = (c_login_nodig, +# c_logout, +# (1.0, c_idles)) ## "normal player" definition -#ACTIONS = ( c_login, -# c_logout, -# (0.01, c_digs), -# (0.39, c_looks), -# (0.2, c_help), -# (0.4, c_moves)) +ACTIONS = ( c_login, + c_logout, + (0.01, c_digs), + (0.39, c_looks), + (0.2, c_help), + (0.4, c_moves)) # walking tester. This requires a pre-made # "loop" of multiple rooms that ties back # to limbo (using @tunnel and @open) diff --git a/evennia/typeclasses/models.py b/evennia/typeclasses/models.py index 1617c798ba..19c4b18c1b 100644 --- a/evennia/typeclasses/models.py +++ b/evennia/typeclasses/models.py @@ -528,13 +528,13 @@ class TypedObject(SharedMemoryModel): # Memory management # - def flush_from_cache(self): - """ - Flush this object instance from cache, forcing an object reload. - Note that this will kill all temporary attributes on this object - since it will be recreated as a new Typeclass instance. - """ - self.__class__.flush_cached_instance(self) + #def flush_from_cache(self): + # """ + # Flush this object instance from cache, forcing an object reload. + # Note that this will kill all temporary attributes on this object + # since it will be recreated as a new Typeclass instance. + # """ + # self.__class__.flush_cached_instance(self) # # Attribute storage diff --git a/evennia/utils/idmapper/models.py b/evennia/utils/idmapper/models.py index 5c92f3a46e..463895b861 100644 --- a/evennia/utils/idmapper/models.py +++ b/evennia/utils/idmapper/models.py @@ -17,7 +17,7 @@ from django.db.models.signals import post_save from django.db.models.base import Model, ModelBase from django.db.models.signals import pre_delete, post_syncdb from evennia.utils import logger -from evennia.utils.utils import dbref, get_evennia_pids, to_str +from evennia.utils.utils import dbref, get_evennia_pids, to_str,calledby from manager import SharedMemoryManager @@ -59,10 +59,12 @@ class SharedMemoryModelBase(ModelBase): instance_key = cls._get_cache_key(args, kwargs) # depending on the arguments, we might not be able to infer the PK, so in that case we create a new instance + #print "SharedMemoryModelBase.__call__ 1: calledby:", calledby(3) + #print "SharedMemoryModelBase.__call__ 2: instance_key:", instance_key if instance_key is None: return new_instance() - cached_instance = cls.get_cached_instance(instance_key) + #print "SharedMemoryModelBase.__call__ 3: cached_instance:", cached_instance if cached_instance is None: cached_instance = new_instance() cls.cache_instance(cached_instance, new=True) @@ -242,9 +244,6 @@ class SharedMemoryModel(Model): # if the pk value happens to be a model instance (which can happen wich a FK), we'd rather use its own pk as the key result = result._get_pk_val() return result - #_get_cache_key = classmethod(_get_cache_key) - - @classmethod def get_cached_instance(cls, id): @@ -264,11 +263,12 @@ class SharedMemoryModel(Model): instance (Class instance): the instance to cache. new (bool, optional): this is the first time this instance is cached (i.e. this is not an update - operation). + operation like after a db save). """ - if instance._get_pk_val() is not None: - cls.__dbclass__.__instance_cache__[instance._get_pk_val()] = instance + pk = instance._get_pk_val() + if pk is not None: + cls.__dbclass__.__instance_cache__[pk] = instance if new: try: # trigger the at_init hook only @@ -327,8 +327,9 @@ class SharedMemoryModel(Model): 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) + pk = self._get_pk_val() + if pk and (force or not self._idmapper_recache_protection): + self.__class__.__dbclass__.__instance_cache__.pop(pk, None) def set_recache_protection(self, mode=True): """