Merge branch '2.1' into develop
This commit is contained in:
commit
1d8d526a01
@ -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.
|
||||
|
47
Documentation/Release-Notes/MaxScale-2.1.1-Release-Notes.md
Normal file
47
Documentation/Release-Notes/MaxScale-2.1.1-Release-Notes.md
Normal 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).
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user