151 #include <freeradius-devel/rad_assert.h>
153 #ifndef HAVE_PTHREAD_H
157 # define pthread_mutex_init(_x, _y)
158 # define pthread_mutex_destroy(_x)
159 # define pthread_mutex_lock(_x)
160 # define pthread_mutex_unlock(_x)
163 #define KEY_SLOTS 16384
171 #define CLOSED_PERIOD 10000
174 #define CLOSED_WEIGHT 1
177 #define FAILED_PERIOD 10000
180 #define FAILED_WEIGHT 1
183 #define RELEASED_PERIOD 10000
187 #define RELEASED_MIN_WEIGHT 1000
206 unsigned int cumulative;
207 }
node[UINT8_MAX - 1];
273 #ifdef HAVE_PTHREAD_H
274 pthread_mutex_t mutex;
292 p = memchr(key,
'{', key_len);
298 q = memchr(key,
'}', key_len);
299 if (!q || (q < p) || (q == p + 1))
goto all;
323 if (ret != 0)
return ret;
405 redisReply *redirect)
412 rad_assert(redirect && (redirect->type == REDIS_REPLY_ERROR));
423 if ((q - p) >= redirect->len) {
428 key = strtoul(p, &q, 10);
441 if (
fr_inet_pton_port(&ipaddr, &port, p, redirect->len - (p - redirect->str), AF_UNSPEC,
false,
true) < 0) {
446 if (key_slot) *key_slot = key;
448 node_addr->
ipaddr = ipaddr;
449 node_addr->
port = port;
500 uint8_t rollback[UINT8_MAX];
501 bool active[UINT8_MAX];
504 # define SET_ADDR(_addr, _map) \
507 _ret = fr_inet_pton(&_addr.ipaddr, _map->element[0]->str, _map->element[0]->len, AF_UNSPEC, false, true);\
508 rad_assert(_ret == 0);\
509 _addr.port = _map->element[1]->integer; \
512 # define SET_ADDR(_addr, _map) \
514 fr_inet_pton(&_addr.ipaddr, _map->element[0]->str, _map->element[0]->len, AF_UNSPEC, false, true);\
515 _addr.port = _map->element[1]->integer; \
519 #define SET_INACTIVE(_node) \
521 (_node)->active = false; \
522 rbtree_deletebydata(cluster->used_nodes, _node); \
523 fr_fifo_push(cluster->free_nodes, _node); \
526 #define SET_ACTIVE(_node) \
528 (_node)->active = true; \
529 rbtree_insert(cluster->used_nodes, _node); \
530 fr_fifo_pop(cluster->free_nodes); \
531 active[(_node)->id] = true; \
532 rollback[r++] = (_node)->id; \
537 memset(&rollback, 0,
sizeof(rollback));
538 memset(active, 0,
sizeof(active));
557 for (i = 0; i < reply->elements; i++) {
564 redisReply *
map = reply->element[i];
566 memset(&tmpl_slot, 0,
sizeof(tmpl_slot));
571 active[found->
id] =
true;
597 if (rcode < 0)
goto error;
618 for (j = 3; (j < map->elements); j++) {
622 active[found->
id] =
true;
627 if (!spare)
goto out_of_nodes;
636 tmpl_slot.
slave[slaves++] = found->
id;
647 for (k = map->element[0]->integer; k <= map->element[1]->integer; k++) {
733 if (node->type != REDIS_REPLY_ARRAY) {
740 if (node->elements != 2) {
741 fr_strerror_printf(
"Cluster map %i node %i has incorrect number of elements, expected 2 got %zu",
742 map_idx, node_idx, node->elements);
746 if (node->element[0]->type != REDIS_REPLY_STRING) {
747 fr_strerror_printf(
"Cluster map %i node %i ip address is wrong type, expected string got %s",
753 if (
fr_inet_pton(&ipaddr, node->element[0]->str, node->element[0]->len, AF_UNSPEC,
false,
true) < 0) {
757 if (node->element[1]->type != REDIS_REPLY_INTEGER) {
758 fr_strerror_printf(
"Cluster map %i node %i port is wrong type, expected integer got %s",
764 if (node->element[1]->integer < 0) {
766 map_idx, node_idx, node->element[1]->integer);
770 if (node->element[1]->integer > UINT16_MAX) {
772 "got %lli", map_idx, node_idx, node->element[1]->integer);
802 reply = redisCommand(conn->
handle,
"cluster slots");
811 if (reply && reply->type == REDIS_REPLY_ERROR) {
823 if (reply->type != REDIS_REPLY_ARRAY) {
824 fr_strerror_printf(
"Bad response to \"cluster slots\" command, expected array got %s",
832 if (reply->elements == 0) {
833 fr_strerror_printf(
"Empty response to \"cluster slots\" command (zero length array)");
840 for (i = 0; i < reply->elements; i++) {
844 map = reply->element[i];
845 if (map->type != REDIS_REPLY_ARRAY) {
853 if (map->elements < 3) {
854 fr_strerror_printf(
"Cluster map %zu has too few elements, expected at least 3, got %zu",
862 if (map->element[0]->type != REDIS_REPLY_INTEGER) {
863 fr_strerror_printf(
"Cluster map %zu key slot start is wrong type, expected integer got %s",
868 if (map->element[0]->integer < 0) {
869 fr_strerror_printf(
"Cluster map %zu key slot start is too low, expected >= 0 got %lli",
870 i, map->element[0]->integer);
874 if (map->element[0]->integer >
KEY_SLOTS) {
883 if (map->element[1]->type != REDIS_REPLY_INTEGER) {
884 fr_strerror_printf(
"Cluster map %zu key slot end is wrong type, expected integer got %s",
889 if (map->element[1]->integer < 0) {
891 i, map->element[1]->integer);
895 if (map->element[1]->integer >
KEY_SLOTS) {
901 if (map->element[1]->integer < map->element[0]->integer) {
903 "Start was %lli, end was %lli", i, map->element[0]->integer,
904 map->element[1]->integer);
916 for (j = 3; j < map->elements; j++) {
953 RDEBUG(
"Cluster remapping in progress, ignoring remap request");
960 RDEBUG(
"Cluster was updated less than a second ago, ignoring remap request");
964 RINFO(
"Initiating cluster remap");
987 RINFO(
"Cluster map consists of %zu key ranges", map->elements);
988 for (i = 0; i < map->elements; i++) {
989 redisReply *map_node = map->element[i];
991 RINFO(
"%zu - keys %lli-%lli", i,
992 map_node->element[0]->integer,
993 map_node->element[1]->integer);
996 RINFO(
"master: %s:%lli",
997 map_node->element[2]->element[0]->str,
998 map_node->element[2]->element[1]->integer);
999 for (j = 3; j < map_node->elements; j++) {
1000 RINFO(
"slave%zu: %s:%lli", j - 3,
1001 map_node->element[j]->element[0]->str,
1002 map_node->element[j]->element[1]->integer);
1052 memset(&find, 0,
sizeof(find));
1134 if (live->
skip == node->
id)
return 0;
1159 struct timeval diff;
1204 RDEBUG2(
"[%i] Executing command: PING", node->
id);
1205 reply = redisCommand(conn->
handle,
"PING");
1208 RERROR(
"[%i] PING failed to %s:%i: %s", node->
id, node->
name,
1214 if (reply->type != REDIS_REPLY_STATUS) {
1215 RERROR(
"[%i] Bad PING response from %s:%i, expected status got %s",
1222 RDEBUG2(
"[%i] Got response: %s", node->
id, reply->str);
1275 RDEBUG2(
"Searching for live cluster nodes");
1279 RERROR(
"No alternative nodes available");
1291 if (live->
next == 1)
goto no_alts;
1293 gettimeofday(&now, NULL);
1302 int first, last, pivot;
1303 unsigned int find, cumulative = 0;
1305 RDEBUG3(
"(Re)assigning node weights:");
1307 for (j = 0; j < live->
next; j++) {
1311 RDEBUG3(
"Node %i weight: %i", live->
node[j].id, weight);
1312 live->
node[j].cumulative = (cumulative += weight);
1319 find = (
fr_rand() & (cumulative - 1));
1321 last = live->
next - 1;
1322 pivot = (first + last) / 2;
1324 while (first <= last) {
1325 if (live->
node[pivot].cumulative < find) {
1327 }
else if (live->
node[pivot].cumulative == find) {
1332 pivot = (first + last) / 2;
1337 if (first > last) pivot = last + 1;
1343 node = &cluster->
node[live->
node[pivot].id];
1346 RDEBUG2(
"Selected node %i (using random value %i)", node->
id, find);
1349 RERROR(
"No connections available to node %i %s:%i", node->
id,
1356 if (pivot == live->
next) {
1360 memcpy(&live->
node[pivot], &live->
node[live->
next - 1],
sizeof(live->
node[pivot]));
1388 RERROR(
"Hit max alt limit %i, and no live connections found", cluster->
conf->
max_alt);
1419 redisContext *handle;
1420 redisReply *reply = NULL;
1424 handle = redisConnectWithTimeout(node->
name, node->
addr.
port, *timeout);
1425 if ((handle != NULL) && handle->err) {
1426 ERROR(
"%s [%i]: Connection failed: %s", node->
conf->
prefix, node->
id, handle->errstr);
1429 }
else if (!handle) {
1436 reply = redisCommand(handle,
"AUTH %s", node->
conf->
password);
1438 ERROR(
"%s [%i]: Failed authenticating: %s", node->
conf->
prefix, node->
id, handle->errstr);
1445 switch (reply->type) {
1446 case REDIS_REPLY_STATUS:
1447 if (strcmp(reply->str,
"OK") != 0) {
1449 node->
id, reply->str);
1455 case REDIS_REPLY_ERROR:
1456 ERROR(
"%s [%i]: Failed authenticating: %s", node->
conf->
prefix, node->
id, reply->str);
1460 ERROR(
"%s [%i]: Unexpected reply of type %s to AUTH", node->
conf->
prefix, node->
id,
1468 reply = redisCommand(handle,
"SELECT %i", node->
conf->
database);
1470 ERROR(
"%s [%i]: Failed selecting database %i: %s", node->
conf->
prefix, node->
id,
1475 switch (reply->type) {
1476 case REDIS_REPLY_STATUS:
1477 if (strcmp(reply->str,
"OK") != 0) {
1478 ERROR(
"%s [%i]: Failed selecting database %i: %s", node->
conf->
prefix, node->
id,
1485 case REDIS_REPLY_ERROR:
1486 ERROR(
"%s [%i]: Failed selecting database %i: %s", node->
conf->
prefix, node->
id,
1491 ERROR(
"%s [%i]: Unexpected reply of type %s, to SELECT", node->
conf->
prefix, node->
id,
1519 uint8_t
const *key,
size_t key_len)
1523 if (!key || (key_len == 0)) {
1539 p =
fr_asprint(request, (
char const *)key, key_len,
'"');
1546 RDEBUG3(
"Single node available, skipping key selection");
1596 uint8_t
const *key,
size_t key_len,
bool read_only)
1607 memset(state, 0,
sizeof(*state));
1610 if (used_nodes == 0) {
1611 REDEBUG(
"No nodes in cluster");
1624 for (i = 0; i < key_slot->
slave_num; i++) {
1628 node = &cluster->
node[node_id];
1631 RDEBUG2(
"[%i] No connections available (key slot %zu slave %i)",
1651 RDEBUG2(
"[%i] No connections available (key slot %zu master)",
1729 RDEBUG2(
"[%i] Connection no longer viable, closing it", state->
node->
id);
1778 REDEBUG(
"[%i] Hit maximum retry attempts", state->
node->
id);
1791 nanosleep(&ts, NULL);
1809 REDEBUG(
"[%i] Hit maximum reconnect attempts", state->
node->
id);
1855 RDEBUG(
"[%i] Processing redirect \"%s\"", state->
node->
id, (*reply)->str);
1863 if (
new == state->
node) {
1864 REDEBUG(
"[%i] %s:%i issued redirect to itself", state->
node->
id,
1870 state->
node->
addr.
port, new->id, new->name, new->addr.port);
1931 char buffer[INET6_ADDRSTRLEN];
1939 fr_strerror_printf(
"No existing node found with address %s, port %i", hostname, port);
1965 *pool = found->
pool;
1970 #ifdef HAVE_PTHREAD_H
1994 char const *min_version = context;
2001 if (!conn)
return 0;
2010 if (ret < 0)
return 0;
2036 memcpy(&p, &min_version,
sizeof(p));
2042 return ret < 0 ?
false :
true;
2065 char const *cs_name1, *cs_name2;
2082 if (!cs_name2) cs_name2 = cs_name1;
2083 conf->
prefix = talloc_asprintf(conf,
"rlm_%s (%s)", cs_name1, cs_name2);
2096 ERROR(
"%s: Maximum number of connected nodes allowed is %i", conf->
prefix, UINT8_MAX - 1);
2097 talloc_free(cluster);
2102 ERROR(
"%s: Minimum number of nodes allowed is 1", conf->
prefix);
2103 talloc_free(cluster);
2110 talloc_free(cluster);
2114 cluster->
module = module;
2129 talloc_free(cluster);
2134 if (!cluster->
node)
goto oom;
2140 #ifdef HAVE_PTHREAD_H
2142 talloc_set_destructor(cluster, _fr_redis_cluster_free);
2177 ERROR(
"%s: Number of bootstrap servers exceeds 'max_nodes'", conf->
prefix);
2183 talloc_array_length(server) - 1, af,
true,
true) < 0) {
2195 WARN(
"%s: Skipping duplicate bootstrap server \"%s\"", conf->
prefix, server);
2212 WARN(
"%s: Can't contact bootstrap server \"%s\"", conf->
prefix, server);
2223 INFO(
"%s: Cluster map consists of %zu key ranges", conf->
prefix, map->elements);
2224 for (j = 0; j < map->elements; j++) {
2225 redisReply *map_node = map->element[j];
2227 INFO(
"%s: %zu - keys %lli-%lli", conf->
prefix, j,
2228 map_node->element[0]->integer,
2229 map_node->element[1]->integer);
2231 map_node->element[2]->element[0]->str,
2232 map_node->element[2]->element[1]->integer);
2233 for (k = 3; k < map_node->elements; k++) {
2234 INFO(
"%s: slave%zu: %s:%lli", conf->
prefix, k - 3,
2235 map_node->element[k]->element[0]->str,
2236 map_node->element[k]->element[1]->integer);
2253 WARN(
"%s: Bootstrap server \"%s\" returned invalid data: %s",
2259 WARN(
"%s: Can't contact bootstrap server \"%s\": %s",
2270 DEBUG2(
"%s: Bootstrap server \"%s\" returned: %s",
2282 ERROR(
"%s: Can't contact any bootstrap servers", conf->
prefix);
cluster_key_slot_t key_slot[KEY_SLOTS]
Lookup table of slots to pools.
static cluster_rcode_t cluster_node_ping(REQUEST *request, cluster_node_t *node, fr_redis_conn_t *conn)
Issue a ping request against a cluster node.
Operation failed because we couldn't find a live connection.
uint8_t slave[MAX_SLAVES]
R/O node (slave) for this key slot.
#define RINDENT()
Indent R* messages by one level.
cluster_node_addr_t addr
Current node address.
fr_redis_conf_t * conf
Commmon configuration (database number, password, etc..).
Configuration parameters for a redis connection.
FR_NAME_NUMBER const redis_rcodes[]
static void _cluster_node_conf_apply(void *opaque)
Reconnect callback to apply new pool config.
uint32_t reconnects
How many connections we've tried in this pool.
#define RDEBUG_ENABLED2
True if request debug level 1-2 messages are enabled.
uint32_t fr_rand(void)
Return a 32-bit random number.
cluster_node_t * node
Structure containing a node id, its address and a pool of its connections.
Operation completed successfully.
FR_NAME_NUMBER const redis_reply_types[]
3rd highest priority debug messages (-xxx | -Xx).
static cluster_rcode_t cluster_map_apply(fr_redis_cluster_t *cluster, redisReply *reply)
Apply a cluster map received from a cluster node.
static int _cluster_pool_walk(void *context, void *data)
Walk all used pools adding them to the live node list.
char name[INET6_ADDRSTRLEN]
Buffer to hold IP + port.
int fr_inet_pton_port(fr_ipaddr_t *out, uint16_t *port_out, char const *value, ssize_t inlen, int af, bool resolve, bool mask)
Parses IPv4/6 address + port, to fr_ipaddr_t and integer (port)
#define FAILED_WEIGHT
What weight to give to nodes that.
#define FAILED_PERIOD
How recently must the spawn failure.
#define pthread_mutex_destroy(_x)
static int cluster_node_find_live(cluster_node_t **live_node, fr_redis_conn_t **live_conn, REQUEST *request, fr_redis_cluster_t *cluster, cluster_node_t *skip)
Attempt to find a live pool in the cluster.
bool remap_needed
Set true if at least one cluster node is definitely unreachable.
char const * prefix
Logging prefix for errors in fr_redis_cluster_conn_create.
#define MAX_SLAVES
Maximum number of slaves associated.
struct cluster_nodes_live::@23 node[UINT8_MAX-1]
Array of live node IDs (and weights).
#define pthread_mutex_lock(_x)
char const * inet_ntop(int af, void const *src, char *dst, size_t cnt)
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
bool close_conn
Set by caller of fr_redis_cluster_state_next, to indicate that connection must be closed...
static cluster_rcode_t cluster_redirect(cluster_node_t **out, fr_redis_cluster_t *cluster, redisReply *reply)
Retrieve or associate a node with the server indicated in the redirect.
static cluster_rcode_t cluster_node_conf_from_redirect(uint16_t *key_slot, cluster_node_addr_t *node_addr, redisReply *redirect)
Parse a -MOVED or -ASK redirect.
fr_connection_pool_t * pool
Pool associated with this node.
#define pthread_mutex_init(_x, _y)
static char const * hostname(char *buf, size_t buflen, uint32_t ipaddr)
uint8_t const * key
Key we performed hashing on.
static int _cluster_version_walk(void *context, void *data)
Walk all used pools checking their versions.
#define fr_redis_reply_free(_p)
Wrap freeReplyObject so we consistently check for NULL pointers.
void * rbtree_finddata(rbtree_t *tree, void const *data)
Find the user data.
#define SET_INACTIVE(_node)
static int _cluster_conn_free(fr_redis_conn_t *conn)
Callback for freeing a Redis connection.
uint32_t database
number on Redis server.
void fr_redis_reply_print(log_lvl_t lvl, redisReply *reply, REQUEST *request, int idx)
Print the response data in a useful treelike form.
CONF_PAIR * cf_pair_find(CONF_SECTION const *, char const *name)
char const * cf_pair_value(CONF_PAIR const *pair)
uint32_t redirects
How many redirects have we followed.
uint8_t master
R/W node (master) for this key slot.
static uint16_t cluster_key_hash(uint8_t const *key, size_t key_len)
Resolve key to key slot.
bool remapping
True when cluster is being remapped.
#define SET_ACTIVE(_node)
void fr_timeval_subtract(struct timeval *out, struct timeval const *end, struct timeval const *start)
Subtract one timeval from another.
int fr_inet_pton(fr_ipaddr_t *out, char const *value, ssize_t inlen, int af, bool resolve, bool mask)
Simple wrapper to decide whether an IP value is v4 or v6 and call the appropriate parser...
redisContext * handle
Hiredis context used when issuing commands.
struct fr_redis_cluster_node cluster_node_t
A Redis cluster node.
void fr_connection_pool_reconnect_func(fr_connection_pool_t *pool, fr_connection_pool_reconnect_t reconnect)
Set a reconnection callback for the connection pool.
rbtree_t * rbtree_create(TALLOC_CTX *ctx, rb_comparator_t compare, rb_free_t node_free, int flags)
Create a new RED-BLACK tree.
fr_fifo_t * fr_fifo_create(TALLOC_CTX *ctx, int max_entries, fr_fifo_free_t freeNode)
Indexes in the cluster_node_t array for a single key slot.
uint32_t fr_redis_version_num(char const *version)
Convert version string into a 32bit unsigned integer for comparisons.
cluster_rcode_t
Return values for internal functions.
void * fr_redis_cluster_conn_create(TALLOC_CTX *ctx, void *instance, struct timeval const *timeout)
Create a new connection to a Redis node.
struct timeval last_released
Last time a connection was released.
#define RELEASED_PERIOD
Period after which we don't care.
struct cluster_key_slot cluster_key_slot_t
Indexes in the cluster_node_t array for a single key slot.
struct fr_redis_cluster_node * node
Node we're communicating with.
static int cluster_map_node_validate(redisReply *node, int map_idx, int node_idx)
Validate a cluster map node entry.
union fr_ipaddr_t::@1 ipaddr
uint16_t fr_crc16_xmodem(uint8_t const *in, size_t in_len)
CRC16 implementation according to CCITT standards.
int rbtree_walk(rbtree_t *tree, rb_order_t order, rb_walker_t compare, void *context)
Unrecoverable library/server error.
fr_redis_rcode_t fr_redis_command_status(fr_redis_conn_t *conn, redisReply *reply)
Check the reply for errors.
Attempt operation on an alternative node.
uint16_t port
of Redis daemon.
fr_fifo_t * free_nodes
Queue of free nodes (or nodes waiting to be reused).
Live nodes data, used to perform weighted random selection of alternative nodes.
Redis connection sequence state.
char const * password
to authenticate to Redis.
CONF_PAIR * cf_pair_find_next(CONF_SECTION const *, CONF_PAIR const *, char const *name)
Find a pair with a name matching attr, after specified pair.
int fr_fifo_push(fr_fifo_t *fi, void *data)
cluster_node_addr_t pending_addr
New node address to be applied when the pool is reconnected.
#define REXDENT()
Exdent (unindent) R* messages by one level.
Configuration AVP similar to a VALUE_PAIR.
fr_redis_rcode_t fr_redis_get_version(char *out, size_t out_len, fr_redis_conn_t *conn)
Get the version of Redis running on the remote server.
uint8_t slave_num
Number of slaves associated with this key slot.
uint32_t max_alt
Maximum alternative nodes to try.
size_t key_len
Length of the key.
#define CLOSED_WEIGHT
What weight to give to nodes that.
char const * fr_strerror(void)
Get the last library error.
uint32_t max_retries
Maximum number of times we attempt a command when receiving successive -TRYAGAIN messages.
CONF_SECTION * cf_section_sub_find(CONF_SECTION const *, char const *name)
Find a sub-section in a section.
void * fr_fifo_pop(fr_fifo_t *fi)
fr_redis_conf_t * conf
Base configuration data such as the database number and passwords.
Attempt operation on an alternative node with remap.
char const * cf_section_name1(CONF_SECTION const *cs)
fr_redis_rcode_t fr_redis_cluster_state_next(fr_redis_cluster_state_t *state, fr_redis_conn_t **conn, fr_redis_cluster_t *cluster, REQUEST *request, fr_redis_rcode_t status, redisReply **reply)
Get the next connection to attempt a command against.
#define REDIS_ERROR_MOVED_STR
uint8_t max_nodes
Maximum number of cluster nodes to connect to.
fr_ipaddr_t ipaddr
IP Address of Redis cluster node.
static cluster_rcode_t cluster_node_connect(fr_redis_cluster_t *cluster, cluster_node_t *node)
Establish a connection to a cluster node.
time_t last_failed
Last time we tried to spawn a connection but failed.
struct cluster_nodes_live cluster_nodes_live_t
Live nodes data, used to perform weighted random selection of alternative nodes.
#define SET_ADDR(_addr, _map)
uint8_t id
Node ID (index in node array).
Transitory error, caller should retry the operation with a new connection.
uint32_t in_pool
How many available connections are there in the pool.
int fr_redis_cluster_pool_by_node_addr(fr_connection_pool_t **pool, fr_redis_cluster_t *cluster, fr_ipaddr_t *ipaddr, uint16_t port, bool create)
Get the pool associated with a node in the cluster.
Common functions for interacting with Redis via hiredis.
uint32_t num
Number of connections in the pool.
rbtree_t * used_nodes
Tree of used nodes.
char * fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
Escape string that may contain binary data, and write it to a new buffer.
bool rbtree_insert(rbtree_t *tree, void *data)
void * fr_connection_get(fr_connection_pool_t *pool)
Reserve a connection in the connection pool.
int fr_connection_pool_reconnect(fr_connection_pool_t *pool)
Mark connections for reconnection, and spawn at least 'start' connections.
int fr_connection_close(fr_connection_pool_t *pool, void *conn)
Delete a connection from the connection pool.
static cluster_rcode_t cluster_map_get(redisReply **out, fr_redis_conn_t *conn)
Learn a new cluster layout by querying the node that issued the -MOVE.
uint32_t max_redirects
Maximum number of times we can be redirected.
CONF_SECTION * cf_section_alloc(CONF_SECTION *parent, char const *name1, char const *name2)
Allocate a CONF_SECTION.
unsigned int fr_fifo_num_elements(fr_fifo_t *fi)
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
time_t last_updated
Last time the cluster mappings were updated.
bool fr_redis_cluster_min_version(fr_redis_cluster_t *cluster, char const *min_version)
Check if members of the cluster are above a certain version.
fr_redis_cluster_t * fr_redis_cluster_alloc(TALLOC_CTX *ctx, CONF_SECTION *module, fr_redis_conf_t *conf)
Allocate and initialise a new cluster structure.
static int cluster_node_pool_health(struct timeval const *now, fr_connection_pool_state_t const *state)
Try to determine the health of a cluster node passively by examining its pool state.
Common functions for interacting with Redis cluster via Hiredis.
fr_redis_rcode_t
Codes are ordered inversely by priority.
int fr_talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link a parent and a child context, so the child is freed before the parent.
Configuration for a single node.
struct timeval last_closed
Last time a connection was closed.
bool active
Whether this node is in the active node set.
uint32_t retries
How many time's we've received TRYAGAIN.
static cluster_key_slot_t * cluster_slot_by_key(fr_redis_cluster_t *cluster, REQUEST *request, uint8_t const *key, size_t key_len)
Implements the key slot selection scheme used by freeradius.
fr_redis_rcode_t fr_redis_cluster_state_init(fr_redis_cluster_state_t *state, fr_redis_conn_t **conn, fr_redis_cluster_t *cluster, REQUEST *request, uint8_t const *key, size_t key_len, bool read_only)
Resolve a key to a pool, and reserve a connection in that pool.
static cluster_rcode_t cluster_remap(REQUEST *request, fr_redis_cluster_t *cluster, fr_redis_conn_t *conn)
Perform a runtime remap of the cluster.
fr_connection_pool_t * fr_connection_pool_init(TALLOC_CTX *ctx, CONF_SECTION *cs, void *opaque, fr_connection_create_t c, fr_connection_alive_t a, char const *log_prefix, char const *trigger_prefix)
Create a new connection pool.
char const * fr_int2str(FR_NAME_NUMBER const *table, int number, char const *def)
uint16_t port
Port of Redis cluster node.
#define REDIS_ERROR_ASK_STR
struct timeval retry_delay
How long to wait when we received a -TRYAGAIN message.
uint8_t next
Next index in live.
void fr_connection_release(fr_connection_pool_t *pool, void *conn)
Release a connection.
#define FR_TIMEVAL_TO_MS(_x)
static int _cluster_node_cmp(void const *a, void const *b)
Compare two redis nodes to check equality.
Operation was successfull.
struct cluster_node_conf cluster_node_addr_t
Configuration for a single node.
CONF_SECTION * module
Module configuration.
#define RELEASED_MIN_WEIGHT
Minimum weight to assign to node.
void cf_section_add(CONF_SECTION *parent, CONF_SECTION *cs)
fr_connection_pool_state_t const * fr_connection_pool_state(fr_connection_pool_t *pool)
Get the number of connections currently in the pool.
#define CLOSED_PERIOD
How recently must the closed have.
int fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
#define KEY_SLOTS
Maximum number of keyslots (should not change).
cluster_key_slot_t key_slot_pending[KEY_SLOTS]
Pending key slot table.
char const * cf_section_name2(CONF_SECTION const *cs)
Connection handle, holding a redis context.
#define pthread_mutex_unlock(_x)
uint32_t rbtree_num_elements(rbtree_t *tree)
void * fr_fifo_peek(fr_fifo_t *fi)