Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MDEV-34009] Client-side implementation of instant failover mechanism #246

Open
wants to merge 3 commits into
base: 3.4
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/mysql.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ extern const char *SQLSTATE_UNKNOWN;
/* MariaDB specific */
MYSQL_PROGRESS_CALLBACK=5999,
MYSQL_OPT_NONBLOCK,
MARIADB_OPT_FOLLOW_INSTANT_FAILOVERS,

/* MariaDB Connector/C specific */
MYSQL_DATABASE_DRIVER=7000,
MARIADB_OPT_SSL_FP, /* deprecated, use MARIADB_OPT_TLS_PEER_FP instead */
Expand Down Expand Up @@ -340,6 +342,8 @@ struct st_mysql_options {
char *bind_address;
my_bool secure_auth;
my_bool report_data_truncation;
my_bool follow_instant_failovers;

/* function pointers for local infile support */
int (*local_infile_init)(void **, const char *, void *);
int (*local_infile_read)(void *, char *, unsigned int);
Expand Down
3 changes: 2 additions & 1 deletion include/mysqld_error.h
Original file line number Diff line number Diff line change
Expand Up @@ -1236,5 +1236,6 @@
#define ER_JSON_HISTOGRAM_PARSE_FAILED 4186
#define ER_SF_OUT_INOUT_ARG_NOT_ALLOWED 4187
#define ER_INCONSISTENT_SLAVE_TEMP_TABLE 4188
#define ER_ERROR_LAST 4188
#define ER_INSTANT_FAILOVER 4200
#define ER_ERROR_LAST 4200
#endif /* ER_ERROR_FIRST */
48 changes: 47 additions & 1 deletion libmariadb/mariadb_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
#define MA_RPL_VERSION_HACK "5.5.5-"

#define CHARSET_NAME_LEN 64
#define INSTANT_FAILOVER_LIMIT 8

#undef max_allowed_packet
#undef net_buffer_length
Expand Down Expand Up @@ -668,6 +669,7 @@ struct st_default_options mariadb_defaults[] =
{{MYSQL_SET_CHARSET_NAME}, MARIADB_OPTION_STR, "default-character-set"},
{{MARIADB_OPT_INTERACTIVE}, MARIADB_OPTION_NONE, "interactive-timeout"},
{{MYSQL_OPT_CONNECT_TIMEOUT}, MARIADB_OPTION_INT, "connect-timeout"},
{{MARIADB_OPT_FOLLOW_INSTANT_FAILOVERS}, MARIADB_OPTION_BOOL, "follow-instant-failovers"},
{{MYSQL_OPT_LOCAL_INFILE}, MARIADB_OPTION_BOOL, "local-infile"},
{{0}, 0 ,"disable-local-infile",},
{{MYSQL_OPT_SSL_CIPHER}, MARIADB_OPTION_STR, "ssl-cipher"},
Expand Down Expand Up @@ -1304,6 +1306,7 @@ mysql_init(MYSQL *mysql)
goto error;
mysql->options.report_data_truncation= 1;
mysql->options.connect_timeout=CONNECT_TIMEOUT;
mysql->options.follow_instant_failovers= TRUE;
mysql->charset= mysql_find_charset_name(MARIADB_DEFAULT_CHARSET);
mysql->methods= &MARIADB_DEFAULT_METHODS;
strcpy(mysql->net.sqlstate, "00000");
Expand Down Expand Up @@ -1591,7 +1594,7 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
my_bool is_multi= 0;
char *host_copy= NULL;
struct st_host *host_list= NULL;
int connect_attempts= 0;
int connect_attempts= 0, instant_failovers=0;

if (!mysql->methods)
mysql->methods= &MARIADB_DEFAULT_METHODS;
Expand Down Expand Up @@ -1731,6 +1734,7 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
else
#endif
{
tcp_redirect:
cinfo.unix_socket=0; /* This is not used */
if (!port)
port=mysql_port;
Expand Down Expand Up @@ -1963,7 +1967,46 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,

if (run_plugin_auth(mysql, scramble_data, scramble_len,
scramble_plugin, db))
{
if (mysql->net.last_errno == ER_INSTANT_FAILOVER)
{
if (!mysql->options.follow_instant_failovers)
{
/* Client has disabled instant failover. Fall through and treat this
* as a "normal" error. */
}
else if (instant_failovers >= INSTANT_FAILOVER_LIMIT)
{
/* Too many instant failovers */
my_set_error(mysql, ER_INSTANT_FAILOVER, SQLSTATE_UNKNOWN,
"Too many instant failovers (>= %d)",
INSTANT_FAILOVER_LIMIT);
}
else
{
char *p= mysql->net.last_error; /* Should look like '|message|host[:port]' */
if (p && p[0] == '|')
p= strchr(p + 1, '|') ? : NULL;
if (p && *++p) {
host= p;
p= strchr(p, ':') ? : NULL;
if (p) {
*p++ = '\0';
port= atoi(p);
}
else
{
/* Restore to the default port, rather than reusing our current one */
port= 0;
}
fprintf(stderr, "Got instant failover to '%s' (port %d)\n", host, port);
++instant_failovers;
goto tcp_redirect;
}
}
}
goto error;
}

if (mysql->client_flag & CLIENT_COMPRESS ||
mysql->client_flag & CLIENT_ZSTD_COMPRESSION)
Expand Down Expand Up @@ -3470,6 +3513,9 @@ mysql_optionsv(MYSQL *mysql,enum mysql_option option, ...)
case MYSQL_OPT_RECONNECT:
mysql->options.reconnect= *(my_bool *)arg1;
break;
case MARIADB_OPT_FOLLOW_INSTANT_FAILOVERS:
mysql->options.follow_instant_failovers= *(my_bool *)arg1;
break;
case MYSQL_OPT_PROTOCOL:
mysql->options.protocol= *((uint *)arg1);
break;
Expand Down