Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle integer replies, implement a connect timeout and dynamic config in VCL #2

Merged
merged 6 commits into from May 31, 2012
11 changes: 11 additions & 0 deletions README.md
Expand Up @@ -14,6 +14,17 @@ So far the module builds and runs on FreeBSD--on other platforms, you are on you
Functions and procedures
------------------------

*redis.init_redis(host, port, timeout_ms)*

Use the redis server at the given _host_ and _port_ with a timeout
of _timeout__ms_ milliseconds.
If _port_ is less than or equal to zero, the default port of 6379 is used.
If _timeout__ms_ is less than or equal to zero, a default timeout of 200ms is used.

This function is supposed to be called from the Varnish subroutine _vcl__init_.
If the call is left out, the module will attempt to connect to the Redis server
at 127.0.0.1:6379 with a connect timeout of 200ms.

*redis.send(command)*

Sends the given _command_ to redis; the response will be ignored.
Expand Down
13 changes: 12 additions & 1 deletion examples/example.vcl
Expand Up @@ -9,9 +9,20 @@ backend be1 {
.port = "80";
}

#sub vcl_init {
#
# By default, the redis module will attempt to connect to a Redis server
# at 127.0.0.1:6379 with a connect timeout of 200 milliseconds.
#
# The function redis.init_redis(host, port, timeout_ms) may be used to
# connect to an alternate Redis server or use a different connect timeout.
#
# redis.init_redis("localhost", 6379, 200); /* default values */
#}

sub vcl_recv {
#
# redis.call is a procedure, it will send the command to redis and ignore
# redis.send is a procedure, it will send the command to redis and ignore
# the response. If the command errors out, it will be logged but the VCL
# will not know.
#
Expand Down
70 changes: 58 additions & 12 deletions src/vmod_redis.c
@@ -1,5 +1,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>

#include "vrt.h"
#include "bin/varnishd/cache.h"
Expand All @@ -10,6 +11,9 @@
#include <hiredis/hiredis.h>


#define REDIS_TIMEOUT_MS 200 /* 200 milliseconds */


#define LOG_E(...) fprintf(stderr, __VA_ARGS__);
#ifdef DEBUG
# define LOG_T(...) fprintf(stderr, __VA_ARGS__);
Expand All @@ -20,6 +24,7 @@
typedef struct redisConfig {
char *host;
int port;
struct timeval timeout;
} config_t;

static pthread_key_t redis_key;
Expand All @@ -44,36 +49,73 @@ make_key()
{
(void)pthread_key_create(&redis_key, NULL);
}

static config_t *
make_config(const char *host, int port, int timeout_ms)
{
config_t *cfg;

LOG_T("make_config(%s,%d,%d)\n", host, port, timeout_ms);

cfg = malloc(sizeof(config_t));
if(cfg == NULL)
return NULL;

if(port <= 0)
port = 6379;

if(timeout_ms <= 0)
timeout_ms = REDIS_TIMEOUT_MS;

cfg->host = strdup(host);
cfg->port = port;

cfg->timeout.tv_sec = timeout_ms / 1000;
cfg->timeout.tv_usec = (timeout_ms % 1000) * 1000;

return cfg;
}

int
init_function(struct vmod_priv *priv, const struct VCL_conf *conf)
{
config_t *cfg;

LOG_T("redis init called\n");

(void)pthread_once(&redis_key_once, make_key);

if (priv->priv == NULL) {
priv->priv = make_config("127.0.0.1", 6379, REDIS_TIMEOUT_MS);
priv->free = free;
}

return (0);
}

void
vmod_init_redis(struct sess *sp, struct vmod_priv *priv, const char *host, int port, int timeout_ms)
{
config_t *old_cfg = priv->priv;

priv->priv = make_config(host, port, timeout_ms);
if(priv->priv && old_cfg) {
free(old_cfg->host);
free(old_cfg);
}
}

static redisReply *
redis_common(struct sess *sp, struct vmod_priv *priv, const char *command)
{
config_t *cfg;
config_t *cfg = priv->priv;
redisContext *c;
redisReply *reply = NULL;

LOG_T("redis(%x): running %s %p\n", pthread_self(), command, priv->priv);

cfg = priv->priv;
if (cfg == NULL) {
priv->priv = cfg = malloc(sizeof(config_t));
priv->free = free;
cfg->host = strdup("127.0.0.1");
cfg->port = 6379;
}

if ((c = pthread_getspecific(redis_key)) == NULL) {
c = redisConnect(cfg->host, cfg->port);
c = redisConnectWithTimeout(cfg->host, cfg->port, cfg->timeout);
if (c->err) {
LOG_E("redis error (connect): %s\n", c->errstr);
}
Expand All @@ -82,7 +124,7 @@ redis_common(struct sess *sp, struct vmod_priv *priv, const char *command)

reply = redisCommand(c, command);
if (reply == NULL && c->err == REDIS_ERR_EOF) {
c = redisConnect(cfg->host, cfg->port);
c = redisConnectWithTimeout(cfg->host, cfg->port, cfg->timeout);
if (c->err) {
LOG_E("redis error (reconnect): %s\n", c->errstr);
redisFree(c);
Expand Down Expand Up @@ -114,6 +156,7 @@ vmod_call(struct sess *sp, struct vmod_priv *priv, const char *command)
{
redisReply *reply = NULL;
const char *ret = NULL;
char *digits;

reply = redis_common(sp, priv, command);
if (reply == NULL) {
Expand All @@ -128,7 +171,10 @@ vmod_call(struct sess *sp, struct vmod_priv *priv, const char *command)
ret = strdup(reply->str);
break;
case REDIS_REPLY_INTEGER:
ret = strdup("integer"); /* FIXME */
digits = malloc(21); /* sizeof(long long) == 8; 20 digits + NUL */
if(digits)
sprintf(digits, "%lld", reply->integer);
ret = digits;
break;
case REDIS_REPLY_NIL:
ret = NULL;
Expand Down
5 changes: 3 additions & 2 deletions src/vmod_redis.vcc
@@ -1,4 +1,5 @@
Module redis
Init init_function
Function VOID send(PRIV_CALL, STRING)
Function STRING call(PRIV_CALL, STRING)
Function VOID init_redis(PRIV_VCL, STRING, INT, INT)
Function VOID send(PRIV_VCL, STRING)
Function STRING call(PRIV_VCL, STRING)