When log messages are written with both address and port information, IPv6
addresses can cause confusion if the normal address:port formatting is
used. The RFC 3986 suggests that all IPv6 addresses are expressed as a
bracket enclosed address optionally followed by the port that is separate
from the address by a colon.
In practice, the "all interfaces" address and port number 3306 can be
written in IPv4 numbers-and-dots notation as 0.0.0.0:3306 and in IPv6
notation as [::]:3306. Using the latter format in log messages keeps the
output consistent with all types of addresses.
The details of the standard can be found at the following addresses:
     https://www.ietf.org/rfc/rfc3986.txt
     https://www.rfc-editor.org/std/std66.txt
		
	
		
			
				
	
	
		
			561 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			561 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2016 MariaDB Corporation Ab
 | 
						|
 *
 | 
						|
 * Use of this software is governed by the Business Source License included
 | 
						|
 * in the LICENSE.TXT file and at www.mariadb.com/bsl11.
 | 
						|
 *
 | 
						|
 * Change Date: 2019-07-01
 | 
						|
 *
 | 
						|
 * On the date above, in accordance with the Business Source License, use
 | 
						|
 * of this software will be governed by version 2 or later of the General
 | 
						|
 * Public License.
 | 
						|
 */
 | 
						|
 | 
						|
#include "readwritesplit.h"
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
#include <strings.h>
 | 
						|
#include <string.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdint.h>
 | 
						|
 | 
						|
#include <maxscale/router.h>
 | 
						|
#include "rwsplit_internal.h"
 | 
						|
/**
 | 
						|
 * @file rwsplit_select_backends.c   The functions that implement back end
 | 
						|
 * selection for the read write split router. All of these functions are
 | 
						|
 * internal to that router and not intended to be called from elsewhere.
 | 
						|
 *
 | 
						|
 * @verbatim
 | 
						|
 * Revision History
 | 
						|
 *
 | 
						|
 * Date          Who                 Description
 | 
						|
 * 08/08/2016    Martin Brampton     Initial implementation
 | 
						|
 *
 | 
						|
 * @endverbatim
 | 
						|
 */
 | 
						|
 | 
						|
static bool connect_server(backend_ref_t *bref, MXS_SESSION *session, bool execute_history);
 | 
						|
 | 
						|
static void log_server_connections(select_criteria_t select_criteria,
 | 
						|
                                   backend_ref_t *backend_ref, int router_nservers);
 | 
						|
 | 
						|
static SERVER_REF *get_root_master(backend_ref_t *servers, int router_nservers);
 | 
						|
 | 
						|
static int bref_cmp_global_conn(const void *bref1, const void *bref2);
 | 
						|
 | 
						|
static int bref_cmp_router_conn(const void *bref1, const void *bref2);
 | 
						|
 | 
						|
static int bref_cmp_behind_master(const void *bref1, const void *bref2);
 | 
						|
 | 
						|
static int bref_cmp_current_load(const void *bref1, const void *bref2);
 | 
						|
 | 
						|
/**
 | 
						|
 * The order of functions _must_ match with the order the select criteria are
 | 
						|
 * listed in select_criteria_t definition in readwritesplit.h
 | 
						|
 */
 | 
						|
int (*criteria_cmpfun[LAST_CRITERIA])(const void *, const void *) =
 | 
						|
{
 | 
						|
    NULL,
 | 
						|
    bref_cmp_global_conn,
 | 
						|
    bref_cmp_router_conn,
 | 
						|
    bref_cmp_behind_master,
 | 
						|
    bref_cmp_current_load
 | 
						|
};
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief Check whether it's possible to connect to this server
 | 
						|
 *
 | 
						|
 * @param bref Backend reference
 | 
						|
 * @return True if a connection to this server can be attempted
 | 
						|
 */
 | 
						|
