Starting in Anope 1.9.9, Anope has Redis database support (https://redis.io/).
This document explains the data structure used by Anope, and explains how
keyspace notification works.

This is not a tutorial on how to use Redis, see https://redis.io/documentation
for that.

Table of Contents
-----------------
1) Data structure
2) Keyspace notifications
3) Examples of modifying, deleting, and creating objects

1) Data structure

    There are 4 key namespaces in Anope, they are:

        id -    The keys in id are used to atomically create object ids for new
                objects. For example, if I were to create a new BotInfo I would first:

                redis 127.0.0.1:6379> INCR id:BotInfo

                To get the object ID of the new object.

        ids -   The keys in ids contain a set of all object ids of the given type.
                For example:

                redis 127.0.0.1:6379> SMEMBERS ids:BotInfo

                Returns "1", "2", "3", "4", "5", "6", "7", "8" because I have 8 bots that
                have IDs 1, 2, 3, 4, 5, 6, 7, and 8, respectively.

        hash -  The keys in hash are the actual objects, stored as hashes. For
                example, if I had just looked up all BotInfo ids and wanted to iterate
                over all of them, I would start by:

                redis 127.0.0.1:6379> HGETALL hash:BotInfo:1

                Which gets all keys and values from the hash of type BotInfo with id 1.
                This may return:

                "nick" -> "BotServ"
                "user" -> "services"
                "host" -> "services.anope.org"
                "created" -> "1368704765"

        value - The keys in value only exist to aid looking up object IDs. They
                are sets of object IDs and are used to map key+value pairs to objects.
                For example:

                redis 127.0.0.1:6379> SMEMBERS value:NickAlias:nick:Adam

                Returns a set of object ids of NickAlias objects that have the key
                'nick' set to the value 'Adam' in its hash. Clearly this can only
                ever contain at most one object, since it is not possible to have
                more than one registered nick with the same name, but other keys
                will contain more than one, such as:

                redis 127.0.0.1:6379> SMEMBERS value:NickCore:email:adam@anope.org

                Which would return all accounts with the email "adam@anope.org".

                redis 127.0.0.1:6379> SMEMBERS value:ChanAccess:mask:Adam

                Which would return all access entries set on the account "Adam".

                Behavior similar to SQL's AND, can be achieved using the
                SINTER command, which does set intersection on one or more sets.

2) Keyspace notifications

    Redis 2.7 (unstable) and 2.8 (stable) and newer support keyspace notifications
    (https://redis.io/topics/notifications). This allows Redis to notify Anope of
    any external changes to objects in the database. Once notified, Anope will
    immediately update the object. Otherwise, Anope keeps all objects in memory
    and will not regularly read from the database once started.

    You can use this to modify objects in Redis and have them immediately reflected
    back into Anope. Additionally you can use this feature to run multiple Anope
    instances simultaneously from the same database (see also, Redis database
    replication).

    To use keyspace notifications you MUST execute

    redis 127.0.0.1:6379> CONFIG SET notify-keyspace-events KA
    OK

    or set notify-keyspace-events in redis.conf properly. Anope always executes
    CONFIG SET when it first connects.

    If you do not enable keyspace events properly Anope will be UNABLE to see any
    object modifications you do.

    The key space ids and value are managed entirely by Anope, you do
    not (and should not) modify them. Once you modify the object (hash), Anope will
    update them for you to correctly reflect any changes made to the object.

    Finally, always use atomic operations. If you are inserting a new object with
    multiple commands, or inserting multiple objects at once, specifically if the
    objects depend on each other, you MUST use a transaction.

3) Examples of modifying, deleting, and creating objects

   These examples will ONLY work if you meet the criteria in section 2.

    If I want to change the email account 'Adam' to 'Adam@anope.org', I would execute the following:

        redis 127.0.0.1:6379> SMEMBERS value:NickCore:display:Adam

        Which returns a value of "1", which is the object id I want to modify.
        Now to change the email:

        redis 127.0.0.1:6379> HSET hash:NickCore:1 email Adam@anope.org

        You can now see this in NickServ's INFO command:
        -NickServ-   Email address: Adam@anope.org

   If I want to drop the account "Adam", I would execute the following:

        redis 127.0.0.1:6379> SMEMBERS value:NickCore:display:Adam

        Which returns a value of "1". I would then check:

        redis 127.0.0.1:6379> SMEMBERS value:NickAlias:nc:Adam

        To see what nicknames depend on this account to exist, as I will
        have to remove those too. This returns the values "2", and "3".

        Finally, I can drop the nick using a transaction via:

        redis 127.0.0.1:6379> MULTI
        OK
        redis 127.0.0.1:6379> DEL hash:NickAlias:2
        QUEUED
        redis 127.0.0.1:6379> DEL hash:NickAlias:3
        QUEUED
        redis 127.0.0.1:6379> DEL hash:NickCore:1
        QUEUED
        redis 127.0.0.1:6379> EXEC

        Or alternatively simply:

        redis 127.0.0.1:6379> DEL hash:NickAlias:2 hash:NickAlias:3 hash:NickCore:1

  If I wanted to create a BotServ bot, I would execute the following:

        redis 127.0.0.1:6379> INCR id:BotInfo

        Which returns a new object ID for me, in this example it will be "8".
        Now I can create the object:

        HMSET hash:BotInfo:8 nick redis user redis host services.anope.org realname "Services for IRC Networks"

        Note if you are using HSET instead of HMSET you will need to use a transaction, as shown in the above example.
        If you are watching your services logs you will immediately see:

        USERS: redis!redis@services.anope.org (Services for IRC Networks) connected to the network (services.anope.org)

        And the bot redis will be in BotServ's bot list.
        Notice how ids:BotInfo and the value keys are updated automatically.
