Added more caching to channelhandler as well as players in order to cut back on unnecessary database calls.

This commit is contained in:
Griatch 2012-04-26 13:38:34 +02:00
parent a8373c685f
commit 1a6ef5d983
6 changed files with 75 additions and 45 deletions

View file

@ -410,6 +410,7 @@ class CmdCBoot(MuxCommand):
nick.delete() nick.delete()
# disconnect player # disconnect player
channel.disconnect_from(player) channel.disconnect_from(player)
CHANNELHANDLER.update()
class CmdCemit(MuxCommand): class CmdCemit(MuxCommand):
""" """

View file

@ -10,7 +10,7 @@ to just write
For this to work, 'newbie', the name of the channel, must For this to work, 'newbie', the name of the channel, must
be identified by the cmdhandler as a command name. The be identified by the cmdhandler as a command name. The
channelhandler stores all channels as custom 'commands' channelhandler stores all channels as custom 'commands'
that the cmdhandler can import and look through. that the cmdhandler can import and look through.
Warning - channel names take precedence over command names, Warning - channel names take precedence over command names,
so make sure to not pick clashing channel names. so make sure to not pick clashing channel names.
@ -20,7 +20,7 @@ the channelhandler at all - the create_channel method handles the update.
To delete a channel cleanly, delete the channel object, then call To delete a channel cleanly, delete the channel object, then call
update() on the channelhandler. Or use Channel.objects.delete() which update() on the channelhandler. Or use Channel.objects.delete() which
does this for you. does this for you.
""" """
from src.comms.models import Channel, Msg from src.comms.models import Channel, Msg
@ -32,7 +32,7 @@ class ChannelCommand(command.Command):
Channel Channel
Usage: Usage:
<channel name or alias> <message> <channel name or alias> <message>
This is a channel. If you have subscribed to it, you can send to This is a channel. If you have subscribed to it, you can send to
it by entering its name or alias, followed by the text you want to it by entering its name or alias, followed by the text you want to
@ -41,12 +41,12 @@ class ChannelCommand(command.Command):
# this flag is what identifies this cmd as a channel cmd # this flag is what identifies this cmd as a channel cmd
# and branches off to the system send-to-channel command # and branches off to the system send-to-channel command
# (which is customizable by admin) # (which is customizable by admin)
is_channel = True is_channel = True
key = "general" key = "general"
help_category = "Channel Names" help_category = "Channel Names"
locks = "cmd:all()" locks = "cmd:all()"
obj = None obj = None
def parse(self): def parse(self):
""" """
Simple parser Simple parser
@ -63,12 +63,12 @@ class ChannelCommand(command.Command):
caller = self.caller caller = self.caller
if not msg: if not msg:
caller.msg("Say what?") caller.msg("Say what?")
return return
channel = Channel.objects.get_channel(channelkey) channel = Channel.objects.get_channel(channelkey)
if not channel: if not channel:
caller.msg("Channel '%s' not found." % channelkey) caller.msg("Channel '%s' not found." % channelkey)
return return
if not channel.has_connection(caller): if not channel.has_connection(caller):
string = "You are not connected to channel '%s'." string = "You are not connected to channel '%s'."
caller.msg(string % channelkey) caller.msg(string % channelkey)
@ -77,9 +77,9 @@ class ChannelCommand(command.Command):
string = "You are not permitted to send to channel '%s'." string = "You are not permitted to send to channel '%s'."
caller.msg(string % channelkey) caller.msg(string % channelkey)
return return
msg = "[%s] %s: %s" % (channel.key, caller.name, msg) msg = "[%s] %s: %s" % (channel.key, caller.name, msg)
# we can't use the utils.create function to make the Msg, # we can't use the utils.create function to make the Msg,
# since that creates an import recursive loop. # since that creates an import recursive loop.
try: try:
sender = caller.player sender = caller.player
except AttributeError: except AttributeError:
@ -88,19 +88,20 @@ class ChannelCommand(command.Command):
msgobj = Msg(db_sender=sender, db_message=msg) msgobj = Msg(db_sender=sender, db_message=msg)
msgobj.save() msgobj.save()
msgobj.channels = channel msgobj.channels = channel
# send new message object to channel # send new message object to channel
channel.msg(msgobj, from_obj=sender) channel.msg(msgobj, from_obj=sender)
class ChannelHandler(object): class ChannelHandler(object):
""" """
Handles the set of commands related to channels. Handles the set of commands related to channels.
""" """
def __init__(self): def __init__(self):
self.cached_channel_cmds = [] self.cached_channel_cmds = []
self.cached_cmdsets = {}
def __str__(self): def __str__(self):
return ", ".join(str(cmd) for cmd in self.cached_channel_cmds) return ", ".join(str(cmd) for cmd in self.cached_channel_cmds)
def clear(self): def clear(self):
""" """
Reset the cache storage. Reset the cache storage.
@ -114,57 +115,61 @@ class ChannelHandler(object):
if not utils.is_iter(aliases): if not utils.is_iter(aliases):
aliases = [aliases] aliases = [aliases]
ustring = "%s <message>" % key.lower() + "".join(["\n %s <message>" % alias.lower() for alias in aliases]) ustring = "%s <message>" % key.lower() + "".join(["\n %s <message>" % alias.lower() for alias in aliases])
desc = channel.desc desc = channel.desc
string = \ string = \
""" """
Channel '%s' Channel '%s'
Usage (not including your personal aliases): Usage (not including your personal aliases):
%s %s
%s %s
""" % (key, ustring, desc) """ % (key, ustring, desc)
return string return string
def add_channel(self, channel): def add_channel(self, channel):
""" """
Add an individual channel to the handler. This should be Add an individual channel to the handler. This should be
called whenever a new channel is created. To called whenever a new channel is created. To
remove a channel, simply delete the channel object remove a channel, simply delete the channel object
and run self.update on the handler. and run self.update on the handler.
""" """
# map the channel to a searchable command # map the channel to a searchable command
cmd = ChannelCommand() cmd = ChannelCommand()
cmd.key = channel.key.strip().lower() cmd.key = channel.key.strip().lower()
cmd.obj = channel cmd.obj = channel
cmd.__doc__= self._format_help(channel) cmd.__doc__= self._format_help(channel)
if channel.aliases: if channel.aliases:
cmd.aliases = channel.aliases cmd.aliases = channel.aliases
cmd.lock_storage = "cmd:all();%s" % channel.locks cmd.lock_storage = "cmd:all();%s" % channel.locks
cmd.lockhandler.reset() cmd.lockhandler.reset()
self.cached_channel_cmds.append(cmd) self.cached_channel_cmds.append(cmd)
self.cached_cmdsets = {}
def update(self): def update(self):
"Updates the handler completely." "Updates the handler completely."
self.cached_channel_cmds = [] self.cached_channel_cmds = []
self.cached_cmdsets = {}
for channel in Channel.objects.all(): for channel in Channel.objects.all():
self.add_channel(channel) self.add_channel(channel)
def get_cmdset(self, source_object): def get_cmdset(self, source_object):
""" """
Retrieve cmdset for channels this source_object has Retrieve cmdset for channels this source_object has
access to send to. access to send to.
""" """
# create a temporary cmdset holding all channels if source_object in self.cached_cmdsets:
chan_cmdset = cmdset.CmdSet() return self.cached_cmdsets[source_object]
chan_cmdset.key = '_channelset' else:
chan_cmdset.priority = 10 # create a new cmdset holding all channels
chan_cmdset.duplicates = True chan_cmdset = cmdset.CmdSet()
chan_cmdset.key = '_channelset'
for cmd in [cmd for cmd in self.cached_channel_cmds chan_cmdset.priority = 10
if cmd.access(source_object, 'listen')]: chan_cmdset.duplicates = True
chan_cmdset.add(cmd) for cmd in [cmd for cmd in self.cached_channel_cmds
return chan_cmdset if cmd.access(source_object, 'send')]:
chan_cmdset.add(cmd)
self.cached_cmdsets[source_object] = chan_cmdset
return chan_cmdset
CHANNELHANDLER = ChannelHandler() CHANNELHANDLER = ChannelHandler()

