X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/2612e0521fde55db2c720092d4ad02a8f015f46e..cc017c58398480b0b1976a58463954b4bfc084a4:/deps/hiredis/README.md diff --git a/deps/hiredis/README.md b/deps/hiredis/README.md index 51ca2a93..62fe1067 100644 --- a/deps/hiredis/README.md +++ b/deps/hiredis/README.md @@ -35,16 +35,18 @@ To consume the synchronous API, there are only a few function calls that need to ### Connecting -The function `redisConnect` is used to create a so-called `redisContext`. The context is where -Hiredis holds state for a connection. The `redisContext` struct has an `error` field that is -non-NULL when the connection is in an error state. It contains a string with a textual -representation of the error. After trying to connect to Redis using `redisConnect` you should -check the `error` field to see if establishing the connection was successful: +The function `redisConnect` is used to create a so-called `redisContext`. The +context is where Hiredis holds state for a connection. The `redisContext` +struct has an integer `err` field that is non-zero when an the connection is in +an error state. The field `errstr` will contain a string with a description of +the error. More information on errors can be found in the **Errors** section. +After trying to connect to Redis using `redisConnect` you should +check the `err` field to see if establishing the connection was successful: redisContext *c = redisConnect("127.0.0.1", 6379); - if (c->error != NULL) { - printf("Error: %s\n", c->error); - // handle error + if (c->err) { + printf("Error: %s\n", c->errstr); + // handle error } ### Sending commands @@ -71,13 +73,13 @@ convert it to the protocol used to communicate with Redis. One or more spaces separates arguments, so you can use the specifiers anywhere in an argument: - reply = redisCommand("SET key:%s %s", myid, value); + reply = redisCommand(context, "SET key:%s %s", myid, value); ### Using replies The return value of `redisCommand` holds a reply when the command was -successfully executed. When the return value is `NULL`, the `error` field -in the context can be used to find out what was the cause of failure. +successfully executed. When an error occurs, the return value is `NULL` and +the `err` field in the context will be set (see section on **Errors**). Once an error is returned the context cannot be reused and you should set up a new connection. @@ -106,7 +108,7 @@ was received: * **`REDIS_REPLY_ARRAY`**: * A multi bulk reply. The number of elements in the multi bulk reply is stored in `reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well - and can be accessed via `reply->elements[..index..]`. + and can be accessed via `reply->element[..index..]`. Redis may reply with nested arrays but this is fully supported. Replies should be freed using the `freeReplyObject()` function. @@ -114,6 +116,12 @@ Note that this function will take care of freeing sub-replies objects contained in arrays and nested arrays, so there is no need for the user to free the sub replies (it is actually harmful and will corrupt the memory). +**Important:** the current version of hiredis (0.10.0) free's replies when the +asynchronous API is used. This means you should not call `freeReplyObject` when +you use this API. The reply is cleaned up by hiredis _after_ the callback +returns. This behavior will probably change in future releases, so make sure to +keep an eye on the changelog when upgrading (see issue #39). + ### Cleaning up To disconnect and free the context the following function can be used: @@ -166,10 +174,10 @@ to the `redisCommand` family, apart from not returning a reply: After calling either function one or more times, `redisGetReply` can be used to receive the subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where the latter means an error occurred while reading a reply. Just as with the other commands, -the `error` field in the context can be used to find out what the cause of this error is. +the `err` field in the context can be used to find out what the cause of this error is. The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and -a single call to `write(2)`): +a single call to `read(2)`): redisReply *reply; redisAppendCommand(context,"SET foo bar"); @@ -184,10 +192,35 @@ This API can also be used to implement a blocking subscriber: reply = redisCommand(context,"SUBSCRIBE foo"); freeReplyObject(reply); while(redisGetReply(context,&reply) == REDIS_OK) { - // consume message - freeReplyObject(reply); + // consume message + freeReplyObject(reply); } +### Errors + +When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is +returned. The `err` field inside the context will be non-zero and set to one of the +following constants: + +* **`REDIS_ERR_IO`**: + There was an I/O error while creating the connection, trying to write + to the socket or read from the socket. If you included `errno.h` in your + application, you can use the global `errno` variable to find out what is + wrong. + +* **`REDIS_ERR_EOF`**: + The server closed the connection which resulted in an empty read. + +* **`REDIS_ERR_PROTOCOL`**: + There was an error while parsing the protocol. + +* **`REDIS_ERR_OTHER`**: + Any other error. Currently, it is only used when a specified hostname to connect + to cannot be resolved. + +In every case, the `errstr` field in the context will be set to hold a string representation +of the error. + ## Asynchronous API Hiredis comes with an asynchronous API that works easily with any event library. @@ -197,15 +230,15 @@ and [libevent](http://monkey.org/~provos/libevent/). ### Connecting The function `redisAsyncConnect` can be used to establish a non-blocking connection to -Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `error` field +Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field should be checked after creation to see if there were errors creating the connection. Because the connection that will be created is non-blocking, the kernel is not able to instantly return if the specified host and port is able to accept a connection. redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379); - if (c->error != NULL) { - printf("Error: %s\n", c->error); - // handle error + if (c->err) { + printf("Error: %s\n", c->errstr); + // handle error } The asynchronous context can hold a disconnect callback function that is called when the @@ -215,7 +248,7 @@ have the following prototype: void(const redisAsyncContext *c, int status); On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the -user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `error` +user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err` field in the context can be accessed to find out the cause of the error. The context object is always free'd after the disconnect callback fired. When a reconnect is needed, @@ -253,7 +286,8 @@ is being disconnected per user-request, no new commands may be added to the outp returned on calls to the `redisAsyncCommand` family. If the reply for a command with a `NULL` callback is read, it is immediately free'd. When the callback -for a command is non-`NULL`, it is responsible for cleaning up the reply. +for a command is non-`NULL`, the memory is free'd immediately following the callback: the reply is only +valid for the duration of the callback. All pending callbacks are called with a `NULL` reply when the context encountered an error. @@ -276,7 +310,68 @@ See the `adapters/` directory for bindings to *libev* and *libevent*. ## Reply parsing API -To be done. +Hiredis comes with a reply parsing API that makes it easy for writing higher +level language bindings. + +The reply parsing API consists of the following functions: + + redisReader *redisReaderCreate(void); + void redisReaderFree(redisReader *reader); + int redisReaderFeed(redisReader *reader, const char *buf, size_t len); + int redisReaderGetReply(redisReader *reader, void **reply); + +The same set of functions are used internally by hiredis when creating a +normal Redis context, the above API just exposes it to the user for a direct +usage. + +### Usage + +The function `redisReaderCreate` creates a `redisReader` structure that holds a +buffer with unparsed data and state for the protocol parser. + +Incoming data -- most likely from a socket -- can be placed in the internal +buffer of the `redisReader` using `redisReaderFeed`. This function will make a +copy of the buffer pointed to by `buf` for `len` bytes. This data is parsed +when `redisReaderGetReply` is called. This function returns an integer status +and a reply object (as described above) via `void **reply`. The returned status +can be either `REDIS_OK` or `REDIS_ERR`, where the latter means something went +wrong (either a protocol error, or an out of memory error). + +### Customizing replies + +The function `redisReaderGetReply` creates `redisReply` and makes the function +argument `reply` point to the created `redisReply` variable. For instance, if +the response of type `REDIS_REPLY_STATUS` then the `str` field of `redisReply` +will hold the status as a vanilla C string. However, the functions that are +responsible for creating instances of the `redisReply` can be customized by +setting the `fn` field on the `redisReader` struct. This should be done +immediately after creating the `redisReader`. + +For example, [hiredis-rb](https://github.com/pietern/hiredis-rb/blob/master/ext/hiredis_ext/reader.c) +uses customized reply object functions to create Ruby objects. + +### Reader max buffer + +Both when using the Reader API directly or when using it indirectly via a +normal Redis context, the redisReader structure uses a buffer in order to +accumulate data from the server. +Usually this buffer is destroyed when it is empty and is larger than 16 +kb in order to avoid wasting memory in unused buffers + +However when working with very big payloads destroying the buffer may slow +down performances considerably, so it is possible to modify the max size of +an idle buffer changing the value of the `maxbuf` field of the reader structure +to the desired value. The special value of 0 means that there is no maximum +value for an idle buffer, so the buffer will never get freed. + +For instance if you have a normal Redis context you can set the maximum idle +buffer to zero (unlimited) just with: + + context->reader->maxbuf = 0; + +This should be done only in order to maximize performances when working with +large payloads. The context should be set back to `REDIS_READER_MAX_BUF` again +as soon as possible in order to prevent allocation of useless memory. ## AUTHORS