MXS-2313: Use rank in readwritesplit

Readwritesplit now respects server ranks. When servers are selected for
either routing or connection creation, the servers are partitioned by
their rank into sets of servers. These sets of servers are never mixed so
the end result is that only servers of the same rank are considered for
candidacy.

The master selection is slightly different: the server with the best rank
that is capable of acting as a master is chosen. This means that a session
can have a master with a lower rank and slaves with higher ranks than the
master. In most cases this actually is the preferred behavior as the rank
is used to prioritize usage but not outright prevent it.
This commit is contained in:
Markus Mäkelä
2019-03-10 05:47:55 +02:00
parent ba448cb12c
commit 4f0538a041

View File

@ -202,35 +202,13 @@ BackendSelectFunction get_backend_select_function(select_criteria_t sc)
return backend_cmp_current_load;
}
/**
* @brief Find the best slave candidate for routing reads.
*
* @param backends All backends
* @param select Server selection function
* @param masters_accepts_reads
*
* @return iterator to the best slave or backends.end() if none found
*/
PRWBackends::iterator find_best_backend(PRWBackends& backends,
BackendSelectFunction select,
bool masters_accepts_reads)
// Calculates server priority
int get_backend_priority(RWBackend* backend, bool masters_accepts_reads)
{
// Group backends by priority. The set of highest priority backends will then compete.
int best_priority {INT_MAX}; // low numbers are high priority
thread_local std::array<PRWBackends, 3> priority_map;
for (auto& a : priority_map)
{
a.clear();
}
for (auto& psBackend : backends)
{
auto& backend = *psBackend;
bool is_busy = backend.in_use() && backend.has_session_commands();
bool acts_slave = backend.is_slave() || (backend.is_master() && masters_accepts_reads);
int priority;
bool is_busy = backend->in_use() && backend->has_session_commands();
bool acts_slave = backend->is_slave() || (backend->is_master() && masters_accepts_reads);
if (acts_slave)
{
if (!is_busy)
@ -247,21 +225,68 @@ PRWBackends::iterator find_best_backend(PRWBackends& backends,
priority = 1; // idle masters with masters_accept_reads==false
}
priority_map[priority].push_back(psBackend);
best_priority = std::min(best_priority, priority);
return priority;
}
auto best = select(priority_map[best_priority]);
auto rval = backends.end();
if (best != priority_map[best_priority].end())
/**
* @brief Find the best slave candidate for routing reads.
*
* @param backends All backends
* @param select Server selection function
* @param masters_accepts_reads
*
* @return iterator to the best slave or backends.end() if none found
*/
PRWBackends::iterator find_best_backend(PRWBackends& backends,
BackendSelectFunction select,
bool masters_accepts_reads)
{
rval = std::find(backends.begin(), backends.end(), *best);
// Group backends by priority and rank (lower is better). The set of best backends will then compete.
int best_priority {INT_MAX};
int best_rank {std::numeric_limits<int>::max()};
thread_local PRWBackends candidates;
candidates.clear();
for (auto& psBackend : backends)
{
int priority = get_backend_priority(psBackend, masters_accepts_reads);
int rank = psBackend->server()->rank();
if (rank < best_rank || (rank == best_rank && priority < best_priority))
{
candidates.clear();
best_rank = rank;
best_priority = priority;
}
if (rank == best_rank && priority == best_priority)
{
candidates.push_back(psBackend);
}
}
auto best = select(candidates);
auto rval = std::find(backends.begin(), backends.end(), *best);
return rval;
}
void add_backend_with_rank(RWBackend* backend, PRWBackends* candidates, int* best_rank)
{
int rank = backend->server()->rank();
if (rank < *best_rank)
{
*best_rank = rank;
candidates->clear();
}
if (rank == *best_rank)
{
candidates->push_back(backend);
}
}
/**
* @brief Log server connections
*
@ -339,17 +364,20 @@ RWBackend* get_root_master(const PRWBackends& backends, RWBackend* current_maste
return current_master;
}
thread_local PRWBackends sort_buffer;
sort_buffer.clear();
thread_local PRWBackends candidates;
candidates.clear();
int best_rank {std::numeric_limits<int>::max()};
std::copy_if(backends.begin(), backends.end(), std::back_inserter(sort_buffer),
[](RWBackend* backend) {
return backend->can_connect() && backend->is_master();
});
for (const auto& backend : backends)
{
if (backend->can_connect() && backend->is_master())
{
add_backend_with_rank(backend, &candidates, &best_rank);
}
}
auto it = func(sort_buffer);
return it != sort_buffer.end() ? *it : nullptr;
auto it = func(candidates);
return it != candidates.end() ? *it : nullptr;
}
std::pair<int, int> get_slave_counts(PRWBackends& backends, RWBackend* master)
@ -421,6 +449,7 @@ bool RWSplitSession::open_connections()
int n_slaves = get_slave_counts(m_raw_backends, master).second;
int max_nslaves = m_router->max_slave_count();
int best_rank {std::numeric_limits<int>::max()};
PRWBackends candidates;
mxb_assert(n_slaves <= max_nslaves || max_nslaves == 0);
@ -428,7 +457,7 @@ bool RWSplitSession::open_connections()
{
if (!pBackend->in_use() && pBackend->can_connect() && valid_for_slave(pBackend, master))
{
candidates.push_back(pBackend);
add_backend_with_rank(pBackend, &candidates, &best_rank);
}
}