From 06a0bea8d6127df4687a38e2025a0a870fa525ab Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 21 Oct 2013 21:17:32 +0200 Subject: [PATCH] Added fix to object.contents cache as well as reworked the extent of cache hooks on location modification. --- src/commands/default/building.py | 1 - src/comms/managers.py | 1 + src/objects/manager.py | 2 +- src/objects/models.py | 278 +------------------------------ src/players/models.py | 65 +------- src/scripts/models.py | 127 -------------- src/scripts/scripts.py | 34 ++-- src/server/caches.py | 157 +++++++++-------- src/server/initial_setup.py | 4 +- src/server/server.py | 12 +- src/typeclasses/models.py | 97 +---------- 11 files changed, 125 insertions(+), 653 deletions(-) diff --git a/src/commands/default/building.py b/src/commands/default/building.py index b38ba6135f..94e60424de 100644 --- a/src/commands/default/building.py +++ b/src/commands/default/building.py @@ -421,7 +421,6 @@ class CmdCreate(ObjManipCommand): if 'drop' in self.switches: if caller.location: obj.home = caller.location - print "Doing the drop" obj.move_to(caller.location, quiet=True) if string: caller.msg(string) diff --git a/src/comms/managers.py b/src/comms/managers.py index 018aa79ed8..7991a80604 100644 --- a/src/comms/managers.py +++ b/src/comms/managers.py @@ -338,6 +338,7 @@ class ChannelManager(models.Manager): ostring - the key or database id of the channel. """ channels = [] + if not ostring: return channels try: # try an id match first dbref = int(ostring.strip('#')) diff --git a/src/objects/manager.py b/src/objects/manager.py index 267f907301..3bce33c878 100644 --- a/src/objects/manager.py +++ b/src/objects/manager.py @@ -174,7 +174,7 @@ class ObjectManager(TypedObjectManager): excludeobj - one or more object keys to exclude from the match """ - exclude_restriction = excludeobj and Q(pk__in=[_GA(obj, "in") for obj in make_iter(excludeobj)]) or Q() + exclude_restriction = Q(pk__in=[_GA(obj, "id") for obj in make_iter(excludeobj)]) if excludeobj else Q() return self.filter(db_location=location).exclude(exclude_restriction) @returns_typeclass_list diff --git a/src/objects/models.py b/src/objects/models.py index 4cb5178298..9b993d6d66 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -145,265 +145,14 @@ class ObjectDB(TypedObject): _SA(self, "aliases", AliasHandler(self, category_prefix="object_")) _SA(self, "nicks", NickHandler(self)) # make sure to sync the contents cache when initializing - _GA(self, "contents_update")() + #_GA(self, "contents_update")() - # Wrapper properties to easily set database fields. These are - # @property decorators that allows to access these fields using - # normal python operations (without having to remember to save() - # etc). So e.g. a property 'attr' has a get/set/del decorator - # defined that allows the user to do self.attr = value, - # value = self.attr and del self.attr respectively (where self - # is the object in question). - - - ## player property (wraps db_player) - ##@property - #def __player_get(self): - # """ - # Getter. Allows for value = self.player. - # We have to be careful here since Player is also - # a TypedObject, so as to not create a loop. - # """ - # player = _GA(self, "db_player") - # #player = get_field_cache(self, "player") - # if player: - # try: - # return player.typeclass - # except Exception,e: - # print "player_get:", e - # return player - - ##@player.setter - #def __player_set(self, player): - # "Setter. Allows for self.player = value" - # if inherits_from(player, TypeClass): - # player = player.dbobj - # _SA(self, "db_player", player) - # _GA(self, "save")() - # #set_field_cache(self, "player", player) - # # we must set this here or superusers won't be able to - # # bypass lockchecks unless they start the game connected - # # to the character in question. - # self.locks.cache_lock_bypass(self) - - ##@player.deleter - #def __player_del(self): - # "Deleter. Allows for del self.player" - # _SA(self, "db_player", None) - # _GA(self, "save")() - # #del_field_cache(self, "player") - #player = property(__player_get, __player_set, __player_del) - - #sessid property (wraps db_sessid) - #@property - #def __sessid_get(self): - # """ - # Getter. Allows for value = self.sessid. Since sessid - # is directly related to self.player, we cannot have - # a sessid without a player being connected (but the - # opposite could be true). - # """ - # return _GA(self, "db_sessid") - # #if not get_field_cache(self, "sessid"): - # # del_field_cache(self, "sessid") - # #return get_field_cache(self, "sessid") - ##@sessid.setter - #def __sessid_set(self, sessid): - # "Setter. Allows for self.player = value" - # _SA(self, "db_sessid", sessid) - # _GA(self, "save")() - # #set_field_cache(self, "sessid", sessid) - ##@sessid.deleter - #def __sessid_del(self): - # "Deleter. Allows for del self.player" - # _SA(self, "db_sessid", None) - # _GA(self, "save")() - # #del_field_cache(self, "sessid") - #sessid = property(__sessid_get, __sessid_set, __sessid_del) - - def _at_db_player_save(self, new_value, old_value=None): + def _at_db_player_presave(self): """ - This is called automatically just before a new player is saved. + This hook is called automatically just before the player field is saved. """ # we need to re-cache this for superusers to bypass. self.locks.cache_lock_bypass(self) - return new_value - - def _at_db_location_save(self, new_value, old_value=None): - """ - This is called automatically just before a new location is saved. - """ - loc = new_value - try: - old_loc = old_value - # recursive location check - def is_loc_loop(loc, depth=0): - "Recursively traverse the target location to make sure we are not in it." - if depth > 10: return - elif loc == self: raise RuntimeError - elif loc == None: raise RuntimeWarning # just to quickly get out - return is_loc_loop(_GA(loc, "db_location"), depth+1) - # check so we don't create a location loop - if so, RuntimeError will be raised. - try: is_loc_loop(loc) - except RuntimeWarning: pass - - # update the contents of each location - if old_loc: - _GA(_GA(old_loc, "dbobj"), "contents_update")() - #print "after contents_update for old_loc:", old_loc.key, old_loc.contents - if loc: - _GA(_GA(loc, "dbobj"), "contents_update")() - #print "after contents_update for loc:", loc.key, loc.contents - return loc - except RuntimeError: - string = "Cannot set location, " - string += "%s.location = %s would create a location-loop." % (self.key, loc) - _GA(self, "msg")(_(string)) - logger.log_trace(string) - raise RuntimeError(string) - except Exception, e: - string = "Cannot set location (%s): " % str(e) - string += "%s is not a valid location." % loc - _GA(self, "msg")(_(string)) - logger.log_trace(string) - raise Exception(string) - - ## location property (wraps db_location) - ##@property - #def __location_get(self): - # "Getter. Allows for value = self.location." - # loc = get_field_cache(self, "location") - # if loc: - # return _GA(loc, "typeclass") - # return None - ##@location.setter - #def __location_set(self, location): - # "Setter. Allows for self.location = location" - # try: - # old_loc = _GA(self, "location") - # if ObjectDB.objects.dbref(location): - # # dbref search - # loc = ObjectDB.objects.dbref_search(location) - # loc = loc and _GA(loc, "dbobj") - # elif location and type(location) != ObjectDB: - # loc = _GA(location, "dbobj") - # else: - # loc = location - - # # recursive location check - # def is_loc_loop(loc, depth=0): - # "Recursively traverse the target location to make sure we are not in it." - # if depth > 10: return - # elif loc == self: raise RuntimeError - # elif loc == None: raise RuntimeWarning # just to quickly get out - # return is_loc_loop(_GA(loc, "db_location"), depth+1) - # # check so we don't create a location loop - if so, RuntimeError will be raised. - # try: is_loc_loop(loc) - # except RuntimeWarning: pass - - # # set the location - # set_field_cache(self, "location", loc) - # # update the contents of each location - # if old_loc: - # _GA(_GA(old_loc, "dbobj"), "contents_update")() - # if loc: - # _GA(loc, "contents_update")() - # except RuntimeError: - # string = "Cannot set location, " - # string += "%s.location = %s would create a location-loop." % (self.key, loc) - # _GA(self, "msg")(_(string)) - # logger.log_trace(string) - # raise RuntimeError(string) - # except Exception, e: - # string = "Cannot set location (%s): " % str(e) - # string += "%s is not a valid location." % location - # _GA(self, "msg")(_(string)) - # logger.log_trace(string) - # raise Exception(string) - ##@location.deleter - #def __location_del(self): - # "Deleter. Allows for del self.location" - # _GA(self, "location").contents_update() - # _SA(self, "db_location", None) - # _GA(self, "save")() - # del_field_cache(self, "location") - #location = property(__location_get, __location_set, __location_del) - - # home property (wraps db_home) - #@property - #def __home_get(self): - # "Getter. Allows for value = self.home" - # home = get_field_cache(self, "home") - # if home: - # return _GA(home, "typeclass") - # return None - ##@home.setter - #def __home_set(self, home): - # "Setter. Allows for self.home = value" - # try: - # if home == None or type(home) == ObjectDB: - # hom = home - # elif ObjectDB.objects.dbref(home): - # hom = ObjectDB.objects.dbref_search(home) - # if hom and hasattr(hom,'dbobj'): - # hom = _GA(hom, "dbobj") - # else: - # hom = _GA(home, "dbobj") - # else: - # hom = _GA(home, "dbobj") - # set_field_cache(self, "home", hom) - # except Exception: - # string = "Cannot set home: " - # string += "%s is not a valid home." - # _GA(self, "msg")(_(string) % home) - # logger.log_trace(string) - # #raise - ##@home.deleter - #def __home_del(self): - # "Deleter. Allows for del self.home." - # _SA(self, "db_home", None) - # _GA(self, "save")() - # del_field_cache(self, "home") - #home = property(__home_get, __home_set, __home_del) - - # destination property (wraps db_destination) - #@property - #def __destination_get(self): - # "Getter. Allows for value = self.destination." - # dest = get_field_cache(self, "destination") - # if dest: - # return _GA(dest, "typeclass") - # return None - ##@destination.setter - #def __destination_set(self, destination): - # "Setter. Allows for self.destination = destination" - # try: - # if destination == None or type(destination) == ObjectDB: - # # destination is None or a valid object - # dest = destination - # elif ObjectDB.objects.dbref(destination): - # # destination is a dbref; search - # dest = ObjectDB.objects.dbref_search(destination) - # if dest and _GA(self, "_hasattr")(dest,'dbobj'): - # dest = _GA(dest, "dbobj") - # else: - # dest = _GA(destination, "dbobj") - # else: - # dest = destination.dbobj - # set_field_cache(self, "destination", dest) - # except Exception: - # string = "Cannot set destination: " - # string += "%s is not a valid destination." % destination - # _GA(self, "msg")(string) - # logger.log_trace(string) - # raise - ##@destination.deleter - #def __destination_del(self): - # "Deleter. Allows for del self.destination" - # _SA(self, "db_destination", None) - # _GA(self, "save")() - # del_field_cache(self, "destination") - #destination = property(__destination_get, __destination_set, __destination_del) # cmdset_storage property. We use a custom wrapper to manage this. This also # seems very sensitive to caching, so leaving it be for now. /Griatch @@ -434,7 +183,6 @@ class ObjectDB(TypedObject): # ObjectDB class access methods/properties # - #@property def __sessions_get(self): """ @@ -473,23 +221,11 @@ class ObjectDB(TypedObject): exclude is one or more objects to not return """ - contents = get_prop_cache(self, "_contents") - if contents == None: - # this is the case if this is the first call - contents = _GA(self, "contents_update")() if exclude: - exclude = [obj.typeclass for obj in make_iter(exclude)] - return [obj for obj in contents if obj not in exclude] - else: - return contents + return ObjectDB.objects.get_contents(self, excludeobj=exclude) + return ObjectDB.objects.get_contents(self) contents = property(contents_get) - def contents_update(self): - "Re-sync the contents cache" - contents = ObjectDB.objects.get_contents(self) - set_prop_cache(self, "_contents", contents) - return contents - #@property def __exits_get(self): """ @@ -972,6 +708,6 @@ class ObjectDB(TypedObject): # Perform the deletion of the object super(ObjectDB, self).delete() # clear object's old location's content cache of this object - if old_loc: - _GA(old_loc.dbobj, "contents_update")() + #if old_loc: + # _GA(old_loc.dbobj, "contents_update")() return True diff --git a/src/players/models.py b/src/players/models.py index 47f94d4c3e..06f48ba4a5 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -117,29 +117,11 @@ class PlayerDB(TypedObject, AbstractUser): _SA(self, "aliases", AliasHandler(self, category_prefix="player_")) _SA(self, "nicks", NickHandler(self)) - # Wrapper properties to easily set database fields. These are - # @property decorators that allows to access these fields using - # normal python operations (without having to remember to save() - # etc). So e.g. a property 'attr' has a get/set/del decorator - # defined that allows the user to do self.attr = value, - # value = self.attr and del self.attr respectively (where self - # is the object in question). - - # obj property (wraps db_obj) - #@property - def objs_get(self): - "Getter. Allows for value = self.obj" - return list(self.db_objs.all()) - #@objs.setter - def objs_set(self, value): - "Setter. Allows for self.objs = value" - raise Exception("Use access methods to add new characters instead.") - #@obj.deleter - def objs_del(self): - "Deleter. Allows for del self.obj" - raise Exception("Use access methods to delete new characters instead.") - objs = property(objs_get, objs_set, objs_del) - characters = property(objs_get, objs_set, objs_del) + # alias to the objs property + def __characters_get(self): return self.objs + def __characters_set(self, value): self.objs = value + def __characters_del(self): raise Exception("Cannot delete name") + characters = property(__characters_get, __characters_set, __characters_del) # cmdset_storage property # This seems very sensitive to caching, so leaving it be for now /Griatch @@ -161,20 +143,6 @@ class PlayerDB(TypedObject, AbstractUser): _GA(self, "save")() cmdset_storage = property(cmdset_storage_get, cmdset_storage_set, cmdset_storage_del) - ##@property - #def is_connected_get(self): - # "Getter. Allows for value = self.is_connected" - # return get_field_cache(self, "is_connected") - ##@is_connected.setter - #def is_connected_set(self, value): - # "Setter. Allows for self.is_connected = value" - # set_field_cache(self, "is_connected", value) - ##@is_connected.deleter - #def is_connected_del(self): - # "Deleter. Allows for del is_connected" - # set_field_cache(self, "is_connected", False) - #is_connected = property(is_connected_get, is_connected_set, is_connected_del) - class Meta: "Define Django meta options" verbose_name = "Player" @@ -190,35 +158,14 @@ class PlayerDB(TypedObject, AbstractUser): def __unicode__(self): return u"%s(player#%s)" % (_GA(self, "name"), _GA(self, "dbid")) - - # name property (wraps self.user.username) #@property - #def __name_get(self): - # "Getter. Allows for value = self.name" - # return self.username - # #name = get_prop_cache(self, "_name") - # #if not name: - # # name = _GA(self,"user").username - # # set_prop_cache(self, "_name", name) - # #return name - ##@name.setter - #def __name_set(self, value): - # "Setter. Allows for player.name = newname" - # _SA(self, "username", value) - - # #_GA(self, "user").username = value - # #_GA(self, "user").save() - # #set_prop_cache(self, "_name", value) - ##@name.deleter - #def __name_del(self): - # "Deleter. Allows for del self.name" - # raise Exception("Player name cannot be deleted!") def __username_get(self): return _GA(self, "username") def __username_set(self, value): _SA(self, "username", value) def __username_del(self): _DA(self, "username", value) + # aliases name = property(__username_get, __username_set, __username_del) key = property(__username_get, __username_set, __username_del) diff --git a/src/scripts/models.py b/src/scripts/models.py index ae6924b996..c208a271bd 100644 --- a/src/scripts/models.py +++ b/src/scripts/models.py @@ -111,133 +111,6 @@ class ScriptDB(TypedObject): _SA(self, "attributes", AttributeHandler(self)) #_SA(self, "aliases", AliasHandler(self, category_prefix="script_")) - # Wrapper properties to easily set database fields. These are - # @property decorators that allows to access these fields using - # normal python operations (without having to remember to save() - # etc). So e.g. a property 'attr' has a get/set/del decorator - # defined that allows the user to do self.attr = value, - # value = self.attr and del self.attr respectively (where self - # is the script in question). - - # desc property (wraps db_desc) - #@property - #def __desc_get(self): - # "Getter. Allows for value = self.desc" - # return self.db_desc - ##@desc.setter - #def __desc_set(self, value): - # "Setter. Allows for self.desc = value" - # self.db_desc = value - # self.save() - ##@desc.deleter - #def __desc_del(self): - # "Deleter. Allows for del self.desc" - # self.db_desc = "" - # self.save() - #desc = property(__desc_get, __desc_set, __desc_del) - - # obj property (wraps db_obj) - #@property - #def __obj_get(self): - # "Getter. Allows for value = self.obj" - # return self.db_obj - ##@obj.setter - #def __obj_set(self, value): - # "Setter. Allows for self.obj = value" - # self.db_obj = value - # self.save() - ##@obj.deleter - #def __obj_del(self): - # "Deleter. Allows for del self.obj" - # self.db_obj = None - # self.save() - #obj = property(__obj_get, __obj_set, __obj_del) - - # interval property (wraps db_interval) - #@property - #def __interval_get(self): - # "Getter. Allows for value = self.interval" - # return self.db_interval - ##@interval.setter - #def __interval_set(self, value): - # "Setter. Allows for self.interval = value" - # self.db_interval = int(value) - # self.save() - ##@interval.deleter - #def __interval_del(self): - # "Deleter. Allows for del self.interval" - # self.db_interval = 0 - # self.save() - #interval = property(__interval_get, __interval_set, __interval_del) - - # start_delay property (wraps db_start_delay) - #@property - #def __start_delay_get(self): - # "Getter. Allows for value = self.start_delay" - # return self.db_start_delay - ##@start_delay.setter - #def __start_delay_set(self, value): - # "Setter. Allows for self.start_delay = value" - # self.db_start_delay = value - # self.save() - ##@start_delay.deleter - #def __start_delay_del(self): - # "Deleter. Allows for del self.start_delay" - # self.db_start_delay = False - # self.save() - #start_delay = property(__start_delay_get, __start_delay_set, __start_delay_del) - - # repeats property (wraps db_repeats) - #@property - #def __repeats_get(self): - # "Getter. Allows for value = self.repeats" - # return self.db_repeats - ##@repeats.setter - #def __repeats_set(self, value): - # "Setter. Allows for self.repeats = value" - # self.db_repeats = int(value) - # self.save() - ##@repeats.deleter - #def __repeats_del(self): - # "Deleter. Allows for del self.repeats" - # self.db_repeats = 0 - # self.save() - #repeats = property(__repeats_get, __repeats_set, __repeats_del) - - # persistent property (wraps db_persistent) - #@property - #def __persistent_get(self): - # "Getter. Allows for value = self.persistent" - # return self.db_persistent - ##@persistent.setter - #def __persistent_set(self, value): - # "Setter. Allows for self.persistent = value" - # self.db_persistent = value - # self.save() - ##@persistent.deleter - #def __persistent_del(self): - # "Deleter. Allows for del self.persistent" - # self.db_persistent = False - # self.save() - #persistent = property(__persistent_get, __persistent_set, __persistent_del) - - # is_active property (wraps db_is_active) - #@property - #def __is_active_get(self): - # "Getter. Allows for value = self.is_active" - # return self.db_is_active - ##@is_active.setter - #def __is_active_set(self, value): - # "Setter. Allows for self.is_active = value" - # self.db_is_active = value - # self.save() - ##@is_active.deleter - #def __is_active_del(self): - # "Deleter. Allows for del self.is_active" - # self.db_is_active = False - # self.save() - #is_active = property(__is_active_get, __is_active_set, __is_active_del) - # # # ScriptDB class properties diff --git a/src/scripts/scripts.py b/src/scripts/scripts.py index eafa2d2195..b150326b70 100644 --- a/src/scripts/scripts.py +++ b/src/scripts/scripts.py @@ -19,8 +19,6 @@ from src.utils import logger, is_pypy from django.utils.translation import ugettext as _ __all__ = ["Script", "DoNothing", "CheckSessions", "ValidateScripts", "ValidateChannelHandler"] -if not is_pypy: - __all__.append("ClearAttributeCache") _SESSIONS = None _ATTRIBUTE_CACHE_MAXSIZE = settings.ATTRIBUTE_CACHE_MAXSIZE # attr-cache size in MB. @@ -451,19 +449,19 @@ class ValidateChannelHandler(Script): #print "ValidateChannelHandler run." channelhandler.CHANNELHANDLER.update() -class ClearAttributeCache(Script): - "Clear the attribute cache." - def at_script_creation(self): - "Setup the script" - self.key = "sys_cache_clear" - self.desc = _("Clears the Attribute Cache") - self.interval = 3600 * 2 - self.persistent = True - def at_repeat(self): - "called every 2 hours. Sets a max attr-cache limit to 100 MB." # enough for normal usage? - if is_pypy: - # pypy don't support get_size, so we have to skip out here. - return - attr_cache_size, _, _ = caches.get_cache_sizes() - if attr_cache_size > _ATTRIBUTE_CACHE_MAXSIZE: - caches.flush_attr_cache() +#class ClearAttributeCache(Script): +# "Clear the attribute cache." +# def at_script_creation(self): +# "Setup the script" +# self.key = "sys_cache_clear" +# self.desc = _("Clears the Attribute Cache") +# self.interval = 3600 * 2 +# self.persistent = True +# def at_repeat(self): +# "called every 2 hours. Sets a max attr-cache limit to 100 MB." # enough for normal usage? +# if is_pypy: +# # pypy don't support get_size, so we have to skip out here. +# return +# attr_cache_size, _, _ = caches.get_cache_sizes() +# if attr_cache_size > _ATTRIBUTE_CACHE_MAXSIZE: +# caches.flush_attr_cache() diff --git a/src/server/caches.py b/src/server/caches.py index 1d92146daf..c1ea0086d6 100644 --- a/src/server/caches.py +++ b/src/server/caches.py @@ -97,27 +97,35 @@ def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwarg update_fields = _GA(_GA(instance, "_meta"), "fields") for field in update_fields: fieldname = field.name - new_value = _GA(instance, fieldname)#field.value_from_object(instance) - # try to see if there is a handler on object that should be triggered when saving. - handlername = "_at_%s_save" % fieldname + handlername = "_at_%s_presave" % fieldname handler = _GA(instance, handlername) if handlername in _GA(sender, '__dict__') else None if callable(handler): - #hid = hashid(instance, "-%s" % fieldname) - try: - old_value = _GA(instance, _GA(field, "get_cache_name")()) - except AttributeError: - old_value=None - # the handler may modify the stored value in various ways - # don't catch exceptions, the handler must work! - new_value = handler(new_value, old_value=old_value) - # we re-assign this to the field, save() will pick it up from there - _SA(instance, fieldname, new_value) + handler() + +def field_post_save(sender, instance=None, update_fields=None, raw=False, **kwargs): + """ + Called at the beginning of the field save operation. The save method + must be called with the update_fields keyword in order to be most efficient. + This method should NOT save; rather it is the save() that triggers this function. + Its main purpose is to allow to plug-in a save handler and oob handlers. + """ + if raw: + return + if update_fields: + # this is a list of strings at this point. We want field objects + update_fields = (_GA(_GA(instance, "_meta"), "get_field_by_name")(field)[0] for field in update_fields) + else: + # meta.fields are already field objects; get them all + update_fields = _GA(_GA(instance, "_meta"), "fields") + for field in update_fields: + fieldname = field.name + handlername = "_at_%s_postsave" % fieldname + handler = _GA(instance, handlername) if handlername in _GA(sender, '__dict__') else None + if callable(handler): + handler() trackerhandler = _GA(instance, "_trackerhandler") if "_trackerhandler" in _GA(instance, '__dict__') else None if trackerhandler: - trackerhandler.update(fieldname, new_value) - #if hid: - # # update cache - # _FIELD_CACHE[hid] = new_value + trackerhandler.update(fieldname, _GA(instance, fieldname)) #------------------------------------------------------------ # Attr cache - caching the attribute objects related to a given object to @@ -125,63 +133,64 @@ def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwarg # to any property). #------------------------------------------------------------ -# connected to m2m_changed signal in respective model class -def post_attr_update(sender, **kwargs): - "Called when the many2many relation changes (NOT when updating the value of an Attribute!)" - obj = kwargs['instance'] - model = kwargs['model'] - action = kwargs['action'] - if kwargs['reverse']: - # the reverse relation changed (the Attribute itself was acted on) - pass - else: - # forward relation changed (the Object holding the Attribute m2m field) - if not kwargs["pk_set"]: - return - if action == "post_add": - # cache all added objects - for attr_id in kwargs["pk_set"]: - attr_obj = model.objects.get(pk=attr_id) - set_attr_cache(obj, _GA(attr_obj, "db_key"), attr_obj) - elif action == "post_remove": - # obj.db_attributes.remove(attr) was called - for attr_id in kwargs["pk_set"]: - attr_obj = model.objects.get(pk=attr_id) - del_attr_cache(obj, _GA(attr_obj, "db_key")) - attr_obj.delete() - elif action == "post_clear": - # obj.db_attributes.clear() was called - clear_obj_attr_cache(obj) - -# access methods - -def get_attr_cache(obj, attrname): - "Called by getting attribute" - hid = hashid(obj, "-%s" % attrname) - return _ATTR_CACHE.get(hid, None) - -def set_attr_cache(obj, attrname, attrobj): - "Set the attr cache manually; this can be used to update" - global _ATTR_CACHE - hid = hashid(obj, "-%s" % attrname) - _ATTR_CACHE[hid] = attrobj - -def del_attr_cache(obj, attrname): - "Del attribute cache" - global _ATTR_CACHE - hid = hashid(obj, "-%s" % attrname) - if hid in _ATTR_CACHE: - del _ATTR_CACHE[hid] - -def flush_attr_cache(): - "Clear attribute cache" - global _ATTR_CACHE - _ATTR_CACHE = {} - -def clear_obj_attr_cache(obj): - global _ATTR_CACHE - hid = hashid(obj) - _ATTR_CACHE = {key:value for key, value in _ATTR_CACHE if not key.startswith(hid)} +## connected to m2m_changed signal in respective model class +#def post_attr_update(sender, **kwargs): +# "Called when the many2many relation changes (NOT when updating the value of an Attribute!)" +# obj = kwargs['instance'] +# model = kwargs['model'] +# action = kwargs['action'] +# if kwargs['reverse']: +# # the reverse relation changed (the Attribute itself was acted on) +# pass +# else: +# # forward relation changed (the Object holding the Attribute m2m field) +# if not kwargs["pk_set"]: +# return +# if action == "post_add": +# # cache all added objects +# for attr_id in kwargs["pk_set"]: +# attr_obj = model.objects.get(pk=attr_id) +# set_attr_cache(obj, _GA(attr_obj, "db_key"), attr_obj) +# elif action == "post_remove": +# # obj.db_attributes.remove(attr) was called +# for attr_id in kwargs["pk_set"]: +# attr_obj = model.objects.get(pk=attr_id) +# del_attr_cache(obj, _GA(attr_obj, "db_key")) +# attr_obj.delete() +# elif action == "post_clear": +# # obj.db_attributes.clear() was called +# clear_obj_attr_cache(obj) +# +# +## attr cache - this is only left as deprecated cache +# +#def get_attr_cache(obj, attrname): +# "Called by getting attribute" +# hid = hashid(obj, "-%s" % attrname) +# return _ATTR_CACHE.get(hid, None) +# +#def set_attr_cache(obj, attrname, attrobj): +# "Set the attr cache manually; this can be used to update" +# global _ATTR_CACHE +# hid = hashid(obj, "-%s" % attrname) +# _ATTR_CACHE[hid] = attrobj +# +#def del_attr_cache(obj, attrname): +# "Del attribute cache" +# global _ATTR_CACHE +# hid = hashid(obj, "-%s" % attrname) +# if hid in _ATTR_CACHE: +# del _ATTR_CACHE[hid] +# +#def flush_attr_cache(): +# "Clear attribute cache" +# global _ATTR_CACHE +# _ATTR_CACHE = {} +# +#def clear_obj_attr_cache(obj): +# global _ATTR_CACHE +# hid = hashid(obj) +# _ATTR_CACHE = {key:value for key, value in _ATTR_CACHE if not key.startswith(hid)} #------------------------------------------------------------ # Property cache - this is a generic cache for properties stored on models. diff --git a/src/server/initial_setup.py b/src/server/initial_setup.py index 8753638ace..f9226890e4 100644 --- a/src/server/initial_setup.py +++ b/src/server/initial_setup.py @@ -152,8 +152,8 @@ def create_system_scripts(): # update the channel handler to make sure it's in sync script3 = create.create_script(scripts.ValidateChannelHandler) # clear the attribute cache regularly - script4 = create.create_script(scripts.ClearAttributeCache) - if not script1 or not script2 or not script3 or not script4: + #script4 = create.create_script(scripts.ClearAttributeCache) + if not script1 or not script2 or not script3:# or not script4: print " Error creating system scripts." def start_game_time(): diff --git a/src/server/server.py b/src/server/server.py index e900b0694a..825f4b91ce 100644 --- a/src/server/server.py +++ b/src/server/server.py @@ -32,15 +32,17 @@ from src.server.sessionhandler import SESSIONS # setting up server-side field cache -from django.db.models.signals import pre_save +from django.db.models.signals import pre_save, post_save from src.server.caches import field_pre_save -pre_save.connect(field_pre_save, dispatch_uid="fieldcache") +#pre_save.connect(field_pre_save, dispatch_uid="fieldcache") +post_save.connect(field_pre_save, dispatch_uid="fieldcache") -from django.db.models.signals import m2m_changed from src.typeclasses.models import TypedObject -from src.server.caches import post_attr_update +#from src.server.caches import post_attr_update +#from django.db.models.signals import m2m_changed + # connect to attribute cache signal -m2m_changed.connect(post_attr_update, sender=TypedObject.db_attributes.through) +#m2m_changed.connect(post_attr_update, sender=TypedObject.db_attributes.through) _SA = object.__setattr__ diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index de4ff63144..bc88d0a336 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -40,7 +40,7 @@ from django.utils.encoding import smart_str from django.contrib.contenttypes.models import ContentType from src.utils.idmapper.models import SharedMemoryModel -from src.server.caches import get_prop_cache, set_prop_cache, flush_attr_cache +from src.server.caches import get_prop_cache, set_prop_cache #from src.server.caches import call_ndb_hooks from src.server.models import ServerConfig @@ -634,99 +634,12 @@ class TypedObject(SharedMemoryModel): # value = self.attr and del self.attr respectively (where self # is the object in question). - # key property (wraps db_key) - #@property - #def __key_get(self): - # "Getter. Allows for value = self.key" - # #return _GA(self, "db_key") - # return get_field_cache(self, "key") - ##@key.setter - #def __key_set(self, value): - # "Setter. Allows for self.key = value" - # set_field_cache(self, "key", value) - ##@key.deleter - #def __key_del(self): - # "Deleter. Allows for del self.key" - # raise Exception("Cannot delete objectdb key!") - #key = property(__key_get, __key_set, __key_del) - # name property (alias to self.key) def __name_get(self): return self.key def __name_set(self, value): self.key = value def __name_del(self): raise Exception("Cannot delete name") name = property(__name_get, __name_set, __name_del) - # typeclass_path property - we manage this separately. - #@property - #def __typeclass_path_get(self): - # "Getter. Allows for value = self.typeclass_path" - # return _GA(self, "db_typeclass_path") - ##@typeclass_path.setter - #def __typeclass_path_set(self, value): - # "Setter. Allows for self.typeclass_path = value" - # _SA(self, "db_typeclass_path", value) - # update_fields = ["db_typeclass_path"] if _GA(self, "_get_pk_val")(_GA(self, "_meta")) is not None else None - # _GA(self, "save")(update_fields=update_fields) - ##@typeclass_path.deleter - #def __typeclass_path_del(self): - # "Deleter. Allows for del self.typeclass_path" - # self.db_typeclass_path = "" - # _GA(self, "save")(update_fields=["db_typeclass_path"]) - #typeclass_path = property(__typeclass_path_get, __typeclass_path_set, __typeclass_path_del) - - # date_created property - #@property - #def __date_created_get(self): - # "Getter. Allows for value = self.date_created" - # return get_field_cache(self, "date_created") - ##@date_created.setter - #def __date_created_set(self, value): - # "Setter. Allows for self.date_created = value" - # raise Exception("Cannot change date_created!") - ##@date_created.deleter - #def __date_created_del(self): - # "Deleter. Allows for del self.date_created" - # raise Exception("Cannot delete date_created!") - #date_created = property(__date_created_get, __date_created_set, __date_created_del) - - # permissions property - #@property - #def __permissions_get(self): - # "Getter. Allows for value = self.name. Returns a list of permissions." - # perms = get_field_cache(self, "permissions") - # if perms: - # return [perm.strip() for perm in perms.split(',')] - # return [] - ##@permissions.setter - #def __permissions_set(self, value): - # "Setter. Allows for self.name = value. Stores as a comma-separated string." - # value = ",".join([utils.to_unicode(val).strip() for val in make_iter(value)]) - # set_field_cache(self, "permissions", value) - ##@permissions.deleter - #def __permissions_del(self): - # "Deleter. Allows for del self.name" - # self.db_permissions = "" - # self.save() - # del_field_cache(self, "permissions") - #permissions = property(__permissions_get, __permissions_set, __permissions_del) - - # lock_storage property (wraps db_lock_storage) - #@property - #def __lock_storage_get(self): - # "Getter. Allows for value = self.lock_storage" - # return get_field_cache(self, "lock_storage") - ##@lock_storage.setter - #def __lock_storage_set(self, value): - # """Saves the lock_storage. This is usually not called directly, but through self.lock()""" - # set_field_cache(self, "lock_storage", value) - ##@lock_storage.deleter - #def __lock_storage_del(self): - # "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" - # logger.log_errmsg("Lock_Storage (on %s) cannot be deleted. Use obj.lock.delete() instead." % self) - #lock_storage = property(__lock_storage_get, __lock_storage_set, __lock_storage_del) - - - # # # TypedObject main class methods and properties @@ -1000,14 +913,12 @@ class TypedObject(SharedMemoryModel): """ Type-level cleanup """ - flush_attr_cache() super(TypedObject, self).delete(*args, **kwargs) # # Object manipulation methods # - # def swap_typeclass(self, new_typeclass, clean_attributes=False, no_default=True): """ @@ -1230,16 +1141,12 @@ class TypedObject(SharedMemoryModel): raise Exception("Cannot delete the ndb object!") ndb = property(__ndb_get, __ndb_set, __ndb_del) - - - - # # ***** DEPRECATED METHODS BELOW ******* # # - # Fully attr_obj attributes. You usually access these + # Full attr_obj attributes. You usually access these # through the obj.db.attrname method. # Helper methods for attr_obj attributes