# HG changeset patch # User Tero Marttila # Date 1219876454 -10800 # Node ID 03a7e064f833f1290602cb83b31d7a1208adc7fd # Parent e5b714190dee5333ac5cb56b3e65e704e6b6cf28 stub functions and documentation diff -r e5b714190dee -r 03a7e064f833 memcache.h --- a/memcache.h Thu Aug 28 00:29:39 2008 +0300 +++ b/memcache.h Thu Aug 28 01:34:14 2008 +0300 @@ -21,7 +21,24 @@ * 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; }; @@ -29,9 +46,27 @@ * 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; }; @@ -39,8 +74,21 @@ * 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; }; @@ -49,33 +97,124 @@ */ 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, - 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, - 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, - // MEMCACHE_CMD_FETCH_* + // 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, - // MEMCACHE_CMD_STORE_* + // 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, @@ -83,15 +222,50 @@ enum memcache_req_state { MEMCACHE_STATE_INVALID, + + /* + * The request is queued, and has not been sent yet + */ + MEMCACHE_STATE_QUEUED, - 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, - MEMCACHE_STATE_REPLY_DATA, + /* + * 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, }; @@ -112,27 +286,94 @@ /* * 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 a key into the cache + * 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, void *cb_arg); +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 + * Request state. + * + * Should always return a valid value. */ enum memcache_req_state memcache_req_state (struct memcache_req *req); /* - * Request key + * Request command. + * + * Should always return a valid value. */ -int memcache_req_key (struct memcache_req *req, const struct memcache_key *key); +enum memcache_command memcache_req_cmd (struct memcache_req *req); /* - * Request data + * Request reply. + * + * Will return a valid value in the STATE_REPLY, STATE_REPLY_DATA, STATE_DONE and STATE_DATA_DONE states. */ -int memcache_req_obj (struct memcache_req *req, const struct memcache_obj *obj); +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 */ diff -r e5b714190dee -r 03a7e064f833 memcache/connection.c --- a/memcache/connection.c Thu Aug 28 00:29:39 2008 +0300 +++ b/memcache/connection.c Thu Aug 28 01:34:14 2008 +0300 @@ -298,7 +298,7 @@ ERROR("got reply with wrong key !?!"); // notify the request (no reply data is ready for reading yet, though) - memcache_req_reply(conn->req, reply_type); + memcache_req_recv(conn->req, reply_type); // does the reply include data? if (has_data) { diff -r e5b714190dee -r 03a7e064f833 memcache/memcache.c --- a/memcache/memcache.c Thu Aug 28 00:29:39 2008 +0300 +++ b/memcache/memcache.c Thu Aug 28 01:34:14 2008 +0300 @@ -50,12 +50,12 @@ return mc->server_list.lh_first; } -struct memcache_req *memcache_fetch (struct memcache *mc, const struct memcache_key *key, void *cb_arg) { +static struct memcache_req *_memcache_req (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) { struct memcache_req *req = NULL; struct memcache_server *server = NULL; // alloc the request - if ((req = memcache_req_alloc(mc, MEMCACHE_CMD_FETCH_GET, key, cb_arg)) == NULL) + if ((req = memcache_req_alloc(mc, MEMCACHE_CMD_FETCH_GET, key, obj, buf, cb_arg)) == NULL) ERROR("failed to allocate request"); // pick a server @@ -76,3 +76,24 @@ return NULL; } +struct memcache_req *memcache_fetch (struct memcache *mc, const struct memcache_key *key, void *cb_arg) { + return _memcache_req(mc, MEMCACHE_CMD_FETCH_GET, key, NULL, NULL, cb_arg); +} + +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) { + if ( + (cmd != MEMCACHE_CMD_STORE_SET) + && (cmd != MEMCACHE_CMD_STORE_ADD) + && (cmd != MEMCACHE_CMD_STORE_REPLACE) + && (cmd != MEMCACHE_CMD_STORE_APPEND) + && (cmd != MEMCACHE_CMD_STORE_PREPEND) + && (cmd != MEMCACHE_CMD_STORE_CAS) + ) + ERROR("invalid command for store"); + + return _memcache_req(mc, cmd, key, obj, buf, cb_arg); + +error: + return NULL; +} + diff -r e5b714190dee -r 03a7e064f833 memcache/request.c --- a/memcache/request.c Thu Aug 28 00:29:39 2008 +0300 +++ b/memcache/request.c Thu Aug 28 01:34:14 2008 +0300 @@ -6,8 +6,11 @@ #include "memcache.h" #include "../common.h" -struct memcache_req *memcache_req_alloc (struct memcache *mc, enum memcache_command cmd_type, const struct memcache_key *key, void *cb_arg) { +struct memcache_req *memcache_req_alloc (struct memcache *mc, enum memcache_command cmd_type, const struct memcache_key *key, const struct memcache_obj *obj, const struct memcache_buf *buf, void *cb_arg) { struct memcache_req *req = NULL; + + // ensure key is provided + assert(key != NULL); // allocate it if ((req = calloc(1, sizeof(*req))) == NULL) @@ -20,9 +23,20 @@ if ((req->key.buf = malloc(key->len)) == NULL) ERROR("malloc key buf"); - // copy over the key memcpy(req->key.buf, key->buf, key->len); req->key.len = key->len; + + // copy the obj if provided + if (obj) { + memcpy(&req->obj, obj, sizeof(req->obj)); + req->have_obj = 1; + } + + // copy the buf if provided + if (buf) { + memcpy(&req->buf, buf, sizeof(req->buf)); + req->have_buf = 1; + } // store the other data req->mc = mc; @@ -41,6 +55,32 @@ return NULL; } +// accessors +enum memcache_req_state memcache_req_state (struct memcache_req *req) { + return req->state; +} + +enum memcache_command memcache_req_cmd (struct memcache_req *req) { + return req->cmd_type; +} + +enum memcache_reply memcache_req_reply (struct memcache_req *req) { + return req->reply_type; +} + +const struct memcache_key *keymemcache_req_key (struct memcache_req *req) { + return &req->key; +} + +const struct memcache_obj *memcache_req_obj (struct memcache_req *req) { + return req->have_obj ? &req->obj : NULL; +} + +const struct memcache_buf *memcache_req_buf (struct memcache_req *req) { + return req->have_buf ? &req->buf : NULL; +} + +// events static void _memcache_req_notify (struct memcache_req *req) { req->mc->cb_fn(req, req->cb_arg); } @@ -57,10 +97,13 @@ // _memcache_req_notify(req); } -void memcache_req_reply (struct memcache_req *req, enum memcache_reply reply_type) { +void memcache_req_recv (struct memcache_req *req, enum memcache_reply reply_type) { req->state = MEMCACHE_STATE_REPLY; req->reply_type = reply_type; + // we must surely have a valid obj now + req->have_obj = 1; + _memcache_req_notify(req); } @@ -68,6 +111,9 @@ assert(req->state == MEMCACHE_STATE_REPLY || req->state == MEMCACHE_STATE_REPLY_DATA); req->state = MEMCACHE_STATE_REPLY_DATA; + + // we must surely have a valid buf now + req->have_buf = 1; _memcache_req_notify(req); } @@ -75,15 +121,27 @@ void memcache_req_done (struct memcache_req *req) { // make sure we are in the REPLY/REPLY_DATA state assert(req->state == MEMCACHE_STATE_REPLY || req->state == MEMCACHE_STATE_REPLY_DATA); + + // are we supposed to have data? + if (req->buf.data) { + // make sure we really have the full data, if applicable + assert(req->buf.offset == req->buf.len); + + // yes... + req->have_buf = 1; - // make sure we really have the full data, if applicable - assert(req->buf.data == NULL || req->buf.offset == req->buf.len); + // have data + req->state = MEMCACHE_STATE_DATA_DONE; + + } else { + // no data + req->state = MEMCACHE_STATE_DONE; + } // forget the connection req->conn = NULL; - // state depends on if we have data or not... - req->state = req->buf.data ? MEMCACHE_STATE_DATA_DONE : MEMCACHE_STATE_DONE; + _memcache_req_notify(req); } void memcache_req_error (struct memcache_req *req) { diff -r e5b714190dee -r 03a7e064f833 memcache/request.h --- a/memcache/request.h Thu Aug 28 00:29:39 2008 +0300 +++ b/memcache/request.h Thu Aug 28 01:34:14 2008 +0300 @@ -21,6 +21,9 @@ struct memcache_obj obj; struct memcache_buf buf; + // flags to indicate if we have valid obj/buf data + char have_obj, have_buf; + // our state enum memcache_req_state state; @@ -37,7 +40,7 @@ /* * Allocate and return a new req, or NULL if unsuccesfull. */ -struct memcache_req *memcache_req_alloc (struct memcache *mc, enum memcache_command cmd_type, const struct memcache_key *key, void *cb_arg); +struct memcache_req *memcache_req_alloc (struct memcache *mc, enum memcache_command cmd_type, const struct memcache_key *key, const struct memcache_obj *obj, const struct memcache_buf *buf, void *cb_arg); /* * The request has been queued. @@ -57,7 +60,7 @@ * * Note that no req data will not be available when this is first called, only once req_data/req_done is called. */ -void memcache_req_reply (struct memcache_req *req, enum memcache_reply reply_type); +void memcache_req_recv (struct memcache_req *req, enum memcache_reply reply_type); /* * Some amount of reply data has been received. This may be called between the req_reply/MEMCACHE_RPL_VALUE and diff -r e5b714190dee -r 03a7e064f833 memcache_test.c --- a/memcache_test.c Thu Aug 28 00:29:39 2008 +0300 +++ b/memcache_test.c Thu Aug 28 01:34:14 2008 +0300 @@ -18,15 +18,19 @@ if ((mc = memcache_alloc(&_memcache_cb)) == NULL) ERROR("memcache_alloc"); + // fix up the endpoint endpoint_init(&server_endpoint, 11211); if (endpoint_parse(&server_endpoint, "localhost")) ERROR("config_endpoint_parse"); - + + // add the server if (memcache_add_server(mc, &server_endpoint, 1)) ERROR("memcache_add_server"); + + // add a request or two - // XXX: we should have a connect() running now + error: return;