Merge. Reworked the added Command.__ne__ operator a bit for a tiny speed optimization (it's after all one of the most called methods in Evennia).

This commit is contained in:
Griatch 2012-11-03 19:21:19 +01:00
commit 8966f03713
9 changed files with 324 additions and 180 deletions

View file

@ -149,8 +149,15 @@ class Command(object):
return cmd in self._matchset return cmd in self._matchset
def __ne__(self, cmd): def __ne__(self, cmd):
"The logical negation of __eq__." """
return not self.__eq__(cmd) The logical negation of __eq__. Since this is one of the
most called methods in Evennia (along with __eq__) we do some
code-duplication here rather than issuing a method-lookup to __eq__.
"""
try:
return not cmd.key in self._matcheset
except AttributeError:
return not cmd in self._matchset
def __contains__(self, query): def __contains__(self, query):
""" """

View file

@ -212,7 +212,7 @@ class CmdBan(MuxCommand):
# replace * with regex form and compile it # replace * with regex form and compile it
ipregex = ban.replace('.','\.') ipregex = ban.replace('.','\.')
ipregex = ipregex.replace('*', '[0-9]{1,3}') ipregex = ipregex.replace('*', '[0-9]{1,3}')
print "regex:",ipregex #print "regex:",ipregex
ipregex = re.compile(r"%s" % ipregex) ipregex = re.compile(r"%s" % ipregex)
bantup = ("", ban, ipregex, now, reason) bantup = ("", ban, ipregex, now, reason)
# save updated banlist # save updated banlist

View file

@ -281,7 +281,7 @@ class CmdGet(MuxCommand):
if caller == obj: if caller == obj:
caller.msg("You can't get yourself.") caller.msg("You can't get yourself.")
return return
print obj, obj.location, caller, caller==obj.location #print obj, obj.location, caller, caller==obj.location
if caller == obj.location: if caller == obj.location:
caller.msg("You already hold that.") caller.msg("You already hold that.")
return return

View file

@ -11,6 +11,7 @@ import sys
import django, twisted import django, twisted
from django.conf import settings from django.conf import settings
from src.server.caches import get_cache_sizes
from src.server.sessionhandler import SESSIONS from src.server.sessionhandler import SESSIONS
from src.scripts.models import ScriptDB from src.scripts.models import ScriptDB
from src.objects.models import ObjectDB from src.objects.models import ObjectDB
@ -607,13 +608,11 @@ class CmdServerLoad(MuxCommand):
if not utils.host_os_is('posix'): if not utils.host_os_is('posix'):
string = "Process listings are only available under Linux/Unix." string = "Process listings are only available under Linux/Unix."
else: else:
global _resource, _idmapper, _attribute_cache global _resource, _idmapper
if not _resource: if not _resource:
import resource as _resource import resource as _resource
if not _idmapper: if not _idmapper:
from src.utils.idmapper import base as _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 import resource
loadavg = os.getloadavg() loadavg = os.getloadavg()
@ -684,10 +683,13 @@ class CmdServerLoad(MuxCommand):
ftable = utils.format_table(table, 5) ftable = utils.format_table(table, 5)
for row in ftable: for row in ftable:
string += "\n " + row[0] + row[1] + row[2] string += "\n " + row[0] + row[1] + row[2]
# attribute cache # get sizes of other caches
size = sum([sum([getsizeof(obj) for obj in dic.values()]) for dic in _attribute_cache.values()])/1024.0 attr_cache_info, field_cache_info, prop_cache_info = get_cache_sizes()
count = sum([len(dic) for dic in _attribute_cache.values()]) #size = sum([sum([getsizeof(obj) for obj in dic.values()]) for dic in _attribute_cache.values()])/1024.0
string += "\n{w On-entity Attribute cache usage:{n %5.2f MB (%i items)" % (size, count) #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) caller.msg(string)

View file

@ -20,7 +20,8 @@ from django.conf import settings
from src.utils.idmapper.models import SharedMemoryModel from src.utils.idmapper.models import SharedMemoryModel
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler 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.typeclasses.typeclass import TypeClass
from src.players.models import PlayerNick from src.players.models import PlayerNick
from src.objects.manager import ObjectManager from src.objects.manager import ObjectManager
@ -45,10 +46,6 @@ _ME = _("me")
_SELF = _("self") _SELF = _("self")
_HERE = _("here") _HERE = _("here")
def clean_content_cache(obj):
"Clean obj's content cache"
_SA(obj, "_contents_cache", None)
#------------------------------------------------------------ #------------------------------------------------------------
# #
# ObjAttribute # ObjAttribute
@ -222,25 +219,24 @@ class ObjectDB(TypedObject):
#@property #@property
def __aliases_get(self): def __aliases_get(self):
"Getter. Allows for value = self.aliases" "Getter. Allows for value = self.aliases"
try: aliases = get_prop_cache(self, "_aliases")
return _GA(self, "_cached_aliases") if aliases == None:
except AttributeError:
aliases = list(Alias.objects.filter(db_obj=self).values_list("db_key", flat=True)) aliases = list(Alias.objects.filter(db_obj=self).values_list("db_key", flat=True))
_SA(self, "_cached_aliases", aliases) set_prop_cache(self, "_aliases", aliases)
return aliases return aliases
#@aliases.setter #@aliases.setter
def __aliases_set(self, aliases): def __aliases_set(self, aliases):
"Setter. Allows for self.aliases = value" "Setter. Allows for self.aliases = value"
for alias in make_iter(aliases): for alias in make_iter(aliases):
new_alias = Alias(db_key=alias, db_obj=self) new_alias = Alias(db_key=alias, db_obj=self)
new_alias.save() new_alias.save()
_SA(self, "_cached_aliases", aliases) set_prop_cache(self, "_aliases", aliases)
#@aliases.deleter #@aliases.deleter
def __aliases_del(self): def __aliases_del(self):
"Deleter. Allows for del self.aliases" "Deleter. Allows for del self.aliases"
for alias in Alias.objects.filter(db_obj=self): for alias in Alias.objects.filter(db_obj=self):
alias.delete() alias.delete()
_DA(self, "_cached_aliases") del_prop_cache(self, "_aliases")
aliases = property(__aliases_get, __aliases_set, __aliases_del) aliases = property(__aliases_get, __aliases_set, __aliases_del)
# player property (wraps db_player) # player property (wraps db_player)
@ -251,24 +247,24 @@ class ObjectDB(TypedObject):
We have to be careful here since Player is also We have to be careful here since Player is also
a TypedObject, so as to not create a loop. a TypedObject, so as to not create a loop.
""" """
return _get_cache(self, "player") return get_field_cache(self, "player")
#@player.setter #@player.setter
def __player_set(self, player): def __player_set(self, player):
"Setter. Allows for self.player = value" "Setter. Allows for self.player = value"
if inherits_from(player, TypeClass): if inherits_from(player, TypeClass):
player = player.dbobj player = player.dbobj
_set_cache(self, "player", player) set_field_cache(self, "player", player)
#@player.deleter #@player.deleter
def __player_del(self): def __player_del(self):
"Deleter. Allows for del self.player" "Deleter. Allows for del self.player"
_del_cache(self, "player") del_field_cache(self, "player")
player = property(__player_get, __player_set, __player_del) player = property(__player_get, __player_set, __player_del)
# location property (wraps db_location) # location property (wraps db_location)
#@property #@property
def __location_get(self): def __location_get(self):
"Getter. Allows for value = self.location." "Getter. Allows for value = self.location."
loc = _get_cache(self, "location") loc = get_field_cache(self, "location")
if loc: if loc:
return _GA(loc, "typeclass") return _GA(loc, "typeclass")
return None return None
@ -298,20 +294,20 @@ class ObjectDB(TypedObject):
except RuntimeWarning: pass except RuntimeWarning: pass
# set the location # set the location
_set_cache(self, "location", loc) set_field_cache(self, "location", loc)
# update the contents of each location # update the contents of each location
if old_loc: if old_loc:
_GA(_GA(old_loc, "dbobj"), "contents_update")(self, remove=True) _GA(_GA(old_loc, "dbobj"), "contents_update")()
if loc: if loc:
_GA(loc, "contents_update")(_GA(self, "typeclass")) _GA(loc, "contents_update")()
except RuntimeError: except RuntimeError:
string = "Cannot set location: " string = "Cannot set location, "
string += "%s.location = %s would create a location-loop." % (self.key, location) string += "%s.location = %s would create a location-loop." % (self.key, loc)
_GA(self, "msg")(_(string)) _GA(self, "msg")(_(string))
logger.log_trace(string) logger.log_trace(string)
raise RuntimeError(string) raise RuntimeError(string)
except Exception: except Exception, e:
string = "Cannot set location: " string = "Cannot set location (%s): " % str(e)
string += "%s is not a valid location." % location string += "%s is not a valid location." % location
_GA(self, "msg")(_(string)) _GA(self, "msg")(_(string))
logger.log_trace(string) logger.log_trace(string)
@ -319,17 +315,17 @@ class ObjectDB(TypedObject):
#@location.deleter #@location.deleter
def __location_del(self): def __location_del(self):
"Deleter. Allows for del self.location" "Deleter. Allows for del self.location"
_GA(self, "location").contents_update(self, remove=True) _GA(self, "location").contents_update()
_SA(self, "db_location", None) _SA(self, "db_location", None)
_GA(self, "save")() _GA(self, "save")()
_del_cache(self, "location") del_field_cache(self, "location")
location = property(__location_get, __location_set, __location_del) location = property(__location_get, __location_set, __location_del)
# home property (wraps db_home) # home property (wraps db_home)
#@property #@property
def __home_get(self): def __home_get(self):
"Getter. Allows for value = self.home" "Getter. Allows for value = self.home"
home = _get_cache(self, "home") home = get_field_cache(self, "home")
if home: if home:
return _GA(home, "typeclass") return _GA(home, "typeclass")
return None return None
@ -347,7 +343,7 @@ class ObjectDB(TypedObject):
hom = _GA(home, "dbobj") hom = _GA(home, "dbobj")
else: else:
hom = _GA(home, "dbobj") hom = _GA(home, "dbobj")
_set_cache(self, "home", hom) set_field_cache(self, "home", hom)
except Exception: except Exception:
string = "Cannot set home: " string = "Cannot set home: "
string += "%s is not a valid home." string += "%s is not a valid home."
@ -359,14 +355,14 @@ class ObjectDB(TypedObject):
"Deleter. Allows for del self.home." "Deleter. Allows for del self.home."
_SA(self, "db_home", None) _SA(self, "db_home", None)
_GA(self, "save")() _GA(self, "save")()
_del_cache(self, "home") del_field_cache(self, "home")
home = property(__home_get, __home_set, __home_del) home = property(__home_get, __home_set, __home_del)
# destination property (wraps db_destination) # destination property (wraps db_destination)
#@property #@property
def __destination_get(self): def __destination_get(self):
"Getter. Allows for value = self.destination." "Getter. Allows for value = self.destination."
dest = _get_cache(self, "destination") dest = get_field_cache(self, "destination")
if dest: if dest:
return _GA(dest, "typeclass") return _GA(dest, "typeclass")
return None return None
@ -386,7 +382,7 @@ class ObjectDB(TypedObject):
dest = _GA(destination, "dbobj") dest = _GA(destination, "dbobj")
else: else:
dest = destination.dbobj dest = destination.dbobj
_set_cache(self, "destination", dest) set_field_cache(self, "destination", dest)
except Exception: except Exception:
string = "Cannot set destination: " string = "Cannot set destination: "
string += "%s is not a valid destination." % destination string += "%s is not a valid destination." % destination
@ -398,7 +394,7 @@ class ObjectDB(TypedObject):
"Deleter. Allows for del self.destination" "Deleter. Allows for del self.destination"
_SA(self, "db_destination", None) _SA(self, "db_destination", None)
_GA(self, "save")() _GA(self, "save")()
_del_cache(self, "destination") del_field_cache(self, "destination")
destination = property(__destination_get, __destination_set, __destination_del) destination = property(__destination_get, __destination_set, __destination_del)
# cmdset_storage property. # cmdset_storage property.
@ -461,13 +457,11 @@ class ObjectDB(TypedObject):
#@property #@property
def __is_superuser_get(self): def __is_superuser_get(self):
"Check if user has a player, and if so, if it is a superuser." "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") return any(_GA(self, "sessions")) and _GA(_GA(self, "player"), "is_superuser")
is_superuser = property(__is_superuser_get) is_superuser = property(__is_superuser_get)
# contents # contents
_contents_cache = None
#@property #@property
def contents_get(self, exclude=None): def contents_get(self, exclude=None):
""" """
@ -477,29 +471,25 @@ class ObjectDB(TypedObject):
exclude is one or more objects to not return exclude is one or more objects to not return
""" """
if _GA(self, "_contents_cache") == None: cont = get_prop_cache(self, "_contents")
# create the cache exclude = make_iter(exclude)
_SA(self, "_contents_cache", dict((obj.id, obj) for obj in ObjectDB.objects.get_contents(self))) if cont == None:
if exclude: cont = _GA(self, "contents_update")()
exclude = make_iter(exclude) return [obj for obj in cont if obj not in 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)
contents = property(contents_get) 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. self.location_set.
obj -
remove (true/false) - remove obj from content list
""" """
# this creates/updates the cache cont = ObjectDB.objects.get_contents(self)
_GA(self, "contents") set_prop_cache(self, "_contents", cont)
# set/remove objects from contents cache return cont
cache = _GA(self, "_contents_cache")
if remove and obj.id in cache:
del cache[obj.id]
else:
cache[obj.id] = obj
#@property #@property
def __exits_get(self): def __exits_get(self):
@ -941,9 +931,10 @@ class ObjectDB(TypedObject):
_GA(self, "clear_exits")() _GA(self, "clear_exits")()
# Clear out any non-exit objects located within the object # Clear out any non-exit objects located within the object
_GA(self, "clear_contents")() _GA(self, "clear_contents")()
# clear current location's content cache of this object old_loc = _GA(self, "location")
if _GA(self, "location"):
_GA(self, "location").contents_update(self, remove=True)
# Perform the deletion of the object # Perform the deletion of the object
super(ObjectDB, self).delete() super(ObjectDB, self).delete()
# clear object's old location's content cache of this object
if old_loc:
old_loc.contents_update()
return True return True

