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:
@ -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}
|
||||
}
|
||||
};
|
||||
|
@ -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 */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
|
Reference in New Issue
Block a user