memcache.h
author Tero Marttila <terom@fixme.fi>
Thu, 28 Aug 2008 01:34:14 +0300
changeset 44 03a7e064f833
parent 43 e5b714190dee
child 45 10d514029c64
permissions -rw-r--r--
stub functions and documentation
#ifndef MEMCACHE_H
#define MEMCACHE_H

/*
 * A libevent based memcached client that aims for high performance, concurrency and low latency.
 */

#include "config.h"

/*
 * Used to store the global information for a memcache context. A context contains both servers and active connections.
 */
struct memcache;

/*
 * A transaction.
 */
struct memcache_req;

/*
 * Keys used
 */
struct memcache_key {
    /*
     * Pointer to a char buffer containing the key itself.
     *
     * A key is a text tring which should uniquely identify a cache entry.
     *
     * The length limit for a key is usually 250 characters, but this is imposed by the server side (a longer key will
     * presumeably cause a CLIENT_ERROR reply).
     *
     * The key should presumeably also not contain any spaces, carriage returns or newlines, as these are used to
     * delimit tokens in the protocol itself.
     *
     * The buffer does not need to be zero-terminated.
     */
    char *buf;

    /*
     * The length of the key buffer in bytes. This does not include a NUL byte.
     */
    size_t len;
};

/*
 * Object attributes
 */
struct memcache_obj {
    /*
     * An arbitrary 16-bit (32-bit for >1.2.1) that the server stores transparently.
     */
    unsigned int flags;

    /*
     * Expiration time. If non-zero, either an offset in seconds from current time (up to 60*60*24*30 seconds, or 30d),
     * or an absolute 32-bit unix timestamp.
     */
    time_t exptime;

    /*
     * Number of bytes in the entry. This may be zero, in which case there will be no data.
     */
    size_t bytes;

    /*
     * Used for the CMD_STORE_CAS command. An unique 64-bit value that changes if the entry is modified.
     *
     * Not needed for other commands, and may or may not be provided by CMD_FETCH_GET (will be set to zero).
     */
    unsigned long long cas;
};

/*
 * Object data
 */
struct memcache_buf {
    /*
     * The char buffer containing the data.
     */
    char *data;

    /*
     * The total length of the char buffer, and thence the cache entry.
     */
    size_t len;

    /*
     * The amount of data currently available in the buffer. This is used to provide streaming fetches.
     *
     * This field is *IMPORTANT*! Don't disregard it, or you *will* get incomplete data.
     */
    size_t offset;
};

/*
 * Available commands
 */
enum memcache_command {
    MEMCACHE_CMD_INVALID,
    
    /*
     * Retrieve the value of a key from the memcached server. 
     *
     * If the key exists, you will get a RPL_VALUE reply followed by a RPL_END reply. 
     * If it does not exist, you will just get a RPL_END reply.
     */
    MEMCACHE_CMD_FETCH_GET,

    /*
     * Store this data.
     *
     * Returns either RPL_STORED or RPL_NOT_STORED.
     */
    MEMCACHE_CMD_STORE_SET,

    /*
     * Store this data, but only if the server doesn't already hold data for this key.
     *
     * Returns either RPL_STORED or RPL_NOT_STORED.
     */
    MEMCACHE_CMD_STORE_ADD,

    /*
     * Store this data, but only if the server does already hold data for this key.
     *
     * Returns either RPL_STORED or RPL_NOT_STORED.
     */
    MEMCACHE_CMD_STORE_REPLACE,

    /*
     * Add this data to an existing key after existing data.
     *
     * obj.flags and obj.exptime are ignored.
     *
     * Returns ???
     */
    MEMCACHE_CMD_STORE_APPEND,

    /*
     * Add this data to an existing key before existing data.
     *
     * obj.flags and obj.exptime are ignored.
     *
     * Returns ???
     */
    MEMCACHE_CMD_STORE_PREPEND,

    /*
     * Check and Set - store this data but only if no one else had updated it since I last fetched it.
     *
     * obj.cas is required.
     *
     * Returns RPL_STORED, RPL_NOT_STORED, RPL_EXISTS (data has been modified), or RPL_NOT_FOUND (the item does not
     * exist or has been deleted).
     */
    MEMCACHE_CMD_STORE_CAS,

    MEMCACHE_CMD_MAX,
};

/*
 * Replies from the server
 */
enum memcache_reply {
    MEMCACHE_RPL_INVALID,
    
    /*
     * The library sent an unknown command. This probably means the server is not compatible with said command.
     */
    MEMCACHE_RPL_ERROR,

    /*
     * There was an error in the request that the library sent. The error message is printed out via ERROR.
     */
    MEMCACHE_RPL_CLIENT_ERROR,

    /*
     * There was an error with the server when processing the request. The error message is printed out via ERROR.
     */
    MEMCACHE_RPL_SERVER_ERROR,
    
    // CMD_FETCH_*

    /*
     * The given key was found in the cache. The obj attributes are now known, and the buf data is on the way.
     */
    MEMCACHE_RPL_VALUE,

    /*
     * No more RPL_VALUE replies will be sent. This may be the only reply sent if the key was not found.
     */
    MEMCACHE_RPL_END,
    
    // CMD_STORE_*

    /*
     * The object was succesfully stored in the cache.
     */
    MEMCACHE_RPL_STORED,

    /*
     * The object was not stored in the cache.
     *
     * This could be for a number of reasons, perhaps the object is too large, the cache is full, the key is in the
     * delete queue, or some condition imposed by the STORE_* command was not met.
     */
    MEMCACHE_RPL_NOT_STORED,