View file

@ -45,7 +45,8 @@ from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.encoding import smart_str 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.server.sessionhandler import SESSIONS
from src.players import manager from src.players import manager
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
@ -196,7 +197,7 @@ class PlayerDB(TypedObject):
#@property #@property
def obj_get(self): def obj_get(self):
"Getter. Allows for value = self.obj" "Getter. Allows for value = self.obj"
return _get_cache(self, "obj") return get_field_cache(self, "obj")
#@obj.setter #@obj.setter
def obj_set(self, value): def obj_set(self, value):
"Setter. Allows for self.obj = value" "Setter. Allows for self.obj = value"
@ -207,14 +208,14 @@ class PlayerDB(TypedObject):
if isinstance(value, _TYPECLASS): if isinstance(value, _TYPECLASS):
value = value.dbobj value = value.dbobj
try: try:
_set_cache(self, "obj", value) set_field_cache(self, "obj", value)
except Exception: except Exception:
logger.log_trace() logger.log_trace()
raise Exception("Cannot assign %s as a player object!" % value) raise Exception("Cannot assign %s as a player object!" % value)
#@obj.deleter #@obj.deleter
def obj_del(self): def obj_del(self):
"Deleter. Allows for del self.obj" "Deleter. Allows for del self.obj"
_del_cache(self, "obj") del_field_cache(self, "obj")
obj = property(obj_get, obj_set, obj_del) obj = property(obj_get, obj_set, obj_del)
# whereas the name 'obj' is consistent with the rest of the code, # whereas the name 'obj' is consistent with the rest of the code,
@ -223,17 +224,17 @@ class PlayerDB(TypedObject):
#@property #@property
def character_get(self): def character_get(self):
"Getter. Allows for value = self.character" "Getter. Allows for value = self.character"
return _get_cache(self, "obj") return get_field_cache(self, "obj")
#@character.setter #@character.setter
def character_set(self, character): def character_set(self, character):
"Setter. Allows for self.character = value" "Setter. Allows for self.character = value"
if inherits_from(character, TypeClass): if inherits_from(character, TypeClass):
character = character.dbobj character = character.dbobj
_set_cache(self, "obj", character) set_field_cache(self, "obj", character)
#@character.deleter #@character.deleter
def character_del(self): def character_del(self):
"Deleter. Allows for del self.character" "Deleter. Allows for del self.character"
_del_cache(self, "obj") del_field_cache(self, "obj")
character = property(character_get, character_set, character_del) character = property(character_get, character_set, character_del)
# cmdset_storage property # cmdset_storage property
# This seems very sensitive to caching, so leaving it be for now /Griatch # This seems very sensitive to caching, so leaving it be for now /Griatch
@ -260,15 +261,15 @@ class PlayerDB(TypedObject):
#@property #@property
def is_connected_get(self): def is_connected_get(self):
"Getter. Allows for value = self.is_connected" "Getter. Allows for value = self.is_connected"
return _get_cache(self, "is_connected") return get_field_cache(self, "is_connected")
#@is_connected.setter #@is_connected.setter
def is_connected_set(self, value): def is_connected_set(self, value):
"Setter. Allows for self.is_connected = value" "Setter. Allows for self.is_connected = value"
_set_cache(self, "is_connected", value) set_field_cache(self, "is_connected", value)
#@is_connected.deleter #@is_connected.deleter
def is_connected_del(self): def is_connected_del(self):
"Deleter. Allows for del is_connected" "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) is_connected = property(is_connected_get, is_connected_set, is_connected_del)
class Meta: class Meta:
@ -292,20 +293,21 @@ class PlayerDB(TypedObject):
_db_model_name = "playerdb" # used by attributes to safely store objects _db_model_name = "playerdb" # used by attributes to safely store objects
_default_typeclass_path = settings.BASE_PLAYER_TYPECLASS or "src.players.player.Player" _default_typeclass_path = settings.BASE_PLAYER_TYPECLASS or "src.players.player.Player"
_name_cache = None
# name property (wraps self.user.username) # name property (wraps self.user.username)
#@property #@property
def name_get(self): def name_get(self):
"Getter. Allows for value = self.name" "Getter. Allows for value = self.name"
if not _GA(self, "_name_cache"): name = get_prop_cache(self, "_name")
_SA(self, "_name_cache", _GA(self,"user").username) if not name:
return _GA(self, "_name_cache") name = _GA(self,"user").username
set_prop_cache(self, "_name", name)
return name
#@name.setter #@name.setter
def name_set(self, value): def name_set(self, value):
"Setter. Allows for player.name = newname" "Setter. Allows for player.name = newname"
_GA(self, "user").username = value _GA(self, "user").username = value
_GA(self, "user").save() _GA(self, "user").save()
_SA(self, "_name_cache", value) set_prop_cache(self, "_name", value)
#@name.deleter #@name.deleter
def name_del(self): def name_del(self):
"Deleter. Allows for del self.name" "Deleter. Allows for del self.name"
@ -313,13 +315,14 @@ class PlayerDB(TypedObject):
name = property(name_get, name_set, name_del) name = property(name_get, name_set, name_del)
key = property(name_get, name_set, name_del) key = property(name_get, name_set, name_del)
_uid_cache = None
#@property #@property
def uid_get(self): def uid_get(self):
"Getter. Retrieves the user id" "Getter. Retrieves the user id"
if not _GA(self, "_uid_cache"): uid = get_prop_cache(self, "_uid")
_SA(self, "_uid_cache", _GA(self, "user").id) if not uid:
return _GA(self, "_uid_cache") uid = _GA(self, "user").id
set_prop_cache(self, "_uid", uid)
return uid
def uid_set(self, value): def uid_set(self, value):
raise Exception("User id cannot be set!") raise Exception("User id cannot be set!")
def uid_del(self): def uid_del(self):
@ -342,12 +345,13 @@ class PlayerDB(TypedObject):
sessions = property(sessions_get, sessions_set, sessions_del) sessions = property(sessions_get, sessions_set, sessions_del)
#@property #@property
_is_superuser_cache = None
def is_superuser_get(self): def is_superuser_get(self):
"Superusers have all permissions." "Superusers have all permissions."
if _GA(self, "_is_superuser_cache") == None: is_suser = get_prop_cache(self, "_is_superuser")
_SA(self, "_is_superuser_cache", _GA(self, "user").is_superuser) if is_suser == None:
return _GA(self, "_is_superuser_cache") is_suser = _GA(self, "user").is_superuser
set_prop_cache(self, "_is_superuser", is_suser)
return is_suser
is_superuser = property(is_superuser_get) is_superuser = property(is_superuser_get)
# #

