The FreeRADIUS server
$Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
|
#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/server/cf_parse.h>
#include <freeradius-devel/util/fifo.h>
#include <freeradius-devel/util/misc.h>
#include <freeradius-devel/util/rand.h>
#include <freeradius-devel/util/time.h>
#include "config.h"
#include "base.h"
#include "cluster.h"
#include "crc16.h"
Go to the source code of this file.
Data Structures | |
struct | cluster_nodes_live_t |
Live nodes data, used to perform weighted random selection of alternative nodes. More... | |
struct | cluster_nodes_live_t.node |
struct | fr_redis_cluster |
A redis cluster. More... | |
struct | fr_redis_cluster_key_slot_s |
Indexes in the fr_redis_cluster_node_t array for a single key slot. More... | |
struct | fr_redis_cluster_node_s |
A Redis cluster node. More... | |
Macros | |
#define | CLOSED_PERIOD 10000 |
How recently must the closed have. More... | |
#define | CLOSED_WEIGHT 1 |
What weight to give to nodes that. More... | |
#define | FAILED_PERIOD 10000 |
How recently must the spawn failure. More... | |
#define | FAILED_WEIGHT 1 |
What weight to give to nodes that. More... | |
#define | KEY_SLOTS 16384 |
Maximum number of keyslots (should not change). More... | |
#define | MAX_SLAVES 5 |
Maximum number of slaves associated. More... | |
#define | RELEASED_MIN_WEIGHT 1000 |
Minimum weight to assign to node. More... | |
#define | RELEASED_PERIOD 10000 |
Period after which we don't care. More... | |
#define | SET_ACTIVE(_node) |
#define | SET_ADDR(_addr, _map) |
#define | SET_INACTIVE(_node) |
Functions | |
static int | _cluster_conn_free (fr_redis_conn_t *conn) |
Callback for freeing a Redis connection. More... | |
static int8_t | _cluster_node_cmp (void const *one, void const *two) |
Compare two redis nodes to check equality. More... | |
static void | _cluster_node_conf_apply (fr_pool_t *pool, void *opaque) |
Reconnect callback to apply new pool config. More... | |
static int | _fr_redis_cluster_free (fr_redis_cluster_t *cluster) |
Destroy mutex associated with cluster slots structure. More... | |
static uint16_t | cluster_key_hash (uint8_t const *key, size_t key_len) |
Resolve key to key slot. More... | |
static fr_redis_cluster_rcode_t | cluster_map_apply (fr_redis_cluster_t *cluster, redisReply *reply) |
Apply a cluster map received from a cluster node. More... | |
static fr_redis_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. More... | |
static int | cluster_map_node_validate (redisReply *node, int map_idx, int node_idx) |
Validate a cluster map node entry. More... | |
static fr_redis_cluster_rcode_t | cluster_node_conf_from_redirect (uint16_t *key_slot, fr_socket_t *node_addr, redisReply *redirect) |
Parse a -MOVED or -ASK redirect. More... | |
static fr_redis_cluster_rcode_t | cluster_node_connect (fr_redis_cluster_t *cluster, fr_redis_cluster_node_t *node) |
Establish a connection to a cluster node. More... | |
static int | cluster_node_find_live (fr_redis_cluster_node_t **live_node, fr_redis_conn_t **live_conn, request_t *request, fr_redis_cluster_t *cluster, fr_redis_cluster_node_t *skip) |
Attempt to find a live pool in the cluster. More... | |
static fr_redis_cluster_rcode_t | cluster_node_ping (request_t *request, fr_redis_cluster_node_t *node, fr_redis_conn_t *conn) |
Issue a ping request against a cluster node. More... | |
static int | cluster_node_pool_health (fr_time_t now, fr_pool_state_t const *state) |
Try to determine the health of a cluster node passively by examining its pool state. More... | |
static fr_redis_cluster_rcode_t | cluster_redirect (fr_redis_cluster_node_t **out, fr_redis_cluster_t *cluster, redisReply *reply) |
Retrieve or associate a node with the server indicated in the redirect. More... | |
fr_redis_cluster_t * | fr_redis_cluster_alloc (TALLOC_CTX *ctx, CONF_SECTION *module, fr_redis_conf_t *conf, bool triggers_enabled, char const *log_prefix, char const *trigger_prefix, fr_pair_list_t *trigger_args) |
Allocate and initialise a new cluster structure. More... | |
void * | fr_redis_cluster_conn_create (TALLOC_CTX *ctx, void *instance, fr_time_delta_t timeout) |
Create a new connection to a Redis node. More... | |
int | fr_redis_cluster_ipaddr (fr_ipaddr_t *out, fr_redis_cluster_node_t const *node) |
Return the ipaddr of a particular node. More... | |
fr_redis_cluster_node_t const * | fr_redis_cluster_master (fr_redis_cluster_t *cluster, fr_redis_cluster_key_slot_t const *key_slot) |
Return the master node that would be used for a particular key. More... | |
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. More... | |
ssize_t | fr_redis_cluster_node_addr_by_role (TALLOC_CTX *ctx, fr_socket_t *out[], fr_redis_cluster_t *cluster, bool is_master, bool is_slave) |
Return an array of IP addresses belonging to masters or slaves. More... | |
int | fr_redis_cluster_pool_by_node_addr (fr_pool_t **pool, fr_redis_cluster_t *cluster, fr_socket_t *node_addr, bool create) |
Get the pool associated with a node in the cluster. More... | |
int | fr_redis_cluster_port (uint16_t *out, fr_redis_cluster_node_t const *node) |
Return the port of a particular node. More... | |
fr_redis_cluster_rcode_t | fr_redis_cluster_remap (request_t *request, fr_redis_cluster_t *cluster, fr_redis_conn_t *conn) |
Perform a runtime remap of the cluster. More... | |
fr_redis_cluster_node_t const * | fr_redis_cluster_slave (fr_redis_cluster_t *cluster, fr_redis_cluster_key_slot_t const *key_slot, uint8_t slave_num) |
Return the slave node that would be used for a particular key. More... | |
fr_redis_cluster_key_slot_t const * | fr_redis_cluster_slot_by_key (fr_redis_cluster_t *cluster, request_t *request, uint8_t const *key, size_t key_len) |
Implements the key slot selection scheme used by freeradius. More... | |
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_t *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. More... | |
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_t *request, fr_redis_rcode_t status, redisReply **reply) |
Get the next connection to attempt a command against. More... | |
Variables | |
fr_table_num_sorted_t const | fr_redis_cluster_rcodes_table [] |
size_t | fr_redis_cluster_rcodes_table_len = NUM_ELEMENTS(fr_redis_cluster_rcodes_table) |
struct cluster_nodes_live_t |
struct cluster_nodes_live_t.node |
Data Fields | ||
---|---|---|
unsigned int | cumulative | Cumulative weight. |
uint8_t | id | Node ID. |
fr_pool_state_t const * | pool_state | Connection pool stats. |
struct fr_redis_cluster |
A redis cluster.
Holds all the structures and collections of nodes, to represent a Redis cluster.
Data Fields | ||
---|---|---|
fr_redis_conf_t * | conf | Base configuration data such as the database number and passwords. |
fr_fifo_t * | free_nodes | Queue of free nodes (or nodes waiting to be reused). |
fr_redis_cluster_key_slot_t | key_slot[KEY_SLOTS] | Lookup table of slots to pools. |
fr_redis_cluster_key_slot_t | key_slot_pending[KEY_SLOTS] | Pending key slot table. |
fr_time_t | last_updated | Last time the cluster mappings were updated. |
char const * | log_prefix | What to prepend to log messages. |
CONF_SECTION * | module | Module configuration. |
pthread_mutex_t | mutex | Mutex to synchronise cluster operations. |
fr_redis_cluster_node_t * | node | Structure containing a node id, its address and a pool of its connections. |
bool | remap_needed |
Set true if at least one cluster node is definitely unreachable. Set false on successful remap. |
bool | remapping | True when cluster is being remapped. |
fr_pair_list_t | trigger_args | Arguments to pass to triggers. |
char const * | trigger_prefix | Trigger path. |
bool | triggers_enabled | Whether triggers are enabled. |
fr_rb_tree_t * | used_nodes | Tree of used nodes. |
struct fr_redis_cluster_key_slot_s |
Indexes in the fr_redis_cluster_node_t array for a single key slot.
When dealing with 16K entries, space is a concern. It's significantly more memory efficient to use 8bit indexes than 64bit pointers for each of the key slot to node mappings.
Data Fields | ||
---|---|---|
uint8_t | master | R/W node (master) for this key slot. |
uint8_t | slave[MAX_SLAVES] | R/O node (slave) for this key slot. |
uint8_t | slave_num | Number of slaves associated with this key slot. |
struct fr_redis_cluster_node_s |
A Redis cluster node.
Passed as opaque data to pools which open connection to nodes.
Data Fields | ||
---|---|---|
fr_socket_t | addr | Current node address. |
fr_redis_cluster_t * | cluster | Common configuration (database number, password, etc..). |
uint8_t | id | Node ID (index in node array). |
bool | is_active | Whether this node is in the active node set. |
bool | is_master |
Whether this node is a master. This is needed for commands like 'KEYS', which we need to issue to every master in the cluster. |
char | name[INET6_ADDRSTRLEN] |
Buffer to hold IP string. text for debug messages. |
fr_socket_t | pending_addr | New node address to be applied when the pool is reconnected. |
fr_pool_t * | pool | Pool associated with this node. |
CONF_SECTION * | pool_cs | Pool configuration section associated with node. |
fr_rb_node_t | rbnode | Entry into the tree of redis nodes. |
#define CLOSED_PERIOD 10000 |
#define CLOSED_WEIGHT 1 |
#define FAILED_PERIOD 10000 |
#define FAILED_WEIGHT 1 |
#define KEY_SLOTS 16384 |
#define MAX_SLAVES 5 |
#define RELEASED_MIN_WEIGHT 1000 |
#define RELEASED_PERIOD 10000 |
#define SET_ACTIVE | ( | _node | ) |
#define SET_ADDR | ( | _addr, | |
_map | |||
) |
#define SET_INACTIVE | ( | _node | ) |
|
static |
|
static |
Compare two redis nodes to check equality.
[in] | one | first node. |
[in] | two | second node. |
Definition at line 323 of file cluster.c.
|
static |
|
static |
Resolve key to key slot.
Identical to the example implementation, except it uses memchr which will be faster, and isn't so needlessly complex.
[in] | key | to resolve. |
[in] | key_len | length of key. |
Definition at line 299 of file cluster.c.
|
static |
Apply a cluster map received from a cluster node.
Key slot range structure
[0] -> key slot range 0 [0] -> key_slot_start [1] -> key_slot_end [2] -> master_node [0] -> master 0 ip (string) [1] -> master 0 port (number) [3..n] -> slave_node(s) [1] -> key slot range 1) [0] -> key_slot_start [1] -> key_slot_end [2] -> master_node [0] -> master 1 ip (string) [1] -> master 1 port (number) [3..n] -> slave_node(s) [n] -> key slot range n [0] -> key_slot_start [1] -> key_slot_end [2] -> master_node [0] -> master n ip (string) [1] -> master n port (number) [3..n] -> slave_node(s)
[in,out] | cluster | to apply map to. |
[in] | reply | from cluster_map_get. |
Definition at line 538 of file cluster.c.
|
static |
Learn a new cluster layout by querying the node that issued the -MOVE.
Also validates the response from the Redis cluster, so we can be sure that it's well formed, before doing more expensive operations.
[out] | out | Where to write cluster map. |
[in] | conn | to use for learning the new cluster map. |
Definition at line 864 of file cluster.c.
|
static |
Validate a cluster map node entry.
[in] | node | we're validating. |
[in] | map_idx | we're processing. |
[in] | node_idx | we're processing. |
Definition at line 786 of file cluster.c.
|
static |
Parse a -MOVED or -ASK redirect.
Converts the body of the -MOVED or -ASK error into an IPv4/6 address and port.
[out] | key_slot | value extracted from redirect string (may be NULL). |
[out] | node_addr | Redis node ipaddr and port extracted from redirect string. |
[in] | redirect | to process. |
Definition at line 447 of file cluster.c.
|
static |
Establish a connection to a cluster node.
[in] | cluster | to search in. |
[in] | node | config. |
Definition at line 372 of file cluster.c.
|
static |
Attempt to find a live pool in the cluster.
The intent here is to find pools/nodes where a connection was released the shortest time ago. Having a connection be released (vs closed) indicates that the pool is live.
We don't want to have all workers try and grab a connection to this node however, as it may still be dead (we don't know).
So we use an inverse transform sample, to weight the nodes, based on time between now and when the connection was released. Connections released closest to the current time are given a higher weighting.
Weight range is between 1 - 11,000.
Using the above algorithm we use the experience of other workers using the cluster to inform our alternative node selection.
Suggestions on improving live node selection appreciated.
Inverse transform sampling based roughly on the solution from this post: http://stackoverflow.com/questions/17250568/randomly-choosing-from-a-list-with-weighted-probabilities
Wikipedia page here: https://en.wikipedia.org/wiki/Inverse_transform_sampling
[out] | live_node | we found. |
[out] | live_conn | to that node. |
[in] | request | The current request (used for logging). |
[in] | cluster | to search for live pools in. |
[in] | skip | this node (it's bad). |
Definition at line 1311 of file cluster.c.
|
static |
Issue a ping request against a cluster node.
Establishes whether the connection to the node we have is live.
request | The current request. |
node | to ping. |
conn | the connection to ping on. |
Definition at line 1244 of file cluster.c.
|
static |
Try to determine the health of a cluster node passively by examining its pool state.
Returns an integer value representing the likelihood that the pool is live. Range is between 1 and 11,000.
If a weight of 1 is returned, connections from the pool should be checked (by pinging) before use.
now | The current time. |
state | of the connection pool. |
Definition at line 1209 of file cluster.c.
|
static |
Retrieve or associate a node with the server indicated in the redirect.
[out] | out | Where to write the node representing the redirect server. |
[in] | cluster | to draw node from. |
[in] | reply | Redis reply containing the redirect information. |
Definition at line 1119 of file cluster.c.
fr_redis_cluster_t* fr_redis_cluster_alloc | ( | TALLOC_CTX * | ctx, |
CONF_SECTION * | module, | ||
fr_redis_conf_t * | conf, | ||
bool | triggers_enabled, | ||
char const * | log_prefix, | ||
char const * | trigger_prefix, | ||
fr_pair_list_t * | trigger_args | ||
) |
Allocate and initialise a new cluster structure.
This holds all the data necessary to manage a pool of pools for a specific redis cluster.
ctx | to link the lifetime of the cluster structure to. |
module | Configuration section to search for 'server' conf pairs in. |
conf | Base redis server configuration. Cluster nodes share database number and password. |
triggers_enabled | Whether triggers should be enabled. |
log_prefix | Custom log prefix. Defaults torlm_<module> (<instance>). |
trigger_prefix | Custom trigger prefix. Defaults tomodules.<module>.pool. |
trigger_args | Argument pairs to pass to the trigger in addition to Connection-Pool-Server, and Connection-Pool-Port (which are always set by the cluster code). |
Definition at line 2261 of file cluster.c.
void* fr_redis_cluster_conn_create | ( | TALLOC_CTX * | ctx, |
void * | instance, | ||
fr_time_delta_t | timeout | ||
) |
Create a new connection to a Redis node.
[in] | ctx | to allocate connection structure in. Will be freed at the same time as the pool. |
[in] | instance | data of type fr_redis_cluster_node_t. Holds parameters for establishing new connection. |
[in] | timeout | The maximum time allowed to complete the connection. |
Definition at line 1468 of file cluster.c.
int fr_redis_cluster_ipaddr | ( | fr_ipaddr_t * | out, |
fr_redis_cluster_node_t const * | node | ||
) |
fr_redis_cluster_node_t const* fr_redis_cluster_master | ( | fr_redis_cluster_t * | cluster, |
fr_redis_cluster_key_slot_t const * | key_slot | ||
) |
Return the master node that would be used for a particular key.
[in] | cluster | To resolve key in. |
[in] | key_slot | to resolve to node. |
Definition at line 1639 of file cluster.c.
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.
cluster | to perform check on. |
min_version | that must be found on each node for the check to succeed. Must be in the format<major>.<minor>.<release>. |
Definition at line 2202 of file cluster.c.
ssize_t fr_redis_cluster_node_addr_by_role | ( | TALLOC_CTX * | ctx, |
fr_socket_t * | out[], | ||
fr_redis_cluster_t * | cluster, | ||
bool | is_master, | ||
bool | is_slave | ||
) |
Return an array of IP addresses belonging to masters or slaves.
[in] | ctx | to allocate array of IP addresses in. |
[out] | out | Where to write the addresses of the nodes. |
[in] | cluster | to search for nodes in. |
[in] | is_master | If true, include the addresses of all the master nodes. |
[in] | is_slave | If true, include the addresses of all the slaves nodes. |
Definition at line 2138 of file cluster.c.
int fr_redis_cluster_pool_by_node_addr | ( | fr_pool_t ** | pool, |
fr_redis_cluster_t * | cluster, | ||
fr_socket_t * | node_addr, | ||
bool | create | ||
) |
Get the pool associated with a node in the cluster.
[out] | pool | associated with the node. |
[in] | cluster | to search for node in. |
[in] | node_addr | to retrieve pool for. Specifies IP and port of node. |
[in] | create | Establish a connection to the specified node if it was previously unknown to the cluster client. |
Definition at line 2070 of file cluster.c.
int fr_redis_cluster_port | ( | uint16_t * | out, |
fr_redis_cluster_node_t const * | node | ||
) |
fr_redis_cluster_rcode_t fr_redis_cluster_remap | ( | request_t * | request, |
fr_redis_cluster_t * | cluster, | ||
fr_redis_conn_t * | conn | ||
) |
Perform a runtime remap of the cluster.
[in] | request | The current request. |
[in,out] | cluster | to remap. |
[in] | conn | to use to query the cluster. |
Definition at line 1009 of file cluster.c.
fr_redis_cluster_node_t const* fr_redis_cluster_slave | ( | fr_redis_cluster_t * | cluster, |
fr_redis_cluster_key_slot_t const * | key_slot, | ||
uint8_t | slave_num | ||
) |
Return the slave node that would be used for a particular key.
[in] | cluster | To resolve key in. |
[in] | key_slot | To resolve to node. |
[in] | slave_num | 0..n. |
Definition at line 1655 of file cluster.c.
fr_redis_cluster_key_slot_t const* fr_redis_cluster_slot_by_key | ( | fr_redis_cluster_t * | cluster, |
request_t * | request, | ||
uint8_t const * | key, | ||
size_t | key_len | ||
) |
Implements the key slot selection scheme used by freeradius.
Like the scheme in the clustering specification but with some differences if the key is NULL or zero length, then a random keyslot is chosen.
If there's only a single node in the cluster, then we avoid the CRC16 and just use key slot 0.
cluster | to determine key slot for. |
request | The current request. |
key | the key to resolve. |
key_len | the length of the key. |
Definition at line 1603 of file cluster.c.
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_t * | 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.
This should be used with fr_redis_cluster_state_next, and fr_redis_command_status, to transparently locate the cluster node we need to perform the operation on.
Example code below shows how this function is used in conjunction with fr_redis_cluster_state_next to follow redirects, and reconnect handles.
[out] | state | to track current pool and various counters, will be initialised. |
[out] | conn | Where to write the reserved connection to. |
[in] | cluster | of pools. |
[in] | request | The current request. |
[in] | key | to resolve to a cluster node/pool. If no key is NULL or key_len is 0 a random slot will be chosen. |
[in] | key_len | Length of the key. |
[in] | read_only | If true, will use random slave pool in preference to the master, falling back to the master if no slaves are available. |
Definition at line 1741 of file cluster.c.
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_t * | request, | ||
fr_redis_rcode_t | status, | ||
redisReply ** | reply | ||
) |
Get the next connection to attempt a command against.
Will process reconnect and redirect states performing the actions necessary.
If a remap is in progress, has occurred within the last second, has recently failed, or fails, the '-MOVE' will be treated as a temporary redirect (-ASK).
This allows the server to be more responsive during remaps, as unless the worker has been redirected to a node we don't currently have a pool for, it can grab a connection for the node it was redirected to, and continue.
[in,out] | state | containing the current pool, and various counters which control retries, and limit redirects. |
[in,out] | conn | we received the '-ASK' or '-MOVE' redirect on. Will be replaced with a connection in the new pool the key points to. |
[in] | request | The current request. |
[in] | cluster | of pools. |
[in] | status | of the last command, must be REDIS_RCODE_MOVE or REDIS_RCODE_ASK. |
[in] | reply | from last command. Freed if 0 is returned, else caller must free. |
Definition at line 1863 of file cluster.c.
fr_table_num_sorted_t const fr_redis_cluster_rcodes_table[] |
size_t fr_redis_cluster_rcodes_table_len = NUM_ELEMENTS(fr_redis_cluster_rcodes_table) |