]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Redis read-only replica support (#1019)
authorYorgos Thessalonikefs <yorgos@nlnetlabs.nl>
Fri, 4 Apr 2025 08:20:47 +0000 (10:20 +0200)
committerGitHub <noreply@github.com>
Fri, 4 Apr 2025 08:20:47 +0000 (10:20 +0200)
* Set version to 1.19.1 for point release.

* Initial work for Redis read-only replica support.

* Test for Redis replica.

* Documentation for the Redis replica timeouts.

* redis replica, rewrite set_timeout()

* clean merge.

* Add new options for fast reload.

* Apply suggestions from code review

Co-authored-by: Wouter Wijngaards <wcawijngaards@users.noreply.github.com>
* some more typos

---------

Co-authored-by: W.C.A. Wijngaards <wouter@nlnetlabs.nl>
Co-authored-by: Wouter Wijngaards <wcawijngaards@users.noreply.github.com>
21 files changed:
cachedb/redis.c
daemon/remote.c
doc/example.conf.in
doc/unbound.conf.5.in
testcode/do-tests.sh
testdata/redis_replica.tdir/after.zone [new file with mode: 0644]
testdata/redis_replica.tdir/before.zone [new file with mode: 0644]
testdata/redis_replica.tdir/redis.conf [new file with mode: 0644]
testdata/redis_replica.tdir/redis_replica.conf [new file with mode: 0644]
testdata/redis_replica.tdir/redis_replica.dsc [new file with mode: 0644]
testdata/redis_replica.tdir/redis_replica.post [new file with mode: 0644]
testdata/redis_replica.tdir/redis_replica.pre [new file with mode: 0644]
testdata/redis_replica.tdir/redis_replica.test [new file with mode: 0644]
testdata/redis_replica.tdir/unbound_control.key [new file with mode: 0644]
testdata/redis_replica.tdir/unbound_control.pem [new file with mode: 0644]
testdata/redis_replica.tdir/unbound_server.key [new file with mode: 0644]
testdata/redis_replica.tdir/unbound_server.pem [new file with mode: 0644]
util/config_file.c
util/config_file.h
util/configlexer.lex
util/configparser.y

index 183879dd00af5bbdbe332b20e5f0c77741842dcb..3dfa95859eb8bf3d272ef1dba0e12f7942d02f63 100644 (file)
 #include "hiredis/hiredis.h"
 
 struct redis_moddata {
-       redisContext** ctxs;    /* thread-specific redis contexts */
-       int numctxs;            /* number of ctx entries */
-       const char* server_host; /* server's IP address or host name */
-       int server_port;         /* server's TCP port */
-       const char* server_path; /* server's unix path, or "", NULL if unused */
-       const char* server_password; /* server's AUTH password, or "", NULL if unused */
-       struct timeval command_timeout;  /* timeout for commands */
-       struct timeval connect_timeout;  /* timeout for connect */
-       int logical_db;         /* the redis logical database to use */
-       int set_with_ex_available;    /* if the SET with EX command is supported */
+       /* thread-specific redis contexts */
+       redisContext** ctxs;
+       redisContext** replica_ctxs;
+       /* number of ctx entries */
+       int numctxs;
+       /* server's IP address or host name */
+       const char* server_host;
+       const char* replica_server_host;
+       /* server's TCP port */
+       int server_port;
+       int replica_server_port;
+       /* server's unix path, or "", NULL if unused */
+       const char* server_path;
+       const char* replica_server_path;
+       /* server's AUTH password, or "", NULL if unused */
+       const char* server_password;
+       const char* replica_server_password;
+       /* timeout for commands */
+       struct timeval command_timeout;
+       struct timeval replica_command_timeout;
+       /* timeout for connection setup */
+       struct timeval connect_timeout;
+       struct timeval replica_connect_timeout;
+       /* the redis logical database to use */
+       int logical_db;
+       int replica_logical_db;
+       /* if the SET with EX command is supported */
+       int set_with_ex_available;
 };
 
 static redisReply* redis_command(struct module_env*, struct cachedb_env*,
-       const char*, const uint8_t*, size_t);
+       const char*, const uint8_t*, size_t, int);
 
 static void
 moddata_clean(struct redis_moddata** moddata) {
@@ -79,21 +97,30 @@ moddata_clean(struct redis_moddata** moddata) {
                }
                free((*moddata)->ctxs);
        }
+       if((*moddata)->replica_ctxs) {
+               int i;
+               for(i = 0; i < (*moddata)->numctxs; i++) {
+                       if((*moddata)->replica_ctxs[i])
+                               redisFree((*moddata)->replica_ctxs[i]);
+               }
+               free((*moddata)->replica_ctxs);
+       }
        free(*moddata);
        *moddata = NULL;
 }
 
 static redisContext*
