Merge branch '2.1' into develop

This commit is contained in:
Markus Mäkelä 2017-02-20 18:39:41 +02:00
commit 1d8d526a01
13 changed files with 213 additions and 93 deletions

View File

@ -164,6 +164,21 @@ can start is `monitor_interval * failcount`. This means that to trigger a
failover after 10 seconds of master failure with a _monitor_interval_ of 1000
milliseconds, the value of _failcount_ must be 10.
### `failover_recovery`
Allow recovery after failover. This feature takes a boolean parameter is
disabled by default.
Normally if a failover has been triggered and the last remaining server is
chosen as the master, the monitor will set all of the failed servers into
maintenance mode. When this option is enabled, the failed servers are allowed to
rejoin the cluster.
This option should be enabled when failover in MaxScale is used in conjunction
with an external agent that resets the slave status for new master servers. One
of these agents is the _replication-manager_ which clears the slave
configuration for each new master and removes the read-only mode.
## Example 1 - Monitor script
Here is an example shell script which sends an email to an admin when a server goes down.

View File

@ -0,0 +1,47 @@
# MariaDB MaxScale 2.1.1 Release Notes
Release 2.1.1 is a Beta release.
This document describes the changes in release 2.1.1, when compared to
release 2.1.0.
For any problems you encounter, please consider submitting a bug
report at [Jira](https://jira.mariadb.org).
## New Features
### Failover Recovery for MySQL Monitor
The `failover_recovery` option allows the failed nodes to rejoin the cluster
after a failover has been triggered. This makes it possible for external actors
to recover the failed nodes without having to manually clear the maintenance
mode.
For more information about the failover mode and how it works, read the
[MySQL Monitor](../Monitors/MySQL-Monitor.md) documentation.
## Bug fixes
[Here is a list of bugs fixed since the release of MaxScale 2.1.0.](https://jira.mariadb.org/issues/?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20resolution%20in%20(Fixed%2C%20Done)%20AND%20fixVersion%20%3D%202.1.1%20AND%20fixVersion%20NOT%20IN%20(2.1.0))
*
## Known Issues and Limitations
There are some limitations and known issues within this version of MaxScale.
For more information, please refer to the [Limitations](../About/Limitations.md) document.
## Packaging
RPM and Debian packages are provided for the Linux distributions supported
by MariaDB Enterprise.
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
## Source Code
The source code of MaxScale is tagged at GitHub with a tag, which is identical
with the version of MaxScale. For instance, the tag of version X.Y.Z of MaxScale
is X.Y.Z. Further, *master* always refers to the latest released non-beta version.
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).

View File

@ -49,11 +49,12 @@ bool maxavro_verify_block(MAXAVRO_FILE *file)
int rc = fread(sync, 1, SYNC_MARKER_SIZE, file->file);
if (rc != SYNC_MARKER_SIZE)
{
if (rc == -1)
if (ferror(file->file))
{
MXS_ERROR("Failed to read file: %d %s", errno, strerror(errno));
char err[MXS_STRERROR_BUFLEN];
MXS_ERROR("Failed to read file: %d %s", errno, strerror_r(errno, err, sizeof(err)));
}
else
else if (rc > 0 || !feof(file->file))
{
MXS_ERROR("Short read when reading sync marker. Read %d bytes instead of %d",
rc, SYNC_MARKER_SIZE);

View File

@ -49,9 +49,11 @@ static json_t* read_and_pack_value(MAXAVRO_FILE *file, MAXAVRO_SCHEMA_FIELD *fie
case MAXAVRO_TYPE_LONG:
{
uint64_t val = 0;
maxavro_read_integer(file, &val);
json_int_t jsonint = val;
value = json_pack("I", jsonint);
if (maxavro_read_integer(file, &val))
{
json_int_t jsonint = val;
value = json_pack("I", jsonint);
}
}
break;
@ -74,11 +76,23 @@ static json_t* read_and_pack_value(MAXAVRO_FILE *file, MAXAVRO_SCHEMA_FIELD *fie
break;
case MAXAVRO_TYPE_FLOAT:
{
float f = 0;
if (maxavro_read_float(file, &f))
{
double d = f;
value = json_pack("f", d);
}
}
break;
case MAXAVRO_TYPE_DOUBLE:
{
double d = 0;
maxavro_read_double(file, &d);
value = json_pack("f", d);
if (maxavro_read_double(file, &d))
{
value = json_pack("f", d);
}
}
break;

View File

@ -29,6 +29,7 @@
#include <maxscale/log_manager.h>
#include <maxscale/paths.h>
#include <maxscale/random_jkiss.h>
struct option options[] =
{
@ -94,6 +95,7 @@ int main(int argc, char **argv)
}
mxs_log_init(NULL, NULL, MXS_LOG_TARGET_DEFAULT);
random_jkiss_init();
if (secrets_write_keys(directory) != 0)
{

View File

@ -31,6 +31,7 @@
#include <maxscale/paths.h>
#include <maxscale/log_manager.h>
#include <maxscale/random_jkiss.h>
#include "maxscale/secrets.h"
@ -154,6 +155,8 @@ int main(int argc, char **argv)
mxs_log_set_priority_enabled(LOG_INFO, false);
mxs_log_set_priority_enabled(LOG_DEBUG, false);
random_jkiss_init();
size_t len = strlen(password);
if (len > MXS_PASSWORD_MAXLEN)

View File

@ -290,7 +290,7 @@ static void unpack_datetime2(uint8_t *ptr, uint8_t decimals, struct tm *dest)
dest->tm_min = (time >> 6) % (1 << 6);
dest->tm_hour = time >> 12;
dest->tm_mday = date % (1 << 5);
dest->tm_mon = yearmonth % 13;
dest->tm_mon = (yearmonth % 13) - 1;
/** struct tm stores the year as: Year - 1900 */
dest->tm_year = (yearmonth / 13) - 1900;
@ -347,7 +347,7 @@ static void unpack_date(uint8_t *ptr, struct tm *dest)
uint64_t val = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16);
memset(dest, 0, sizeof(struct tm));
dest->tm_mday = val & 31;
dest->tm_mon = (val >> 5) & 15;
dest->tm_mon = ((val >> 5) & 15) - 1;
dest->tm_year = (val >> 9) - 1900;
}
@ -560,34 +560,42 @@ static uint64_t unpack_bytes(uint8_t *ptr, size_t bytes)
switch (bytes)
{
case 1:
val = ptr[0];
break;
case 2:
val = ptr[1] | ((uint64_t)(ptr[0]) << 8);
break;
case 3:
val = (uint64_t)ptr[2] | ((uint64_t)ptr[1] << 8) | ((uint64_t)ptr[0] << 16);
break;
case 4:
val = (uint64_t)ptr[3] | ((uint64_t)ptr[2] << 8) | ((uint64_t)ptr[1] << 16) | ((uint64_t)ptr[0] << 24);
break;
case 5:
val = (uint64_t)ptr[4] | ((uint64_t)ptr[3] << 8) | ((uint64_t)ptr[2] << 16) | ((uint64_t)ptr[1] << 24) | ((
uint64_t)ptr[0] << 32);
break;
case 6:
val = (uint64_t)ptr[5] | ((uint64_t)ptr[4] << 8) | ((uint64_t)ptr[3] << 16) | ((uint64_t)ptr[2] << 24) | ((
uint64_t)ptr[1] << 32) | ((uint64_t)ptr[0] << 40);
break;
case 7:
val = (uint64_t)ptr[6] | ((uint64_t)ptr[5] << 8) | ((uint64_t)ptr[4] << 16) | ((uint64_t)ptr[3] << 24) | ((
uint64_t)ptr[2] << 32) | ((uint64_t)ptr[1] << 40) | ((uint64_t)ptr[0] << 48);
break;
case 8:
val = (uint64_t)ptr[7] | ((uint64_t)ptr[6] << 8) | ((uint64_t)ptr[5] << 16) | ((uint64_t)ptr[4] << 24) | ((
uint64_t)ptr[3] << 32) | ((uint64_t)ptr[2] << 40) | ((uint64_t)ptr[1] << 48) | ((uint64_t)ptr[0] << 56);
break;
case 1:
val = ptr[0];
break;
case 2:
val = ptr[1] | ((uint64_t)(ptr[0]) << 8);
break;
case 3:
val = (uint64_t)ptr[2] | ((uint64_t)ptr[1] << 8) |
((uint64_t)ptr[0] << 16);
break;
case 4:
val = (uint64_t)ptr[3] | ((uint64_t)ptr[2] << 8) |
((uint64_t)ptr[1] << 16) | ((uint64_t)ptr[0] << 24);
break;
case 5:
val = (uint64_t)ptr[4] | ((uint64_t)ptr[3] << 8) |
((uint64_t)ptr[2] << 16) | ((uint64_t)ptr[1] << 24) |
((uint64_t)ptr[0] << 32);
break;
case 6:
val = (uint64_t)ptr[5] | ((uint64_t)ptr[4] << 8) |
((uint64_t)ptr[3] << 16) | ((uint64_t)ptr[2] << 24) |
((uint64_t)ptr[1] << 32) | ((uint64_t)ptr[0] << 40);
break;
case 7:
val = (uint64_t)ptr[6] | ((uint64_t)ptr[5] << 8) |
((uint64_t)ptr[4] << 16) | ((uint64_t)ptr[3] << 24) |
((uint64_t)ptr[2] << 32) | ((uint64_t)ptr[1] << 40) |
((uint64_t)ptr[0] << 48);
break;
case 8:
val = (uint64_t)ptr[7] | ((uint64_t)ptr[6] << 8) |
((uint64_t)ptr[5] << 16) | ((uint64_t)ptr[4] << 24) |
((uint64_t)ptr[3] << 32) | ((uint64_t)ptr[2] << 40) |
((uint64_t)ptr[1] << 48) | ((uint64_t)ptr[0] << 56);
break;
}
return val;
@ -608,12 +616,11 @@ size_t unpack_decimal_field(uint8_t *ptr, uint8_t *metadata, double *val_float)
int fbytes = fpart1 * 4 + dig_bytes[fpart2];
/** Remove the sign bit and store it locally */
bool signed_int = (ptr[0] & 0x80);
bool negative = (ptr[0] & 0x80) == 0;
ptr[0] ^= 0x80;
if (!signed_int)
if (negative)
{
ptr[0] |= 0x80;
for (int i = 0; i < ibytes; i++)
{
ptr[i] = ~ptr[i];
@ -628,7 +635,7 @@ size_t unpack_decimal_field(uint8_t *ptr, uint8_t *metadata, double *val_float)
int64_t val_i = unpack_bytes(ptr, ibytes);
int64_t val_f = fbytes ? unpack_bytes(ptr + ibytes, fbytes) : 0;
if (!signed_int)
if (negative)
{
val_i = -val_i;
val_f = -val_f;

View File

@ -77,6 +77,7 @@ typedef struct
bool failover; /**< If simple failover is enabled */
int failcount; /**< How many monitoring cycles servers must be
down before failover is initiated */
bool failover_recovery; /**< Allow servers to rejoin the cluster in failover mode */
bool warn_failover; /**< Log a warning when failover happens */
} MYSQL_MONITOR;

View File

@ -127,6 +127,7 @@ MXS_MODULE* MXS_CREATE_MODULE()
{"multimaster", MXS_MODULE_PARAM_BOOL, "false"},
{"failover", MXS_MODULE_PARAM_BOOL, "false"},
{"failcount", MXS_MODULE_PARAM_COUNT, "5"},
{"failover_recovery", MXS_MODULE_PARAM_BOOL, "false"},
{
"script",
MXS_MODULE_PARAM_PATH,
@ -280,6 +281,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params)
handle->multimaster = config_get_bool(params, "multimaster");
handle->failover = config_get_bool(params, "failover");
handle->failcount = config_get_integer(params, "failcount");
handle->failover_recovery = config_get_bool(params, "failover_recovery");
handle->mysql51_replication = config_get_bool(params, "mysql51_replication");
handle->script = config_copy_string(params, "script");
handle->events = config_get_enum(params, "events", mxs_monitor_event_enum_values);
@ -1006,9 +1008,10 @@ void do_failover(MYSQL_MONITOR *handle, MXS_MONITOR_SERVERS *db)
{
if (!SERVER_IS_MASTER(db->server) && handle->warn_failover)
{
MXS_WARNING("Failover initiated, server '%s' is now the master. "
"All other servers are set into maintenance mode.",
db->server->unique_name);
MXS_WARNING("Failover initiated, server '%s' is now the master.%s",
db->server->unique_name,
handle->failover_recovery ?
"" : " All other servers are set into maintenance mode.");
handle->warn_failover = false;
}
@ -1016,7 +1019,7 @@ void do_failover(MYSQL_MONITOR *handle, MXS_MONITOR_SERVERS *db)
monitor_set_pending_status(db, SERVER_MASTER);
monitor_clear_pending_status(db, SERVER_SLAVE);
}
else
else if (!handle->failover_recovery)
{
server_set_status_nolock(db->server, SERVER_MAINT);
monitor_set_pending_status(db, SERVER_MAINT);

View File

@ -991,7 +991,8 @@ extract_message(GWBUF *errpkt)
*
*/
static void
errorReply(MXS_ROUTER *instance, void *router_session, GWBUF *message, DCB *backend_dcb, mxs_error_action_t action,
errorReply(MXS_ROUTER *instance, void *router_session, GWBUF *message, DCB *backend_dcb,
mxs_error_action_t action,
bool *succp)
{
/** We should never end up here */

View File

@ -788,25 +788,25 @@ static bool avro_client_stream_data(AVRO_CLIENT *client)
{
switch (client->format)
{
case AVRO_FORMAT_JSON:
/** Currently only JSON format supports seeking to a GTID */
if (client->requested_gtid &&
seek_to_index_pos(client, client->file_handle) &&
seek_to_gtid(client, client->file_handle))
{
client->requested_gtid = false;
}
case AVRO_FORMAT_JSON:
/** Currently only JSON format supports seeking to a GTID */
if (client->requested_gtid &&
seek_to_index_pos(client, client->file_handle) &&
seek_to_gtid(client, client->file_handle))
{
client->requested_gtid = false;
}
read_more = stream_json(client);
break;
read_more = stream_json(client);
break;
case AVRO_FORMAT_AVRO:
read_more = stream_binary(client);
break;
case AVRO_FORMAT_AVRO:
read_more = stream_binary(client);
break;
default:
MXS_ERROR("Unexpected format: %d", client->format);
break;
default:
MXS_ERROR("Unexpected format: %d", client->format);
break;
}
@ -847,13 +847,15 @@ GWBUF* read_avro_json_schema(const char *avrofile, const char* dir)
if (file)
{
int nread;
while ((nread = fread(buffer, 1, sizeof(buffer), file)) > 0)
while ((nread = fread(buffer, 1, sizeof(buffer) - 1, file)) > 0)
{
while (isspace(buffer[nread - 1]))
{
nread--;
}
buffer[nread++] = '\n';
GWBUF * newbuf = gwbuf_alloc_and_load(nread, buffer);
if (newbuf)

View File

@ -88,18 +88,22 @@ void avro_index_file(AVRO_INSTANCE *router, const char* filename)
snprintf(sql, sizeof(sql), "SELECT position FROM "INDEX_TABLE_NAME
" WHERE filename=\"%s\";", name);
if (sqlite3_exec(router->sqlite_handle, sql, index_query_cb, &pos, &errmsg) != SQLITE_OK)
{
MXS_ERROR("Failed to read last indexed position of file '%s': %s",
name, errmsg);
sqlite3_free(errmsg);
maxavro_file_close(file);
return;
}
else if (pos > 0)
/** Continue from last position */
if (pos > 0 && !maxavro_record_set_pos(file, pos))
{
/** Continue from last position */
maxavro_record_set_pos(file, pos);
maxavro_file_close(file);
return;
}
sqlite3_free(errmsg);
errmsg = NULL;
gtid_pos_t prev_gtid = {0, 0, 0, 0, 0};

View File

@ -352,44 +352,64 @@ bool handle_row_event(AVRO_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr)
*/
void set_numeric_field_value(avro_value_t *field, uint8_t type, uint8_t *metadata, uint8_t *value)
{
int64_t i = 0;
switch (type)
{
case TABLE_COL_TYPE_TINY:
i = *value;
avro_value_set_int(field, i);
break;
{
char c = *value;
avro_value_set_int(field, c);
break;
}
case TABLE_COL_TYPE_SHORT:
memcpy(&i, value, 2);
avro_value_set_int(field, i);
break;
{
short s = gw_mysql_get_byte2(value);
avro_value_set_int(field, s);
break;
}
case TABLE_COL_TYPE_INT24:
memcpy(&i, value, 3);
avro_value_set_int(field, i);
break;
{
int x = gw_mysql_get_byte3(value);
if (x & 0x800000)
{
x = -((0xffffff & (~x)) + 1);
}
avro_value_set_int(field, x);
break;
}
case TABLE_COL_TYPE_LONG:
memcpy(&i, value, 4);
avro_value_set_int(field, i);
break;
{
int x = gw_mysql_get_byte4(value);
avro_value_set_int(field, x);
break;
}
case TABLE_COL_TYPE_LONGLONG:
memcpy(&i, value, 8);
avro_value_set_int(field, i);
break;
{
long l = gw_mysql_get_byte8(value);
avro_value_set_long(field, l);
break;
}
case TABLE_COL_TYPE_FLOAT:
memcpy(&i, value, 4);
avro_value_set_float(field, (float)i);
break;
{
float f = 0;
memcpy(&f, value, 4);
avro_value_set_float(field, f);
break;
}
case TABLE_COL_TYPE_DOUBLE:
memcpy(&i, value, 8);
avro_value_set_float(field, (double)i);
break;
{
double d = 0;
memcpy(&d, value, 8);
avro_value_set_double(field, d);
break;
}
default:
break;