From d2d9953f948206a1a1f5fbfaf464e2c4b1790eac Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 1 Nov 2012 11:20:07 +0100 Subject: [PATCH] Cache changes: Moved all caches (except idmapper) to central caching module. This makes it easier to overview cache memory usage (and clean it) as well as plug-in external cache mechanisms. --- src/commands/default/admin.py | 2 +- src/commands/default/general.py | 2 +- src/commands/default/system.py | 16 +-- src/objects/models.py | 99 +++++++++---------- src/players/models.py | 50 +++++----- src/server/caches.py | 166 ++++++++++++++++++++++++++++++++ src/server/initial_setup.py | 2 - src/typeclasses/models.py | 156 +++++++++++++----------------- 8 files changed, 315 insertions(+), 178 deletions(-) create mode 100644 src/server/caches.py diff --git a/src/commands/default/admin.py b/src/commands/default/admin.py index f16354b821..da6c78d15e 100644 --- a/src/commands/default/admin.py +++ b/src/commands/default/admin.py @@ -212,7 +212,7 @@ class CmdBan(MuxCommand): # replace * with regex form and compile it ipregex = ban.replace('.','\.') ipregex = ipregex.replace('*', '[0-9]{1,3}') - print "regex:",ipregex + #print "regex:",ipregex ipregex = re.compile(r"%s" % ipregex) bantup = ("", ban, ipregex, now, reason) # save updated banlist diff --git a/src/commands/default/general.py b/src/commands/default/general.py index 0bf5e50505..1dc74e3584 100644 --- a/src/commands/default/general.py +++ b/src/commands/default/general.py @@ -281,7 +281,7 @@ class CmdGet(MuxCommand): if caller == obj: caller.msg("You can't get yourself.") return - print obj, obj.location, caller, caller==obj.location + #print obj, obj.location, caller, caller==obj.location if caller == obj.location: caller.msg("You already hold that.") return diff --git a/src/commands/default/system.py b/src/commands/default/system.py index a727326ad1..76637c85d9 100644 --- a/src/commands/default/system.py +++ b/src/commands/default/system.py @@ -11,6 +11,7 @@ import sys import django, twisted from django.conf import settings +from src.server.caches import get_cache_sizes from src.server.sessionhandler import SESSIONS from src.scripts.models import ScriptDB from src.objects.models import ObjectDB @@ -607,13 +608,11 @@ class CmdServerLoad(MuxCommand): if not utils.host_os_is('posix'): string = "Process listings are only available under Linux/Unix." else: - global _resource, _idmapper, _attribute_cache + global _resource, _idmapper if not _resource: import resource as _resource if not _idmapper: from src.utils.idmapper import base as _idmapper - if not _attribute_cache: - from src.typeclasses.models import _ATTRIBUTE_CACHE as _attribute_cache import resource loadavg = os.getloadavg() @@ -684,10 +683,13 @@ class CmdServerLoad(MuxCommand): ftable = utils.format_table(table, 5) for row in ftable: string += "\n " + row[0] + row[1] + row[2] - # attribute cache - size = sum([sum([getsizeof(obj) for obj in dic.values()]) for dic in _attribute_cache.values()])/1024.0 - count = sum([len(dic) for dic in _attribute_cache.values()]) - string += "\n{w On-entity Attribute cache usage:{n %5.2f MB (%i items)" % (size, count) + # get sizes of other caches + attr_cache_info, field_cache_info, prop_cache_info = get_cache_sizes() + #size = sum([sum([getsizeof(obj) for obj in dic.values()]) for dic in _attribute_cache.values()])/1024.0 + #count = sum([len(dic) for dic in _attribute_cache.values()]) + string += "\n{w On-entity Attribute cache usage:{n %5.2f MB (%i attrs)" % (attr_cache_info[1], attr_cache_info[0]) + string += "\n{w On-entity Field cache usage:{n %5.2f MB (%i fields)" % (field_cache_info[1], field_cache_info[0]) + string += "\n{w On-entity Property cache usage:{n %5.2f MB (%i props)" % (prop_cache_info[1], prop_cache_info[0]) caller.msg(string) diff --git a/src/objects/models.py b/src/objects/models.py index 99118229b4..881b2b2e8f 100644 --- a/src/objects/models.py +++ b/src/objects/models.py @@ -20,7 +20,8 @@ from django.conf import settings from src.utils.idmapper.models import SharedMemoryModel from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler -from src.typeclasses.models import _get_cache, _set_cache, _del_cache +from src.server.caches import get_field_cache, set_field_cache, del_field_cache +from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache, hashid from src.typeclasses.typeclass import TypeClass from src.players.models import PlayerNick from src.objects.manager import ObjectManager @@ -45,10 +46,6 @@ _ME = _("me") _SELF = _("self") _HERE = _("here") -def clean_content_cache(obj): - "Clean obj's content cache" - _SA(obj, "_contents_cache", None) - #------------------------------------------------------------ # # ObjAttribute @@ -222,25 +219,24 @@ class ObjectDB(TypedObject): #@property def __aliases_get(self): "Getter. Allows for value = self.aliases" - try: - return _GA(self, "_cached_aliases") - except AttributeError: + aliases = get_prop_cache(self, "_aliases") + if aliases == None: aliases = list(Alias.objects.filter(db_obj=self).values_list("db_key", flat=True)) - _SA(self, "_cached_aliases", aliases) - return aliases + set_prop_cache(self, "_aliases", aliases) + return aliases #@aliases.setter def __aliases_set(self, aliases): "Setter. Allows for self.aliases = value" for alias in make_iter(aliases): new_alias = Alias(db_key=alias, db_obj=self) new_alias.save() - _SA(self, "_cached_aliases", aliases) + set_prop_cache(self, "_aliases", aliases) #@aliases.deleter def __aliases_del(self): "Deleter. Allows for del self.aliases" for alias in Alias.objects.filter(db_obj=self): alias.delete() - _DA(self, "_cached_aliases") + del_prop_cache(self, "_aliases") aliases = property(__aliases_get, __aliases_set, __aliases_del) # player property (wraps db_player) @@ -251,24 +247,24 @@ class ObjectDB(TypedObject): We have to be careful here since Player is also a TypedObject, so as to not create a loop. """ - return _get_cache(self, "player") + return get_field_cache(self, "player") #@player.setter def __player_set(self, player): "Setter. Allows for self.player = value" if inherits_from(player, TypeClass): player = player.dbobj - _set_cache(self, "player", player) + set_field_cache(self, "player", player) #@player.deleter def __player_del(self): "Deleter. Allows for del self.player" - _del_cache(self, "player") + del_field_cache(self, "player") player = property(__player_get, __player_set, __player_del) # location property (wraps db_location) #@property def __location_get(self): "Getter. Allows for value = self.location." - loc = _get_cache(self, "location") + loc = get_field_cache(self, "location") if loc: return _GA(loc, "typeclass") return None @@ -298,20 +294,20 @@ class ObjectDB(TypedObject): except RuntimeWarning: pass # set the location - _set_cache(self, "location", loc) + set_field_cache(self, "location", loc) # update the contents of each location if old_loc: - _GA(_GA(old_loc, "dbobj"), "contents_update")(self, remove=True) + _GA(_GA(old_loc, "dbobj"), "contents_update")() if loc: - _GA(loc, "contents_update")(_GA(self, "typeclass")) + _GA(loc, "contents_update")() except RuntimeError: - string = "Cannot set location: " - string += "%s.location = %s would create a location-loop." % (self.key, location) + 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: - string = "Cannot set location: " + 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) @@ -319,17 +315,17 @@ class ObjectDB(TypedObject): #@location.deleter def __location_del(self): "Deleter. Allows for del self.location" - _GA(self, "location").contents_update(self, remove=True) + _GA(self, "location").contents_update() _SA(self, "db_location", None) _GA(self, "save")() - _del_cache(self, "location") + 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_cache(self, "home") + home = get_field_cache(self, "home") if home: return _GA(home, "typeclass") return None @@ -347,7 +343,7 @@ class ObjectDB(TypedObject): hom = _GA(home, "dbobj") else: hom = _GA(home, "dbobj") - _set_cache(self, "home", hom) + set_field_cache(self, "home", hom) except Exception: string = "Cannot set home: " string += "%s is not a valid home." @@ -359,14 +355,14 @@ class ObjectDB(TypedObject): "Deleter. Allows for del self.home." _SA(self, "db_home", None) _GA(self, "save")() - _del_cache(self, "home") + 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_cache(self, "destination") + dest = get_field_cache(self, "destination") if dest: return _GA(dest, "typeclass") return None @@ -386,7 +382,7 @@ class ObjectDB(TypedObject): dest = _GA(destination, "dbobj") else: dest = destination.dbobj - _set_cache(self, "destination", dest) + set_field_cache(self, "destination", dest) except Exception: string = "Cannot set destination: " string += "%s is not a valid destination." % destination @@ -398,7 +394,7 @@ class ObjectDB(TypedObject): "Deleter. Allows for del self.destination" _SA(self, "db_destination", None) _GA(self, "save")() - _del_cache(self, "destination") + del_field_cache(self, "destination") destination = property(__destination_get, __destination_set, __destination_del) # cmdset_storage property. @@ -461,13 +457,11 @@ class ObjectDB(TypedObject): #@property def __is_superuser_get(self): "Check if user has a player, and if so, if it is a superuser." - #return any(self.sessions) and self.player.is_superuser return any(_GA(self, "sessions")) and _GA(_GA(self, "player"), "is_superuser") is_superuser = property(__is_superuser_get) # contents - _contents_cache = None #@property def contents_get(self, exclude=None): """ @@ -477,29 +471,25 @@ class ObjectDB(TypedObject): exclude is one or more objects to not return """ - if _GA(self, "_contents_cache") == None: - # create the cache - _SA(self, "_contents_cache", dict((obj.id, obj) for obj in ObjectDB.objects.get_contents(self))) - if exclude: - exclude = make_iter(exclude) - return [obj for obj in _GA(self, "_contents_cache").values() if obj not in exclude] - return _GA(self, "_contents_cache").values() - #return ObjectDB.objects.get_contents(self, excludeobj=exclude) + cont = get_prop_cache(self, "_contents") + exclude = make_iter(exclude) + if cont == None: + cont = _GA(self, "contents_update")() + return [obj for obj in cont if obj not in exclude] contents = property(contents_get) - def contents_update(self, obj, remove=False): + def contents_update(self): """ - Updates the contents property of the object. Called by + Updates the contents property of the object with a new + object Called by self.location_set. + + obj - + remove (true/false) - remove obj from content list """ - # this creates/updates the cache - _GA(self, "contents") - # set/remove objects from contents cache - cache = _GA(self, "_contents_cache") - if remove and obj.id in cache: - del cache[obj.id] - else: - cache[obj.id] = obj + cont = ObjectDB.objects.get_contents(self) + set_prop_cache(self, "_contents", cont) + return cont #@property def __exits_get(self): @@ -941,9 +931,10 @@ class ObjectDB(TypedObject): _GA(self, "clear_exits")() # Clear out any non-exit objects located within the object _GA(self, "clear_contents")() - # clear current location's content cache of this object - if _GA(self, "location"): - _GA(self, "location").contents_update(self, remove=True) + old_loc = _GA(self, "location") # Perform the deletion of the object super(ObjectDB, self).delete() + # clear object's old location's content cache of this object + if old_loc: + old_loc.contents_update() return True diff --git a/src/players/models.py b/src/players/models.py index 13c3e8a837..4306a8476e 100644 --- a/src/players/models.py +++ b/src/players/models.py @@ -45,7 +45,8 @@ from django.db import models from django.contrib.auth.models import User from django.utils.encoding import smart_str -from src.typeclasses.models import _get_cache, _set_cache, _del_cache +from src.server.caches import get_field_cache, set_field_cache, del_field_cache +from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache from src.server.sessionhandler import SESSIONS from src.players import manager from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler @@ -196,7 +197,7 @@ class PlayerDB(TypedObject): #@property def obj_get(self): "Getter. Allows for value = self.obj" - return _get_cache(self, "obj") + return get_field_cache(self, "obj") #@obj.setter def obj_set(self, value): "Setter. Allows for self.obj = value" @@ -207,14 +208,14 @@ class PlayerDB(TypedObject): if isinstance(value, _TYPECLASS): value = value.dbobj try: - _set_cache(self, "obj", value) + set_field_cache(self, "obj", value) except Exception: logger.log_trace() raise Exception("Cannot assign %s as a player object!" % value) #@obj.deleter def obj_del(self): "Deleter. Allows for del self.obj" - _del_cache(self, "obj") + del_field_cache(self, "obj") obj = property(obj_get, obj_set, obj_del) # whereas the name 'obj' is consistent with the rest of the code, @@ -223,17 +224,17 @@ class PlayerDB(TypedObject): #@property def character_get(self): "Getter. Allows for value = self.character" - return _get_cache(self, "obj") + return get_field_cache(self, "obj") #@character.setter def character_set(self, character): "Setter. Allows for self.character = value" if inherits_from(character, TypeClass): character = character.dbobj - _set_cache(self, "obj", character) + set_field_cache(self, "obj", character) #@character.deleter def character_del(self): "Deleter. Allows for del self.character" - _del_cache(self, "obj") + del_field_cache(self, "obj") character = property(character_get, character_set, character_del) # cmdset_storage property # This seems very sensitive to caching, so leaving it be for now /Griatch @@ -260,15 +261,15 @@ class PlayerDB(TypedObject): #@property def is_connected_get(self): "Getter. Allows for value = self.is_connected" - return _get_cache(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_cache(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_cache(self, "is_connected", False) + set_field_cache(self, "is_connected", False) is_connected = property(is_connected_get, is_connected_set, is_connected_del) class Meta: @@ -292,20 +293,21 @@ class PlayerDB(TypedObject): _db_model_name = "playerdb" # used by attributes to safely store objects _default_typeclass_path = settings.BASE_PLAYER_TYPECLASS or "src.players.player.Player" - _name_cache = None # name property (wraps self.user.username) #@property def name_get(self): "Getter. Allows for value = self.name" - if not _GA(self, "_name_cache"): - _SA(self, "_name_cache", _GA(self,"user").username) - return _GA(self, "_name_cache") + 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" _GA(self, "user").username = value _GA(self, "user").save() - _SA(self, "_name_cache", value) + set_prop_cache(self, "_name", value) #@name.deleter def name_del(self): "Deleter. Allows for del self.name" @@ -313,13 +315,14 @@ class PlayerDB(TypedObject): name = property(name_get, name_set, name_del) key = property(name_get, name_set, name_del) - _uid_cache = None #@property def uid_get(self): "Getter. Retrieves the user id" - if not _GA(self, "_uid_cache"): - _SA(self, "_uid_cache", _GA(self, "user").id) - return _GA(self, "_uid_cache") + uid = get_prop_cache(self, "_uid") + if not uid: + uid = _GA(self, "user").id + set_prop_cache(self, "_uid", uid) + return uid def uid_set(self, value): raise Exception("User id cannot be set!") def uid_del(self): @@ -342,12 +345,13 @@ class PlayerDB(TypedObject): sessions = property(sessions_get, sessions_set, sessions_del) #@property - _is_superuser_cache = None def is_superuser_get(self): "Superusers have all permissions." - if _GA(self, "_is_superuser_cache") == None: - _SA(self, "_is_superuser_cache", _GA(self, "user").is_superuser) - return _GA(self, "_is_superuser_cache") + is_suser = get_prop_cache(self, "_is_superuser") + if is_suser == None: + is_suser = _GA(self, "user").is_superuser + set_prop_cache(self, "_is_superuser", is_suser) + return is_suser is_superuser = property(is_superuser_get) # diff --git a/src/server/caches.py b/src/server/caches.py new file mode 100644 index 0000000000..19c560a6d4 --- /dev/null +++ b/src/server/caches.py @@ -0,0 +1,166 @@ +""" +Central caching module. + +""" + +from sys import getsizeof +from collections import defaultdict +from weakref import WeakKeyDictionary + +_GA = object.__getattribute__ +_SA = object.__setattr__ +_DA = object.__delattr__ + +# Cache stores +_ATTR_CACHE = defaultdict(dict) +_FIELD_CACHE = defaultdict(dict) +_PROP_CACHE = defaultdict(dict) + + +def get_cache_sizes(): + """ + Get cache sizes, expressed in number of objects and memory size in MB + """ + global _ATTR_CACHE, _FIELD_CACHE, _PROP_CACHE + + attr_n = sum(len(dic) for dic in _ATTR_CACHE.values()) + attr_mb = sum(sum(getsizeof(obj) for obj in dic.values()) for dic in _ATTR_CACHE.values()) / 1024.0 + + field_n = sum(len(dic) for dic in _FIELD_CACHE.values()) + field_mb = sum(sum([getsizeof(obj) for obj in dic.values()]) for dic in _FIELD_CACHE.values()) / 1024.0 + + prop_n = sum(len(dic) for dic in _PROP_CACHE.values()) + prop_mb = sum(sum([getsizeof(obj) for obj in dic.values()]) for dic in _PROP_CACHE.values()) / 1024.0 + + return (attr_n, attr_mb), (field_n, field_mb), (prop_n, prop_mb) + +def hashid(obj): + """ + Returns a per-class unique that combines the object's + class name with its idnum and creation time. This makes this id unique also + between different typeclassed entities such as scripts and + objects (which may still have the same id). + """ + try: + hid = _GA(obj, "_hashid") + except AttributeError: + date, idnum = _GA(obj, "db_date_created"), _GA(obj, "id") + if not idnum or not date: + # this will happen if setting properties on an object + # which is not yet saved + return None + # build the hashid + hid = "%s-%s-#%s" % (_GA(obj, "__class__"), date, idnum) + _SA(obj, "_hashid", hid) + return hid + +# on-object database field cache +def get_field_cache(obj, name): + "On-model Cache handler." + global _FIELD_CACHE + hid = hashid(obj) + if hid: + try: + return _FIELD_CACHE[hid][name] + except KeyError: + val = _GA(obj, "db_%s" % name) + _FIELD_CACHE[hid][name] = val + return val + return _GA(obj, "db_%s" % name) + +def set_field_cache(obj, name, val): + "On-model Cache setter. Also updates database." + _SA(obj, "db_%s" % name, val) + _GA(obj, "save")() + hid = hashid(obj) + if hid: + global _FIELD_CACHE + _FIELD_CACHE[hid][name] = val + +def del_field_cache(obj, name): + "On-model cache deleter" + hid = hashid(obj) + if hid: + try: + del _FIELD_CACHE[hid][name] + except KeyError: + pass + +def flush_field_cache(obj): + "On-model cache resetter" + hid = hashid(obj) + if hid: + global _FIELD_CACHE + del _FIELD_CACHE[hashid(obj)] + +# on-object property cache (unrelated to database) +# Note that the get/set_prop_cache handler do not actually +# get/set the property "on" the object but only reads the +# value to/from the cache. This is intended to be used +# with a get/setter property on the object. + +def get_prop_cache(obj, name, default=None): + "On-model Cache handler." + global _PROP_CACHE + hid = hashid(obj) + if hid: + try: + return _PROP_CACHE[hid][name] + except KeyError: + return default + _PROP_CACHE[hid][name] = val + return val + return default + +def set_prop_cache(obj, name, val): + "On-model Cache setter. Also updates database." + hid = hashid(obj) + if hid: + global _PROP_CACHE + _PROP_CACHE[hid][name] = val + +def del_prop_cache(obj, name): + "On-model cache deleter" + try: + del _PROP_CACHE[hashid(obj)][name] + except KeyError: + pass +def flush_field_cache(obj): + "On-model cache resetter" + hid = hashid(obj) + if hid: + global _PROP_CACHE + del _PROP_CACHE[hashid(obj)] + + +# attribute cache + +def get_attr_cache(obj, attrname): + """ + Attribute cache store + """ + return _ATTR_CACHE[hashid(obj)].get(attrname) + +def set_attr_cache(obj, attrname, attrobj): + """ + Cache an attribute object + """ + global _ATTR_CACHE + _ATTR_CACHE[hashid(obj)][attrname] = attrobj + +def del_attr_cache(obj, attrname): + """ + Remove attribute from cache + """ + global _ATTR_CACHE + try: + del _ATTR_CACHE[hashid(obj)][attrname] + except KeyError: + pass + +def flush_attr_cache(obj): + """ + Flush the attribute cache for this object. + """ + global _ATTR_CACHE + del _ATTR_CACHE[hashid(obj)] diff --git a/src/server/initial_setup.py b/src/server/initial_setup.py index f6127e2700..8df3d3686b 100644 --- a/src/server/initial_setup.py +++ b/src/server/initial_setup.py @@ -45,7 +45,6 @@ def create_objects(): # accessed by user.get_profile() and can also store attributes. # It also holds mud permissions, but for a superuser these # have no effect anyhow. - character_typeclass = settings.BASE_CHARACTER_TYPECLASS # Create the Player object as well as the in-game god-character @@ -85,7 +84,6 @@ def create_objects(): if not god_character.home: god_character.home = limbo_obj - def create_channels(): """ Creates some sensible default channels. diff --git a/src/typeclasses/models.py b/src/typeclasses/models.py index 5102bf0bf3..976b406057 100644 --- a/src/typeclasses/models.py +++ b/src/typeclasses/models.py @@ -39,6 +39,9 @@ from django.conf import settings 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_field_cache, set_field_cache, del_field_cache +from src.server.caches import get_attr_cache, set_attr_cache, del_attr_cache +from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache from src.server.models import ServerConfig from src.typeclasses import managers from src.locks.lockhandler import LockHandler @@ -59,28 +62,28 @@ _PDUMPS = pickle.dumps # Property Cache mechanism. -def _get_cache(obj, name): - "On-model Cache handler." - try: - return _GA(obj, "_cached_db_%s" % name) - except AttributeError: - val = _GA(obj, "db_%s" % name) - _SA(obj, "_cached_db_%s" % name, val) - return val -def _set_cache(obj, name, val): - "On-model Cache setter. Also updates database." - _SA(obj, "db_%s" % name, val) - _GA(obj, "save")() - _SA(obj, "_cached_db_%s" % name, val) -def _del_cache(obj, name): - "On-model cache deleter" - try: - _DA(obj, "_cached_db_%s" % name) - except AttributeError: - pass -def _clean_cache(obj): - "On-model cache resetter" - [_DA(obj, cname) for cname in obj.__dict__.keys() if cname.startswith("_cached_db_")] +#def _get_cache(obj, name): +# "On-model Cache handler." +# try: +# return _GA(obj, "_cached_db_%s" % name) +# except AttributeError: +# val = _GA(obj, "db_%s" % name) +# _SA(obj, "_cached_db_%s" % name, val) +# return val +#def set_prop_cache(obj, name, val): +# "On-model Cache setter. Also updates database." +# _SA(obj, "db_%s" % name, val) +# _GA(obj, "save")() +# _SA(obj, "_cached_db_%s" % name, val) +#def del_prop_cache(obj, name): +# "On-model cache deleter" +# try: +# _DA(obj, "_cached_db_%s" % name) +# except AttributeError: +# pass +#def _clean_cache(obj): +# "On-model cache resetter" +# [_DA(obj, cname) for cname in obj.__dict__.keys() if cname.startswith("_cached_db_")] # this cache holds the attributes loaded on objects, one dictionary @@ -371,11 +374,11 @@ class Attribute(SharedMemoryModel): #@property def __key_get(self): "Getter. Allows for value = self.key" - return _get_cache(self, "key") + return get_field_cache(self, "key") #@key.setter def __key_set(self, value): "Setter. Allows for self.key = value" - _set_cache(self, "key", value) + set_field_cache(self, "key", value) #@key.deleter def __key_del(self): "Deleter. Allows for del self.key" @@ -386,24 +389,24 @@ class Attribute(SharedMemoryModel): #@property def __obj_get(self): "Getter. Allows for value = self.obj" - return _get_cache(self, "obj") + return get_field_cache(self, "obj") #@obj.setter def __obj_set(self, value): "Setter. Allows for self.obj = value" - _set_cache(self, "obj", value) + set_field_cache(self, "obj", value) #@obj.deleter def __obj_del(self): "Deleter. Allows for del self.obj" self.db_obj = None self.save() - _del_cache(self, "obj") + del_field_cache(self, "obj") obj = property(__obj_get, __obj_set, __obj_del) # date_created property (wraps db_date_created) #@property def __date_created_get(self): "Getter. Allows for value = self.date_created" - return _get_cache(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" @@ -454,7 +457,7 @@ class Attribute(SharedMemoryModel): #@property def __lock_storage_get(self): "Getter. Allows for value = self.lock_storage" - return _get_cache(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()""" @@ -830,11 +833,11 @@ class TypedObject(SharedMemoryModel): #@property def __key_get(self): "Getter. Allows for value = self.key" - return _get_cache(self, "key") + return get_field_cache(self, "key") #@key.setter def __key_set(self, value): "Setter. Allows for self.key = value" - _set_cache(self, "key", value) + set_field_cache(self, "key", value) #@key.deleter def __key_del(self): "Deleter. Allows for del self.key" @@ -845,11 +848,11 @@ class TypedObject(SharedMemoryModel): #@property def __name_get(self): "Getter. Allows for value = self.name" - return _get_cache(self, "key") + return get_field_cache(self, "key") #@name.setter def __name_set(self, value): "Setter. Allows for self.name = value" - _set_cache(self, "key", value) + set_field_cache(self, "key", value) #@name.deleter def __name_del(self): "Deleter. Allows for del self.name" @@ -860,24 +863,24 @@ class TypedObject(SharedMemoryModel): #@property def __typeclass_path_get(self): "Getter. Allows for value = self.typeclass_path" - return _get_cache(self, "typeclass_path") + return get_field_cache(self, "typeclass_path") #@typeclass_path.setter def __typeclass_path_set(self, value): "Setter. Allows for self.typeclass_path = value" - _set_cache(self, "typeclass_path", value) + set_field_cache(self, "typeclass_path", value) #@typeclass_path.deleter def __typeclass_path_del(self): "Deleter. Allows for del self.typeclass_path" self.db_typeclass_path = "" self.save() - _del_cache(self, "typeclass_path") + del_field_cache(self, "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_cache(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" @@ -892,7 +895,7 @@ class TypedObject(SharedMemoryModel): #@property def __permissions_get(self): "Getter. Allows for value = self.name. Returns a list of permissions." - perms = _get_cache(self, "permissions") + perms = get_field_cache(self, "permissions") if perms: return [perm.strip() for perm in perms.split(',')] return [] @@ -900,24 +903,24 @@ class TypedObject(SharedMemoryModel): 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_cache(self, "permissions", 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_cache(self, "permissions") + 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_cache(self, "lock_storage") + return get_field_cache(self, "lock_storage") #@lock_storage.setter def __lock_storage_set(self, value): """Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" - _set_cache(self, "lock_storage", value) + 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""" @@ -975,16 +978,15 @@ class TypedObject(SharedMemoryModel): return False #@property - _dbid_cache = None def __dbid_get(self): """ Caches and returns the unique id of the object. Use this instead of self.id, which is not cached. """ - dbid = _GA(self, "_dbid_cache") + dbid = get_prop_cache(self, "_dbid") if not dbid: dbid = _GA(self, "id") - _SA(self, "_dbid_cache", dbid) + set_prop_cache(self, "_dbid", dbid) return dbid def __dbid_set(self, value): raise Exception("dbid cannot be set!") @@ -1004,26 +1006,6 @@ class TypedObject(SharedMemoryModel): raise Exception("dbref cannot be deleted!") dbref = property(__dbref_get, __dbref_set, __dbref_del) - #@property - _hashid_cache = None - def __hashid_get(self): - """ - Returns a per-class unique that combines the object's - class name with its idnum. This makes this id unique also - between different typeclassed entities such as scripts and - objects (which may still have the same id). - Primarily used by Attribute caching system. - """ - hashid = _GA(self, "_hashid_cache") - if not hashid: - hashid = "%s<#%s>" % (_GA(self, "__class__"), _GA(self, "dbid")) - _SA(self, "_hashid_cache", hashid) - return hashid - def __hashid_set(self): - raise Exception("hashid cannot be set!") - def __hashid_del(self): - raise Exception("hashid cannot be deleted!") - hashid = property(__hashid_get, __hashid_set, __hashid_del) # typeclass property #@property @@ -1333,11 +1315,11 @@ class TypedObject(SharedMemoryModel): attribute_name: (str) The attribute's name. """ - if attribute_name not in _ATTRIBUTE_CACHE[_GA(self, "hashid")]: + if attribute_name not in get_attr_cache(self): attrib_obj = _GA(self, "_attribute_class").objects.filter(db_obj=self).filter( db_key__iexact=attribute_name) if attrib_obj: - _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] = attrib_obj[0] + set_attr_cache(self, attribute_name, attrib_obj[0]) else: return False return True @@ -1351,7 +1333,7 @@ class TypedObject(SharedMemoryModel): new_value: (python obj) The value to set the attribute to. If this is not a str, the object will be stored as a pickle. """ - attrib_obj = _ATTRIBUTE_CACHE[_GA(self, "hashid")].get("attribute_name") + attrib_obj = get_attr_cache(self, attribute_name) if not attrib_obj: attrclass = _GA(self, "_attribute_class") # check if attribute already exists. @@ -1369,24 +1351,23 @@ class TypedObject(SharedMemoryModel): except IntegrityError: # this can happen if the cache was stale and the databse object is # missing. If so we need to clean self.hashid from the cache - if _GA(self, "hashid") in _ATTRIBUTE_CACHE: - del _ATTRIBUTE_CACHE[_GA(self, "hashid")] - self.delete() + flush_attr_cache(self) + self.delete() raise IntegrityError("Attribute could not be saved - object %s was deleted from database." % self.key) - _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] = attrib_obj + set_attr_cache(self, attribute_name, attrib_obj) def get_attribute_obj(self, attribute_name, default=None): """ Get the actual attribute object named attribute_name """ - attrib_obj = _ATTRIBUTE_CACHE[_GA(self, "hashid")].get(attribute_name) + attrib_obj = get_attribute_cache(self, attribute_name) if not attrib_obj: attrib_obj = _GA(self, "_attribute_class").objects.filter( db_obj=self).filter(db_key__iexact=attribute_name) if not attrib_obj: return default - _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] = attrib_obj[0] #query is first evaluated here - return _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] + set_attr_cache(self, attribute_name, attrib_obj[0]) #query is first evaluated here + return attrib_obj[0] return attrib_obj def get_attribute(self, attribute_name, default=None): @@ -1398,14 +1379,14 @@ class TypedObject(SharedMemoryModel): attribute_name: (str) The attribute's name. default: What to return if no attribute is found """ - attrib_obj = _ATTRIBUTE_CACHE[_GA(self, "hashid")].get(attribute_name) + attrib_obj = get_attr_cache(self, attribute_name) if not attrib_obj: attrib_obj = _GA(self, "_attribute_class").objects.filter( db_obj=self).filter(db_key__iexact=attribute_name) if not attrib_obj: return default - _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] = attrib_obj[0] #query is first evaluated here - return _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name].value + set_attr_cache(self, attribute_name, attrib_obj[0]) #query is first evaluated here + return attrib_obj[0].value return attrib_obj.value def get_attribute_raise(self, attribute_name): @@ -1415,14 +1396,14 @@ class TypedObject(SharedMemoryModel): attribute_name: (str) The attribute's name. """ - attrib_obj = _ATTRIBUTE_CACHE[_GA(self, "hashid")].get(attribute_name) + attrib_obj = get_attr_cache(self, attribute_name) if not attrib_obj: attrib_obj = _GA(self, "_attribute_class").objects.filter( db_obj=self).filter(db_key__iexact=attribute_name) if not attrib_obj: raise AttributeError - _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] = attrib_obj[0] #query is first evaluated here - return _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name].value + set_attr_cache(self, attribute_name, attrib_obj[0]) #query is first evaluated here + return attrib_obj[0].value return attrib_obj.value def del_attribute(self, attribute_name): @@ -1431,9 +1412,9 @@ class TypedObject(SharedMemoryModel): attribute_name: (str) The attribute's name. """ - attr_obj = _ATTRIBUTE_CACHE[_GA(self, "hashid")].get(attribute_name) + attr_obj = get_attr_cache(self, attribute_name) if attr_obj: - del _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] + del_attr_cache(self, attribute_name) attr_obj.delete() else: try: @@ -1449,9 +1430,9 @@ class TypedObject(SharedMemoryModel): attribute_name: (str) The attribute's name. """ - attr_obj = _ATTRIBUTE_CACHE[_GA(self, "hashid")].get(attribute_name) + attr_obj = get_attr_cache(self, attribute_name) if attr_obj: - del _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] + del_attr_cache(self, attribute_name) attr_obj.delete() else: try: @@ -1695,11 +1676,6 @@ class TypedObject(SharedMemoryModel): if hperm in [p.lower() for p in self.permissions] and hpos > ppos) return False - def flush_attr_cache(self): - """ - Flush only the attribute cache for this object. - """ - _ATTRIBUTE_CACHE[_GA(self, "hashid")] = {} def flush_from_cache(self): """