View file

@ -193,7 +193,6 @@ class LockHandler(object):
locks = {} locks = {}
if not storage_lockstring: if not storage_lockstring:
return locks return locks
nlocks = storage_lockstring.count(';') + 1
duplicates = 0 duplicates = 0
elist = [] # errors elist = [] # errors
wlist = [] # warnings wlist = [] # warnings

View file

@ -247,6 +247,8 @@ class ObjectDB(TypedObject):
"Setter. Allows for self.player = value" "Setter. Allows for self.player = value"
if isinstance(player, TypeClass): if isinstance(player, TypeClass):
player = player.dbobj player = player.dbobj
self.db_player = player
self.save()
_set_cache(self, "player", player) _set_cache(self, "player", player)
#@player.deleter #@player.deleter
def __player_del(self): def __player_del(self):

View file

@ -46,6 +46,7 @@ from django.contrib.auth.models import User
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.typeclasses.models import _get_cache, _set_cache, _del_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
@ -57,6 +58,10 @@ __all__ = ("PlayerAttribute", "PlayerNick", "PlayerDB")
_AT_SEARCH_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1)) _AT_SEARCH_RESULT = utils.variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
_GA = object.__getattribute__
_SA = object.__setattr__
_DA = object.__delattr__
#------------------------------------------------------------ #------------------------------------------------------------
# #
# PlayerAttribute # PlayerAttribute
@ -186,7 +191,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 self.db_obj return _get_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"
@ -194,16 +199,14 @@ class PlayerDB(TypedObject):
if isinstance(value, TypeClass): if isinstance(value, TypeClass):
value = value.dbobj value = value.dbobj
try: try:
self.db_obj = value _set_cache(self, "obj", value)
self.save()
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"
self.db_obj = None _del_cache(self, "obj")
self.save()
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,
@ -212,18 +215,18 @@ 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 self.db_obj return _get_cache(self, "obj")
#@character.setter #@character.setter
def character_set(self, value): def character_set(self, value):
"Setter. Allows for self.character = value" "Setter. Allows for self.character = value"
self.obj = value _set_cache(self, "obj", value)
#@character.deleter #@character.deleter
def character_del(self): def character_del(self):
"Deleter. Allows for del self.character" "Deleter. Allows for del self.character"
self.db_obj = None _del_cache(self, "obj")
self.save()
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
#@property #@property
def cmdset_storage_get(self): def cmdset_storage_get(self):
"Getter. Allows for value = self.name. Returns a list of cmdset_storage." "Getter. Allows for value = self.name. Returns a list of cmdset_storage."
@ -265,16 +268,20 @@ 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 property (wraps self.user.username) _name_cache = None
# 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"
return self.user.username if not self._name_cache:
self._name_cache = self.user.username
return self._name_cache
#@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"
self.user.username = value self.user.username = value
self.user.save() # this might be stopped by Django? self.user.save() # this might be stopped by Django?
self._name_cache = 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"
@ -282,6 +289,19 @@ 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
def uid_get(self):
"Getter. Retrieves the user id"
if not self._uid_cache:
self._uid_cache = self.user.id
return self._uid_cache
def uid_set(self, value):
raise Exception("User id cannot be set!")
def uid_del(self):
raise Exception("User id cannot be deleted!")
uid = property(uid_get, uid_set, uid_del)
# sessions property # sessions property
#@property #@property
def sessions_get(self): def sessions_get(self):
@ -298,9 +318,12 @@ 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."
return self.user.is_superuser if self._is_superuser_cache == None:
self._is_superuser_cache = self.user.is_superuser
return self._is_superuser_cache
is_superuser = property(is_superuser_get) is_superuser = property(is_superuser_get)
# #

View file

@ -65,7 +65,7 @@ def _get_cache(obj, name):
if val: _SA(obj, "_cached_db_%s" % name, val) if val: _SA(obj, "_cached_db_%s" % name, val)
return val return val
def _set_cache(obj, name, val): def _set_cache(obj, name, val):
"On-model Cache setter" "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)