    /*
     * The item you were trying to store with a STORE_CAS request has been modified since you last fetched it (i.e.
     * obj.cas does not match).
     */
    MEMCACHE_RPL_EXISTS,

    /*
     * The item you were trying to store with a STORE_CAS request did not exist (or has been deleted).
     */
    MEMCACHE_RPL_NOT_FOUND,

    MEMCACHE_RPL_MAX,
};

enum memcache_req_state {
    MEMCACHE_STATE_INVALID,
    
    /*
     * The request is queued, and has not been sent yet
     */
    MEMCACHE_STATE_QUEUED,

    /*
     * The request is being sent, and the reply has not yet been received
     */
    MEMCACHE_STATE_SEND,

    /*
     * The reply has been received.
     *
     * req_reply and req_obj will return a non-NULL value, but there is no reply data yet.
     */
    MEMCACHE_STATE_REPLY,

    /*
     * The reply and part of the reply data has been received.
     *
     * req_reply, req_obj and req_buf will all return non-NULL values, but buf.offset will be smaller than buf.len.
     */
    MEMCACHE_STATE_REPLY_DATA,
    
    /*
     * The full reply has been received.
     *
     * req_reply, req_obj will return a non-NULL value, but there is no reply data.
     */
    MEMCACHE_STATE_DONE,

    /*
     * The full reply and reply data has been received.
     *
     * req_reply, req_obj and req_buf will all return non-NULL values, and buf.offset will be equal to buf.len.
     */
    MEMCACHE_STATE_DATA_DONE,
    
    /*
     * An error has occurred.
     *
     * req_reply, req_obj and req_buf may or may not work.
     */
    MEMCACHE_STATE_ERROR,
};

/*
 * Callback used
 */
typedef int (*memcache_cb) (struct memcache_req *, void*);

/*
 * Allocate a new memcache context for use with other methods.
 */
struct memcache *memcache_alloc (memcache_cb cb_fn);

/*
 * Add a server to the pool of available servers.
 */
int memcache_add_server (struct memcache *mc, struct config_endpoint *endpoint, int max_connections);

/*
 * Attempt to fetch a key from the cache.
 *
 * The state machine will work as follows:
 *
 *  req_state                  multi   req_reply
 *  ---------------------------------------------
 *  STATE_QUEUE                 ?
 *  STATE_SEND
 *  STATE_REPLY                         RPL_VALUE
 *      STATE_REPLY_DATA        *       RPL_VALUE
 *      STATE_DATA_DONE                 RPL_END
 *
 *      STATE_DONE                      RPL_END
 *
 *  STATE_ERROR                         RPL_{ERROR,CLIENT_ERROR,SERVER_ERROR}
 *
 * The item attributes/data can be accessed via req_obj/req_buf as described in `enum memcache_state`.
 */
struct memcache_req *memcache_fetch (struct memcache *mc, const struct memcache_key *key, void *cb_arg);

/*
 * Attempt to store an item into the cache.
 *
 * The cmd argument can be used to specify what CMD_STORE_* command to use.
 *
 * The given memcache_key is copied, including the char array pointed to by buf.
 * Both obj and buf are also copied, but buf.data will not be copied - the pointer must remain valid until the request is done.
 *
 * The state machine will work as follows:
 *
 *  req_state               multi       req_reply
 *  ---------------------------------------------
 *  STATE_QUEUE             ?
 *  STATE_SEND
 *  STATE_REPLY                         RPL_{STORED,NOT_STORED,EXISTS,NOT_FOUND}
 *  STATE_DONE                          RPL_{STORED,NOT_STORED,EXISTS,NOT_FOUND}
 * 
 *  STATE_ERROR                         RPL_{ERROR,CLIENT_ERROR,SERVER_ERROR}
 *
 */
struct memcache_req *memcache_store (struct memcache *mc, enum memcache_command cmd, const struct memcache_key *key, const struct memcache_obj *obj, const struct memcache_buf *buf, void *cb_arg);

/*
 * Request state.
 *
 * Should always return a valid value.
 */ 
enum memcache_req_state memcache_req_state (struct memcache_req *req);

/*
 * Request command.
 *
 * Should always return a valid value.
 */
enum memcache_command memcache_req_cmd (struct memcache_req *req);

/*
 * Request reply.
 *
 * Will return a valid value in the STATE_REPLY, STATE_REPLY_DATA, STATE_DONE and STATE_DATA_DONE states.
 */
enum memcache_reply memcache_req_reply (struct memcache_req *req);

/*
 * Request key.
 *
 * Will return a valid valuein all states
 */
const struct memcache_key *keymemcache_req_key (struct memcache_req *req);

/*
 * Request data.
 *
 * Will return a valid value in the STATE_REPLY, STATE_REPLY_DATA, STATE_DONE and STATE_DATA_DONE states.
 */
const struct memcache_obj *memcache_req_obj (struct memcache_req *req);

/*
 * Request buf.
 *
 * Will return a valid value in the STATE_REPLY_DATA and STATE_DATA_DONE states.
 *
 * Note that buf.offset may be less than buf.len in the STATE_REPLY_DATA state.
 */
const struct memcache_buf *memcache_req_buf (struct memcache_req *req);

/*
 * Free a req that is in the STATE_DONE, STATE_DATA_DONE or STATE_ERROR state.
 */
void memcache_req_free (struct memcache_req *req);

#endif /* MEMCACHE_H */