# HG changeset patch # User Tero Marttila # Date 1219821104 -10800 # Node ID 0e21a65074a694905fbff8aebcf9f78eb64258a1 # Parent 9894df13b77920657fb4840cda919168d2749a2b memcache connect error handling and req queuein diff -r 9894df13b779 -r 0e21a65074a6 Makefile --- a/Makefile Tue Aug 26 01:30:53 2008 +0300 +++ b/Makefile Wed Aug 27 10:11:44 2008 +0300 @@ -16,7 +16,7 @@ web_main: web_main.o common.o config.o socket.o http.o render.o remote_node.o remote_pool.o render_remote.o tile.o static.o coro_test: coro_test.o common.o config.o socket.o cache_test: cache_test.o common.o cache/cache.o cache/req.o cache/op.o cache/engines/fs.o -memcache_test: memcache_test.o common.o memcache/memcache.o memcache/connection.o memcache/server.o socket.o config.o +memcache_test: memcache_test.o common.o memcache/memcache.o memcache/connection.o memcache/server.o memcache/request.o socket.o config.o clean : -rm *.o ${EXECS} diff -r 9894df13b779 -r 0e21a65074a6 memcache.h --- a/memcache.h Tue Aug 26 01:30:53 2008 +0300 +++ b/memcache.h Wed Aug 27 10:11:44 2008 +0300 @@ -21,7 +21,7 @@ * Keys used */ struct memcache_key { - const char *buf; + char *buf; size_t len; }; @@ -48,7 +48,7 @@ STORE_CAS, }; -enum memcache_state { +enum memcache_req_state { STATE_INVALID, STATE_ERROR, @@ -82,7 +82,7 @@ /* * Request state */ -int memcache_req_state (struct memcache_req *req, enum memcache_state *state); +enum memcache_req_state memcache_req_state (struct memcache_req *req); /* * Request key diff -r 9894df13b779 -r 0e21a65074a6 memcache/connection.c --- a/memcache/connection.c Tue Aug 26 01:30:53 2008 +0300 +++ b/memcache/connection.c Wed Aug 27 10:11:44 2008 +0300 @@ -1,6 +1,7 @@ #include #include +#include #include #include "connection.h" @@ -32,12 +33,15 @@ } int memcache_conn_connect (struct memcache_conn *conn) { - assert(conn->fd <= 0); + assert(conn->fd <= 0 && !conn->is_connected); // begin connect if ((conn->fd = socket_connect_async(conn->server->endpoint, SOCK_STREAM)) == -1) goto error; + // fd 0 should be stdin... + assert(conn->fd > 0); + // set up the connect event event_set(&conn->ev, conn->fd, EV_WRITE, &_memcache_conn_ev_connect, conn); @@ -59,8 +63,12 @@ return -1; } +int memcache_conn_is_available (struct memcache_conn *conn) { + return (conn->fd > 0 && conn->is_connected && conn->req == NULL); +} + int memcache_conn_do_req (struct memcache_conn *conn, struct memcache_req *req) { - assert(conn->fd > 0); + assert(conn->fd > 0 && conn->is_connected); assert(conn->req == NULL); // store the req @@ -77,10 +85,44 @@ */ static void _memcache_conn_ev_connect (evutil_socket_t fd, short what, void *arg) { struct memcache_conn *conn = arg; + int error; - // XXX: check if the connect() failed + if (socket_check_error(fd, &error)) + goto error; + + if (error) + ERROR("connect failed: %s", strerror(error)); + + // mark us as succesfully connected + conn->is_connected = 1; // notify the server memcache_server_conn_ready(conn->server, conn); + + // good + return; + +error: + // notify the server + memcache_server_conn_dead(conn->server, conn); } +void memcache_conn_free (struct memcache_conn *conn) { + // ensure that the connection is not considered to be connected anymore + assert(!conn->is_connected); + + // close the fd if needed + if (conn->fd > 0) { + if (close(conn->fd)) + PWARNING("close"); + + conn->fd = 0; + } + + // ensure that the event is not pending anymore + assert(event_pending(&conn->ev, EV_READ|EV_WRITE|EV_TIMEOUT, NULL) == 0); + + // free it + free(conn); +} + diff -r 9894df13b779 -r 0e21a65074a6 memcache/connection.h --- a/memcache/connection.h Tue Aug 26 01:30:53 2008 +0300 +++ b/memcache/connection.h Wed Aug 27 10:11:44 2008 +0300 @@ -20,6 +20,9 @@ // socket event struct event ev; + + // have we succesfully connected yet? + int is_connected; // the request (if any) that we are currently processing - NULL for idle connections struct memcache_req *req; @@ -38,8 +41,18 @@ int memcache_conn_connect (struct memcache_conn *conn); /* + * Check if the given connection is available, that is, connected and not busy + */ +int memcache_conn_is_available (struct memcache_conn *conn); + +/* * The given connection must be idle, whereupon the given request is processed. */ int memcache_conn_do_req (struct memcache_conn *conn, struct memcache_req *req); +/* + * Free all resources associated with a closed/failed connection + */ +void memcache_conn_free (struct memcache_conn *conn); + #endif /* MEMCACHE_CONNECTION_H */ diff -r 9894df13b779 -r 0e21a65074a6 memcache/memcache.c --- a/memcache/memcache.c Tue Aug 26 01:30:53 2008 +0300 +++ b/memcache/memcache.c Wed Aug 27 10:11:44 2008 +0300 @@ -44,3 +44,34 @@ return -1; } +struct memcache_server *memcache_choose_server (struct memcache *mc, const struct memcache_key *key) { + // XXX: support multiple servers + return mc->server_list.lh_first; +} + +struct memcache_req *memcache_fetch (struct memcache *mc, const struct memcache_key *key, void *cb_arg) { + struct memcache_req *req = NULL; + struct memcache_server *server = NULL; + + // alloc the request + if ((req = memcache_req_alloc(key, cb_arg)) == NULL) + ERROR("failed to allocate request"); + + // pick a server + if ((server = memcache_choose_server(mc, key)) == NULL) + ERROR("failed to find a server to use"); + + // enqueue it! + if (memcache_server_add_req(server, req)) + ERROR("failed to hand over the request for processing"); + + // success! + return 0; + +error: + if (req) + memcache_req_free(req); + + return -1; +} + diff -r 9894df13b779 -r 0e21a65074a6 memcache/memcache.h --- a/memcache/memcache.h Tue Aug 26 01:30:53 2008 +0300 +++ b/memcache/memcache.h Wed Aug 27 10:11:44 2008 +0300 @@ -13,4 +13,6 @@ LIST_HEAD(memcache_serverlist_head, memcache_server) server_list; }; +struct memcache_server *memcache_choose_server (struct memcache *mc, const struct memcache_key *key); + #endif /* MEMCACHE_MEMCACHE_H */ diff -r 9894df13b779 -r 0e21a65074a6 memcache/request.h --- a/memcache/request.h Tue Aug 26 01:30:53 2008 +0300 +++ b/memcache/request.h Wed Aug 27 10:11:44 2008 +0300 @@ -7,14 +7,41 @@ #include "connection.h" struct memcache_req { - struct memcached_connection *conn; + // the memcache context that we belong to + struct memcache *mc; + + // our key/obj + struct memcache_key key; + struct memcache_obj *obj; + + // our state + enum memcache_req_state state; + + // our user callback argument + void *cb_arg; + + // the conn + struct memcache_connection *conn; // we are a member of struct memcache_server.req_queue TAILQ_ENTRY(memcache_req) reqqueue_node; - - // our key/obj - struct memcached_key *key; - struct memcached_obj *obj; }; +/* + * Allocate and return a new req, or NULL if unsuccesfull. + */ +struct memcache_req *memcache_req_alloc (struct memcache *mc, struct memcache_key *key, void *cb_arg); + + +/* + * An error occurred, and the request must be abandoned. This will assume that the req is not active or enqueued + * anymore, and the req should not be accessed by memcache_* code after this. + */ +void memcache_req_error (struct memcache_req *req); + +/* + * Free an unused req. Should always be called by the user, not via internal means. + */ +void memcache_req_free (struct memcache_req *req); + #endif /* MEMCACHE_REQ_H */ diff -r 9894df13b779 -r 0e21a65074a6 memcache/server.c --- a/memcache/server.c Tue Aug 26 01:30:53 2008 +0300 +++ b/memcache/server.c Wed Aug 27 10:11:44 2008 +0300 @@ -59,7 +59,7 @@ // look for an idle connection for (conn = server->conn_list.lh_first; conn != NULL; conn = conn->connlist_node.le_next) { - if (conn->req == NULL) { + if (memcache_conn_is_available(conn)) { // we found an idle connection break; } @@ -91,9 +91,22 @@ if (memcache_conn_do_req(conn, req)) { WARNING("processing enqueued request failed"); - - // XXX: error handling for req + + // notify the request/user, the req is now out of our hands + memcache_req_error(req); } } } +void memcache_server_conn_dead (struct memcache_server *server, struct memcache_conn *conn) { + assert(server == conn->server); + + // XXX: reconnect/error out requests? + + // remove it from the list + LIST_REMOVE(conn, connlist_node); + + // free it + memcache_conn_free(conn); +} + diff -r 9894df13b779 -r 0e21a65074a6 memcache/server.h --- a/memcache/server.h Tue Aug 26 01:30:53 2008 +0300 +++ b/memcache/server.h Wed Aug 27 10:11:44 2008 +0300 @@ -43,4 +43,9 @@ */ void memcache_server_conn_ready (struct memcache_server *server, struct memcache_conn *conn); +/* + * The given connection failed/died + */ +void memcache_server_conn_dead (struct memcache_server *server, struct memcache_conn *conn); + #endif /* MEMCACHE_SERVER_H */ diff -r 9894df13b779 -r 0e21a65074a6 socket.c --- a/socket.c Tue Aug 26 01:30:53 2008 +0300 +++ b/socket.c Wed Aug 27 10:11:44 2008 +0300 @@ -77,6 +77,23 @@ return sock; } +/* + * Check if the given socket has an error condition set, mostly intended for use with socket_connect_async. + * + * Returns 0 and sets *error on success (zero = no error, nonzero = error), -1 on failure (invalid socket). + */ +int socket_check_error (int sock, int *error) { + socklen_t optlen = sizeof(*error); + + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, error, &optlen)) + PERROR("getsockopt"); + + return 0; + +error: + return -1; +} + /* * Do something to apply an endpoint to a socket diff -r 9894df13b779 -r 0e21a65074a6 socket.h --- a/socket.h Tue Aug 26 01:30:53 2008 +0300 +++ b/socket.h Wed Aug 27 10:11:44 2008 +0300 @@ -9,5 +9,6 @@ int socket_listen (struct config_endpoint *endpoint, int sock_type); int socket_connect_async (struct config_endpoint *endpoint, int sock_type); +int socket_check_error (int sock, int *error); #endif /* SOCKET_H */