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
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
4 changed files with 46 additions and 4 deletions

View File

@ -105,6 +105,24 @@ connections alive even if they are not used. This is a common problem if the
backend servers have a low _wait_timeout_ value and the client connections live
for a long time.
### `allow_master_change`
Allow the master server to change mid-session. This feature was introduced in
MaxScale 2.3.0 and is disabled by default.
When a readwritesplit session starts, it will pick a master server as the
current master server of that session. By default, when this master server
changes mid-session, the connection will be closed.
If the `allow_master_change` parameter is enabled, the master server is allowed
to change as long as the session meets the following criteria:
* The session is already connected to the slave that was chosen to be the new master
* No transaction is open
* Autocommit is enabled
* No `LOAD DATA LOCAL INFILE` is in progress
* There are no queries being actively routed to the old master
## Router options
**`router_options`** may include multiple **readwritesplit**-specific options.

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);