mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 13:02:28 +01:00
some code for replication awareness support - this is a temporary
commit - it compiles, and test suite still passes, but it is not finished . The purpose of the commit is backup/code sharing/code review
This commit is contained in:
parent
ad0b92f941
commit
1d05b89980
2 changed files with 223 additions and 1 deletions
|
@ -127,6 +127,15 @@ typedef struct st_mysql_data {
|
||||||
struct st_mysql_options {
|
struct st_mysql_options {
|
||||||
unsigned int connect_timeout,client_flag;
|
unsigned int connect_timeout,client_flag;
|
||||||
my_bool compress,named_pipe;
|
my_bool compress,named_pipe;
|
||||||
|
my_bool rpl_probe; /* on connect, find out the replication
|
||||||
|
role of the server, and establish connections
|
||||||
|
to all the peers */
|
||||||
|
my_bool rpl_parse; /* each call to mysql_real_query() will parse
|
||||||
|
it to tell if it is a read or a write, and
|
||||||
|
direct it to the slave or the master */
|
||||||
|
my_bool no_master_reads; /* if set, never read from
|
||||||
|
a master,only from slave, when doing
|
||||||
|
a read that is replication-aware */
|
||||||
unsigned int port;
|
unsigned int port;
|
||||||
char *host,*init_command,*user,*password,*unix_socket,*db;
|
char *host,*init_command,*user,*password,*unix_socket,*db;
|
||||||
char *my_cnf_file,*my_cnf_group, *charset_dir, *charset_name;
|
char *my_cnf_file,*my_cnf_group, *charset_dir, *charset_name;
|
||||||
|
@ -168,6 +177,13 @@ typedef struct st_mysql {
|
||||||
char scramble_buff[9];
|
char scramble_buff[9];
|
||||||
struct charset_info_st *charset;
|
struct charset_info_st *charset;
|
||||||
unsigned int server_language;
|
unsigned int server_language;
|
||||||
|
|
||||||
|
/* pointers to the master, and the next slave
|
||||||
|
connections, points to itself if lone connection */
|
||||||
|
struct st_mysql* master, *next_slave;
|
||||||
|
|
||||||
|
struct st_mysql* last_used_slave; /* needed for round-robin slave pick */
|
||||||
|
my_bool is_slave; /* will be false for a lone connection */
|
||||||
} MYSQL;
|
} MYSQL;
|
||||||
|
|
||||||
|
|
||||||
|
@ -242,6 +258,31 @@ int STDCALL mysql_send_query(MYSQL *mysql, const char *q,
|
||||||
int STDCALL mysql_read_query_result(MYSQL *mysql);
|
int STDCALL mysql_read_query_result(MYSQL *mysql);
|
||||||
int STDCALL mysql_real_query(MYSQL *mysql, const char *q,
|
int STDCALL mysql_real_query(MYSQL *mysql, const char *q,
|
||||||
unsigned int length);
|
unsigned int length);
|
||||||
|
/* perform query on master */
|
||||||
|
int STDCALL mysql_master_query(MYSQL *mysql, const char *q,
|
||||||
|
unsigned int length);
|
||||||
|
/* perform query on slave */
|
||||||
|
int STDCALL mysql_slave_query(MYSQL *mysql, const char *q,
|
||||||
|
unsigned int length);
|
||||||
|
|
||||||
|
/* enable/disable parsing of all queries to decide
|
||||||
|
if they go on master or slave */
|
||||||
|
void STDCALL mysql_enable_rpl_parse(MYSQL* mysql);
|
||||||
|
void STDCALL mysql_disable_rpl_parse(MYSQL* mysql);
|
||||||
|
/* get the value of the parse flag */
|
||||||
|
int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql);
|
||||||
|
|
||||||
|
/* enable/disable reads from master */
|
||||||
|
void STDCALL mysql_enable_reads_from_master(MYSQL* mysql);
|
||||||
|
void STDCALL mysql_disable_reads_from_master(MYSQL* mysql);
|
||||||
|
/* get the value of the master read flag */
|
||||||
|
int STDCALL mysql_reads_from_master_enabled(MYSQL* mysql);
|
||||||
|
|
||||||
|
int STDCALL mysql_query_goes_to_master(const char* q, int len);
|
||||||
|
|
||||||
|
/* discover the master and its slaves */
|
||||||
|
int STDCALL mysql_rpl_probe(MYSQL* mysql);
|
||||||
|
|
||||||
int STDCALL mysql_create_db(MYSQL *mysql, const char *DB);
|
int STDCALL mysql_create_db(MYSQL *mysql, const char *DB);
|
||||||
int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB);
|
int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB);
|
||||||
int STDCALL mysql_shutdown(MYSQL *mysql);
|
int STDCALL mysql_shutdown(MYSQL *mysql);
|
||||||
|
|
|
@ -678,7 +678,8 @@ static const char *default_options[]=
|
||||||
"init-command", "host", "database", "debug", "return-found-rows",
|
"init-command", "host", "database", "debug", "return-found-rows",
|
||||||
"ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath",
|
"ssl-key" ,"ssl-cert" ,"ssl-ca" ,"ssl-capath",
|
||||||
"character-set-dir", "default-character-set", "interactive-timeout",
|
"character-set-dir", "default-character-set", "interactive-timeout",
|
||||||
"connect_timeout",
|
"connect_timeout", "replication-probe", "enable-reads-from-master",
|
||||||
|
"repl-parse-query",
|
||||||
NullS
|
NullS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -812,6 +813,15 @@ static void mysql_read_default_options(struct st_mysql_options *options,
|
||||||
case 19: /* Interactive-timeout */
|
case 19: /* Interactive-timeout */
|
||||||
options->client_flag|=CLIENT_INTERACTIVE;
|
options->client_flag|=CLIENT_INTERACTIVE;
|
||||||
break;
|
break;
|
||||||
|
case 21: /* replication probe */
|
||||||
|
options->rpl_probe = 1;
|
||||||
|
break;
|
||||||
|
case 22: /* enable-reads-from-master */
|
||||||
|
options->rpl_parse = 1;
|
||||||
|
break;
|
||||||
|
case 23: /* repl-parse-query */
|
||||||
|
options->no_master_reads = 0;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
DBUG_PRINT("warning",("unknown option: %s",option[0]));
|
DBUG_PRINT("warning",("unknown option: %s",option[0]));
|
||||||
}
|
}
|
||||||
|
@ -987,6 +997,175 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* perform query on master */
|
||||||
|
int STDCALL mysql_master_query(MYSQL *mysql, const char *q,
|
||||||
|
unsigned int length)
|
||||||
|
{
|
||||||
|
if(!length)
|
||||||
|
length = strlen(q);
|
||||||
|
return mysql_real_query(mysql->master, q, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* perform query on slave */
|
||||||
|
int STDCALL mysql_slave_query(MYSQL *mysql, const char *q,
|
||||||
|
unsigned int length)
|
||||||
|
{
|
||||||
|
MYSQL* last_used_slave, *slave_to_use = 0;
|
||||||
|
|
||||||
|
if((last_used_slave = mysql->last_used_slave))
|
||||||
|
slave_to_use = last_used_slave->next_slave;
|
||||||
|
else
|
||||||
|
slave_to_use = mysql->next_slave;
|
||||||
|
/* next_slave is always safe to use - we have a circular list of slaves
|
||||||
|
if there are no slaves, mysql->next_slave == mysql
|
||||||
|
*/
|
||||||
|
mysql->last_used_slave = slave_to_use;
|
||||||
|
if(!length)
|
||||||
|
length = strlen(q);
|
||||||
|
return mysql_real_query(slave_to_use, q, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable/disable parsing of all queries to decide
|
||||||
|
if they go on master or slave */
|
||||||
|
void STDCALL mysql_enable_rpl_parse(MYSQL* mysql)
|
||||||
|
{
|
||||||
|
mysql->options.rpl_parse = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void STDCALL mysql_disable_rpl_parse(MYSQL* mysql)
|
||||||
|
{
|
||||||
|
mysql->options.rpl_parse = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the value of the parse flag */
|
||||||
|
int STDCALL mysql_rpl_parse_enabled(MYSQL* mysql)
|
||||||
|
{
|
||||||
|
return mysql->options.rpl_parse;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* enable/disable reads from master */
|
||||||
|
void STDCALL mysql_enable_reads_from_master(MYSQL* mysql)
|
||||||
|
{
|
||||||
|
mysql->options.no_master_reads = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void STDCALL mysql_disable_reads_from_master(MYSQL* mysql)
|
||||||
|
{
|
||||||
|
mysql->options.no_master_reads = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the value of the master read flag */
|
||||||
|
int STDCALL mysql_reads_from_master_enabled(MYSQL* mysql)
|
||||||
|
{
|
||||||
|
return !(mysql->options.no_master_reads);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function assumes we have just called SHOW SLAVE STATUS and have
|
||||||
|
read the given result and row
|
||||||
|
*/
|
||||||
|
static inline int get_master(MYSQL* mysql, MYSQL_RES* res, MYSQL_ROW row)
|
||||||
|
{
|
||||||
|
MYSQL* master;
|
||||||
|
if(mysql_num_rows(res) < 3)
|
||||||
|
return 1; /* safety */
|
||||||
|
if(!(master = mysql_init(0)))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* use the same username and password as the original connection */
|
||||||
|
master->user = mysql->user;
|
||||||
|
master->passwd = mysql->passwd;
|
||||||
|
master->host = row[0];
|
||||||
|
master->port = atoi(row[2]);
|
||||||
|
master->db = mysql->db;
|
||||||
|
mysql->master = master;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int get_slaves_from_master(MYSQL* mysql)
|
||||||
|
{
|
||||||
|
if(!mysql->net.vio && !mysql_real_connect(mysql, mysql->host, mysql->user,
|
||||||
|
mysql->passwd, mysql->db, mysql->port,
|
||||||
|
mysql->unix_socket, mysql->client_flag))
|
||||||
|
return 1;
|
||||||
|
/* more to be written */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int STDCALL mysql_rpl_probe(MYSQL* mysql)
|
||||||
|
{
|
||||||
|
MYSQL_RES* res;
|
||||||
|
MYSQL_ROW row;
|
||||||
|
int error = 1;
|
||||||
|
/* first determine the replication role of the server we connected to
|
||||||
|
the most reliable way to do this is to run SHOW SLAVE STATUS and see
|
||||||
|
if we have a non-empty master host. This is still not fool-proof -
|
||||||
|
it is not a sin to have a master that has a dormant slave thread with
|
||||||
|
a non-empty master host. However, it is more reliable to check
|
||||||
|
for empty master than whether the slave thread is actually running
|
||||||
|
*/
|
||||||
|
if(mysql_query(mysql, "SHOW SLAVE STATUS") ||
|
||||||
|
!(res = mysql_store_result(mysql)))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if(!(row = mysql_fetch_row(res)))
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
/* check master host for emptiness/NULL */
|
||||||
|
if(row[0] && *(row[0]))
|
||||||
|
{
|
||||||
|
/* this is a slave, ask it for the master */
|
||||||
|
if(get_master(mysql, res, row) || get_slaves_from_master(mysql))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mysql->master = mysql;
|
||||||
|
if(get_slaves_from_master(mysql))
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = 0;
|
||||||
|
err:
|
||||||
|
mysql_free_result(res);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* make a not so fool-proof decision on where the query should go, to
|
||||||
|
the master or the slave. Ideally the user should always make this
|
||||||
|
decision himself with mysql_master_query() or mysql_slave_query().
|
||||||
|
However, to be able to more easily port the old code, we support the
|
||||||
|
option of an educated guess - this should work for most applications,
|
||||||
|
however, it may make the wrong decision in some particular cases. If
|
||||||
|
that happens, the user would have to change the code to call
|
||||||
|
mysql_master_query() or mysql_slave_query() explicitly in the place
|
||||||
|
where we have made the wrong decision
|
||||||
|
*/
|
||||||
|
int STDCALL mysql_query_goes_to_master(const char* q, int len)
|
||||||
|
{
|
||||||
|
const char* q_end;
|
||||||
|
q_end = (len) ? q + len : strend(q);
|
||||||
|
for(; q < q_end; ++q)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
if(isalpha(c=*q))
|
||||||
|
switch(tolower(c))
|
||||||
|
{
|
||||||
|
case 'i': /* insert */
|
||||||
|
case 'u': /* update or unlock tables */
|
||||||
|
case 'l': /* lock tables or load data infile */
|
||||||
|
case 'd': /* drop or delete */
|
||||||
|
case 'a': /* alter */
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
** Init MySQL structure or allocate one
|
** Init MySQL structure or allocate one
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
@ -1005,6 +1184,8 @@ mysql_init(MYSQL *mysql)
|
||||||
else
|
else
|
||||||
bzero((char*) (mysql),sizeof(*(mysql)));
|
bzero((char*) (mysql),sizeof(*(mysql)));
|
||||||
mysql->options.connect_timeout=CONNECT_TIMEOUT;
|
mysql->options.connect_timeout=CONNECT_TIMEOUT;
|
||||||
|
mysql->next_slave = mysql->master = mysql;
|
||||||
|
mysql->last_used_slave = 0;
|
||||||
#if defined(SIGPIPE) && defined(THREAD)
|
#if defined(SIGPIPE) && defined(THREAD)
|
||||||
if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE))
|
if (!((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE))
|
||||||
(void) signal(SIGPIPE,pipe_sig_handler);
|
(void) signal(SIGPIPE,pipe_sig_handler);
|
||||||
|
|
Loading…
Reference in a new issue