evennia/docs/sphinx/source/wiki/Caches.rst
2013-12-02 16:43:44 +01:00

151 lines
7.3 KiB
ReStructuredText

Caches
======
*Note: This is an advanced topic. You might want to skip it on a first
read-through.*
Evennia is a fully persistent system, which means that it will store
things in the database whenever its state changes. Since accessing the
database i comparably expensive, Evennia uses an extensive *caching*
scheme. Caching normally means that once data is read from the database,
it is stored in memory for quick retrieval henceforth. Only when data
changes will the database be accessed again (and the cache updated).
With a few exceptions, caching are primarily motivated by speed and to
minimize bottlenecks found by profiling the server. Some systems must
access certain pieces of data often, and going through the django API
over and over builds up. Depending on operation, individual speedups of
hundreds of times can be achieved by clever caching.
The price for extended caching is memory consumption and added
complexity. This page tries to explain the various cache strategies in
place. Most users should not have to worry about them, but if you ever
try to "bang the metal" with Evennia, you should know what you are
seeing.
All caching schemes except Idmapper is centralized in
``src/server.caches/py``. You can turn off all caches handled by that
module by use of the ``GAME_CACHE_TYPE`` setting.
The default ``@server`` command will give a brief listing of the memory
usage of most relevant caches.
Idmapper
--------
Evennia's django object model is extended by *idmapper* functionality.
The idmapper is an external third-party system that sits in
``src/utils/idmapper``. The idmapper is an on-demand memory mapper for
all database models in the game. This means that a given database object
is represented by the same memory area whenever it is accessed. This may
sound trivial but it is not - in plain Django there is no such
guarantee. Doing something like ``objdb.test = "Test"`` (were objdb is a
django model instance) would be unsafe and most likely the ``test``
variable would be lost next time the model is retrieved from the
database. As Evennia ties `Typeclasses <Typeclasses.html>`_ to django
models, this would be a catastophy.
Idmapper is originally a memory saver for django websites. In the case
of a website, object access is brief and fleeting - not so for us. So we
have extended idmapper to never loose its reference to a stored object
(not doing this was the cause of a very long-standing, very hard-to-find
bug).
Idmapper is an on-demand cache, meaning that it will only cache objects
that are actually accessed. Whereas it is possible to clean the idmapper
cache via on-model methods, this does not necessarily mean its memory
will be freed - this depends on any lingering references in the system
(this is how Python's reference counting works). If you ever need to
clean the idmapper cache, the safest way is therefore a soft-reload of
the server (via e.g. the ``@reload`` command).
Most developers will not need to care with the idmapper cache - it just
makes models work intuitively. It is visible mostly in that many
database models in Evennia inherit from
``src.utils.idmapper.models.SharedMemoryModel``.
On-object variable cache
------------------------
All database fields on all objects in Evennia are cached by use of
`Python
properties <http://docs.python.org/library/functions.html#property>`_.
So when you do ``name = obj.key``, you are actually *not* directly
accessing a database field "key" on the object. What you are doing is
actually to access a handler. This handler reads the field ``db_key``
and caches its value for next time it using a call to the the
``server.caches`` module.
The naming scheme is consistent, so a given field property ``obj.foo``
is always a handler for a database field ``obj.db_key.`` The handler
methods for the property are always named ``obj.foo_get()``,
``obj.foo_set()`` and ``obj.foo_del()`` (all are not always needed).
Apart from caching, property handlers also serves another function -
they hide away Django administration. So doing ``obj.key = "Peter"``
will not only assign (and cache) the string "Peter" in the database
field ``obj.db_key``, it will also call ``obj.save()`` for you in order
to update the database.
Hiding away the model fields presents one complication for developers,
and that is searching using normal django methods. Basically, if you
search using e.g. the standard django ``filter`` method, you must search
for ``db_key``, not ``key``. Only the former is known by django, the
latter will give an invalid-field error. If you use Evennia's own search
methods you don't need to worry about this, they look for the right
things behind the scenes for you.
Content cache
~~~~~~~~~~~~~
A special case of on-object caching is the *content* cache. Finding the
"contents" of a particular location turns out to be a very common and
pretty expensive operation. Whenever a person moves, says something or
does other things, "everyone else" in a given location must be informed.
This means a database search for which objects share the location.
``obj.contents`` is a convenient container that at every moment contains
a cached list of all objects "inside" that particular object. It is
updated by the ``location`` property. So ``obj1.location = obj2`` will
update ``obj2.contents`` on the fly to contain ``obj1``. It will also
remove ``obj1`` from the ``contents`` list of its previous location.
Testing shows that when moving from one room to another, finding and
messaging everyone in the room took up as much as *25%* of the total
computer time needed for the operation. After caching ``contents``,
messaging now takes up *0.25%* instead ...
The contents cache should be used at all times. The main thing to
remember is that if you were to somehow bypass the ``location`` handler
(such as by setting the ``db_location`` field manually), you will bring
the cache and database out of sync until you reload the server.
Typeclass cache
~~~~~~~~~~~~~~~
All typeclasses are cached on the database model. This allows for quick
access to the typeclass through ``dbobj.typeclass``. Behind the scenes
this operation will import the typeclass definition from a path stored
in ``db_typeclass_path`` (available via the property handler
``typeclass_path``). All checks and eventual debug messages will be
handled, and the result cached.
The only exception to the caching is if the typeclass module had some
sort of syntax error or other show-stopping bug. The default typeclass
(as defined in ``settings``) will then be loaded instead. The error will
be reported and *no* caching will take place. This is in order to keep
reloading the typeclass also next try, until it is fixed.
On-object Attribute cache
-------------------------
`Attribute <Attributes.html>`_ lookups on objects are also cached. This
means that after the first access or assignment, ``obj.db.attrname`` is
as fast as accessing any normal python property - this removes the
necessity for subsequent database look-ups in order to retrieve
attributes. Both ``db`` and ``ndn`` work the same way in this regard.
Due to the possibility of storing objects in Attributes, the system
cannot cache the value of data stored in the Attribute (this would allow
for the system not detecting a stored Object being deleted elsewhere -
it would still be accessible from the Attribute). So having to re-access
such objects every load does incur a minor speed penalty.