conn_pool_t *global_conn_pool = NULL;
conn_pool_t *global_sessticket_pool = NULL;
+conn_pool_t *global_redis_pool = NULL;
const conn_pool_fd_t CONN_POOL_FD_INVALID = -1;
extern conn_pool_t *global_conn_pool;
extern conn_pool_t *global_sessticket_pool; // pool for outgoing QUIC connection session tickets
+extern conn_pool_t *global_redis_pool; // pool for connections to Redis database
/*!
* \brief Allocate connection pool.
*/
#include "knot/common/hiredis.h"
-
+#include "contrib/conn_pool.h"
#include "contrib/sockaddr.h"
#include "knot/common/log.h"
#include "libknot/errcode.h"
conf_val_t db_listen = conf_db_param(conf, C_ZONE_DB_LISTEN);
struct sockaddr_storage addr = conf_addr(&db_listen, NULL);
+ redisContext *rdb = (void *)conn_pool_get(global_redis_pool, &addr, &addr);
+ if (rdb != NULL && (intptr_t)rdb != CONN_POOL_FD_INVALID) {
+ return rdb;
+ }
+
int port = sockaddr_port(&addr);
sockaddr_port_set(&addr, 0);
const struct timeval timeout = { 10, 0 };
- redisContext *rdb;
if (addr.ss_family == AF_UNIX) {
rdb = redisConnectUnixWithTimeout(addr_str, timeout);
} else {
return rdb;
}
-void rdb_disconnect(redisContext* rdb)
+void rdb_disconnect(redisContext *rdb, bool pool_save)
{
- if (rdb != NULL) {
- // TODO: is anything more needed for TLS case?
+ if (rdb != NULL && pool_save) {
+ struct sockaddr_storage addr = { 0 };
+ // struct redisContext seems not to have a way to read out sockaddr, only a string, so try-and-error parse the string
+ if (sockaddr_set(&addr, AF_INET6, rdb->tcp.host, rdb->tcp.port) == KNOT_EOK ||
+ sockaddr_set(&addr, AF_INET, rdb->tcp.host, rdb->tcp.port) == KNOT_EOK ||
+ sockaddr_set(&addr, AF_UNIX, rdb->unix_sock.path, 0) == KNOT_EOK) {
+ rdb = (void *)conn_pool_put(global_redis_pool, &addr, &addr, (intptr_t)rdb);
+ }
+ }
+
+ if (rdb != NULL && (intptr_t)rdb != CONN_POOL_FD_INVALID) {
redisFree(rdb);
}
}
redisContext *rdb_connect(conf_t *conf);
-void rdb_disconnect(redisContext* rdb);
+void rdb_disconnect(redisContext *rdb, bool pool_save);
bool rdb_compatible(redisContext *rdb);
if (!zone_timers_serial_notified(&zone->timers, new_serial)) {
zone_schedule_notify(conf, zone, 0);
}
- zone_redis_disconnect(db_ctx);
+ zone_redis_disconnect(db_ctx, true);
zone_skip_free(&skip);
zone->started = true;
zone_update_clear(&up);
zone_contents_deep_free(zf_conts);
zone_contents_deep_free(journal_conts);
- zone_redis_disconnect(db_ctx);
+ zone_redis_disconnect(db_ctx, true);
zone_skip_free(&skip);
zone->started = true;
#include "knot/server/udp-handler.h"
#include "knot/server/tcp-handler.h"
#include "knot/updates/acl.h"
+#include "knot/zone/redis.h"
#include "knot/zone/timers.h"
#include "knot/zone/zonedb-load.h"
#include "knot/worker/pool.h"
#endif
#define SESSION_TICKET_POOL_TIMEOUT 1200
+#define REDIS_CONN_POOL_TIMEOUT (4 * 60)
#define QUIC_LOG "QUIC/TLS, "
freeReplyObject(reply);
}
- rdb_disconnect(s->rdb_ctx);
+ rdb_disconnect(s->rdb_ctx, false);
s->rdb_ctx = NULL;
return KNOT_EOK;
global_conn_pool = NULL;
conn_pool_deinit(global_sessticket_pool);
global_sessticket_pool = NULL;
+ conn_pool_deinit(global_redis_pool);
+ global_redis_pool = NULL;
knot_unreachables_deinit(&global_unreachables);
knot_creds_free(server->quic_creds);
}
}
+static void free_redis_conn(intptr_t ptr)
+{
+ if (ptr != CONN_POOL_FD_INVALID) {
+ zone_redis_disconnect((void *)ptr, false);
+ }
+}
+
+static bool invalid_redis_conn(intptr_t ptr)
+{
+ return ptr == CONN_POOL_FD_INVALID || !zone_redis_ping((void *)ptr);
+}
+
static int reconfigure_remote_pool(conf_t *conf, server_t *server)
{
conf_val_t val = conf_get(conf, C_SRV, C_RMT_POOL_LIMIT);
hash = curr_hash;
}
+ val = conf_get(conf, C_DB, C_ZONE_DB_LISTEN);
+ if (global_redis_pool == NULL && val.code == KNOT_EOK) {
+ size_t bg_wrkrs = conf_bg_threads(conf);
+ conn_pool_t *new_pool = conn_pool_init(bg_wrkrs, REDIS_CONN_POOL_TIMEOUT,
+ free_redis_conn, invalid_redis_conn);
+ if (new_pool == NULL) {
+ return KNOT_ENOMEM;
+ }
+ global_redis_pool = new_pool;
+ }
+
val = conf_get(conf, C_SRV, C_RMT_RETRY_DELAY);
int delay_ms = conf_int(&val);
if (global_unreachables == NULL && delay_ms > 0) {
zone_redis_txn_t txn;
int ret = zone_redis_txn_begin(&txn, db_ctx, db_instance, update->zone->name, incremental);
if (ret != KNOT_EOK) {
- zone_redis_disconnect(db_ctx);
+ zone_redis_disconnect(db_ctx, true);
return ret;
}
db_instance, zone_contents_serial(update->new_cont));
}
- zone_redis_disconnect(db_ctx);
+ zone_redis_disconnect(db_ctx, true);
return ret;
}
return rdb_connect(conf);
}
-void zone_redis_disconnect(struct redisContext *ctx)
+void zone_redis_disconnect(struct redisContext *ctx, bool pool_save)
{
- return rdb_disconnect(ctx);
+ return rdb_disconnect(ctx, pool_save);
+}
+
+bool zone_redis_ping(struct redisContext *ctx)
+{
+ if (ctx == NULL) {
+ return false;
+ }
+
+ redisReply *reply = redisCommand(ctx, "PING");
+ bool res = (reply != NULL &&
+ reply->type == REDIS_REPLY_STATUS &&
+ strcmp(reply->str, "PONG") == 0);
+
+ freeReplyObject(reply);
+
+ return res;
}
static int check_reply(struct redisContext *rdb, redisReply *reply,
return NULL;
}
-void zone_redis_disconnect(struct redisContext *ctx)
+void zone_redis_disconnect(struct redisContext *ctx, bool pool_save)
{
return;
}
+bool zone_redis_ping(struct redisContext *ctx)
+{
+ return false;
+}
+
int zone_redis_txn_begin(zone_redis_txn_t *txn, struct redisContext *rdb,
uint8_t instance, const knot_dname_t *zone_name,
bool incremental)
* \brief Wrappers to rdb_connect and rdb_disconnect not needing #ifdef ENABLE_REDIS around.
*/
struct redisContext *zone_redis_connect(conf_t *conf);
-void zone_redis_disconnect(struct redisContext *ctx);
+void zone_redis_disconnect(struct redisContext *ctx, bool pool_save);
+
+/*!
+ * \brief Check if the conection to the DB is still alive.
+ */
+bool zone_redis_ping(struct redisContext *ctx);
/*!
* \brief Start a writing stransaction into Redis zone database.