166
src/server/caches.py Normal file
View file

@ -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)]

View file

@ -45,7 +45,6 @@ def create_objects():
# accessed by user.get_profile() and can also store attributes. # accessed by user.get_profile() and can also store attributes.
# It also holds mud permissions, but for a superuser these # It also holds mud permissions, but for a superuser these
# have no effect anyhow. # have no effect anyhow.
character_typeclass = settings.BASE_CHARACTER_TYPECLASS character_typeclass = settings.BASE_CHARACTER_TYPECLASS
# Create the Player object as well as the in-game god-character # Create the Player object as well as the in-game god-character
@ -85,7 +84,6 @@ def create_objects():
if not god_character.home: if not god_character.home:
god_character.home = limbo_obj god_character.home = limbo_obj
def create_channels(): def create_channels():
""" """
Creates some sensible default channels. Creates some sensible default channels.

View file

@ -39,6 +39,9 @@ from django.conf import settings
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from src.utils.idmapper.models import SharedMemoryModel 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.server.models import ServerConfig
from src.typeclasses import managers from src.typeclasses import managers
from src.locks.lockhandler import LockHandler from src.locks.lockhandler import LockHandler
@ -59,28 +62,28 @@ _PDUMPS = pickle.dumps
# Property Cache mechanism. # Property Cache mechanism.
def _get_cache(obj, name): #def _get_cache(obj, name):
"On-model Cache handler." # "On-model Cache handler."
try: # try:
return _GA(obj, "_cached_db_%s" % name) # return _GA(obj, "_cached_db_%s" % name)
except AttributeError: # except AttributeError:
val = _GA(obj, "db_%s" % name) # val = _GA(obj, "db_%s" % name)
_SA(obj, "_cached_db_%s" % name, val) # _SA(obj, "_cached_db_%s" % name, val)
return val # return val
def _set_cache(obj, name, val): #def set_prop_cache(obj, name, val):
"On-model Cache setter. Also updates database." # "On-model Cache setter. Also updates database."
_SA(obj, "db_%s" % name, val) # _SA(obj, "db_%s" % name, val)
_GA(obj, "save")() # _GA(obj, "save")()
_SA(obj, "_cached_db_%s" % name, val) # _SA(obj, "_cached_db_%s" % name, val)
def _del_cache(obj, name): #def del_prop_cache(obj, name):
"On-model cache deleter" # "On-model cache deleter"
try: # try:
_DA(obj, "_cached_db_%s" % name) # _DA(obj, "_cached_db_%s" % name)
except AttributeError: # except AttributeError:
pass # pass
def _clean_cache(obj): #def _clean_cache(obj):
"On-model cache resetter" # "On-model cache resetter"
[_DA(obj, cname) for cname in obj.__dict__.keys() if cname.startswith("_cached_db_")] # [_DA(obj, cname) for cname in obj.__dict__.keys() if cname.startswith("_cached_db_")]
# this cache holds the attributes loaded on objects, one dictionary # this cache holds the attributes loaded on objects, one dictionary
@ -371,11 +374,11 @@ class Attribute(SharedMemoryModel):
#@property #@property
def __key_get(self): def __key_get(self):
"Getter. Allows for value = self.key" "Getter. Allows for value = self.key"
return _get_cache(self, "key") return get_field_cache(self, "key")
#@key.setter #@key.setter
def __key_set(self, value): def __key_set(self, value):
"Setter. Allows for self.key = value" "Setter. Allows for self.key = value"
_set_cache(self, "key", value) set_field_cache(self, "key", value)
#@key.deleter #@key.deleter
def __key_del(self): def __key_del(self):
"Deleter. Allows for del self.key" "Deleter. Allows for del self.key"
@ -386,24 +389,24 @@ class Attribute(SharedMemoryModel):
#@property #@property
def __obj_get(self): def __obj_get(self):
"Getter. Allows for value = self.obj" "Getter. Allows for value = self.obj"
return _get_cache(self, "obj") return get_field_cache(self, "obj")
#@obj.setter #@obj.setter
def __obj_set(self, value): def __obj_set(self, value):
"Setter. Allows for self.obj = value" "Setter. Allows for self.obj = value"
_set_cache(self, "obj", value) set_field_cache(self, "obj", value)
#@obj.deleter #@obj.deleter
def __obj_del(self): def __obj_del(self):
"Deleter. Allows for del self.obj" "Deleter. Allows for del self.obj"
self.db_obj = None self.db_obj = None
self.save() self.save()
_del_cache(self, "obj") del_field_cache(self, "obj")
obj = property(__obj_get, __obj_set, __obj_del) obj = property(__obj_get, __obj_set, __obj_del)
# date_created property (wraps db_date_created) # date_created property (wraps db_date_created)
#@property #@property
def __date_created_get(self): def __date_created_get(self):
"Getter. Allows for value = self.date_created" "Getter. Allows for value = self.date_created"
return _get_cache(self, "date_created") return get_field_cache(self, "date_created")
#@date_created.setter #@date_created.setter
def __date_created_set(self, value): def __date_created_set(self, value):
"Setter. Allows for self.date_created = value" "Setter. Allows for self.date_created = value"
@ -454,7 +457,7 @@ class Attribute(SharedMemoryModel):
#@property #@property
def __lock_storage_get(self): def __lock_storage_get(self):
"Getter. Allows for value = self.lock_storage" "Getter. Allows for value = self.lock_storage"
return _get_cache(self, "lock_storage") return get_field_cache(self, "lock_storage")
#@lock_storage.setter #@lock_storage.setter
def __lock_storage_set(self, value): def __lock_storage_set(self, value):
"""Saves the lock_storage. This is usually not called directly, but through self.lock()""" """Saves the lock_storage. This is usually not called directly, but through self.lock()"""
@ -830,11 +833,11 @@ class TypedObject(SharedMemoryModel):
#@property #@property
def __key_get(self): def __key_get(self):
"Getter. Allows for value = self.key" "Getter. Allows for value = self.key"
return _get_cache(self, "key") return get_field_cache(self, "key")
#@key.setter #@key.setter
def __key_set(self, value): def __key_set(self, value):
"Setter. Allows for self.key = value" "Setter. Allows for self.key = value"
_set_cache(self, "key", value) set_field_cache(self, "key", value)
#@key.deleter #@key.deleter
def __key_del(self): def __key_del(self):
"Deleter. Allows for del self.key" "Deleter. Allows for del self.key"
@ -845,11 +848,11 @@ class TypedObject(SharedMemoryModel):
#@property #@property
def __name_get(self): def __name_get(self):
"Getter. Allows for value = self.name" "Getter. Allows for value = self.name"
return _get_cache(self, "key") return get_field_cache(self, "key")
#@name.setter #@name.setter
def __name_set(self, value): def __name_set(self, value):
"Setter. Allows for self.name = value" "Setter. Allows for self.name = value"
_set_cache(self, "key", value) set_field_cache(self, "key", value)
#@name.deleter #@name.deleter
def __name_del(self): def __name_del(self):
"Deleter. Allows for del self.name" "Deleter. Allows for del self.name"
@ -860,24 +863,24 @@ class TypedObject(SharedMemoryModel):
#@property #@property
def __typeclass_path_get(self): def __typeclass_path_get(self):
"Getter. Allows for value = self.typeclass_path" "Getter. Allows for value = self.typeclass_path"
return _get_cache(self, "typeclass_path") return get_field_cache(self, "typeclass_path")
#@typeclass_path.setter #@typeclass_path.setter
def __typeclass_path_set(self, value): def __typeclass_path_set(self, value):
"Setter. Allows for self.typeclass_path = value" "Setter. Allows for self.typeclass_path = value"
_set_cache(self, "typeclass_path", value) set_field_cache(self, "typeclass_path", value)
#@typeclass_path.deleter #@typeclass_path.deleter
def __typeclass_path_del(self): def __typeclass_path_del(self):
"Deleter. Allows for del self.typeclass_path" "Deleter. Allows for del self.typeclass_path"
self.db_typeclass_path = "" self.db_typeclass_path = ""
self.save() 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) typeclass_path = property(__typeclass_path_get, __typeclass_path_set, __typeclass_path_del)
# date_created property # date_created property
#@property #@property
def __date_created_get(self): def __date_created_get(self):
"Getter. Allows for value = self.date_created" "Getter. Allows for value = self.date_created"
return _get_cache(self, "date_created") return get_field_cache(self, "date_created")
#@date_created.setter #@date_created.setter
def __date_created_set(self, value): def __date_created_set(self, value):
"Setter. Allows for self.date_created = value" "Setter. Allows for self.date_created = value"
@ -892,7 +895,7 @@ class TypedObject(SharedMemoryModel):
#@property #@property
def __permissions_get(self): def __permissions_get(self):
"Getter. Allows for value = self.name. Returns a list of permissions." "Getter. Allows for value = self.name. Returns a list of permissions."
perms = _get_cache(self, "permissions") perms = get_field_cache(self, "permissions")
if perms: if perms:
return [perm.strip() for perm in perms.split(',')] return [perm.strip() for perm in perms.split(',')]
return [] return []
@ -900,24 +903,24 @@ class TypedObject(SharedMemoryModel):
def __permissions_set(self, value): def __permissions_set(self, value):
"Setter. Allows for self.name = value. Stores as a comma-separated string." "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)]) 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 #@permissions.deleter
def __permissions_del(self): def __permissions_del(self):
"Deleter. Allows for del self.name" "Deleter. Allows for del self.name"
self.db_permissions = "" self.db_permissions = ""
self.save() self.save()
_del_cache(self, "permissions") del_field_cache(self, "permissions")
permissions = property(__permissions_get, __permissions_set, __permissions_del) permissions = property(__permissions_get, __permissions_set, __permissions_del)
# lock_storage property (wraps db_lock_storage) # lock_storage property (wraps db_lock_storage)
#@property #@property
def __lock_storage_get(self): def __lock_storage_get(self):
"Getter. Allows for value = self.lock_storage" "Getter. Allows for value = self.lock_storage"
return _get_cache(self, "lock_storage") return get_field_cache(self, "lock_storage")
#@lock_storage.setter #@lock_storage.setter
def __lock_storage_set(self, value): def __lock_storage_set(self, value):
"""Saves the lock_storagetodate. This is usually not called directly, but through self.lock()""" """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 #@lock_storage.deleter
def __lock_storage_del(self): def __lock_storage_del(self):
"Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead""" "Deleter is disabled. Use the lockhandler.delete (self.lock.delete) instead"""
@ -975,16 +978,15 @@ class TypedObject(SharedMemoryModel):
return False return False
#@property #@property
_dbid_cache = None
def __dbid_get(self): def __dbid_get(self):
""" """
Caches and returns the unique id of the object. Caches and returns the unique id of the object.
Use this instead of self.id, which is not cached. Use this instead of self.id, which is not cached.
""" """
dbid = _GA(self, "_dbid_cache") dbid = get_prop_cache(self, "_dbid")
if not dbid: if not dbid:
dbid = _GA(self, "id") dbid = _GA(self, "id")
_SA(self, "_dbid_cache", dbid) set_prop_cache(self, "_dbid", dbid)
return dbid return dbid
def __dbid_set(self, value): def __dbid_set(self, value):
raise Exception("dbid cannot be set!") raise Exception("dbid cannot be set!")
@ -1004,26 +1006,6 @@ class TypedObject(SharedMemoryModel):
raise Exception("dbref cannot be deleted!") raise Exception("dbref cannot be deleted!")
dbref = property(__dbref_get, __dbref_set, __dbref_del) 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 # typeclass property
#@property #@property
@ -1333,11 +1315,11 @@ class TypedObject(SharedMemoryModel):
attribute_name: (str) The attribute's name. 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( attrib_obj = _GA(self, "_attribute_class").objects.filter(db_obj=self).filter(
db_key__iexact=attribute_name) db_key__iexact=attribute_name)
if attrib_obj: if attrib_obj:
_ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] = attrib_obj[0] set_attr_cache(self, attribute_name, attrib_obj[0])
else: else:
return False return False
return True return True
@ -1351,7 +1333,7 @@ class TypedObject(SharedMemoryModel):
new_value: (python obj) The value to set the attribute to. If this is not 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. 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: if not attrib_obj:
attrclass = _GA(self, "_attribute_class") attrclass = _GA(self, "_attribute_class")
# check if attribute already exists. # check if attribute already exists.
@ -1369,24 +1351,23 @@ class TypedObject(SharedMemoryModel):
except IntegrityError: except IntegrityError:
# this can happen if the cache was stale and the databse object is # 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 # missing. If so we need to clean self.hashid from the cache
if _GA(self, "hashid") in _ATTRIBUTE_CACHE: flush_attr_cache(self)
del _ATTRIBUTE_CACHE[_GA(self, "hashid")] self.delete()
self.delete()
raise IntegrityError("Attribute could not be saved - object %s was deleted from database." % self.key) 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): def get_attribute_obj(self, attribute_name, default=None):
""" """
Get the actual attribute object named attribute_name 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: if not attrib_obj:
attrib_obj = _GA(self, "_attribute_class").objects.filter( attrib_obj = _GA(self, "_attribute_class").objects.filter(
db_obj=self).filter(db_key__iexact=attribute_name) db_obj=self).filter(db_key__iexact=attribute_name)
if not attrib_obj: if not attrib_obj:
return default return default
_ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] = attrib_obj[0] #query is first evaluated here set_attr_cache(self, attribute_name, attrib_obj[0]) #query is first evaluated here
return _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] return attrib_obj[0]
return attrib_obj return attrib_obj
def get_attribute(self, attribute_name, default=None): def get_attribute(self, attribute_name, default=None):
@ -1398,14 +1379,14 @@ class TypedObject(SharedMemoryModel):
attribute_name: (str) The attribute's name. attribute_name: (str) The attribute's name.
default: What to return if no attribute is found 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: if not attrib_obj:
attrib_obj = _GA(self, "_attribute_class").objects.filter( attrib_obj = _GA(self, "_attribute_class").objects.filter(
db_obj=self).filter(db_key__iexact=attribute_name) db_obj=self).filter(db_key__iexact=attribute_name)
if not attrib_obj: if not attrib_obj:
return default return default
_ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] = attrib_obj[0] #query is first evaluated here set_attr_cache(self, attribute_name, attrib_obj[0]) #query is first evaluated here
return _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name].value return attrib_obj[0].value
return attrib_obj.value return attrib_obj.value
def get_attribute_raise(self, attribute_name): def get_attribute_raise(self, attribute_name):
@ -1415,14 +1396,14 @@ class TypedObject(SharedMemoryModel):
attribute_name: (str) The attribute's name. 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: if not attrib_obj:
attrib_obj = _GA(self, "_attribute_class").objects.filter( attrib_obj = _GA(self, "_attribute_class").objects.filter(
db_obj=self).filter(db_key__iexact=attribute_name) db_obj=self).filter(db_key__iexact=attribute_name)
if not attrib_obj: if not attrib_obj:
raise AttributeError raise AttributeError
_ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] = attrib_obj[0] #query is first evaluated here set_attr_cache(self, attribute_name, attrib_obj[0]) #query is first evaluated here
return _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name].value return attrib_obj[0].value
return attrib_obj.value return attrib_obj.value
def del_attribute(self, attribute_name): def del_attribute(self, attribute_name):
@ -1431,9 +1412,9 @@ class TypedObject(SharedMemoryModel):
attribute_name: (str) The attribute's name. 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: if attr_obj:
del _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] del_attr_cache(self, attribute_name)
attr_obj.delete() attr_obj.delete()
else: else:
try: try:
@ -1449,9 +1430,9 @@ class TypedObject(SharedMemoryModel):
attribute_name: (str) The attribute's name. 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: if attr_obj:
del _ATTRIBUTE_CACHE[_GA(self, "hashid")][attribute_name] del_attr_cache(self, attribute_name)
attr_obj.delete() attr_obj.delete()
else: else:
try: try:
@ -1695,11 +1676,6 @@ class TypedObject(SharedMemoryModel):
if hperm in [p.lower() for p in self.permissions] and hpos > ppos) if hperm in [p.lower() for p in self.permissions] and hpos > ppos)
return False 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): def flush_from_cache(self):
""" """