-redis_connect(const struct redis_moddata* moddata)
+redis_connect(const char* host, int port, const char* path,
+       const char* password, int logical_db,
+       const struct timeval connect_timeout,
+       const struct timeval command_timeout)
 {
        redisContext* ctx;
 
-       if(moddata->server_path && moddata->server_path[0]!=0) {
-               ctx = redisConnectUnixWithTimeout(moddata->server_path,
-                       moddata->connect_timeout);
+       if(path && path[0]!=0) {
+               ctx = redisConnectUnixWithTimeout(path, connect_timeout);
        } else {
-               ctx = redisConnectWithTimeout(moddata->server_host,
-                       moddata->server_port, moddata->connect_timeout);
+               ctx = redisConnectWithTimeout(host, port, connect_timeout);
        }
        if(!ctx || ctx->err) {
                const char *errstr = "out of memory";
@@ -102,13 +129,13 @@ redis_connect(const struct redis_moddata* moddata)
                log_err("failed to connect to redis server: %s", errstr);
                goto fail;
        }
-       if(redisSetTimeout(ctx, moddata->command_timeout) != REDIS_OK) {
+       if(redisSetTimeout(ctx, command_timeout) != REDIS_OK) {
                log_err("failed to set redis timeout, %s", ctx->errstr);
                goto fail;
        }
-       if(moddata->server_password && moddata->server_password[0]!=0) {
+       if(password && password[0]!=0) {
                redisReply* rep;
-               rep = redisCommand(ctx, "AUTH %s", moddata->server_password);
+               rep = redisCommand(ctx, "AUTH %s", password);
                if(!rep || rep->type == REDIS_REPLY_ERROR) {
                        log_err("failed to authenticate with password");
                        freeReplyObject(rep);
@@ -116,18 +143,25 @@ redis_connect(const struct redis_moddata* moddata)
                }
                freeReplyObject(rep);
        }
-       if(moddata->logical_db > 0) {
+       if(logical_db > 0) {
                redisReply* rep;
-               rep = redisCommand(ctx, "SELECT %d", moddata->logical_db);
+               rep = redisCommand(ctx, "SELECT %d", logical_db);
                if(!rep || rep->type == REDIS_REPLY_ERROR) {
                        log_err("failed to set logical database (%d)",
-                               moddata->logical_db);
+                               logical_db);
                        freeReplyObject(rep);
                        goto fail;
                }
                freeReplyObject(rep);
        }
-       verbose(VERB_OPS, "Connection to Redis established");
+       if(verbosity >= VERB_OPS) {
+               char port_str[6+1];
+               port_str[0] = ' ';
+               (void)snprintf(port_str+1, sizeof(port_str)-1, "%d", port);
+               verbose(VERB_OPS, "Connection to Redis established (%s%s)",
+                       path&&path[0]!=0?path:host,
+                       path&&path[0]!=0?"":port_str);
+       }
        return ctx;
 
 fail:
@@ -136,6 +170,14 @@ fail:
        return NULL;
 }
 
+static void
+set_timeout(struct timeval* timeout, int value, int explicit_value)
+{
+       int v = explicit_value != 0 ? explicit_value : value;
+       timeout->tv_sec = v / 1000;
+       timeout->tv_usec = (v % 1000) * 1000;
+}
+
 static int
 redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
 {
@@ -150,38 +192,60 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
                goto fail;
        }
        moddata->numctxs = env->cfg->num_threads;
-       moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
-       if(!moddata->ctxs) {
-               log_err("out of memory");
-               goto fail;
-       }
-       /* note: server_host is a shallow reference to configured string.
-        * we don't have to free it in this module. */
+       /* note: server_host and similar string configuration options are
+        * shallow references to configured strings; we don't have to free them
+        * in this module. */
        moddata->server_host = env->cfg->redis_server_host;
+       moddata->replica_server_host = env->cfg->redis_replica_server_host;
+
        moddata->server_port = env->cfg->redis_server_port;
+       moddata->replica_server_port = env->cfg->redis_replica_server_port;
+
        moddata->server_path = env->cfg->redis_server_path;
+       moddata->replica_server_path = env->cfg->redis_replica_server_path;
+
        moddata->server_password = env->cfg->redis_server_password;
-       moddata->command_timeout.tv_sec = env->cfg->redis_timeout / 1000;
-       moddata->command_timeout.tv_usec =
-               (env->cfg->redis_timeout % 1000) * 1000;
-       moddata->connect_timeout.tv_sec = env->cfg->redis_timeout / 1000;
-       moddata->connect_timeout.tv_usec =
-               (env->cfg->redis_timeout % 1000) * 1000;
-       if(env->cfg->redis_command_timeout != 0) {
-               moddata->command_timeout.tv_sec =
-                       env->cfg->redis_command_timeout / 1000;
-               moddata->command_timeout.tv_usec =
-                       (env->cfg->redis_command_timeout % 1000) * 1000;
+       moddata->replica_server_password = env->cfg->redis_replica_server_password;
+
+       set_timeout(&moddata->command_timeout,
+               env->cfg->redis_timeout,
+               env->cfg->redis_command_timeout);
+       set_timeout(&moddata->replica_command_timeout,
+               env->cfg->redis_replica_timeout,
+               env->cfg->redis_replica_command_timeout);
+       set_timeout(&moddata->connect_timeout,
+               env->cfg->redis_timeout,
+               env->cfg->redis_connect_timeout);
+       set_timeout(&moddata->replica_connect_timeout,
+               env->cfg->redis_replica_timeout,
+               env->cfg->redis_replica_connect_timeout);
+
+       moddata->logical_db = env->cfg->redis_logical_db;
+       moddata->replica_logical_db = env->cfg->redis_replica_logical_db;
+
+       moddata->ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
+       if(!moddata->ctxs) {
+               log_err("out of memory");
+               goto fail;
        }
-       if(env->cfg->redis_connect_timeout != 0) {
-               moddata->connect_timeout.tv_sec =
-                       env->cfg->redis_connect_timeout / 1000;
-               moddata->connect_timeout.tv_usec =
-                       (env->cfg->redis_connect_timeout % 1000) * 1000;
+       if((moddata->replica_server_host && moddata->replica_server_host[0]!=0)
+               || (moddata->replica_server_path && moddata->replica_server_path[0]!=0)) {
+               /* There is a replica configured, allocate ctxs */
+               moddata->replica_ctxs = calloc(env->cfg->num_threads, sizeof(redisContext*));
+               if(!moddata->replica_ctxs) {
+                       log_err("out of memory");
+                       goto fail;
+               }
        }
-       moddata->logical_db = env->cfg->redis_logical_db;
        for(i = 0; i < moddata->numctxs; i++) {
-               redisContext* ctx = redis_connect(moddata);
+               redisContext* ctx = redis_connect(
+                       moddata->server_host,
+                       moddata->server_port,
+                       moddata->server_path,
+                       moddata->server_password,
+                       moddata->logical_db,
+                       moddata->connect_timeout,
+                       moddata->command_timeout);
                if(!ctx) {
                        log_err("redis_init: failed to init redis "
                                "(for thread %d)", i);
@@ -190,6 +254,25 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
                }
                moddata->ctxs[i] = ctx;
        }
+       if(moddata->replica_ctxs) {
+               for(i = 0; i < moddata->numctxs; i++) {
+                       redisContext* ctx = redis_connect(
+                               moddata->replica_server_host,
+                               moddata->replica_server_port,
+                               moddata->replica_server_path,
+                               moddata->replica_server_password,
+                               moddata->replica_logical_db,
+                               moddata->replica_connect_timeout,
+                               moddata->replica_command_timeout);
+                       if(!ctx) {
+                               log_err("redis_init: failed to init redis "
+                                       "replica (for thread %d)", i);
+                               /* And continue, the context can be established
+                               * later, just like after a disconnect. */
+                       }
+                       moddata->replica_ctxs[i] = ctx;
+               }
+       }
        cachedb_env->backend_data = moddata;
        if(env->cfg->redis_expire_records &&
                moddata->ctxs[env->alloc->thread_num] != NULL) {
@@ -197,7 +280,7 @@ redis_init(struct module_env* env, struct cachedb_env* cachedb_env)
                int redis_reply_type = 0;
                /** check if set with ex command is supported */
                rep = redis_command(env, cachedb_env,
-                       "SET __UNBOUND_REDIS_CHECK__ none EX 1", NULL, 0);
+                       "SET __UNBOUND_REDIS_CHECK__ none EX 1", NULL, 0, 1);
                if(!rep) {
                        /** init failed, no response from redis server*/
                        goto set_with_ex_fail;
@@ -250,9 +333,9 @@ redis_deinit(struct module_env* env, struct cachedb_env* cachedb_env)
  */
 static redisReply*
 redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
-       const char* command, const uint8_t* data, size_t data_len)
+       const char* command, const uint8_t* data, size_t data_len, int write)
 {
-       redisContext* ctx;
+       redisContext* ctx, **ctx_selector;
        redisReply* rep;
        struct redis_moddata* d = (struct redis_moddata*)
                cachedb_env->backend_data;
@@ -263,17 +346,38 @@ redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
         * assumption throughout the unbound architecture, so we simply assert
         * it. */
        log_assert(env->alloc->thread_num < d->numctxs);
-       ctx = d->ctxs[env->alloc->thread_num];
+
+       ctx_selector = !write && d->replica_ctxs
+               ?d->replica_ctxs
+               :d->ctxs;
+       ctx = ctx_selector[env->alloc->thread_num];
 
        /* If we've not established a connection to the server or we've closed
         * it on a failure, try to re-establish a new one.   Failures will be
         * logged in redis_connect(). */
        if(!ctx) {
-               ctx = redis_connect(d);
-               d->ctxs[env->alloc->thread_num] = ctx;
+               if(!write && d->replica_ctxs) {
+                       ctx = redis_connect(
+                               d->replica_server_host,
+                               d->replica_server_port,
+                               d->replica_server_path,
+                               d->replica_server_password,
+                               d->replica_logical_db,
+                               d->replica_connect_timeout,
+                               d->replica_command_timeout);
+               } else {
+                       ctx = redis_connect(
+                               d->server_host,
+                               d->server_port,
+                               d->server_path,
+                               d->server_password,
+                               d->logical_db,
+                               d->connect_timeout,
+                               d->command_timeout);
+               }
+               ctx_selector[env->alloc->thread_num] = ctx;
        }
-       if(!ctx)
-               return NULL;
+       if(!ctx) return NULL;
 
        /* Send the command and get a reply, synchronously. */
        rep = (redisReply*)redisCommand(ctx, command, data, data_len);
@@ -283,7 +387,7 @@ redis_command(struct module_env* env, struct cachedb_env* cachedb_env,
                log_err("redis_command: failed to receive a reply, "
                        "closing connection: %s", ctx->errstr);
                redisFree(ctx);
-               d->ctxs[env->alloc->thread_num] = NULL;
+               ctx_selector[env->alloc->thread_num] = NULL;
                return NULL;
        }
 
@@ -313,7 +417,7 @@ redis_lookup(struct module_env* env, struct cachedb_env* cachedb_env,
                return 0;
        }
 
-       rep = redis_command(env, cachedb_env, cmdbuf, NULL, 0);
+       rep = redis_command(env, cachedb_env, cmdbuf, NULL, 0, 0);
        if(!rep)
                return 0;
        switch(rep->type) {
@@ -391,7 +495,7 @@ redis_store(struct module_env* env, struct cachedb_env* cachedb_env,
                return;
        }
 
-       rep = redis_command(env, cachedb_env, cmdbuf, data, data_len);
+       rep = redis_command(env, cachedb_env, cmdbuf, data, data_len, 1);
        if(rep) {
                verbose(VERB_ALGO, "redis_store set completed");
                if(rep->type != REDIS_REPLY_STATUS &&
index b4b8abe25d574218cefb24c789bcf14618bb9309..df1f0d4e69b7529f12f2a926c005500590fc56ea 100644 (file)
@@ -4903,8 +4903,11 @@ config_file_getmem(struct config_file* cfg)
        m += getmem_str(cfg->cachedb_secret);
 #ifdef USE_REDIS
        m += getmem_str(cfg->redis_server_host);
+       m += getmem_str(cfg->redis_replica_server_host);
        m += getmem_str(cfg->redis_server_path);
+       m += getmem_str(cfg->redis_replica_server_path);
        m += getmem_str(cfg->redis_server_password);
+       m += getmem_str(cfg->redis_replica_server_password);
 #endif
 #endif
 #ifdef USE_IPSET
@@ -5779,12 +5782,22 @@ fr_atomic_copy_cfg(struct config_file* oldcfg, struct config_file* cfg,
        COPY_VAR_int(cachedb_check_when_serve_expired);
 #ifdef USE_REDIS
        COPY_VAR_ptr(redis_server_host);
+       COPY_VAR_ptr(redis_replica_server_host);
        COPY_VAR_int(redis_server_port);
+       COPY_VAR_int(redis_replica_server_port);
        COPY_VAR_ptr(redis_server_path);
+       COPY_VAR_ptr(redis_replica_server_path);
        COPY_VAR_ptr(redis_server_password);
+       COPY_VAR_ptr(redis_replica_server_password);
        COPY_VAR_int(redis_timeout);
+       COPY_VAR_int(redis_replica_timeout);
+       COPY_VAR_int(redis_command_timeout);
+       COPY_VAR_int(redis_replica_command_timeout);
+       COPY_VAR_int(redis_connect_timeout);
+       COPY_VAR_int(redis_replica_connect_timeout);
        COPY_VAR_int(redis_expire_records);
        COPY_VAR_int(redis_logical_db);
+       COPY_VAR_int(redis_replica_logical_db);
 #endif
 #endif
        COPY_VAR_int(do_answer_cookie);
index 7aa7bfa6c65d6d086b1c9494bf475d8a9d508aea..6eabbe5fd970faf597d0f55b475831713866b9be 100644 (file)
@@ -1313,9 +1313,9 @@ remote-control:
 #     # redis server's TCP port
 #     redis-server-port: 6379
 #     # if the server uses a unix socket, set its path, or "" when not used.
-#     redis-server-path: "/var/lib/redis/redis-server.sock"
+#     redis-server-path: "/var/lib/redis/redis-server.sock"
 #     # if the server uses an AUTH password, specify here, or "" when not used.
-#     redis-server-password: ""
+#     redis-server-password: ""
 #     # timeout (in ms) for communication with the redis server
 #     redis-timeout: 100
 #     # timeout (in ms) for commands, if 0, uses redis-timeout.
@@ -1326,6 +1326,22 @@ remote-control:
 #     redis-expire-records: no
 #     # redis logical database to use, 0 is the default database.
 #     redis-logical-db: 0
+#     # redis replica server's IP address or host name
+#     redis-replica-server-host: 127.0.0.1
+#     # redis replica server's TCP port
+#     redis-replica-server-port: 6379
+#     # if the replica server uses a unix socket, set its path, or "" when not used.
+#     redis-replica-server-path: "/var/lib/redis/redis-server.sock"
+#     # if the replica server uses an AUTH password, specify here, or "" when not used.
+#     redis-replica-server-password: ""
+#     # timeout (in ms) for communication with the redis replica server
+#     redis-replica-timeout: 100
+#     # timeout (in ms) for redis replica commands, if 0, uses redis-replica-timeout.
+#     redis-replica-command-timeout: 0
+#     # timeout (in ms) for redis replica connection set up, if 0, uses redis-replica-timeout.
+#     redis-replica-connect-timeout: 0
+#     # redis logical database to use for the replica server, 0 is the default database.
+#     redis-replica-logical-db: 0
 
 # IPSet
 # Add specify domain into set via ipset.
index 1c0e26ce51c7b3a71a206ce9af540d7b92209936..e65125a63721b58f5cffa15cb53c390b6d31cacd 100644 (file)
@@ -2860,12 +2860,12 @@ The TCP port number of the Redis server.
 This option defaults to 6379.
 .TP
 .B redis-server-path: \fI<unix socket path>\fR
-The unix socket path to connect to the redis server. Off by default, and it
+The unix socket path to connect to the Redis server. Off by default, and it
 can be set to "" to turn this off. Unix sockets may have better throughput
 than the IP address option.
 .TP
 .B redis-server-password: \fI"<password>"\fR
-The Redis AUTH password to use for the redis server.
+The Redis AUTH password to use for the Redis server.
 Only relevant if Redis is configured for client password authorisation.
 Off by default, and it can be set to "" to turn this off.
 .TP
@@ -2877,12 +2877,14 @@ re-establish a new connection later.
 This option defaults to 100 milliseconds.
 .TP
 .B redis-command-timeout: \fI<msec>\fR
-The timeout to use for redis commands, in milliseconds. If 0, it uses the
-redis\-timeout value. The default is 0.
+The timeout to use for Redis commands, in milliseconds.
+If 0, it uses the \fBredis\-timeout\fR value.
+The default is 0.
 .TP
 .B redis-connect-timeout: \fI<msec>\fR
-The timeout to use for redis connection set up, in milliseconds. If 0, it
-uses the redis\-timeout value. The default is 0.
+The timeout to use for Redis connection set up, in milliseconds.
+If 0, it uses the \fBredis\-timeout\fR value.
+The default is 0.
 .TP
 .B redis-expire-records: \fI<yes or no>
 If Redis record expiration is enabled.  If yes, Unbound sets timeout for Redis
@@ -2902,6 +2904,52 @@ for multiple unrelated applications.
 The default database in Redis is 0 while other logical databases need to be
 explicitly SELECT'ed upon connecting.
 This option defaults to 0.
+.TP
+.B redis-replica-server-host: \fI<server address or name>\fR
+The IP (either v6 or v4) address or domain name of the Redis replica server.
+In general an IP address should be specified as otherwise Unbound will have to
+resolve the name of the server every time it establishes a connection
+to the server.
+This server is treated as a read-only replica server
+(https://redis.io/docs/management/replication/#read-only-replica).
+If specified, all Redis read commands will go to this replica server, while
+the write commands will go to the \fBredis-server-host\fR.
+This option defaults to "" (disabled).
+.TP
+.B redis-replica-server-port: \fI<port number>\fR
+The TCP port number of the Redis replica server.
+This option defaults to 6379.
+.TP
+.B redis-replica-server-path: \fI<unix socket path>\fR
+The unix socket path to connect to the Redis server. Off by default, and it
+can be set to "" to turn this off. Unix sockets may have better throughput
+than the IP address option.
+.TP
+.B redis-replica-server-password: \fI"<password>"\fR
+The Redis AUTH password to use for the Redis replica server.
+Only relevant if Redis is configured for client password authorisation.
+Off by default, and it can be set to "" to turn this off.
+.TP
+.B redis-replica-timeout: \fI<msec>\fR
+The period until when Unbound waits for a response from the Redis replica sever.
+If this timeout expires Unbound closes the connection, treats it as
+if the Redis replica server does not have the requested data, and will try to
+re-establish a new connection later.
+This option defaults to 100 milliseconds.
+.TP
+.B redis-replica-command-timeout: \fI<msec>\fR
+The timeout to use for Redis replica commands, in milliseconds.
+If 0, it uses the \fBredis\-replica\-timeout\fR value.
+The default is 0.
+.TP
+.B redis-replica-connect-timeout: \fI<msec>\fR
+The timeout to use for Redis replica connection set up, in milliseconds.
+If 0, it uses the \fBredis\-replica\-timeout\fR value.
+The default is 0.
+.TP
+.B redis-replica-logical-db: \fI<logical database index>
+Same as \fBredis-logical-db\fR but for the Redis replica server.
+This option defaults to 0.
 .SS DNSTAP Logging Options
 DNSTAP support, when compiled in by using \fB\-\-enable\-dnstap\fR, is enabled
 in the \fBdnstap:\fR section.
index 6599f9f665945f35a5725b00e01f834d0734ea04..4f769c5fdf8f500d7c632a6c1a82c755317ef053 100755 (executable)
@@ -17,6 +17,7 @@ NEED_IPV6='fwd_ancil.tdir fwd_tcp_tc6.tdir stub_udp6.tdir edns_cache.tdir'
 NEED_NOMINGW='tcp_sigpipe.tdir 07-confroot.tdir 08-host-lib.tdir fwd_ancil.tdir'
 NEED_DNSCRYPT_PROXY='dnscrypt_queries.tdir dnscrypt_queries_chacha.tdir'
 NEED_UNSHARE='acl_interface.tdir proxy_protocol.tdir'
+NEED_REDIS_SERVER='redis_replica.tdir'
 
 # test if dig and ldns-testns are available.
 test_tool_avail "dig"
@@ -52,6 +53,7 @@ for test in `ls -d *.tdir`; do
        skip_if_in_list $test "$NEED_WHOAMI" "whoami"
        skip_if_in_list $test "$NEED_DNSCRYPT_PROXY" "dnscrypt-proxy"
        skip_if_in_list $test "$NEED_UNSHARE" "unshare"
+       skip_if_in_list $test "$NEED_REDIS_SERVER" "redis-server"
 
        if echo $NEED_IPV6 | grep $test >/dev/null; then
                if test "$HAVE_IPV6" = no; then
diff --git a/testdata/redis_replica.tdir/after.zone b/testdata/redis_replica.tdir/after.zone
new file mode 100644 (file)
index 0000000..11c268f
--- /dev/null
@@ -0,0 +1,2 @@
+redis.com. IN SOA server. ma.il 1 2 3 4 5
+redis.com. IN A 2.2.2.2
diff --git a/testdata/redis_replica.tdir/before.zone b/testdata/redis_replica.tdir/before.zone
new file mode 100644 (file)
index 0000000..8e50c62
--- /dev/null
@@ -0,0 +1,2 @@
+redis.com. IN SOA server. ma.il 1 2 3 4 5
+redis.com. IN A 1.1.1.1
diff --git a/testdata/redis_replica.tdir/redis.conf b/testdata/redis_replica.tdir/redis.conf
new file mode 100644 (file)
index 0000000..20a3133
--- /dev/null
@@ -0,0 +1,583 @@
+###
+###  Settings for this test ###################################################
+###
+
+# Accept connections on the specified port, default is 6379 (IANA #815344).
+# If port 0 is specified Redis will not listen on a TCP socket.
+port 0
+
+# Unix socket.
+#
+# Specify the path for the Unix socket that will be used to listen for
+# incoming connections. There is no default, so Redis will not listen
+# on a unix socket when not specified.
+#
+unixsocket @SOCKET@
+# unixsocketperm 700
+
+# By default Redis does not run as a daemon. Use 'yes' if you need it.
+# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
+# When Redis is supervised by upstart or systemd, this parameter has no impact.
+daemonize no
+
+# Specify the server verbosity level.
+# This can be one of:
+# debug (a lot of information, useful for development/testing)
+# verbose (many rarely useful info, but not a mess like the debug level)
+# notice (moderately verbose, what you want in production probably)
+# warning (only very important / critical messages are logged)
+# nothing (nothing is logged)
+loglevel notice
+
+# Specify the log file name. Also the empty string can be used to force
+# Redis to log on the standard output. Note that if you use standard
+# output for logging but daemonize, logs will be sent to /dev/null
+logfile @LOGFILE@
+
+# To enable logging to the system logger, just set 'syslog-enabled' to yes,
+# and optionally update the other syslog parameters to suit your needs.
+syslog-enabled no
+
+# Set the number of databases. The default database is DB 0, you can select
+# a different one on a per-connection basis using SELECT <dbid> where
+# dbid is a number between 0 and 'databases'-1
+databases 2
+
+# Snapshotting can be completely disabled with a single empty string argument
+# as in following example:
+#
+save ""
+
+# The working directory.
+#
+# The DB will be written inside this directory, with the filename specified
+# above using the 'dbfilename' configuration directive.
+#
+# The Append Only File will also be created inside this directory.
+#
+# Note that you must specify a directory here, not a file name.
+dir .
+
+###
+###  Rest of the default Redis settings #######################################
+###
+
+bind 127.0.0.1 -::1
+
+# When protected mode is on and the default user has no password, the server
+# only accepts local connections from the IPv4 address (127.0.0.1), IPv6 address
+# (::1) or Unix domain sockets.
+protected-mode yes
+
+# TCP listen() backlog.
+#
+# In high requests-per-second environments you need a high backlog in order
+# to avoid slow clients connection issues. Note that the Linux kernel
+# will silently truncate it to the value of /proc/sys/net/core/somaxconn so
+# make sure to raise both the value of somaxconn and tcp_max_syn_backlog
+# in order to get the desired effect.
+tcp-backlog 511
+
+# Close the connection after a client is idle for N seconds (0 to disable)
+timeout 0
+
+# TCP keepalive.
+# A reasonable value for this option is 300 seconds, which is the new
+# Redis default starting with Redis 3.2.1.
+tcp-keepalive 300
+
+# By default Redis shows an ASCII art logo only when started to log to the
+# standard output and if the standard output is a TTY and syslog logging is
+# disabled. Basically this means that normally a logo is displayed only in
+# interactive sessions.
+#
+# However it is possible to force the pre-4.0 behavior and always show a
+# ASCII art logo in startup logs by setting the following option to yes.
+always-show-logo no
+
+# By default, Redis modifies the process title (as seen in 'top' and 'ps') to
+# provide some runtime information. It is possible to disable this and leave
+# the process name as executed by setting the following to no.
+set-proc-title yes
+
+# When changing the process title, Redis uses the following template to construct
+# the modified title.
+#
+# Template variables are specified in curly brackets. The following variables are
+# supported:
+#
+# {title}           Name of process as executed if parent, or type of child process.
+# {listen-addr}     Bind address or '*' followed by TCP or TLS port listening on, or
+#                   Unix socket if only that's available.
+# {server-mode}     Special mode, i.e. "[sentinel]" or "[cluster]".
+# {port}            TCP port listening on, or 0.
+# {tls-port}        TLS port listening on, or 0.
+# {unixsocket}      Unix domain socket listening on, or "".
+# {config-file}     Name of configuration file used.
+#
+proc-title-template "{title} {listen-addr} {server-mode}"
+
+# Set the local environment which is used for string comparison operations, and
+# also affect the performance of Lua scripts. Empty String indicates the locale
+# is derived from the environment variables.
+locale-collate ""
+
+# By default Redis will stop accepting writes if RDB snapshots are enabled
+# (at least one save point) and the latest background save failed.
+# This will make the user aware (in a hard way) that data is not persisting
+# on disk properly, otherwise chances are that no one will notice and some
+# disaster will happen.
+#
+# If the background saving process will start working again Redis will
+# automatically allow writes again.
+#
+# However if you have setup your proper monitoring of the Redis server
+# and persistence, you may want to disable this feature so that Redis will
+# continue to work as usual even if there are problems with disk,
+# permissions, and so forth.
+stop-writes-on-bgsave-error yes
+
+# Compress string objects using LZF when dump .rdb databases?
+# By default compression is enabled as it's almost always a win.
+# If you want to save some CPU in the saving child set it to 'no' but
+# the dataset will likely be bigger if you have compressible values or keys.
+rdbcompression yes
+
+# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.
+# This makes the format more resistant to corruption but there is a performance
+# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
+# for maximum performances.
+#
+# RDB files created with checksum disabled have a checksum of zero that will
+# tell the loading code to skip the check.
+rdbchecksum yes
+
+# The filename where to dump the DB
+dbfilename redis.rdb
+
+# Remove RDB files used by replication in instances without persistence
+# enabled. By default this option is disabled, however there are environments
+# where for regulations or other security concerns, RDB files persisted on
+# disk by masters in order to feed replicas, or stored on disk by replicas
+# in order to load them for the initial synchronization, should be deleted
+# ASAP. Note that this option ONLY WORKS in instances that have both AOF
+# and RDB persistence disabled, otherwise is completely ignored.
+#
+# An alternative (and sometimes better) way to obtain the same effect is
+# to use diskless replication on both master and replicas instances. However
+# in the case of replicas, diskless is not always an option.
+rdb-del-sync-files no
+
+# When a replica loses its connection with the master, or when the replication
+# is still in progress, the replica can act in two different ways:
+#
+# 1) if replica-serve-stale-data is set to 'yes' (the default) the replica will
+#    still reply to client requests, possibly with out of date data, or the
+#    data set may just be empty if this is the first synchronization.
+#
+# 2) If replica-serve-stale-data is set to 'no' the replica will reply with error
+#    "MASTERDOWN Link with MASTER is down and replica-serve-stale-data is set to 'no'"
+#    to all data access commands, excluding commands such as:
+#    INFO, REPLICAOF, AUTH, SHUTDOWN, REPLCONF, ROLE, CONFIG, SUBSCRIBE,
+#    UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB, COMMAND, POST,
+#    HOST and LATENCY.
+#
+replica-serve-stale-data yes
+
+# You can configure a replica instance to accept writes or not. Writing against
+# a replica instance may be useful to store some ephemeral data (because data
+# written on a replica will be easily deleted after resync with the master) but
+# may also cause problems if clients are writing to it because of a
+# misconfiguration.
+#
+# Since Redis 2.6 by default replicas are read-only.
+#
+# Note: read only replicas are not designed to be exposed to untrusted clients
+# on the internet. It's just a protection layer against misuse of the instance.
+# Still a read only replica exports by default all the administrative commands
+# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve
+# security of read only replicas using 'rename-command' to shadow all the
+# administrative / dangerous commands.
+replica-read-only yes
+
+# Replication SYNC strategy: disk or socket.
+#
+# New replicas and reconnecting replicas that are not able to continue the
+# replication process just receiving differences, need to do what is called a
+# "full synchronization". An RDB file is transmitted from the master to the
+# replicas.
+#
+# The transmission can happen in two different ways:
+#
+# 1) Disk-backed: The Redis master creates a new process that writes the RDB
+#                 file on disk. Later the file is transferred by the parent
+#                 process to the replicas incrementally.
+# 2) Diskless: The Redis master creates a new process that directly writes the
+#              RDB file to replica sockets, without touching the disk at all.
+#
+# With disk-backed replication, while the RDB file is generated, more replicas
+# can be queued and served with the RDB file as soon as the current child
+# producing the RDB file finishes its work. With diskless replication instead
+# once the transfer starts, new replicas arriving will be queued and a new
+# transfer will start when the current one terminates.
+#
+# When diskless replication is used, the master waits a configurable amount of
+# time (in seconds) before starting the transfer in the hope that multiple
+# replicas will arrive and the transfer can be parallelized.
+#
+# With slow disks and fast (large bandwidth) networks, diskless replication
+# works better.
+repl-diskless-sync yes
+
+# When diskless replication is enabled, it is possible to configure the delay
+# the server waits in order to spawn the child that transfers the RDB via socket
+# to the replicas.
+#
+# This is important since once the transfer starts, it is not possible to serve
+# new replicas arriving, that will be queued for the next RDB transfer, so the
+# server waits a delay in order to let more replicas arrive.
+#
+# The delay is specified in seconds, and by default is 5 seconds. To disable
+# it entirely just set it to 0 seconds and the transfer will start ASAP.
+repl-diskless-sync-delay 5
+
+# When diskless replication is enabled with a delay, it is possible to let
+# the replication start before the maximum delay is reached if the maximum
+# number of replicas expected have connected. Default of 0 means that the
+# maximum is not defined and Redis will wait the full delay.
+repl-diskless-sync-max-replicas 0
+
+# -----------------------------------------------------------------------------
+# WARNING: Since in this setup the replica does not immediately store an RDB on
+# disk, it may cause data loss during failovers. RDB diskless load + Redis
+# modules not handling I/O reads may cause Redis to abort in case of I/O errors
+# during the initial synchronization stage with the master.
+# -----------------------------------------------------------------------------
+#
+# Replica can load the RDB it reads from the replication link directly from the
+# socket, or store the RDB to a file and read that file after it was completely
+# received from the master.
+#
+# In many cases the disk is slower than the network, and storing and loading
+# the RDB file may increase replication time (and even increase the master's
+# Copy on Write memory and replica buffers).
+# However, when parsing the RDB file directly from the socket, in order to avoid
+# data loss it's only safe to flush the current dataset when the new dataset is
+# fully loaded in memory, resulting in higher memory usage.
+# For this reason we have the following options:
+#
+# "disabled"    - Don't use diskless load (store the rdb file to the disk first)
+# "swapdb"      - Keep current db contents in RAM while parsing the data directly
+#                 from the socket. Replicas in this mode can keep serving current
+#                 dataset while replication is in progress, except for cases where
+#                 they can't recognize master as having a data set from same
+#                 replication history.
+#                 Note that this requires sufficient memory, if you don't have it,
+#                 you risk an OOM kill.
+# "on-empty-db" - Use diskless load only when current dataset is empty. This is
+#                 safer and avoid having old and new dataset loaded side by side
+#                 during replication.
+repl-diskless-load disabled
+
+# Master send PINGs to its replicas in a predefined interval. It's possible to
+# change this interval with the repl_ping_replica_period option. The default
+# value is 10 seconds.
+#
+# repl-ping-replica-period 10
+
+# The following option sets the replication timeout for:
+#
+# 1) Bulk transfer I/O during SYNC, from the point of view of replica.
+# 2) Master timeout from the point of view of replicas (data, pings).
+# 3) Replica timeout from the point of view of masters (REPLCONF ACK pings).
+#
+# It is important to make sure that this value is greater than the value
+# specified for repl-ping-replica-period otherwise a timeout will be detected
+# every time there is low traffic between the master and the replica. The default
+# value is 60 seconds.
+#
+# repl-timeout 60
+
+# Disable TCP_NODELAY on the replica socket after SYNC?
+#
+# If you select "yes" Redis will use a smaller number of TCP packets and
+# less bandwidth to send data to replicas. But this can add a delay for
+# the data to appear on the replica side, up to 40 milliseconds with
+# Linux kernels using a default configuration.
+#
+# If you select "no" the delay for data to appear on the replica side will
+# be reduced but more bandwidth will be used for replication.
+#
+# By default we optimize for low latency, but in very high traffic conditions
+# or when the master and replicas are many hops away, turning this to "yes" may
+# be a good idea.
+repl-disable-tcp-nodelay no
+
+# The replica priority is an integer number published by Redis in the INFO
+# output. It is used by Redis Sentinel in order to select a replica to promote
+# into a master if the master is no longer working correctly.
+#
+# A replica with a low priority number is considered better for promotion, so
+# for instance if there are three replicas with priority 10, 100, 25 Sentinel
+# will pick the one with priority 10, that is the lowest.
+#
+# However a special priority of 0 marks the replica as not able to perform the
+# role of master, so a replica with priority of 0 will never be selected by
+# Redis Sentinel for promotion.
+#
+# By default the priority is 100.
+replica-priority 100
+
+# ACL LOG
+#
+# The ACL Log tracks failed commands and authentication events associated
+# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked
+# by ACLs. The ACL Log is stored in memory. You can reclaim memory with
+# ACL LOG RESET. Define the maximum entry length of the ACL Log below.
+acllog-max-len 128
+
+lazyfree-lazy-eviction no
+lazyfree-lazy-expire no
+lazyfree-lazy-server-del no
+replica-lazy-flush no
+
+# It is also possible, for the case when to replace the user code DEL calls
+# with UNLINK calls is not easy, to modify the default behavior of the DEL
+# command to act exactly like UNLINK, using the following configuration
+# directive:
+lazyfree-lazy-user-del no
+
+# FLUSHDB, FLUSHALL, SCRIPT FLUSH and FUNCTION FLUSH support both asynchronous and synchronous
+# deletion, which can be controlled by passing the [SYNC|ASYNC] flags into the
+# commands. When neither flag is passed, this directive will be used to determine
+# if the data should be deleted asynchronously.
+lazyfree-lazy-user-flush no
+
+# On Linux, it is possible to hint the kernel OOM killer on what processes
+# should be killed first when out of memory.
+#
+# Enabling this feature makes Redis actively control the oom_score_adj value
+# for all its processes, depending on their role. The default scores will
+# attempt to have background child processes killed before all others, and
+# replicas killed before masters.
+#
+# Redis supports these options:
+#
+# no:       Don't make changes to oom-score-adj (default).
+# yes:      Alias to "relative" see below.
+# absolute: Values in oom-score-adj-values are written as is to the kernel.
+# relative: Values are used relative to the initial value of oom_score_adj when
+#           the server starts and are then clamped to a range of -1000 to 1000.
+#           Because typically the initial value is 0, they will often match the
+#           absolute values.
+oom-score-adj no
+
+# When oom-score-adj is used, this directive controls the specific values used
+# for master, replica and background child processes. Values range -2000 to
+# 2000 (higher means more likely to be killed).
+#
+# Unprivileged processes (not root, and without CAP_SYS_RESOURCE capabilities)
+# can freely increase their value, but not decrease it below its initial
+# settings. This means that setting oom-score-adj to "relative" and setting the
+# oom-score-adj-values to positive values will always succeed.
+oom-score-adj-values 0 200 800
+
+# Usually the kernel Transparent Huge Pages control is set to "madvise" or
+# or "never" by default (/sys/kernel/mm/transparent_hugepage/enabled), in which
+# case this config has no effect. On systems in which it is set to "always",
+# redis will attempt to disable it specifically for the redis process in order
+# to avoid latency problems specifically with fork(2) and CoW.
+# If for some reason you prefer to keep it enabled, you can set this config to
+# "no" and the kernel global to "always".
+disable-thp yes
+
+# By default Redis asynchronously dumps the dataset on disk. This mode is
+# good enough in many applications, but an issue with the Redis process or
+# a power outage may result into a few minutes of writes lost (depending on
+# the configured save points).
+#
+# The Append Only File is an alternative persistence mode that provides
+# much better durability. For instance using the default data fsync policy
+# (see later in the config file) Redis can lose just one second of writes in a
+# dramatic event like a server power outage, or a single write if something
+# wrong with the Redis process itself happens, but the operating system is
+# still running correctly.
+#
+# AOF and RDB persistence can be enabled at the same time without problems.
+# If the AOF is enabled on startup Redis will load the AOF, that is the file
+# with the better durability guarantees.
+#
+# Please check https://redis.io/topics/persistence for more information.
+appendonly no
+
+# The following time is expressed in microseconds, so 1000000 is equivalent
+# to one second. Note that a negative number disables the slow log, while
+# a value of zero forces the logging of every command.
+slowlog-log-slower-than 10000
+
+# There is no limit to this length. Just be aware that it will consume memory.
+# You can reclaim memory used by the slow log with SLOWLOG RESET.
+slowlog-max-len 128
+
+# By default latency monitoring is disabled since it is mostly not needed
+# if you don't have latency issues, and collecting data has a performance
+# impact, that while very small, can be measured under big load. Latency
+# monitoring can easily be enabled at runtime using the command
+# "CONFIG SET latency-monitor-threshold <milliseconds>" if needed.
+latency-monitor-threshold 0
+
+#  By default all notifications are disabled because most users don't need
+#  this feature and the feature has some overhead. Note that if you don't
+#  specify at least one of K or E, no events will be delivered.
+notify-keyspace-events ""
+
+# Hashes are encoded using a memory efficient data structure when they have a
+# small number of entries, and the biggest entry does not exceed a given
+# threshold. These thresholds can be configured using the following directives.
+hash-max-listpack-entries 512
+hash-max-listpack-value 64
+
+# Lists are also encoded in a special way to save a lot of space.
+# The number of entries allowed per internal list node can be specified
+# as a fixed maximum size or a maximum number of elements.
+# For a fixed maximum size, use -5 through -1, meaning:
+# -5: max size: 64 Kb  <-- not recommended for normal workloads
+# -4: max size: 32 Kb  <-- not recommended
+# -3: max size: 16 Kb  <-- probably not recommended
+# -2: max size: 8 Kb   <-- good
+# -1: max size: 4 Kb   <-- good
+# Positive numbers mean store up to _exactly_ that number of elements
+# per list node.
+# The highest performing option is usually -2 (8 Kb size) or -1 (4 Kb size),
+# but if your use case is unique, adjust the settings as necessary.
+list-max-listpack-size -2
+
+# Lists may also be compressed.
+# Compress depth is the number of quicklist ziplist nodes from *each* side of
+# the list to *exclude* from compression.  The head and tail of the list
+# are always uncompressed for fast push/pop operations.  Settings are:
+# 0: disable all list compression
+# 1: depth 1 means "don't start compressing until after 1 node into the list,
+#    going from either the head or tail"
+#    So: [head]->node->node->...->node->[tail]
+#    [head], [tail] will always be uncompressed; inner nodes will compress.
+# 2: [head]->[next]->node->node->...->node->[prev]->[tail]
+#    2 here means: don't compress head or head->next or tail->prev or tail,
+#    but compress all nodes between them.
+# 3: [head]->[next]->[next]->node->node->...->node->[prev]->[prev]->[tail]
+# etc.
+list-compress-depth 0
+
+# Sets have a special encoding when a set is composed
+# of just strings that happen to be integers in radix 10 in the range
+# of 64 bit signed integers.
+# The following configuration setting sets the limit in the size of the
+# set in order to use this special memory saving encoding.
+set-max-intset-entries 512
+
+# Sets containing non-integer values are also encoded using a memory efficient
+# data structure when they have a small number of entries, and the biggest entry
+# does not exceed a given threshold. These thresholds can be configured using
+# the following directives.
+set-max-listpack-entries 128
+set-max-listpack-value 64
+
+# Similarly to hashes and lists, sorted sets are also specially encoded in
+# order to save a lot of space. This encoding is only used when the length and
+# elements of a sorted set are below the following limits:
+zset-max-listpack-entries 128
+zset-max-listpack-value 64
+
+# HyperLogLog sparse representation bytes limit. The limit includes the
+# 16 bytes header. When a HyperLogLog using the sparse representation crosses
+# this limit, it is converted into the dense representation.
+#
+# A value greater than 16000 is totally useless, since at that point the
+# dense representation is more memory efficient.
+#
+# The suggested value is ~ 3000 in order to have the benefits of
+# the space efficient encoding without slowing down too much PFADD,
+# which is O(N) with the sparse encoding. The value can be raised to
+# ~ 10000 when CPU is not a concern, but space is, and the data set is
+# composed of many HyperLogLogs with cardinality in the 0 - 15000 range.
+hll-sparse-max-bytes 3000
+
+# Streams macro node max size / items. The stream data structure is a radix
+# tree of big nodes that encode multiple items inside. Using this configuration
+# it is possible to configure how big a single node can be in bytes, and the
+# maximum number of items it may contain before switching to a new node when
+# appending new stream entries. If any of the following settings are set to
+# zero, the limit is ignored, so for instance it is possible to set just a
+# max entries limit by setting max-bytes to 0 and max-entries to the desired
+# value.
+stream-node-max-bytes 4096
+stream-node-max-entries 100
+
+# Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in
+# order to help rehashing the main Redis hash table (the one mapping top-level
+# keys to values). The hash table implementation Redis uses (see dict.c)
+# performs a lazy rehashing: the more operation you run into a hash table
+# that is rehashing, the more rehashing "steps" are performed, so if the
+# server is idle the rehashing is never complete and some more memory is used
+# by the hash table.
+#
+# The default is to use this millisecond 10 times every second in order to
+# actively rehash the main dictionaries, freeing memory when possible.
+#
+# If unsure:
+# use "activerehashing no" if you have hard latency requirements and it is
+# not a good thing in your environment that Redis can reply from time to time
+# to queries with 2 milliseconds delay.
+#
+# use "activerehashing yes" if you don't have such hard requirements but
+# want to free memory asap when possible.
+activerehashing yes
+
+# The client output buffer limits can be used to force disconnection of clients
+# that are not reading data from the server fast enough for some reason (a
+# common reason is that a Pub/Sub client can't consume messages as fast as the
+# publisher can produce them).
+#
+# Both the hard or the soft limit can be disabled by setting them to zero.
+client-output-buffer-limit normal 0 0 0
+client-output-buffer-limit replica 256mb 64mb 60
+client-output-buffer-limit pubsub 32mb 8mb 60
+
+# Redis calls an internal function to perform many background tasks, like
+# closing connections of clients in timeout, purging expired keys that are
+# never requested, and so forth.
+#
+# Not all tasks are performed with the same frequency, but Redis checks for
+# tasks to perform according to the specified "hz" value.
+#
+# By default "hz" is set to 10. Raising the value will use more CPU when
+# Redis is idle, but at the same time will make Redis more responsive when
+# there are many keys expiring at the same time, and timeouts may be
+# handled with more precision.
+#
+# The range is between 1 and 500, however a value over 100 is usually not
+# a good idea. Most users should use the default of 10 and raise this up to
+# 100 only in environments where very low latency is required.
+hz 10
+
+# When dynamic HZ is enabled, the actual configured HZ will be used
+# as a baseline, but multiples of the configured HZ value will be actually
+# used as needed once more clients are connected. In this way an idle
+# instance will use very little CPU time while a busy instance will be
+# more responsive.
+dynamic-hz yes
+
+# When a child rewrites the AOF file, if the following option is enabled
+# the file will be fsync-ed every 4 MB of data generated. This is useful
+# in order to commit the file to the disk more incrementally and avoid
+# big latency spikes.
+aof-rewrite-incremental-fsync yes
+
+# When redis saves RDB file, if the following option is enabled
+# the file will be fsync-ed every 4 MB of data generated. This is useful
+# in order to commit the file to the disk more incrementally and avoid
+# big latency spikes.
+rdb-save-incremental-fsync yes
+
+# Jemalloc background thread for purging will be enabled by default
+jemalloc-bg-thread yes
diff --git a/testdata/redis_replica.tdir/redis_replica.conf b/testdata/redis_replica.tdir/redis_replica.conf
new file mode 100644 (file)
index 0000000..3a558e2
--- /dev/null
@@ -0,0 +1,31 @@
+server:
+       verbosity: 7
+       num-threads: 1
+       interface: 127.0.0.1
+       port: @PORT@
+       use-syslog: no
+       directory: ""
+       pidfile: "unbound.pid"
+       chroot: ""
+       username: ""
+       module-config: "cachedb iterator"
+       root-key-sentinel: no
+       trust-anchor-signaling: no
+cachedb:
+        backend: redis
+        redis-server-path: @REDIS_SOCKET@
+        redis-replica-server-path: @REDIS_REPLICA_SOCKET@
+auth-zone:
+       name: "redis.com"
+       for-upstream: yes
+       for-downstream: no
+       zonefile: "redis.zone"
+remote-control:
+       control-enable: yes
+       control-interface: 127.0.0.1
+       # control-interface: ::1
+       control-port: @CONTROL_PORT@
+       server-key-file: "unbound_server.key"
+       server-cert-file: "unbound_server.pem"
+       control-key-file: "unbound_control.key"
+       control-cert-file: "unbound_control.pem"
diff --git a/testdata/redis_replica.tdir/redis_replica.dsc b/testdata/redis_replica.tdir/redis_replica.dsc
new file mode 100644 (file)
index 0000000..03321f1
--- /dev/null
@@ -0,0 +1,16 @@
+BaseName: redis_replica
+Version: 1.0
+Description: Test redis replica operation
+CreationDate: Fri 01 Mar 15:29:09 CET 2024
+Maintainer: Yorgos Thessalonikefs
+Category:
+Component:
+CmdDepends:
+Depends:
+Help:
+Pre: redis_replica.pre
+Post: redis_replica.post
+Test: redis_replica.test
+AuxFiles:
+Passed:
+Failure:
diff --git a/testdata/redis_replica.tdir/redis_replica.post b/testdata/redis_replica.tdir/redis_replica.post
new file mode 100644 (file)
index 0000000..afb41f1
--- /dev/null
@@ -0,0 +1,11 @@
+# #-- redis_replica.post --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# source the test var file when it's there
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+#
+# do your teardown here
+. ../common.sh
+kill_pid $REDIS_PID
+kill_pid $REDIS_REPLICA_PID
+kill_pid $UNBOUND_PID
diff --git a/testdata/redis_replica.tdir/redis_replica.pre b/testdata/redis_replica.tdir/redis_replica.pre
new file mode 100644 (file)
index 0000000..28ccd7b
--- /dev/null
@@ -0,0 +1,46 @@
+# #-- redis_replica.pre--#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+. ../common.sh
+
+if grep "define USE_REDIS 1" $PRE/config.h; then echo test enabled; else skip_test "test skipped"; fi
+
+get_random_port 2
+UNBOUND_PORT=$RND_PORT
+CONTROL_PORT=$(($RND_PORT + 1))
+echo "UNBOUND_PORT=$UNBOUND_PORT" >> .tpkg.var.test
+echo "CONTROL_PORT=$CONTROL_PORT" >> .tpkg.var.test
+
+REDIS_SOCKET=server.sock
+REDIS_REPLICA_SOCKET=replica.sock
+echo "REDIS_SOCKET=$REDIS_SOCKET" >> .tpkg.var.test
+echo "REDIS_REPLICA_SOCKET=$REDIS_REPLICA_SOCKET" >> .tpkg.var.test
+
+# start redis
+sed -e 's/@SOCKET\@/'$REDIS_SOCKET'/' -e 's/@LOGFILE\@/server.log/' < redis.conf > server.conf
+redis-server server.conf &
+REDIS_PID=$!
+echo "REDIS_PID=$REDIS_PID" >> .tpkg.var.test
+
+# start redis replica
+sed -e 's/@SOCKET\@/'$REDIS_REPLICA_SOCKET'/' -e 's/@LOGFILE\@/replica.log/' < redis.conf > replica.conf
+redis-server replica.conf &
+REDIS_REPLICA_PID=$!
+echo "REDIS_REPLICA_PID=$REDIS_REPLICA_PID" >> .tpkg.var.test
+
+# Copy initial zonefile
+cp before.zone redis.zone
+
+# make config file
+sed -e 's/@PORT\@/'$UNBOUND_PORT'/' -e 's/@REDIS_SOCKET\@/'$REDIS_SOCKET'/' -e 's/@REDIS_REPLICA_SOCKET\@/'$REDIS_REPLICA_SOCKET'/' -e 's/@CONTROL_PORT\@/'$CONTROL_PORT'/' < redis_replica.conf > ub.conf
+# start unbound in the background
+$PRE/unbound -d -c ub.conf >unbound.log 2>&1 &
+UNBOUND_PID=$!
+echo "UNBOUND_PID=$UNBOUND_PID" >> .tpkg.var.test
+
+cat .tpkg.var.test
+wait_unbound_up unbound.log
diff --git a/testdata/redis_replica.tdir/redis_replica.test b/testdata/redis_replica.tdir/redis_replica.test
new file mode 100644 (file)
index 0000000..c3e141d
--- /dev/null
@@ -0,0 +1,80 @@
+# #-- redis_replica.test --#
+# source the master var file when it's there
+[ -f ../.tpkg.var.master ] && source ../.tpkg.var.master
+# use .tpkg.var.test for in test variable passing
+[ -f .tpkg.var.test ] && source .tpkg.var.test
+
+PRE="../.."
+# do the test
+
+# Check number of keys in the db
+# $1: socket to connect to
+# $2: expected number of keys
+redis_cli_check_keys () {
+       echo "> redis-cli connecting to $1 to check number of keys; expecting $2"
+       keys=$(redis-cli --no-raw -s $1 keys "*" | grep -vF empty | wc -l)
+       if test $keys -ne $2
+       then
+               echo "Expected $2 keys, got $keys"
+               exit 1
+       fi
+       echo "OK"
+}
+
+# Query and check the expected result
+# $1: query
+# $2: expected answer
+expect_answer () {
+       echo "> dig @127.0.0.1 -p $UNBOUND_PORT $1"
+       dig @127.0.0.1 -p $UNBOUND_PORT $1 > tmp.answer
+       if ! grep -F $2 tmp.answer
+       then
+               echo "Expected $2 in the answer, got:"
+               cat tmp.answer
+               exit 1
+       fi
+       echo "OK"
+}
+
+# Start test
+
+# check Redis server has no keys
+redis_cli_check_keys $REDIS_SOCKET 0
+
+# check Redis replica server has no keys
+redis_cli_check_keys $REDIS_REPLICA_SOCKET 0
+
+# query and check answer
+expect_answer redis.com 1.1.1.1
+
+# check Redis server has 1 key
+redis_cli_check_keys $REDIS_SOCKET 1
+
+# check Redis replica server has no keys
+redis_cli_check_keys $REDIS_REPLICA_SOCKET 0
+
+# change auth zone and reload
+cp after.zone redis.zone
+echo "$PRE/unbound-control -c ub.conf reload"
+$PRE/unbound-control -c ub.conf reload
+if test $? -ne 0; then
+       echo "wrong exit value after success"
+       exit 1
+fi
+
+# query and check answer
+# we are writing to server but reading from replica; which is not actually
+# replicating so the new answer will come through while overwriting the record
+# in the server.
+expect_answer redis.com 2.2.2.2
+
+# check Redis server has 1 key
+redis_cli_check_keys $REDIS_SOCKET 1
+
+# check Redis replica server has no keys
+redis_cli_check_keys $REDIS_REPLICA_SOCKET 0
+
+echo "> cat logfiles"
+cat unbound.log
+echo "> OK"
+exit 0
diff --git a/testdata/redis_replica.tdir/unbound_control.key b/testdata/redis_replica.tdir/unbound_control.key
new file mode 100644 (file)
index 0000000..753a4ef
--- /dev/null
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/testdata/redis_replica.tdir/unbound_control.pem b/testdata/redis_replica.tdir/unbound_control.pem
new file mode 100644 (file)
index 0000000..a1edf70
--- /dev/null
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDszCCAhsCFGD5193whHQ2bVdzbaQfdf1gc4SkMA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB3VuYm91bmQwHhcNMjAwNzA4MTMzMjMwWhcNNDAwMzI1MTMzMjMw
+WjAaMRgwFgYDVQQDDA91bmJvdW5kLWNvbnRyb2wwggGiMA0GCSqGSIb3DQEBAQUA
+A4IBjwAwggGKAoIBgQCy0Sn4/KHxcau1nvsDgWFiO9t4Hd0xrtDasZbGhvOUD2mv
+OEDVoKEC9S0I4C4z6sHo1M0HxhZ9kltAdrDIw2mYLvtyDq9ctgIZRAdnICqJ03Tj
+1EkXlPuPg3xWeK/XsuJF2vRLqzDLg9G8Scg89XjcRAdmoLmNW8ewqSEh+YEX8OPl
+NdHKl5rBZX+bmN5Tr/gdFgx/K0Fl07WY9e5YuHSWBtDhIJuCs5RUPeueIyTXJkht
+A6a+UMdip6SMNv+85bYrFRAsbThpkwnWZZ91vAbQIpCRKMhIQNnWXvq+EzqZMzR/
+Bdsi/IVn3I88KV7WYXlpKXFLZyYa4ZVJFBrDxcX197dr0AIklOvfcd1KnJpmQpEB
+TMtwqosKXfbd1oq/c3DABPTdOPiJAzjXoBIyiLJ8Vr/YZZMpBWN127wKtqvu0Zp/
+nGuWKDgMGasG91gDeePLvb9EPvWEvCaVGgIZTt1G4Ov07ooBJSbVGL3U41n+p2My
++i/XmXa+PgZX4pdHTR0CAwEAATANBgkqhkiG9w0BAQsFAAOCAYEAd++Wen6l8Ifj
+4h3p/y16PhSsWJWuJ4wdNYy3/GM84S26wGjzlEEwiW76HpH6VJzPOiBAeWnFKE83
+hFyetEIxgJeIPbcs9ZP/Uoh8GZH9tRISBSN9Hgk2Slr9llo4t1H0g/XTgA5HqMQU
+9YydlBh43G7Vw3FVwh09OM6poNOGQKNc/tq2/QdKeUMtyBbLWpRmjH5XcCT35fbn
+ZiVOUldqSHD4kKrFO4nJYXZyipRbcXybsLiX9GP0GLemc3IgIvOXyJ2RPp06o/SJ
+pzlMlkcAfLJaSuEW57xRakhuNK7m051TKKzJzIEX+NFYOVdafFHS8VwGrYsdrFvD
+72tMfu+Fu55y3awdWWGc6YlaGogZiuMnJkvQphwgn+5qE/7CGEckoKEsH601rqIZ
+muaIc85+nEcHJeijd/ZlBN9zeltjFoMuqTUENgmv8+tUAdVm/UMY9Vjme6b43ydP
+uv6DS02+k9z8toxXworLiPr94BGaiGV1NxgwZKLZigYJt/Fi2Qte
+-----END CERTIFICATE-----
diff --git a/testdata/redis_replica.tdir/unbound_server.key b/testdata/redis_replica.tdir/unbound_server.key
new file mode 100644 (file)
index 0000000..370a7bb
--- /dev/null
@@ -0,0 +1,39 @@
+-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----
diff --git a/testdata/redis_replica.tdir/unbound_server.pem b/testdata/redis_replica.tdir/unbound_server.pem
new file mode 100644 (file)
index 0000000..9868073
--- /dev/null
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDqzCCAhMCFBHWXeQ6ZIa9QcQbXLFfC6tj+KA+MA0GCSqGSIb3DQEBCwUAMBIx
+EDAOBgNVBAMMB3VuYm91bmQwHhcNMjAwNzA4MTMzMjI5WhcNNDAwMzI1MTMzMjI5
+WjASMRAwDgYDVQQDDAd1bmJvdW5kMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
+igKCAYEAvjSVSN2QMXudpzukdLCqgg/IOhCX8KYkD0FFFfWcQjgKq5wI0x41iG32
+a6wbGanre4IX7VxaSPu9kkHfnGgynCk5nwDRedE/FLFhAU78PoT0+NqqGRS7XVQ2
+4vLmIz9Hqc2Ozx1um1BXBTmIT0UfN2e22I0LWQ6a3seZlEDRj45gnk7Zuh9MDgot
+aBdm+v1JAbupSf6Zis4VEH3JNdvVGE3O1DHEIeuuz/3BDhpf6WBDH+8KWaBe1ca4
+TZHr9ThL2gEMEfAQl0wXDwRWRoi3NjNMH+mw0L1rjwThI5GXqNIee7o5FzUReSXZ
+uTdFMyGe3Owcx+XoYnwi6cplSNoGsDBu4B9bKKglR9YleJVw4L4Xi8xPq6O9UPj4
++nypHk/DOoC7DIM3ufN0yxPBsFo5TVowxfhdjZXJbbftd2TZv7AH8+XLA5UoZgRz
+XgzECelXSCTBFlMTnT48LfA9pMLydyjAz2UdPHs5Iv+TK5nnI+aJoeaP7kFZSngx
+dy1+A/bNAgMBAAEwDQYJKoZIhvcNAQELBQADggGBABunf93MKaCUHiZgnoOTinsW
+84/EgInrgtKzAyH+BhnKkJOhhR0kkIAx5d9BpDlaSiRTACFon9moWCgDIIsK/Ar7
+JE0Kln9cV//wiiNoFU0O4mnzyGUIMvlaEX6QHMJJQYvL05+w/3AAcf5XmMJtR5ca
+fJ8FqvGC34b2WxX9lTQoyT52sRt+1KnQikiMEnEyAdKktMG+MwKsFDdOwDXyZhZg
+XZhRrfX3/NVJolqB6EahjWIGXDeKuSSKZVtCyib6LskyeMzN5lcRfvubKDdlqFVF
+qlD7rHBsKhQUWK/IO64mGf7y/de+CgHtED5vDvr/p2uj/9sABATfbrOQR3W/Of25
+sLBj4OEfrJ7lX8hQgFaxkMI3x6VFT3W8dTCp7xnQgb6bgROWB5fNEZ9jk/gjSRmD
+yIU+r0UbKe5kBk/CmZVFXL2TyJ92V5NYEQh8V4DGy19qZ6u/XKYyNJL4ocs35GGe
+CA8SBuyrmdhx38h1RHErR2Skzadi1S7MwGf1y431fQ==
+-----END CERTIFICATE-----
index f763cea321630ce57930187744000dea92a5a6f6..a24067060e8a998da6b4724351595d282ae4280b 100644 (file)
@@ -395,14 +395,22 @@ config_create(void)
        cfg->cachedb_check_when_serve_expired = 1;
 #ifdef USE_REDIS
        if(!(cfg->redis_server_host = strdup("127.0.0.1"))) goto error_exit;
+       if(!(cfg->redis_replica_server_host = strdup(""))) goto error_exit;
        cfg->redis_server_path = NULL;
+       cfg->redis_replica_server_path = NULL;
        cfg->redis_server_password = NULL;
+       cfg->redis_replica_server_password = NULL;
        cfg->redis_timeout = 100;
+       cfg->redis_replica_timeout = 100;
        cfg->redis_command_timeout = 0;
+       cfg->redis_replica_command_timeout = 0;
        cfg->redis_connect_timeout = 0;
+       cfg->redis_replica_connect_timeout = 0;
        cfg->redis_server_port = 6379;
+       cfg->redis_replica_server_port = 6379;
        cfg->redis_expire_records = 0;
        cfg->redis_logical_db = 0;
+       cfg->redis_replica_logical_db = 0;
 #endif  /* USE_REDIS */
 #endif  /* USE_CACHEDB */
 #ifdef USE_IPSET
@@ -1388,14 +1396,22 @@ config_get_option(struct config_file* cfg, const char* opt,
        else O_YNO(opt, "cachedb-check-when-serve-expired", cachedb_check_when_serve_expired)
 #ifdef USE_REDIS
        else O_STR(opt, "redis-server-host", redis_server_host)
+       else O_STR(opt, "redis-replica-server-host", redis_replica_server_host)
        else O_DEC(opt, "redis-server-port", redis_server_port)
+       else O_DEC(opt, "redis-replica-server-port", redis_replica_server_port)
        else O_STR(opt, "redis-server-path", redis_server_path)
+       else O_STR(opt, "redis-replica-server-path", redis_replica_server_path)
        else O_STR(opt, "redis-server-password", redis_server_password)
+       else O_STR(opt, "redis-replica-server-password", redis_replica_server_password)
        else O_DEC(opt, "redis-timeout", redis_timeout)
+       else O_DEC(opt, "redis-replica-timeout", redis_replica_timeout)
        else O_DEC(opt, "redis-command-timeout", redis_command_timeout)
+       else O_DEC(opt, "redis-replica-command-timeout", redis_replica_command_timeout)
        else O_DEC(opt, "redis-connect-timeout", redis_connect_timeout)
+       else O_DEC(opt, "redis-replica-connect-timeout", redis_replica_connect_timeout)
        else O_YNO(opt, "redis-expire-records", redis_expire_records)
        else O_DEC(opt, "redis-logical-db", redis_logical_db)
+       else O_DEC(opt, "redis-replica-logical-db", redis_replica_logical_db)
 #endif  /* USE_REDIS */
 #endif  /* USE_CACHEDB */
 #ifdef USE_IPSET
@@ -1775,8 +1791,11 @@ config_delete(struct config_file* cfg)
        free(cfg->cachedb_secret);
 #ifdef USE_REDIS
        free(cfg->redis_server_host);
+       free(cfg->redis_replica_server_host);
        free(cfg->redis_server_path);
+       free(cfg->redis_replica_server_path);
        free(cfg->redis_server_password);
+       free(cfg->redis_replica_server_password);
 #endif  /* USE_REDIS */
 #endif  /* USE_CACHEDB */
 #ifdef USE_IPSET
index a1dec45b0dc25f2544484cb1b25e8bd1a25c7738..a5d73f4c6069dd2e8df6c0181c8c5ba8a95fa8b6 100644 (file)
@@ -741,22 +741,30 @@ struct config_file {
 #ifdef USE_REDIS
        /** redis server's IP address or host name */
        char* redis_server_host;
+       char* redis_replica_server_host;
        /** redis server's TCP port */
        int redis_server_port;
+       int redis_replica_server_port;
        /** redis server's unix path. Or "", NULL if unused */
        char* redis_server_path;
+       char* redis_replica_server_path;
        /** redis server's AUTH password. Or "", NULL if unused */
        char* redis_server_password;
+       char* redis_replica_server_password;
        /** timeout (in ms) for communication with the redis server */
        int redis_timeout;
+       int redis_replica_timeout;
        /** timeout (in ms) for redis commands */
        int redis_command_timeout;
+       int redis_replica_command_timeout;
        /** timeout (in ms) for redis connection set up */
        int redis_connect_timeout;
+       int redis_replica_connect_timeout;
        /** set timeout on redis records based on DNS response ttl */
        int redis_expire_records;
        /** set the redis logical database upon connection */
        int redis_logical_db;
+       int redis_replica_logical_db;
 #endif
 #endif
        /** Downstream DNS Cookies */
index f0d470227960e65dd5350ffe8f0c63f4df172afd..1b9eaa35bb80aef22383c962344305722a061914 100644 (file)
@@ -572,15 +572,23 @@ backend{COLON}                    { YDVAR(1, VAR_CACHEDB_BACKEND) }
 secret-seed{COLON}             { YDVAR(1, VAR_CACHEDB_SECRETSEED) }
 cachedb-no-store{COLON}                { YDVAR(1, VAR_CACHEDB_NO_STORE) }
 cachedb-check-when-serve-expired{COLON}                { YDVAR(1, VAR_CACHEDB_CHECK_WHEN_SERVE_EXPIRED) }
-redis-server-host{COLON}       { YDVAR(1, VAR_CACHEDB_REDISHOST) }
-redis-server-port{COLON}       { YDVAR(1, VAR_CACHEDB_REDISPORT) }
-redis-server-path{COLON}       { YDVAR(1, VAR_CACHEDB_REDISPATH) }
-redis-server-password{COLON}   { YDVAR(1, VAR_CACHEDB_REDISPASSWORD) }
-redis-timeout{COLON}           { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) }
+redis-server-host{COLON}               { YDVAR(1, VAR_CACHEDB_REDISHOST) }
+redis-replica-server-host{COLON}       { YDVAR(1, VAR_CACHEDB_REDISREPLICAHOST) }
+redis-server-port{COLON}               { YDVAR(1, VAR_CACHEDB_REDISPORT) }
+redis-replica-server-port{COLON}       { YDVAR(1, VAR_CACHEDB_REDISREPLICAPORT) }
+redis-server-path{COLON}               { YDVAR(1, VAR_CACHEDB_REDISPATH) }
+redis-replica-server-path{COLON}       { YDVAR(1, VAR_CACHEDB_REDISREPLICAPATH) }
+redis-server-password{COLON}           { YDVAR(1, VAR_CACHEDB_REDISPASSWORD) }
+redis-replica-server-password{COLON}   { YDVAR(1, VAR_CACHEDB_REDISREPLICAPASSWORD) }
+redis-timeout{COLON}                   { YDVAR(1, VAR_CACHEDB_REDISTIMEOUT) }
+redis-replica-timeout{COLON}           { YDVAR(1, VAR_CACHEDB_REDISREPLICATIMEOUT) }
 redis-command-timeout{COLON}   { YDVAR(1, VAR_CACHEDB_REDISCOMMANDTIMEOUT) }
+redis-replica-command-timeout{COLON}   { YDVAR(1, VAR_CACHEDB_REDISREPLICACOMMANDTIMEOUT) }
 redis-connect-timeout{COLON}   { YDVAR(1, VAR_CACHEDB_REDISCONNECTTIMEOUT) }
+redis-replica-connect-timeout{COLON}   { YDVAR(1, VAR_CACHEDB_REDISREPLICACONNECTTIMEOUT) }
 redis-expire-records{COLON}    { YDVAR(1, VAR_CACHEDB_REDISEXPIRERECORDS) }
-redis-logical-db{COLON}                { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) }
+redis-logical-db{COLON}                        { YDVAR(1, VAR_CACHEDB_REDISLOGICALDB) }
+redis-replica-logical-db{COLON}                { YDVAR(1, VAR_CACHEDB_REDISREPLICALOGICALDB) }
 ipset{COLON}                   { YDVAR(0, VAR_IPSET) }
 name-v4{COLON}                 { YDVAR(1, VAR_IPSET_NAME_V4) }
 name-v6{COLON}                 { YDVAR(1, VAR_IPSET_NAME_V6) }
index c10a5f475d3263809af878b0028d6ee28f2b542e..af47b0eb71dc65d5e942a4dbdd8ac00468ea9d7c 100644 (file)
@@ -179,10 +179,15 @@ extern struct config_parser_state* cfg_parser;
 %token VAR_IPSECMOD_ENABLED VAR_IPSECMOD_HOOK VAR_IPSECMOD_IGNORE_BOGUS
 %token VAR_IPSECMOD_MAX_TTL VAR_IPSECMOD_WHITELIST VAR_IPSECMOD_STRICT
 %token VAR_CACHEDB VAR_CACHEDB_BACKEND VAR_CACHEDB_SECRETSEED
-%token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISTIMEOUT
-%token VAR_CACHEDB_REDISEXPIRERECORDS VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISPASSWORD
-%token VAR_CACHEDB_REDISLOGICALDB
-%token VAR_CACHEDB_REDISCOMMANDTIMEOUT VAR_CACHEDB_REDISCONNECTTIMEOUT
+%token VAR_CACHEDB_REDISHOST VAR_CACHEDB_REDISREPLICAHOST
+%token VAR_CACHEDB_REDISPORT VAR_CACHEDB_REDISREPLICAPORT
+%token VAR_CACHEDB_REDISTIMEOUT VAR_CACHEDB_REDISREPLICATIMEOUT
+%token VAR_CACHEDB_REDISEXPIRERECORDS
+%token VAR_CACHEDB_REDISPATH VAR_CACHEDB_REDISREPLICAPATH
+%token VAR_CACHEDB_REDISPASSWORD VAR_CACHEDB_REDISREPLICAPASSWORD
+%token VAR_CACHEDB_REDISLOGICALDB VAR_CACHEDB_REDISREPLICALOGICALDB
+%token VAR_CACHEDB_REDISCOMMANDTIMEOUT VAR_CACHEDB_REDISREPLICACOMMANDTIMEOUT
+%token VAR_CACHEDB_REDISCONNECTTIMEOUT VAR_CACHEDB_REDISREPLICACONNECTTIMEOUT
 %token VAR_UDP_UPSTREAM_WITHOUT_DOWNSTREAM VAR_FOR_UPSTREAM
 %token VAR_AUTH_ZONE VAR_ZONEFILE VAR_MASTER VAR_URL VAR_FOR_DOWNSTREAM
 %token VAR_FALLBACK_ENABLED VAR_TLS_ADDITIONAL_PORT VAR_LOW_RTT VAR_LOW_RTT_PERMIL
@@ -3868,10 +3873,16 @@ cachedbstart: VAR_CACHEDB
 contents_cachedb: contents_cachedb content_cachedb
        | ;
 content_cachedb: cachedb_backend_name | cachedb_secret_seed |
-       redis_server_host | redis_server_port | redis_timeout |
-       redis_expire_records | redis_server_path | redis_server_password |
-       cachedb_no_store | redis_logical_db | cachedb_check_when_serve_expired |
-       redis_command_timeout | redis_connect_timeout
+       redis_server_host | redis_replica_server_host |
+       redis_server_port | redis_replica_server_port |
+       redis_timeout | redis_replica_timeout |
+       redis_command_timeout | redis_replica_command_timeout |
+       redis_connect_timeout | redis_replica_connect_timeout |
+       redis_server_path | redis_replica_server_path |
+       redis_server_password | redis_replica_server_password |
+       redis_logical_db | redis_replica_logical_db |
+       cachedb_no_store | redis_expire_records |
+       cachedb_check_when_serve_expired
        ;
 cachedb_backend_name: VAR_CACHEDB_BACKEND STRING_ARG
        {
@@ -3935,6 +3946,18 @@ redis_server_host: VAR_CACHEDB_REDISHOST STRING_ARG
        #endif
        }
        ;
+redis_replica_server_host: VAR_CACHEDB_REDISREPLICAHOST STRING_ARG
+       {
+       #if defined(USE_CACHEDB) && defined(USE_REDIS)
+               OUTYY(("P(redis_replica_server_host:%s)\n", $2));
+               free(cfg_parser->cfg->redis_replica_server_host);
+               cfg_parser->cfg->redis_replica_server_host = $2;
+       #else
+               OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+               free($2);
+       #endif
+       }
+       ;
 redis_server_port: VAR_CACHEDB_REDISPORT STRING_ARG
        {
        #if defined(USE_CACHEDB) && defined(USE_REDIS)
@@ -3950,6 +3973,21 @@ redis_server_port: VAR_CACHEDB_REDISPORT STRING_ARG
                free($2);
        }
        ;
+redis_replica_server_port: VAR_CACHEDB_REDISREPLICAPORT STRING_ARG
+       {
+       #if defined(USE_CACHEDB) && defined(USE_REDIS)
+               int port;
+               OUTYY(("P(redis_replica_server_port:%s)\n", $2));
+               port = atoi($2);
+               if(port == 0 || port < 0 || port > 65535)
+                       yyerror("valid redis server port number expected");
+               else cfg_parser->cfg->redis_replica_server_port = port;
+       #else
+               OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+       #endif
+               free($2);
+       }
+       ;
 redis_server_path: VAR_CACHEDB_REDISPATH STRING_ARG
        {
        #if defined(USE_CACHEDB) && defined(USE_REDIS)
@@ -3962,6 +4000,18 @@ redis_server_path: VAR_CACHEDB_REDISPATH STRING_ARG
        #endif
        }
        ;
+redis_replica_server_path: VAR_CACHEDB_REDISREPLICAPATH STRING_ARG
+       {
+       #if defined(USE_CACHEDB) && defined(USE_REDIS)
+               OUTYY(("P(redis_replica_server_path:%s)\n", $2));
+               free(cfg_parser->cfg->redis_replica_server_path);
+               cfg_parser->cfg->redis_replica_server_path = $2;
+       #else
+               OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+               free($2);
+       #endif
+       }
+       ;
 redis_server_password: VAR_CACHEDB_REDISPASSWORD STRING_ARG
        {
        #if defined(USE_CACHEDB) && defined(USE_REDIS)
@@ -3974,6 +4024,18 @@ redis_server_password: VAR_CACHEDB_REDISPASSWORD STRING_ARG
        #endif
        }
        ;
+redis_replica_server_password: VAR_CACHEDB_REDISREPLICAPASSWORD STRING_ARG
+       {
+       #if defined(USE_CACHEDB) && defined(USE_REDIS)
+               OUTYY(("P(redis_replica_server_password:%s)\n", $2));
+               free(cfg_parser->cfg->redis_replica_server_password);
+               cfg_parser->cfg->redis_replica_server_password = $2;
+       #else
+               OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+               free($2);
+       #endif
+       }
+       ;
 redis_timeout: VAR_CACHEDB_REDISTIMEOUT STRING_ARG
        {
        #if defined(USE_CACHEDB) && defined(USE_REDIS)
@@ -3987,6 +4049,19 @@ redis_timeout: VAR_CACHEDB_REDISTIMEOUT STRING_ARG
                free($2);
        }
        ;
+redis_replica_timeout: VAR_CACHEDB_REDISREPLICATIMEOUT STRING_ARG
+       {
+       #if defined(USE_CACHEDB) && defined(USE_REDIS)
+               OUTYY(("P(redis_replica_timeout:%s)\n", $2));
+               if(atoi($2) == 0)
+                       yyerror("redis timeout value expected");
+               else cfg_parser->cfg->redis_replica_timeout = atoi($2);
+       #else
+               OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+       #endif
+               free($2);
+       }
+       ;
 redis_command_timeout: VAR_CACHEDB_REDISCOMMANDTIMEOUT STRING_ARG
        {
        #if defined(USE_CACHEDB) && defined(USE_REDIS)
@@ -4000,6 +4075,19 @@ redis_command_timeout: VAR_CACHEDB_REDISCOMMANDTIMEOUT STRING_ARG
                free($2);
        }
        ;
+redis_replica_command_timeout: VAR_CACHEDB_REDISREPLICACOMMANDTIMEOUT STRING_ARG
+       {
+       #if defined(USE_CACHEDB) && defined(USE_REDIS)
+               OUTYY(("P(redis_replica_command_timeout:%s)\n", $2));
+               if(atoi($2) == 0 && strcmp($2, "0") != 0)
+                       yyerror("redis command timeout value expected");
+               else cfg_parser->cfg->redis_replica_command_timeout = atoi($2);
+       #else
+               OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+       #endif
+               free($2);
+       }
+       ;
 redis_connect_timeout: VAR_CACHEDB_REDISCONNECTTIMEOUT STRING_ARG
        {
        #if defined(USE_CACHEDB) && defined(USE_REDIS)
@@ -4013,6 +4101,19 @@ redis_connect_timeout: VAR_CACHEDB_REDISCONNECTTIMEOUT STRING_ARG
                free($2);
        }
        ;
+redis_replica_connect_timeout: VAR_CACHEDB_REDISREPLICACONNECTTIMEOUT STRING_ARG
+       {
+       #if defined(USE_CACHEDB) && defined(USE_REDIS)
+               OUTYY(("P(redis_replica_connect_timeout:%s)\n", $2));
+               if(atoi($2) == 0 && strcmp($2, "0") != 0)
+                       yyerror("redis connect timeout value expected");
+               else cfg_parser->cfg->redis_replica_connect_timeout = atoi($2);
+       #else
+               OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+       #endif
+               free($2);
+       }
+       ;
 redis_expire_records: VAR_CACHEDB_REDISEXPIRERECORDS STRING_ARG
        {
        #if defined(USE_CACHEDB) && defined(USE_REDIS)
@@ -4041,6 +4142,21 @@ redis_logical_db: VAR_CACHEDB_REDISLOGICALDB STRING_ARG
                free($2);
        }
        ;
+redis_replica_logical_db: VAR_CACHEDB_REDISREPLICALOGICALDB STRING_ARG
+       {
+       #if defined(USE_CACHEDB) && defined(USE_REDIS)
+               int db;
+               OUTYY(("P(redis_replica_logical_db:%s)\n", $2));
+               db = atoi($2);
+               if((db == 0 && strcmp($2, "0") != 0) || db < 0)
+                       yyerror("valid redis logical database index expected");
+               else cfg_parser->cfg->redis_replica_logical_db = db;
+       #else
+               OUTYY(("P(Compiled without cachedb or redis, ignoring)\n"));
+       #endif
+               free($2);
+       }
+       ;
 server_tcp_connection_limit: VAR_TCP_CONNECTION_LIMIT STRING_ARG STRING_ARG
        {
                OUTYY(("P(server_tcp_connection_limit:%s %s)\n", $2, $3));