You've already forked mariadb-connector-c
mirror of
https://github.com/mariadb-corporation/mariadb-connector-c.git
synced 2025-08-07 02:42:49 +03:00
CONC-365: Failover capabilities
host parameter of mysql_real_connect (and corresponding configuration settings MYSQL_OPT_HOST for mysql_options() api call and host key in configuration files) now accepts to specify multiple hosts and ports. When establishing a connection, the list of specified hosts is run through until a connection can be established. If no connection can be established to any of the specified hosts, an error is returned. Specifications for multiple hosts/ports: - hostname and port must be seperated by a colon (:) - IPv6 addresses must be enclosed within square brackets - hostname:port pairs must be be seperated by a comma (,) - if only one host:port was specified, the host string needs to end with a comma. - if no port was specified, the default port will be used. Examples for failover host string: host=[::1]:3306,192.168.0.1:3306,test.example.com host=localhost:3306,
This commit is contained in:
@@ -1485,6 +1485,44 @@ mysql_real_connect(MYSQL *mysql, const char *host, const char *user,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct st_host {
|
||||||
|
char *host;
|
||||||
|
int port;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ma_get_host_list(char *host_list, struct st_host *host_info, int default_port)
|
||||||
|
{
|
||||||
|
char *token, *start, *save;
|
||||||
|
int host_nr= 0;
|
||||||
|
|
||||||
|
start= host_list;
|
||||||
|
while ((token= strtok_r(start, ",", &save)))
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
/* ipv6 hostname */
|
||||||
|
if ((p= strchr(token, ']')))
|
||||||
|
{
|
||||||
|
host_info[host_nr].host= token + 1;
|
||||||
|
*p++= 0;
|
||||||
|
token= p;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
host_info[host_nr].host= token;
|
||||||
|
/* check if port was specified */
|
||||||
|
if ((p= strchr(token, ':')))
|
||||||
|
{
|
||||||
|
*p++= 0;
|
||||||
|
host_info[host_nr].port= atoi(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
host_info[host_nr].port= default_port;
|
||||||
|
host_nr++;
|
||||||
|
start= NULL;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
|
MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
|
||||||
const char *passwd, const char *db,
|
const char *passwd, const char *db,
|
||||||
uint port, const char *unix_socket, unsigned long client_flag)
|
uint port, const char *unix_socket, unsigned long client_flag)
|
||||||
@@ -1498,6 +1536,10 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
|
|||||||
const char *scramble_plugin;
|
const char *scramble_plugin;
|
||||||
uint pkt_length, scramble_len, pkt_scramble_len= 0;
|
uint pkt_length, scramble_len, pkt_scramble_len= 0;
|
||||||
NET *net= &mysql->net;
|
NET *net= &mysql->net;
|
||||||
|
my_bool is_multi= 0;
|
||||||
|
char *host_copy= NULL;
|
||||||
|
struct st_host *host_list= NULL;
|
||||||
|
int connect_attempts= 0;
|
||||||
|
|
||||||
if (!mysql->methods)
|
if (!mysql->methods)
|
||||||
mysql->methods= &MARIADB_DEFAULT_METHODS;
|
mysql->methods= &MARIADB_DEFAULT_METHODS;
|
||||||
@@ -1520,6 +1562,54 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
|
|||||||
mysql->options.my_cnf_file=mysql->options.my_cnf_group=0;
|
mysql->options.my_cnf_file=mysql->options.my_cnf_group=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!port)
|
||||||
|
port=mysql->options.port;
|
||||||
|
if (!host || !host[0])
|
||||||
|
host= mysql->options.host;
|
||||||
|
|
||||||
|
/* check if we have multi hosts */
|
||||||
|
if (host && host[0] && strchr(host, ','))
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
size_t host_count= 1;
|
||||||
|
|
||||||
|
is_multi= 1;
|
||||||
|
|
||||||
|
/* don't change original entry, so let's make a copy */
|
||||||
|
if (!(host_copy= strdup(host)))
|
||||||
|
{
|
||||||
|
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
p= host_copy;
|
||||||
|
|
||||||
|
while ((p = strchr(p, ',')))
|
||||||
|
{
|
||||||
|
host_count++;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(host_list= (struct st_host *)calloc(host_count + 1, sizeof(struct st_host))))
|
||||||
|
{
|
||||||
|
SET_CLIENT_ERROR(mysql, CR_OUT_OF_MEMORY, SQLSTATE_UNKNOWN, 0);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ma_get_host_list(host_copy, host_list, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
restart:
|
||||||
|
/* check if we reached end of list */
|
||||||
|
if (is_multi)
|
||||||
|
{
|
||||||
|
if (!host_list[connect_attempts].host)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
host= host_list[connect_attempts].host;
|
||||||
|
port= host_list[connect_attempts].port;
|
||||||
|
}
|
||||||
|
|
||||||
ma_set_connect_attrs(mysql, host);
|
ma_set_connect_attrs(mysql, host);
|
||||||
|
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
@@ -1545,8 +1635,6 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
|
|||||||
}
|
}
|
||||||
if (!db || !db[0])
|
if (!db || !db[0])
|
||||||
db=mysql->options.db;
|
db=mysql->options.db;
|
||||||
if (!port)
|
|
||||||
port=mysql->options.port;
|
|
||||||
if (!unix_socket)
|
if (!unix_socket)
|
||||||
unix_socket=mysql->options.unix_socket;
|
unix_socket=mysql->options.unix_socket;
|
||||||
|
|
||||||
@@ -1609,6 +1697,11 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
|
|||||||
if (ma_pvio_connect(pvio, &cinfo) != 0)
|
if (ma_pvio_connect(pvio, &cinfo) != 0)
|
||||||
{
|
{
|
||||||
ma_pvio_close(pvio);
|
ma_pvio_close(pvio);
|
||||||
|
if (is_multi)
|
||||||
|
{
|
||||||
|
connect_attempts++;
|
||||||
|
goto restart;
|
||||||
|
}
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1846,10 +1939,16 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char *host, const char *user,
|
|||||||
/* connection established, apply timeouts */
|
/* connection established, apply timeouts */
|
||||||
ma_pvio_set_timeout(mysql->net.pvio, PVIO_READ_TIMEOUT, mysql->options.read_timeout);
|
ma_pvio_set_timeout(mysql->net.pvio, PVIO_READ_TIMEOUT, mysql->options.read_timeout);
|
||||||
ma_pvio_set_timeout(mysql->net.pvio, PVIO_WRITE_TIMEOUT, mysql->options.write_timeout);
|
ma_pvio_set_timeout(mysql->net.pvio, PVIO_WRITE_TIMEOUT, mysql->options.write_timeout);
|
||||||
|
|
||||||
|
free(host_list);
|
||||||
|
free(host_copy);
|
||||||
|
|
||||||
return(mysql);
|
return(mysql);
|
||||||
|
|
||||||
error:
|
error:
|
||||||
/* Free allocated memory */
|
/* Free allocated memory */
|
||||||
|
free(host_list);
|
||||||
|
free(host_copy);
|
||||||
end_server(mysql);
|
end_server(mysql);
|
||||||
/* only free the allocated memory, user needs to call mysql_close */
|
/* only free the allocated memory, user needs to call mysql_close */
|
||||||
mysql_close_memory(mysql);
|
mysql_close_memory(mysql);
|
||||||
|
@@ -1999,8 +1999,6 @@ static int test_conn_str(MYSQL *my __attribute__((unused)))
|
|||||||
strcat(conn_str, ";ssl_enforce=1");
|
strcat(conn_str, ";ssl_enforce=1");
|
||||||
}
|
}
|
||||||
|
|
||||||
diag("connection string: %s", conn_str);
|
|
||||||
|
|
||||||
if (mariadb_connect(mysql, conn_str))
|
if (mariadb_connect(mysql, conn_str))
|
||||||
{
|
{
|
||||||
diag("host: %s", mysql->host);
|
diag("host: %s", mysql->host);
|
||||||
@@ -2025,7 +2023,9 @@ static int test_conn_str_1(MYSQL *my __attribute__((unused)))
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
fprintf(fp, "[client]\n");
|
fprintf(fp, "[client]\n");
|
||||||
fprintf(fp, "connection=host=%s;user=%s;password=%s;port=%d;ssl_enforce=1\n", hostname, username, password, port);
|
fprintf(fp, "connection=host=%s;user=%s;password=%s;port=%d;ssl_enforce=1\n",
|
||||||
|
hostname ? hostname : "localhost", username ? username : "",
|
||||||
|
password ? password : "", port);
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
@@ -2052,7 +2052,95 @@ static int test_conn_str_1(MYSQL *my __attribute__((unused)))
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int test_conc365(MYSQL *my __attribute__((unused)))
|
||||||
|
{
|
||||||
|
int rc= OK;
|
||||||
|
MYSQL *mysql= mysql_init(NULL);
|
||||||
|
char tmp[1024];
|
||||||
|
|
||||||
|
snprintf(tmp, sizeof(tmp) - 1,
|
||||||
|
"host=127.0.0.1:3300,%s;user=%s;password=%s;port=%d",
|
||||||
|
hostname ? hostname : "localhost", username ? username : "", password ? password : "",
|
||||||
|
port);
|
||||||
|
|
||||||
|
if (IS_SKYSQL(hostname))
|
||||||
|
strcat(tmp, ";ssl_enforce=1");
|
||||||
|
|
||||||
|
if (!mariadb_connect(mysql, tmp))
|
||||||
|
rc= FAIL;
|
||||||
|
|
||||||
|
mysql_close(mysql);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
mysql= mysql_init(NULL);
|
||||||
|
snprintf(tmp, sizeof(tmp) -1, "127.0.0.1:3300,%s:%d", hostname ? hostname : "localhost", port);
|
||||||
|
if (!my_test_connect(mysql, tmp, username,
|
||||||
|
password, schema, port, socketname, 0))
|
||||||
|
{
|
||||||
|
diag("Error: %s", mysql_error(mysql));
|
||||||
|
rc= FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_close(mysql);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
mysql= mysql_init(NULL);
|
||||||
|
mysql_options(mysql, MARIADB_OPT_HOST, tmp);
|
||||||
|
if (!my_test_connect(mysql, NULL, username,
|
||||||
|
password, schema, port, socketname, 0))
|
||||||
|
{
|
||||||
|
diag("Error: %s", mysql_error(mysql));
|
||||||
|
rc= FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_close(mysql);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_conc365_reconnect(MYSQL *my)
|
||||||
|
{
|
||||||
|
int rc= OK;
|
||||||
|
MYSQL *mysql= mysql_init(NULL);
|
||||||
|
char tmp[1024];
|
||||||
|
my_bool reconnect= 1;
|
||||||
|
|
||||||
|
mysql_options(mysql, MYSQL_OPT_RECONNECT, &reconnect);
|
||||||
|
|
||||||
|
snprintf(tmp, sizeof(tmp) - 1,
|
||||||
|
"host=127.0.0.1:3300,%s;user=%s;password=%s;port=%d",
|
||||||
|
hostname ? hostname : "localhost", username ? username : "", password ? password : "",
|
||||||
|
port);
|
||||||
|
|
||||||
|
if (IS_SKYSQL(hostname))
|
||||||
|
strcat(tmp, ";ssl_enforce=1");
|
||||||
|
|
||||||
|
if (!my_test_connect(mysql, tmp, username,
|
||||||
|
password, schema, port, socketname, CLIENT_REMEMBER_OPTIONS))
|
||||||
|
{
|
||||||
|
diag("Error: %s", mysql_error(mysql));
|
||||||
|
rc= FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(tmp, "KILL %ld", mysql_thread_id(mysql));
|
||||||
|
|
||||||
|
rc= mysql_query(my, tmp);
|
||||||
|
check_mysql_rc(rc, my);
|
||||||
|
|
||||||
|
sleep(3);
|
||||||
|
rc= mysql_ping(mysql);
|
||||||
|
check_mysql_rc(rc, my);
|
||||||
|
|
||||||
|
mysql_close(mysql);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
struct my_tests_st my_tests[] = {
|
struct my_tests_st my_tests[] = {
|
||||||
|
{"test_conc365", test_conc365, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||||
|
{"test_conc365_reconnect", test_conc365_reconnect, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
{"test_conn_str", test_conn_str, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
{"test_conn_str", test_conn_str, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||||
{"test_conn_str_1", test_conn_str_1, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
{"test_conn_str_1", test_conn_str_1, TEST_CONNECTION_NONE, 0, NULL, NULL},
|
||||||
{"test_conc544", test_conc544, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
{"test_conc544", test_conc544, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},
|
||||||
|
Reference in New Issue
Block a user