static bool bref_valid_for_connect(const backend_ref_t *bref)
 | 
						|
{
 | 
						|
    return !BREF_HAS_FAILED(bref) && SERVER_IS_RUNNING(bref->ref->server);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Check whether it's possible to use this server as a slave
 | 
						|
 *
 | 
						|
 * @param bref Backend reference
 | 
						|
 * @param master_host The master server
 | 
						|
 * @return True if this server is a valid slave candidate
 | 
						|
 */
 | 
						|
static bool bref_valid_for_slave(const backend_ref_t *bref, const SERVER *master_host)
 | 
						|
{
 | 
						|
    SERVER *server = bref->ref->server;
 | 
						|
 | 
						|
    return (SERVER_IS_SLAVE(server) || SERVER_IS_RELAY_SERVER(server)) &&
 | 
						|
           (master_host == NULL || (server != master_host));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief Find the best slave candidate
 | 
						|
 *
 | 
						|
 * This function iterates through @c bref and tries to find the best backend
 | 
						|
 * reference that is not in use. @c cmpfun will be called to compare the backends.
 | 
						|
 *
 | 
						|
 * @param bref Backend reference
 | 
						|
 * @param n Size of @c bref
 | 
						|
 * @param master The master server
 | 
						|
 * @param cmpfun qsort() compatible comparison function
 | 
						|
 * @return The best slave backend reference or NULL if no candidates could be found
 | 
						|
 */
 | 
						|
backend_ref_t* get_slave_candidate(backend_ref_t *bref, int n, const SERVER *master,
 | 
						|
                                   int (*cmpfun)(const void *, const void *))
 | 
						|
{
 | 
						|
    backend_ref_t *candidate = NULL;
 | 
						|
 | 
						|
    for (int i = 0; i < n; i++)
 | 
						|
    {
 | 
						|
        if (!BREF_IS_IN_USE(&bref[i]) &&
 | 
						|
            bref_valid_for_connect(&bref[i]) &&
 | 
						|
            bref_valid_for_slave(&bref[i], master))
 | 
						|
        {
 | 
						|
            if (candidate)
 | 
						|
            {
 | 
						|
                if (cmpfun(candidate, &bref[i]) > 0)
 | 
						|
                {
 | 
						|
                    candidate = &bref[i];
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
                candidate = &bref[i];
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return candidate;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief Search suitable backend servers from those of router instance
 | 
						|
 *
 | 
						|
 * It is assumed that there is only one master among servers of a router instance.
 | 
						|
 * As a result, the first master found is chosen. There will possibly be more
 | 
						|
 * backend references than connected backends because only those in correct state
 | 
						|
 * are connected to.
 | 
						|
 *
 | 
						|
 * @param p_master_ref Pointer to location where master's backend reference is to  be stored
 | 
						|
 * @param backend_ref Pointer to backend server reference object array
 | 
						|
 * @param router_nservers Number of backend server pointers pointed to by @p backend_ref
 | 
						|
 * @param max_nslaves Upper limit for the number of slaves
 | 
						|
 * @param max_slave_rlag Maximum allowed replication lag for any slave
 | 
						|
 * @param select_criteria Slave selection criteria
 | 
						|
 * @param session Client session
 | 
						|
 * @param router Router instance
 | 
						|
 * @return true, if at least one master and one slave was found.
 | 
						|
 */
 | 
						|
bool select_connect_backend_servers(backend_ref_t **p_master_ref,
 | 
						|
                                    backend_ref_t *backend_ref,
 | 
						|
                                    int router_nservers, int max_nslaves,
 | 
						|
                                    int max_slave_rlag,
 | 
						|
                                    select_criteria_t select_criteria,
 | 
						|
                                    MXS_SESSION *session,
 | 
						|
                                    ROUTER_INSTANCE *router,
 | 
						|
                                    bool active_session)
 | 
						|
{
 | 
						|
    if (p_master_ref == NULL || backend_ref == NULL)
 | 
						|
    {
 | 
						|
        MXS_ERROR("Master reference (%p) or backend reference (%p) is NULL.",
 | 
						|
                  p_master_ref, backend_ref);
 | 
						|
        ss_dassert(false);
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    /* get the root Master */
 | 
						|
    SERVER_REF *master_backend = get_root_master(backend_ref, router_nservers);
 | 
						|
    SERVER  *master_host = master_backend ? master_backend->server : NULL;
 | 
						|
 | 
						|
    if (router->rwsplit_config.master_failure_mode == RW_FAIL_INSTANTLY &&
 | 
						|
        (master_host == NULL || SERVER_IS_DOWN(master_host)))
 | 
						|
    {
 | 
						|
        MXS_ERROR("Couldn't find suitable Master from %d candidates.", router_nservers);
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * New session:
 | 
						|
     *
 | 
						|
     * Connect to both master and slaves
 | 
						|
     *
 | 
						|
     * Existing session:
 | 
						|
     *
 | 
						|
     * Master is already connected or we don't have a master. The function was
 | 
						|
     * called because new slaves must be selected to replace failed ones.
 | 
						|
     */
 | 
						|
    bool master_connected = active_session || *p_master_ref != NULL;
 | 
						|
 | 
						|
    /** Check slave selection criteria and set compare function */
 | 
						|
    int (*p)(const void *, const void *) = criteria_cmpfun[select_criteria];
 | 
						|
    ss_dassert(p);
 | 
						|
 | 
						|
    SERVER *old_master = *p_master_ref ? (*p_master_ref)->ref->server : NULL;
 | 
						|
 | 
						|
    if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO))
 | 
						|
    {
 | 
						|
        log_server_connections(select_criteria, backend_ref, router_nservers);
 | 
						|
    }
 | 
						|
 | 
						|
    int slaves_found = 0;
 | 
						|
    int slaves_connected = 0;
 | 
						|
    const int min_nslaves = 0; /*< not configurable at the time */
 | 
						|
    bool succp = false;
 | 
						|
 | 
						|
    if (!master_connected)
 | 
						|
    {
 | 
						|
        /** Find a master server */
 | 
						|
        for (int i = 0; i < router_nservers; i++)
 | 
						|
        {
 | 
						|
            SERVER *serv = backend_ref[i].ref->server;
 | 
						|
 | 
						|
            if (bref_valid_for_connect(&backend_ref[i]) &&
 | 
						|
                master_host && serv == master_host)
 | 
						|
            {
 | 
						|
                if (connect_server(&backend_ref[i], session, false))
 | 
						|
                {
 | 
						|
                    *p_master_ref = &backend_ref[i];
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /** Calculate how many connections we already have */
 | 
						|
    for (int i = 0; i < router_nservers; i++)
 | 
						|
    {
 | 
						|
        if (bref_valid_for_connect(&backend_ref[i]) &&
 | 
						|
            bref_valid_for_slave(&backend_ref[i], master_host))
 | 
						|
        {
 | 
						|
            slaves_found += 1;
 | 
						|
 | 
						|
            if (BREF_IS_IN_USE(&backend_ref[i]))
 | 
						|
            {
 | 
						|
                slaves_connected += 1;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    ss_dassert(slaves_connected < max_nslaves || max_nslaves == 0);
 | 
						|
 | 
						|
    backend_ref_t *bref = get_slave_candidate(backend_ref, router_nservers, master_host, p);
 | 
						|
 | 
						|
    /** Connect to all possible slaves */
 | 
						|
    while (bref && slaves_connected < max_nslaves)
 | 
						|
    {
 | 
						|
        if (connect_server(bref, session, true))
 | 
						|
        {
 | 
						|
            slaves_connected += 1;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            /** Failed to connect, mark server as failed */
 | 
						|
            bref_set_state(bref, BREF_FATAL_FAILURE);
 | 
						|
        }
 | 
						|
 | 
						|
        bref = get_slave_candidate(backend_ref, router_nservers, master_host, p);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Successful cases
 | 
						|
     */
 | 
						|
    if (slaves_connected >= min_nslaves && slaves_connected <= max_nslaves)
 | 
						|
    {
 | 
						|
        succp = true;
 | 
						|
 | 
						|
        if (MXS_LOG_PRIORITY_IS_ENABLED(LOG_INFO))
 | 
						|
        {
 | 
						|
            if (slaves_connected < max_nslaves)
 | 
						|
            {
 | 
						|
                MXS_INFO("Couldn't connect to maximum number of "
 | 
						|
                         "slaves. Connected successfully to %d slaves "
 | 
						|
                         "of %d of them.", slaves_connected, slaves_found);
 | 
						|
            }
 | 
						|
 | 
						|
            for (int i = 0; i < router_nservers; i++)
 | 
						|
            {
 | 
						|
                if (BREF_IS_IN_USE((&backend_ref[i])))
 | 
						|
                {
 | 
						|
                    MXS_INFO("Selected %s in \t[%s]:%d",
 | 
						|
                             STRSRVSTATUS(backend_ref[i].ref->server),
 | 
						|
                             backend_ref[i].ref->server->name,
 | 
						|
                             backend_ref[i].ref->server->port);
 | 
						|
                }
 | 
						|
            } /* for */
 | 
						|
        }
 | 
						|
    }
 | 
						|
    /** Failure cases */
 | 
						|
    else
 | 
						|
    {
 | 
						|
        MXS_ERROR("Couldn't establish required amount of slave connections for "
 | 
						|
                  "router session. Would need between %d and %d slaves but only have %d.",
 | 
						|
                  min_nslaves, max_nslaves, slaves_connected);
 | 
						|
 | 
						|
        /** Clean up connections */
 | 
						|
        for (int i = 0; i < router_nservers; i++)
 | 
						|
        {
 | 
						|
            if (BREF_IS_IN_USE((&backend_ref[i])))
 | 
						|
            {
 | 
						|
                ss_dassert(backend_ref[i].ref->connections > 0);
 | 
						|
 | 
						|
                close_failed_bref(&backend_ref[i], true);
 | 
						|
 | 
						|
                /** Decrease backend's connection counter. */
 | 
						|
                atomic_add(&backend_ref[i].ref->connections, -1);
 | 
						|
                RW_CHK_DCB(&backend_ref[i], backend_ref[i].bref_dcb);
 | 
						|
                dcb_close(backend_ref[i].bref_dcb);
 | 
						|
                RW_CLOSE_BREF(&backend_ref[i]);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return succp;
 | 
						|
}
 | 
						|
 | 
						|
/** Compare number of connections from this router in backend servers */
 | 
						|
static int bref_cmp_router_conn(const void *bref1, const void *bref2)
 | 
						|
{
 | 
						|
    SERVER_REF *b1 = ((backend_ref_t *)bref1)->ref;
 | 
						|
    SERVER_REF *b2 = ((backend_ref_t *)bref2)->ref;
 | 
						|
 | 
						|
    if (b1->weight == 0 && b2->weight == 0)
 | 
						|
    {
 | 
						|
        return b1->connections - b2->connections;
 | 
						|
    }
 | 
						|
    else if (b1->weight == 0)
 | 
						|
    {
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    else if (b2->weight == 0)
 | 
						|
    {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    return ((1000 + 1000 * b1->connections) / b1->weight) -
 | 
						|
           ((1000 + 1000 * b2->connections) / b2->weight);
 | 
						|
}
 | 
						|
 | 
						|
/** Compare number of global connections in backend servers */
 | 
						|
static int bref_cmp_global_conn(const void *bref1, const void *bref2)
 | 
						|
{
 | 
						|
    SERVER_REF *b1 = ((backend_ref_t *)bref1)->ref;
 | 
						|
    SERVER_REF *b2 = ((backend_ref_t *)bref2)->ref;
 | 
						|
 | 
						|
    if (b1->weight == 0 && b2->weight == 0)
 | 
						|
    {
 | 
						|
        return b1->server->stats.n_current -
 | 
						|
               b2->server->stats.n_current;
 | 
						|
    }
 | 
						|
    else if (b1->weight == 0)
 | 
						|
    {
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    else if (b2->weight == 0)
 | 
						|
    {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    return ((1000 + 1000 * b1->server->stats.n_current) / b1->weight) -
 | 
						|
           ((1000 + 1000 * b2->server->stats.n_current) / b2->weight);
 | 
						|
}
 | 
						|
 | 
						|
/** Compare replication lag between backend servers */
 | 
						|
static int bref_cmp_behind_master(const void *bref1, const void *bref2)
 | 
						|
{
 | 
						|
    SERVER_REF *b1 = ((backend_ref_t *)bref1)->ref;
 | 
						|
    SERVER_REF *b2 = ((backend_ref_t *)bref2)->ref;
 | 
						|
 | 
						|
    if (b1->weight == 0 && b2->weight == 0)
 | 
						|
    {
 | 
						|
        return b1->server->rlag -
 | 
						|
               b2->server->rlag;
 | 
						|
    }
 | 
						|
    else if (b1->weight == 0)
 | 
						|
    {
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    else if (b2->weight == 0)
 | 
						|
    {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    return ((1000 + 1000 * b1->server->rlag) / b1->weight) -
 | 
						|
           ((1000 + 1000 * b2->server->rlag) / b2->weight);
 | 
						|
}
 | 
						|
 | 
						|
/** Compare number of current operations in backend servers */
 | 
						|
static int bref_cmp_current_load(const void *bref1, const void *bref2)
 | 
						|
{
 | 
						|
    SERVER_REF *b1 = ((backend_ref_t *)bref1)->ref;
 | 
						|
    SERVER_REF *b2 = ((backend_ref_t *)bref2)->ref;
 | 
						|
 | 
						|
    if (b1->weight == 0 && b2->weight == 0)
 | 
						|
    {
 | 
						|
        return b1->server->stats.n_current_ops - b2->server->stats.n_current_ops;
 | 
						|
    }
 | 
						|
    else if (b1->weight == 0)
 | 
						|
    {
 | 
						|
        return 1;
 | 
						|
    }
 | 
						|
    else if (b2->weight == 0)
 | 
						|
    {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    return ((1000 + 1000 * b1->server->stats.n_current_ops) / b1->weight) -
 | 
						|
           ((1000 + 1000 * b2->server->stats.n_current_ops) / b2->weight);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief Connect a server
 | 
						|
 *
 | 
						|
 * Connects to a server, adds callbacks to the created DCB and updates
 | 
						|
 * router statistics. If @p execute_history is true, the session command
 | 
						|
 * history will be executed on this server.
 | 
						|
 *
 | 
						|
 * @param b Router's backend structure for the server
 | 
						|
 * @param session Client's session object
 | 
						|
 * @param execute_history Execute session command history
 | 
						|
 * @return True if successful, false if an error occurred
 | 
						|
 */
 | 
						|
static bool connect_server(backend_ref_t *bref, MXS_SESSION *session, bool execute_history)
 | 
						|
{
 | 
						|
    SERVER *serv = bref->ref->server;
 | 
						|
    bool rval = false;
 | 
						|
 | 
						|
    bref->bref_dcb = dcb_connect(serv, session, serv->protocol);
 | 
						|
 | 
						|
    if (bref->bref_dcb != NULL)
 | 
						|
    {
 | 
						|
        bref_clear_state(bref, BREF_CLOSED);
 | 
						|
        bref->closed_at = 0;
 | 
						|
 | 
						|
        if (!execute_history || execute_sescmd_history(bref))
 | 
						|
        {
 | 
						|
            /** Add a callback for unresponsive server */
 | 
						|
            dcb_add_callback(bref->bref_dcb, DCB_REASON_NOT_RESPONDING,
 | 
						|
                             &router_handle_state_switch, (void *) bref);
 | 
						|
            bref->bref_state = 0;
 | 
						|
            bref_set_state(bref, BREF_IN_USE);
 | 
						|
            atomic_add(&bref->ref->connections, 1);
 | 
						|
            rval = true;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            MXS_ERROR("Failed to execute session command in %s ([%s]:%d). See earlier "
 | 
						|
                      "errors for more details.",
 | 
						|
                      bref->ref->server->unique_name,
 | 
						|
                      bref->ref->server->name,
 | 
						|
                      bref->ref->server->port);
 | 
						|
            RW_CHK_DCB(bref, bref->bref_dcb);
 | 
						|
            dcb_close(bref->bref_dcb);
 | 
						|
            RW_CLOSE_BREF(bref);
 | 
						|
            bref->bref_dcb = NULL;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
        MXS_ERROR("Unable to establish connection with server [%s]:%d",
 | 
						|
                  serv->name, serv->port);
 | 
						|
    }
 | 
						|
 | 
						|
    return rval;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * @brief Log server connections
 | 
						|
 *
 | 
						|
 * @param select_criteria Slave selection criteria
 | 
						|
 * @param backend_ref Backend reference array
 | 
						|
 * @param router_nservers Number of backends in @p backend_ref
 | 
						|
 */
 | 
						|
static void log_server_connections(select_criteria_t select_criteria,
 | 
						|
                                   backend_ref_t *backend_ref, int router_nservers)
 | 
						|
{
 | 
						|
    if (select_criteria == LEAST_GLOBAL_CONNECTIONS ||
 | 
						|
        select_criteria == LEAST_ROUTER_CONNECTIONS ||
 | 
						|
        select_criteria == LEAST_BEHIND_MASTER ||
 | 
						|
        select_criteria == LEAST_CURRENT_OPERATIONS)
 | 
						|
    {
 | 
						|
        MXS_INFO("Servers and %s connection counts:",
 | 
						|
                 select_criteria == LEAST_GLOBAL_CONNECTIONS ? "all MaxScale"
 | 
						|
                 : "router");
 | 
						|
 | 
						|
        for (int i = 0; i < router_nservers; i++)
 | 
						|
        {
 | 
						|
            SERVER_REF *b = backend_ref[i].ref;
 | 
						|
 | 
						|
            switch (select_criteria)
 | 
						|
            {
 | 
						|
            case LEAST_GLOBAL_CONNECTIONS:
 | 
						|
                MXS_INFO("MaxScale connections : %d in \t[%s]:%d %s",
 | 
						|
                         b->server->stats.n_current, b->server->name,
 | 
						|
                         b->server->port, STRSRVSTATUS(b->server));
 | 
						|
                break;
 | 
						|
 | 
						|
            case LEAST_ROUTER_CONNECTIONS:
 | 
						|
                MXS_INFO("RWSplit connections : %d in \t[%s]:%d %s",
 | 
						|
                         b->connections, b->server->name,
 | 
						|
                         b->server->port, STRSRVSTATUS(b->server));
 | 
						|
                break;
 | 
						|
 | 
						|
            case LEAST_CURRENT_OPERATIONS:
 | 
						|
                MXS_INFO("current operations : %d in \t[%s]:%d %s",
 | 
						|
                         b->server->stats.n_current_ops,
 | 
						|
                         b->server->name, b->server->port,
 | 
						|
                         STRSRVSTATUS(b->server));
 | 
						|
                break;
 | 
						|
 | 
						|
            case LEAST_BEHIND_MASTER:
 | 
						|
                MXS_INFO("replication lag : %d in \t[%s]:%d %s",
 | 
						|
                         b->server->rlag, b->server->name,
 | 
						|
                         b->server->port, STRSRVSTATUS(b->server));
 | 
						|
            default:
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/********************************
 | 
						|
 * This routine returns the root master server from MySQL replication tree
 | 
						|
 * Get the root Master rule:
 | 
						|
 *
 | 
						|
 * find server with the lowest replication depth level
 | 
						|
 * and the SERVER_MASTER bitval
 | 
						|
 * Servers are checked even if they are in 'maintenance'
 | 
						|
 *
 | 
						|
 * @param   servers     The list of servers
 | 
						|
 * @param   router_nservers The number of servers
 | 
						|
 * @return          The Master found
 | 
						|
 *
 | 
						|
 */
 | 
						|
static SERVER_REF *get_root_master(backend_ref_t *servers, int router_nservers)
 | 
						|
{
 | 
						|
    int i = 0;
 | 
						|
    SERVER_REF *master_host = NULL;
 | 
						|
 | 
						|
    for (i = 0; i < router_nservers; i++)
 | 
						|
    {
 | 
						|
        if (servers[i].ref == NULL)
 | 
						|
        {
 | 
						|
            /** This should not happen */
 | 
						|
            ss_dassert(false);
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
        SERVER_REF *b = servers[i].ref;
 | 
						|
 | 
						|
        if (SERVER_IS_MASTER(b->server))
 | 
						|
        {
 | 
						|
            if (master_host == NULL ||
 | 
						|
                (b->server->depth < master_host->server->depth))
 | 
						|
            {
 | 
						|
                master_host = b;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    return master_host;
 | 
						|
}
 |