MXS-359: Allow master changes mid-session

With the `allow_master_change` parameter enabled, sessions can start using
a different master node if one is available. This will not prevent
sessions from closing if a write query is received while no master
replacement is available.
This commit is contained in:
Markus Mäkelä
2018-03-06 18:58:13 +02:00
parent b18207282d
commit c2341a0003
4 changed files with 46 additions and 4 deletions

View File

@ -1507,6 +1507,7 @@ MXS_MODULE *MXS_CREATE_MODULE()
{"connection_keepalive", MXS_MODULE_PARAM_COUNT, "0"},
{"enable_causal_read", MXS_MODULE_PARAM_BOOL, "false"},
{"causal_read_timeout", MXS_MODULE_PARAM_STRING, "0"},
{"allow_master_change", MXS_MODULE_PARAM_BOOL, "false"},
{MXS_END_MODULE_PARAMS}
}
};

View File

@ -174,7 +174,8 @@ struct Config
rw_max_slave_conn_percent(0),
max_slave_connections(0),
enable_causal_read(config_get_bool(params, "enable_causal_read")),
causal_read_timeout(config_get_string(params, "causal_read_timeout"))
causal_read_timeout(config_get_string(params, "causal_read_timeout")),
allow_master_change(config_get_bool(params, "allow_master_change"))
{
if (enable_causal_read)
{
@ -201,7 +202,7 @@ struct Config
int max_slave_connections; /**< Maximum number of slaves for each connection*/
bool enable_causal_read; /**< Enable causual read */
std::string causal_read_timeout; /**< Timetout, second parameter of function master_wait_gtid */
bool allow_master_change; /**< Allow changes in master server */
};
/**

View File

@ -180,6 +180,11 @@ route_target_t get_target_type(RWSplitSession *rses, GWBUF *buffer,
return route_target;
}
static inline bool locked_to_master(RWSplitSession *rses)
{
return rses->large_query || (rses->current_master && rses->target_node == rses->current_master);
}
/**
* Routing function. Find out query type, backend type, and target DCB(s).
* Then route query to found target(s).
@ -197,8 +202,7 @@ bool route_single_stmt(RWSplit *inst, RWSplitSession *rses, GWBUF *querybuf, con
uint8_t command = info.command;
uint32_t qtype = info.type;
route_target_t route_target = info.target;
bool not_locked_to_master = !rses->large_query &&
(!rses->target_node || rses->target_node != rses->current_master);
bool not_locked_to_master = !locked_to_master(rses);
if (not_locked_to_master && is_ps_command(command))
{
@ -1064,6 +1068,17 @@ static void log_master_routing_failure(RWSplitSession *rses, bool found,
rses->client_dcb->remote, errmsg);
}
bool should_replace_master(RWSplitSession *rses, SRWBackend& target)
{
return rses->rses_config.allow_master_change &&
// We have a target server and it's not the current master
target && target != rses->current_master &&
// We are not inside a transaction (also checks for autocommit=1)
!session_trx_is_active(rses->client_dcb->session) &&
// We are not locked to the old master
!locked_to_master(rses);
}
/**
* @brief Handle master is the target
*
@ -1081,6 +1096,13 @@ bool handle_master_is_target(RWSplit *inst, RWSplitSession *rses,
SRWBackend target = get_target_backend(rses, BE_MASTER, NULL, MAX_RLAG_UNDEFINED);
bool succp = true;
if (should_replace_master(rses, target))
{
MXS_INFO("Replacing old master '%s' with new master '%s'", rses->current_master ?
rses->current_master->name() : "<no previous master>", target->name());
rses->current_master = target;
}
if (target && target == rses->current_master)
{
atomic_add_uint64(&inst->stats().n_master, 1);