diff --git a/CMakeLists.txt b/CMakeLists.txt index d47c6f07b..69fcdd611 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,7 +127,7 @@ install(DIRECTORY DESTINATION log) # See if we are on a RPM-capable or DEB-capable system find_program(RPMBUILD rpmbuild) find_program(DEBBUILD dpkg-buildpackage) - +set(CPACK_GENERATOR "TGZ") if(NOT ( ${RPMBUILD} STREQUAL "RPMBUILD-NOTFOUND" ) ) message(STATUS "Generating RPM packages") set(CPACK_GENERATOR "${CPACK_GENERATOR};RPM") diff --git a/Documentation/Administration Tutorial.pdf b/Documentation/Administration Tutorial.pdf new file mode 100755 index 000000000..49afa4de8 Binary files /dev/null and b/Documentation/Administration Tutorial.pdf differ diff --git a/Documentation/Filter Tutorial.pdf b/Documentation/Filter Tutorial.pdf new file mode 100755 index 000000000..cd7c2fead Binary files /dev/null and b/Documentation/Filter Tutorial.pdf differ diff --git a/Documentation/Galera Cluster Connection Routing Tutorial.pdf b/Documentation/Galera Cluster Connection Routing Tutorial.pdf new file mode 100755 index 000000000..1c10725a0 Binary files /dev/null and b/Documentation/Galera Cluster Connection Routing Tutorial.pdf differ diff --git a/Documentation/Galera Cluster Read-Write Splitting Tutorial.pdf b/Documentation/Galera Cluster Read-Write Splitting Tutorial.pdf new file mode 100755 index 000000000..fbf2d2a5b Binary files /dev/null and b/Documentation/Galera Cluster Read-Write Splitting Tutorial.pdf differ diff --git a/Documentation/Getting Started With MaxScale.pdf b/Documentation/Getting Started With MaxScale.pdf new file mode 100755 index 000000000..1a1f2f572 Binary files /dev/null and b/Documentation/Getting Started With MaxScale.pdf differ diff --git a/Documentation/MaxAdmin The MaxScale Administration And Monitoring Client.pdf b/Documentation/MaxAdmin The MaxScale Administration And Monitoring Client.pdf old mode 100644 new mode 100755 index 9ccd75b42..0a23131e2 Binary files a/Documentation/MaxAdmin The MaxScale Administration And Monitoring Client.pdf and b/Documentation/MaxAdmin The MaxScale Administration And Monitoring Client.pdf differ diff --git a/Documentation/MaxScale Configuration And Usage Scenarios.pdf b/Documentation/MaxScale Configuration And Usage Scenarios.pdf index b297e427c..e5b649413 100644 Binary files a/Documentation/MaxScale Configuration And Usage Scenarios.pdf and b/Documentation/MaxScale Configuration And Usage Scenarios.pdf differ diff --git a/Documentation/MaxScale Debug And Diagnostic Support.pdf b/Documentation/MaxScale Debug And Diagnostic Support.pdf old mode 100644 new mode 100755 index 746a78583..7ea71f97b Binary files a/Documentation/MaxScale Debug And Diagnostic Support.pdf and b/Documentation/MaxScale Debug And Diagnostic Support.pdf differ diff --git a/Documentation/MaxScale HA with Corosync-Pacemaker.pdf b/Documentation/MaxScale HA with Corosync-Pacemaker.pdf new file mode 100755 index 000000000..786839186 Binary files /dev/null and b/Documentation/MaxScale HA with Corosync-Pacemaker.pdf differ diff --git a/Documentation/MaxScale Known Limitiations.pdf b/Documentation/MaxScale Known Limitiations.pdf new file mode 100755 index 000000000..a9155cbf6 Binary files /dev/null and b/Documentation/MaxScale Known Limitiations.pdf differ diff --git a/Documentation/MySQL Replication Connection Routing Tutorial.pdf b/Documentation/MySQL Replication Connection Routing Tutorial.pdf new file mode 100755 index 000000000..7658da9d7 Binary files /dev/null and b/Documentation/MySQL Replication Connection Routing Tutorial.pdf differ diff --git a/Documentation/MySQL Replication Read-Write Splitting Tutorial.pdf b/Documentation/MySQL Replication Read-Write Splitting Tutorial.pdf new file mode 100755 index 000000000..51afe9437 Binary files /dev/null and b/Documentation/MySQL Replication Read-Write Splitting Tutorial.pdf differ diff --git a/Documentation/filters/Regex Filter.pdf b/Documentation/filters/Regex Filter.pdf old mode 100644 new mode 100755 index f45fef043..7f6fe2726 Binary files a/Documentation/filters/Regex Filter.pdf and b/Documentation/filters/Regex Filter.pdf differ diff --git a/Documentation/filters/Tee Filter.pdf b/Documentation/filters/Tee Filter.pdf old mode 100644 new mode 100755 index f8de502ca..59d1e028c Binary files a/Documentation/filters/Tee Filter.pdf and b/Documentation/filters/Tee Filter.pdf differ diff --git a/Documentation/filters/Top Filter.pdf b/Documentation/filters/Top Filter.pdf old mode 100644 new mode 100755 index a7cb2061d..11f0bf924 Binary files a/Documentation/filters/Top Filter.pdf and b/Documentation/filters/Top Filter.pdf differ diff --git a/log_manager/CMakeLists.txt b/log_manager/CMakeLists.txt index 1cbe6cd87..fdef33f6c 100644 --- a/log_manager/CMakeLists.txt +++ b/log_manager/CMakeLists.txt @@ -1,3 +1,6 @@ +if(LOG_DEBUG) + add_definitions(-DSS_LOG_DEBUG) +endif() add_library(log_manager SHARED log_manager.cc) target_link_libraries(log_manager pthread aio stdc++) install(TARGETS log_manager DESTINATION lib) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 958eb10be..929df2b93 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -288,7 +288,8 @@ static char* add_slash(char* str); static bool check_file_and_path( char* filename, - bool* writable); + bool* writable, + bool do_log); static bool file_is_symlink(char* filename); static int skygw_log_disable_raw(logfile_id_t id, bool emergency); /*< no locking */ @@ -444,17 +445,6 @@ static bool logmanager_init_nomutex( return_succp: if (err != 0) { - if (lm != NULL) - { - if (lm->lm_clientmes != NULL) - { - skygw_message_done(lm->lm_clientmes); - } - if (lm->lm_logmes != NULL) - { - skygw_message_done(lm->lm_logmes); - } - } /** This releases memory of all created objects */ logmanager_done_nomutex(); fprintf(stderr, "*\n* Error : Initializing log manager failed.\n*\n"); @@ -732,10 +722,10 @@ static int logmanager_write_log( /** Length of session id */ int sesid_str_len; - /** 2 braces and 2 spaces */ + /** 2 braces, 2 spaces and terminating char */ if (id == LOGFILE_TRACE && tls_log_info.li_sesid != 0) { - sesid_str_len = 2+2+get_decimal_len(tls_log_info.li_sesid); + sesid_str_len = 2+2+get_decimal_len(tls_log_info.li_sesid)+1; } else { @@ -744,13 +734,13 @@ static int logmanager_write_log( timestamp_len = get_timestamp_len(); /** Find out how much can be safely written with current block size */ - if (timestamp_len-1+sesid_str_len+str_len > lf->lf_buf_size) + if (timestamp_len-1+MAX(sesid_str_len-1,0)+str_len > lf->lf_buf_size) { safe_str_len = lf->lf_buf_size; } else { - safe_str_len = timestamp_len-1+sesid_str_len+str_len; + safe_str_len = timestamp_len-1+MAX(sesid_str_len-1,0)+str_len; } /** * Seek write position and register to block buffer. @@ -812,6 +802,7 @@ static int logmanager_write_log( sesid_str_len, "[%lu] ", tls_log_info.li_sesid); + sesid_str_len -= 1; /*< don't calculate terminating char anymore */ } /** * Write next string to overwrite terminating null character @@ -1102,7 +1093,7 @@ static char* blockbuf_get_writepos( simple_mutex_unlock(&bb->bb_mutex); simple_mutex_lock(&bb_list->mlist_mutex, true); - + node = bb_list->mlist_first; } else { @@ -2055,7 +2046,7 @@ static bool logfile_create( * If file exists but is different type, create fails and * new, increased sequence number is added to file name. */ - if (check_file_and_path(lf->lf_full_file_name, &writable)) + if (check_file_and_path(lf->lf_full_file_name, &writable, true)) { /** Found similarly named file which isn't writable */ if (!writable || file_is_symlink(lf->lf_full_file_name)) @@ -2079,13 +2070,11 @@ static bool logfile_create( if (store_shmem) { - if (check_file_and_path(lf->lf_full_file_name, &writable)) + if (check_file_and_path(lf->lf_full_link_name, &writable, true)) { - /** Found similarly named file which isn't writable */ - if (!writable || - file_is_symlink(lf->lf_full_file_name)) + /** Found similarly named link which isn't writable */ + if (!writable) { - unlink(lf->lf_full_file_name); nameconflicts = true; } } @@ -2216,7 +2205,6 @@ return_succp: * * @return Pointer to filename, of NULL if failed. * - * */ static char* form_full_file_name( strpart_t* parts, @@ -2233,9 +2221,21 @@ static char* form_full_file_name( if (lf->lf_name_seqno != -1) { - lf->lf_name_seqno = find_last_seqno(parts, - lf->lf_name_seqno, - seqnoidx); + int file_sn; + int link_sn = 0; + char* tmp = parts[0].sp_string; + + file_sn = find_last_seqno(parts, lf->lf_name_seqno, seqnoidx); + + if (lf->lf_linkpath != NULL) + { + tmp = parts[0].sp_string; + parts[0].sp_string = lf->lf_linkpath; + link_sn = find_last_seqno(parts, lf->lf_name_seqno, seqnoidx); + parts[0].sp_string = tmp; + } + lf->lf_name_seqno = MAX(file_sn, link_sn); + seqno = lf->lf_name_seqno; s = UINTLEN(seqno); seqnostr = (char *)malloc((int)s+1); @@ -2356,7 +2356,8 @@ static char* add_slash( */ static bool check_file_and_path( char* filename, - bool* writable) + bool* writable, + bool do_log) { int fd; bool exists; @@ -2384,11 +2385,23 @@ static bool check_file_and_path( if (fd == -1) { - fprintf(stderr, - "*\n* Error : Can't access %s due " - "to %s.\n", - filename, - strerror(errno)); + if (do_log && file_is_symlink(filename)) + { + fprintf(stderr, + "*\n* Error : Can't access " + "file pointed to by %s due " + "to %s.\n", + filename, + strerror(errno)); + } + else if (do_log) + { + fprintf(stderr, + "*\n* Error : Can't access %s due " + "to %s.\n", + filename, + strerror(errno)); + } if (writable) { *writable = false; @@ -2405,11 +2418,24 @@ static bool check_file_and_path( } else { - fprintf(stderr, - "*\n* Error : Can't write to " - "%s due to %s.\n", - filename, - strerror(errno)); + if (do_log && + file_is_symlink(filename)) + { + fprintf(stderr, + "*\n* Error : Can't write to " + "file pointed to by %s due to " + "%s.\n", + filename, + strerror(errno)); + } + else if (do_log) + { + fprintf(stderr, + "*\n* Error : Can't write to " + "%s due to %s.\n", + filename, + strerror(errno)); + } *writable = false; } } @@ -2419,10 +2445,21 @@ static bool check_file_and_path( } else { - fprintf(stderr, - "*\n* Error : Can't access %s due to %s.\n", - filename, - strerror(errno)); + if (do_log && file_is_symlink(filename)) + { + fprintf(stderr, + "*\n* Error : Can't access the file " + "pointed to by %s due to %s.\n", + filename, + strerror(errno)); + } + else if (do_log) + { + fprintf(stderr, + "*\n* Error : Can't access %s due to %s.\n", + filename, + strerror(errno)); + } exists = false; if (writable) @@ -2574,7 +2611,7 @@ static bool logfile_init( logfile_free_memory(logfile); goto return_with_succp; } -#if defined(SS_DEBUG) + if (store_shmem) { fprintf(stderr, "%s\t: %s->%s\n", @@ -2588,7 +2625,6 @@ static bool logfile_init( STRLOGNAME(logfile_id), logfile->lf_full_file_name); } -#endif succp = true; logfile->lf_state = RUN; CHK_LOGFILE(logfile); @@ -3075,9 +3111,9 @@ static int find_last_seqno( { if (snstr != NULL && i == seqnoidx && strnlen(snstr,NAME_MAX) < NAME_MAX) { - strcat(filename, snstr); /*< add sequence number */ + strncat(filename, snstr, NAME_MAX - 1); /*< add sequence number */ } - strcat(filename, p->sp_string); + strncat(filename, p->sp_string, NAME_MAX - 1); if (p->sp_next == NULL) { @@ -3085,7 +3121,7 @@ static int find_last_seqno( } } - if (check_file_and_path(filename, NULL)) + if (check_file_and_path(filename, NULL, false)) { seqno++; } diff --git a/query_classifier/query_classifier.cc b/query_classifier/query_classifier.cc index 109269252..91342a224 100644 --- a/query_classifier/query_classifier.cc +++ b/query_classifier/query_classifier.cc @@ -362,50 +362,29 @@ static bool create_parse_tree( Parser_state parser_state; bool failp = FALSE; const char* virtual_db = "skygw_virtual"; -#if defined(SS_DEBUG_EXTRA) - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "[readwritesplit:create_parse_tree] 1."))); -#endif - if (parser_state.init(thd, thd->query(), thd->query_length())) { + + if (parser_state.init(thd, thd->query(), thd->query_length())) + { failp = TRUE; goto return_here; } -#if defined(SS_DEBUG_EXTRA) - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "[readwritesplit:create_parse_tree] 2."))); -#endif mysql_reset_thd_for_next_command(thd); -#if defined(SS_DEBUG_EXTRA) - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "[readwritesplit:create_parse_tree] 3."))); -#endif /** * Set some database to thd so that parsing won't fail because of * missing database. Then parse. */ failp = thd->set_db(virtual_db, strlen(virtual_db)); -#if defined(SS_DEBUG_EXTRA) - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "[readwritesplit:create_parse_tree] 4."))); -#endif - if (failp) { + if (failp) + { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Failed to set database in thread context."))); } failp = parse_sql(thd, &parser_state, NULL); -#if defined(SS_DEBUG_EXTRA) - LOGIF(LM, (skygw_log_write_flush( - LOGFILE_MESSAGE, - "[readwritesplit:create_parse_tree] 5."))); -#endif - if (failp) { + if (failp) + { LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [readwritesplit:create_parse_tree] failed to " @@ -417,16 +396,14 @@ return_here: } /** - * @node Set new query type if new is more restrictive than old. + * Set new query type if new is more restrictive than old. * * Parameters: - * @param qtype - - * + * @param qtype Existing type * - * @param new_type - - * + * @param new_type New query type * - * @return + * @return Query type as an unsigned int value which must be casted to qtype. * * * @details The implementation relies on that enumerated values correspond @@ -443,13 +420,11 @@ static u_int32_t set_query_type( } /** - * @node Detect query type, read-only, write, or session update + * Detect query type by examining parsed representation of it. * - * Parameters: - * @param thd - - * + * @param thd MariaDB thread context. * - * @return + * @return Copy of query type value. * * * @details Query type is deduced by checking for certain properties @@ -474,7 +449,7 @@ static skygw_query_type_t resolve_query_type( * all write operations to all nodes. */ #if defined(NOT_IN_USE) - bool force_data_modify_op_replication; + bool force_data_modify_op_replication; force_data_modify_op_replication = FALSE; #endif /* NOT_IN_USE */ ss_info_dassert(thd != NULL, ("thd is NULL\n")); @@ -619,6 +594,7 @@ static skygw_query_type_t resolve_query_type( break; case SQLCOM_SELECT: + case SQLCOM_SHOW_SLAVE_STAT: type |= QUERY_TYPE_READ; break; @@ -867,6 +843,11 @@ return_qtype: * Checks if statement causes implicit COMMIT. * autocommit_stmt gets values 1, 0 or -1 if stmt is enable, disable or * something else than autocommit. + * + * @param lex Parse tree + * @param autocommit_stmt memory address for autocommit status + * + * @return true if statement causes implicit commit and false otherwise */ static bool skygw_stmt_causes_implicit_commit( LEX* lex, @@ -896,7 +877,7 @@ static bool skygw_stmt_causes_implicit_commit( } else { - succp =false; + succp = false; } break; default: @@ -912,7 +893,9 @@ return_succp: * Finds out if stmt is SET autocommit * and if the new value matches with the enable_cmd argument. * - * Returns 1, 0, or -1 if command was: + * @param lex parse tree + * + * @return 1, 0, or -1 if command was: * enable, disable, or not autocommit, respectively. */ static int is_autocommit_stmt( @@ -983,9 +966,11 @@ char* skygw_query_classifier_get_stmtname( } /** - *Returns the LEX struct of the parsed GWBUF - *@param The parsed GWBUF - *@return Pointer to the LEX struct or NULL if an error occurred or the query was not parsed + * Get the parse tree from parsed querybuf. + * @param querybuf The parsed GWBUF + * + * @return Pointer to the LEX struct or NULL if an error occurred or the query + * was not parsed */ LEX* get_lex(GWBUF* querybuf) { @@ -1194,7 +1179,7 @@ bool is_drop_table_query(GWBUF* querybuf) lex->sql_command == SQLCOM_DROP_TABLE; } -/* +/** * Replace user-provided literals with question marks. Return a copy of the * querystr with replacements. * diff --git a/server/MaxScale_template.cnf b/server/MaxScale_template.cnf index 6c980b03b..a3f44ebf1 100644 --- a/server/MaxScale_template.cnf +++ b/server/MaxScale_template.cnf @@ -1,89 +1,212 @@ +## Example MaxScale.cnf configuration file # -# Example MaxScale.cnf configuration file +# Number of worker threads in MaxScale # -# -# -# Number of server threads -# Valid options are: # threads= - +# [maxscale] -threads=1 +threads=4 -# Define a monitor that can be used to determine the state and role of +## Define a monitor that can be used to determine the state and role of # the servers. # -# Valid options for all monitors are: +# Currently valid options for all monitors are: # -# module= -# servers=,,... -# user = -# passwd= -# monitor_interval= +# module=[mysqlmon|galeramon] +# +# List of server names which are being monitored +# +# servers=,,..., +# +# Username for monitor queries, need slave replication and slave client privileges +# Password in plain text format, and monitor's sampling interval in milliseconds. +# +# user= +# passwd= +# monitor_interval= (default 10000) +# +# Timeouts for monitor operations in backend servers - optional. +# +# backend_connect_timeout= +# backend_write_timeout= +# backend_read_timeout= +# +## MySQL monitor-specific options: +# +# Enable detection of replication slaves lag via replication_heartbeat +# table - optional. +# +# detect_replication_lag=[1|0] (default 0) +# +# Allow previous master to be available even in case of stopped or misconfigured +# replication - optional. +# +# detect_stale_master=[1|0] (default 0) +# +## Galera monitor-specific options: +# +# If disable_master_failback is not set, recovery of previously failed master +# causes mastership to be switched back to it. Enabling the option prevents it. +# +# disable_master_failback=[0|1] (default 0) +# +## Examples: [MySQL Monitor] type=monitor module=mysqlmon servers=server1,server2,server3 -user=maxuser -passwd=maxpwd -# -# options for mysql_monitor only -# -# detect_replication_lag= -# detect_stale_master= +user=myuser +passwd=mypwd +monitor_interval=10000 +#backend_connect_timeout= +#backend_read_timeout= +#backend_write_timeout= +#detect_replication_lag= +#detect_stale_master= -# A series of service definition +[Galera Monitor] +type=monitor +module=galeramon +servers=server1,server2,server3 +user=myuser +passwd=mypwd +monitor_interval=10000 +#disable_master_failback= + +## Filter definition # -# Valid options are: +# Type specifies the section # -# router= -# servers=,,... -# user= -# passwd= -# enable_root_user=<0 or 1, default is 0> -# version_string= -# -# use_sql_variables_in=[master|all] (default all) -# router_options=,,... -# where value=[master|slave|synced] +# type=filter +# +# Module specifies which module implements the filter function # -# Read/Write Split Router specific options are: +# module=[qlafilter|regexfilter|topfilter|teefilter] +# +# Options specify the log file for Query Log Filter +# +# options= +# +# Match and replace are used in regexfilter +# +# match=fetch +# replace=select +# +# Count and filebase are used with topfilter to specify how many top queries are +# listed and where. +# +# count= +# filebase= +# +# Match and service are used by tee filter to specify what queries should be +# duplicated and where the copy should be routed. +# +# match=insert.*HighScore.*values +# service=Cassandra +# +## Examples: + +[qla] +type=filter +module=qlafilter +options=/tmp/QueryLog + +[fetch] +type=filter +module=regexfilter +match=fetch +replace=select + + +## A series of service definition +# +# Name of router module, currently valid options are +# +# router=[readconnroute|readwritesplit|debugcli|CLI] +# +# List of server names for use of service - mandatory for readconnroute, +# readwritesplit, and debugcli +# +# servers=,,..., +# +# Username to fetch password information with and password in plaintext +# format - for readconnroute and readwritesplit +# +# user= +# passwd= +# +# flag for enabling the use of root user - for readconnroute and +# readwritesplite - optional. +# +# enable_root_user=[0|1] (default 0) +# +# Version string to be used in server handshake. Default value is that of +# MariaDB embedded library's - for readconnroute and readwritesplite - optional. +# +# version_string= +# +# Filters specify the filters through which the query is transferred and the +# order of their appearance on the list corresponds the order they are +# used. Values refer to names of filters configured in this file - for +# readconnroute and readwritesplit - optional. +# +# filters= +# +## Read Connection Router specific router options. +# +# router_options specify the role in which the selected server must be. +# +# router_options=[master|slave|synced] +# +## Read/Write Split Router specific options. +# +# use_sql_variables_in specifies where sql variable modifications are +# routed - optional. +# +# use_sql_variables_in=[master|all] (default all) +# +# router_options=slave_selection_criteria specifies the selection criteria for +# slaves both in new session creation and when route target is selected - optional. +# +# router_options= +# slave_selection_criteria=[LEAST_CURRENT_OPERATIONS|LEAST_BEHIND_MASTER] +# +# max_slave_connections specifies how many slaves a router session can +# connect to - optional. +# +# max_slave_connections= +# +# max_slave_replication_lag specifies how much a slave is allowed to be behind +# the master and still become chosen routing target - optional, requires that +# monitor has detect_replication_lag=1 . # -# max_slave_connections= # max_slave_replication_lag= -# router_options=slave_selection_criteria=[LEAST_CURRENT_OPERATIONS|LEAST_BEHIND_MASTER] -# +# # Valid router modules currently are: -# readwritesplit, readconnroute and debugcli +# readwritesplit, readconnroute, debugcli and CLI +# +## Examples: + +[Read Connection Router] +type=service +router=readconnroute +servers=server1,server2,server3 +user=myuser +passwd=mypwd +router_options=slave [RW Split Router] type=service router=readwritesplit servers=server1,server2,server3 -user=maxuser -passwd=maxpwd -use_sql_variables_in=all -max_slave_connections=50% -max_slave_replication_lag=30 -router_options=slave_selection_criteria=LEAST_BEHIND_MASTER - - -[Read Connection Router] -type=service -router=readconnroute -router_options=slave -servers=server1,server2,server3 -user=maxuser -passwd=maxpwd +user=myuser +passwd=mypwd +#use_sql_variables_in= +#max_slave_connections=100% +#max_slave_replication_lag=21 +#router_options=slave_selection_criteria= +#filters=fetch|qla [HTTPD Router] type=service @@ -94,15 +217,44 @@ servers=server1,server2,server3 type=service router=debugcli -# Listener definitions for the services +[CLI] +type=service +router=CLI + +## Listener definitions for the services # -# Valid options are: +# Type specifies section as listener one +# +# type=listener +# +# Service links the section to one of the service names used in this configuration +# +# service= +# +# Protocol is client protocol library name. +# +# protocol=[MySQLClient|telnetd|HTTPD|maxscaled] +# +# Port and address specify which port the service listens and the address limits +# listening to a specific network interface only. Address is optional. # -# service= -# protocol= # port= # address=
+# +# Socket is alternative for address. The specified socket path must be writable +# by the Unix user MaxScale runs as. +# # socket= +# +## Examples: + +[Read Connection Listener] +type=listener +service=Read Connection Router +protocol=MySQLClient +address=192.168.100.102 +port=4008 +#socket=/tmp/readconn.sock [RW Split Listener] type=listener @@ -111,19 +263,12 @@ protocol=MySQLClient port=4006 #socket=/tmp/rwsplit.sock -[Read Connection Listener] -type=listener -service=Read Connection Router -protocol=MySQLClient -port=4008 -#socket=/tmp/readconn.sock - [Debug Listener] type=listener service=Debug Interface protocol=telnetd -port=4442 #address=127.0.0.1 +port=4442 [HTTPD Listener] type=listener @@ -131,40 +276,51 @@ service=HTTPD Router protocol=HTTPD port=6444 -# Enable the maxadmin interface to MaxScale -# -# Listen on the default port of 6603 and restrict -# to connections from localhost only. -# Remove the address=localhost entry to enable -# maxadmin connections from any host - -[CLI] -type=service -router=cli - [CLI Listener] type=listener service=CLI protocol=maxscaled -address=localhost +#address=localhost port=6603 -# Definition of the servers +## Definition of the servers +# +# Type specifies the section as server one +# +# type=server +# +# The IP address or hostname of the machine running the database server that is +# being defined. MaxScale will use this address to connect to the backend +# database server. +# +# address= +# +# The port on which the database listens for incoming connections. MaxScale +# will use this port to connect to the database server. +# +# port= +# +# The name for the protocol module to use to connect MaxScale to the database. +# Currently the only backend protocol supported is the MySQLBackend module. +# +# protocol=MySQLBackend +# +## Examples: [server1] type=server -address=127.0.0.1 +address=192.168.100.101 port=3000 protocol=MySQLBackend [server2] type=server -address=127.0.0.1 -port=3001 +address=192.168.100.102 +port=3000 protocol=MySQLBackend [server3] type=server -address=127.0.0.1 -port=3002 +address=192.168.100.103 +port=3000 protocol=MySQLBackend diff --git a/server/core/config.c b/server/core/config.c index 737781d7f..61d7a6b98 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -590,11 +590,12 @@ int error_count = 0; } if (obj->element && options) { - char *s = strtok(options, ","); + char *lasts; + char *s = strtok_r(options, ",", &lasts); while (s) { filterAddOption(obj->element, s); - s = strtok(NULL, ","); + s = strtok_r(NULL, ",", &lasts); } } if (obj->element) @@ -640,7 +641,8 @@ int error_count = 0; router = config_get_value(obj->parameters, "router"); if (servers && obj->element) { - char *s = strtok(servers, ","); + char *lasts; + char *s = strtok_r(servers, ",", &lasts); while (s) { CONFIG_CONTEXT *obj1 = context; @@ -667,7 +669,7 @@ int error_count = 0; "service '%s'.", s, obj->object))); } - s = strtok(NULL, ","); + s = strtok_r(NULL, ",", &lasts); } } else if (servers == NULL && internalService(router) == 0) @@ -681,11 +683,12 @@ int error_count = 0; } if (roptions && obj->element) { - char *s = strtok(roptions, ","); + char *lasts; + char *s = strtok_r(roptions, ",", &lasts); while (s) { serviceAddRouterOption(obj->element, s); - s = strtok(NULL, ","); + s = strtok_r(NULL, ",", &lasts); } } if (filters && obj->element) @@ -818,7 +821,7 @@ int error_count = 0; obj->element = monitor_alloc(obj->object, module); if (servers && obj->element) { - char *s; + char *s, *lasts; /* if id is not set, compute it now with pid only */ if (gateway.id == 0) { @@ -853,7 +856,7 @@ int error_count = 0; monitorSetNetworkTimeout(obj->element, MONITOR_WRITE_TIMEOUT, write_timeout); /* get the servers to monitor */ - s = strtok(servers, ","); + s = strtok_r(servers, ",", &lasts); while (s) { CONFIG_CONTEXT *obj1 = context; @@ -880,7 +883,7 @@ int error_count = 0; "monitor '%s'.", s, obj->object))); - s = strtok(NULL, ","); + s = strtok_r(NULL, ",", &lasts); } } if (obj->element && user && passwd) @@ -1529,7 +1532,8 @@ SERVER *server; filters = config_get_value(obj->parameters, "filters"); if (servers && obj->element) { - char *s = strtok(servers, ","); + char *lasts; + char *s = strtok_r(servers, ",", &lasts); while (s) { CONFIG_CONTEXT *obj1 = context; @@ -1559,17 +1563,18 @@ SERVER *server; "service '%s'.", s, obj->object))); } - s = strtok(NULL, ","); + s = strtok_r(NULL, ",", &lasts); } } if (roptions && obj->element) { - char *s = strtok(roptions, ","); + char *lasts; + char *s = strtok_r(roptions, ",", &lasts); serviceClearRouterOptions(obj->element); while (s) { serviceAddRouterOption(obj->element, s); - s = strtok(NULL, ","); + s = strtok_r(NULL, ",", &lasts); } } if (filters && obj->element) @@ -1667,17 +1672,6 @@ static char *service_params[] = NULL }; -static char *server_params[] = - { - "type", - "address", - "port", - "protocol", - "monitorpw", - "monitoruser", - NULL - }; - static char *listener_params[] = { "type", diff --git a/server/core/dbusers.c b/server/core/dbusers.c index d1d4968e8..17f5ecbd6 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -32,11 +32,13 @@ * x.y.z.%, x.y.%.%, x.%.%.% * 03/10/14 Massimiliano Pinto Added netmask to user@host authentication for wildcard in IPv4 hosts * 13/10/14 Massimiliano Pinto Added (user@host)@db authentication + * 04/12/14 Massimiliano Pinto Added support for IPv$ wildcard hosts: a.%, a.%.% and a.b.% * * @endverbatim */ #include +#include #include #include @@ -82,6 +84,7 @@ void resource_free(HASHTABLE *resource); void *resource_fetch(HASHTABLE *, char *); int resource_add(HASHTABLE *, char *, char *); int resource_hash(char *); +static int normalize_hostname(char *input_host, char *output_host); /** * Load the user/passwd form mysql.user table into the service users' hashtable @@ -217,8 +220,6 @@ int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *p struct sockaddr_in serv_addr; MYSQL_USER_HOST key; char ret_ip[INET_ADDRSTRLEN + 1]=""; - int found_range=0; - int found_any=0; int ret = 0; if (users == NULL || user == NULL || host == NULL) { @@ -255,42 +256,30 @@ int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *p /* ANY */ if (strcmp(host, "%") == 0) { strcpy(ret_ip, "0.0.0.0"); - found_any = 1; + key.netmask = 0; } else { - char *tmp; - strncpy(ret_ip, host, INET_ADDRSTRLEN); - tmp = ret_ip+strlen(ret_ip)-1; + /* hostname without % wildcards has netmask = 32 */ + key.netmask = normalize_hostname(host, ret_ip); - /* start from Class C */ - - while(tmp > ret_ip) { - if (*tmp == '%') { - /* set only the last IPv4 byte to 1 - * avoiding setipadress() failure - * for Class C address - */ - found_range++; - if (found_range == 1) - *tmp = '1'; - else - *tmp = '0'; - } - tmp--; + if (key.netmask == -1) { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : strdup() failed in normalize_hostname for %s@%s", + user, + host))); } } /* fill IPv4 data struct */ - if (setipaddress(&serv_addr.sin_addr, ret_ip)) { + if (setipaddress(&serv_addr.sin_addr, ret_ip) && strlen(ret_ip)) { /* copy IPv4 data into key.ipv4 */ memcpy(&key.ipv4, &serv_addr, sizeof(serv_addr)); - if (found_range) { - /* let's zero the last IP byte: a.b.c.0 we set above to 1*/ + /* if netmask < 32 there are % wildcards */ + if (key.netmask < 32) { + /* let's zero the last IP byte: a.b.c.0 we may have set above to 1*/ key.ipv4.sin_addr.s_addr &= 0x00FFFFFF; - key.netmask = 32 - (found_range * 8); - } else { - key.netmask = 32 - (found_any * 32); } /* add user@host as key and passwd as value in the MySQL users hash table */ @@ -362,10 +351,11 @@ getDatabases(SERVICE *service, MYSQL *con) LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Warning: Loading DB names for service [%s] returned 0 rows." - " SHOW DATABASES grant to user [%s] is required for MaxScale DB Name Authentication", - service->name, - service_user))); + "%s: Unable to load database grant information, MaxScale " + "authentication will proceed without including database " + "permissions. To correct this GRANT select permission " + "on msql.db to the user %s.", + service->name, service_user))); } /* free resut set */ @@ -622,7 +612,11 @@ getUsers(SERVICE *service, USERS *users) LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Error: Loading DB grants failed: GRANT is required on [mysql.db] to user [%s]. Try loading DB users for service [%s] without DB name MaxScale Authentication", service_user, service->name))); + "%s: Unable to load database grant information, MaxScale " + "authentication will proceed without including database " + "permissions. To correct this GRANT select permission " + "on msql.db to the user %s.", + service->name, service_user))); /* check for root user select */ if(service->enable_root) { @@ -649,8 +643,9 @@ getUsers(SERVICE *service, USERS *users) LOGIF(LM, (skygw_log_write_flush( LOGFILE_MESSAGE, - "Loading users from [mysql.user] without DB grants from [mysql.db] for service [%s]." - " MaxScale Authentication with DBname on connect will not work", + "Loading users from [mysql.user] without access to [mysql.db] for " + "service [%s]. MaxScale Authentication with DBname on connect " + "will not consider database grants.", service->name))); } } else { @@ -715,7 +710,23 @@ getUsers(SERVICE *service, USERS *users) int rc = 0; char *password = NULL; + if (row[2] != NULL) { + /* detect mysql_old_password (pre 4.1 protocol) */ + if (strlen(row[2]) == 16) { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "%s: The user %s@%s has on old password in the " + "backend database. MaxScale does not support these " + "old passwords. This user will not be able to connect " + "via MaxScale. Update the users password to correct " + "this.", + service->name, + row[0], + row[1]))); + continue; + } + if (strlen(row[2]) > 1) password = row[2] +1; else @@ -752,19 +763,20 @@ getUsers(SERVICE *service, USERS *users) /* Log the user being added with its db grants */ LOGIF(LD, (skygw_log_write_flush( LOGFILE_DEBUG, - "Added user %s@%s with DB grants on [%s], for service [%s]", + "%s: User %s@%s for database %s added to " + "service user table.", + service->name, row[0], row[1], - dbgrant, - service->name))); + dbgrant))); } else { /* Log the user being added (without db grants) */ LOGIF(LD, (skygw_log_write_flush( LOGFILE_DEBUG, - "Added user %s@%s for service [%s]", + "%s: User %s@%s added to service user table.", + service->name, row[0], - row[1], - service->name))); + row[1]))); } /* Append data in the memory area for SHA1 digest */ @@ -774,7 +786,8 @@ getUsers(SERVICE *service, USERS *users) } else { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, - "Warning: Failed adding user %s@%s for service [%s]", + "Warning: Failed to add user %s@%s for service [%s]. " + "This user will be unavailable via MaxScale.", row[0], row[1], service->name))); @@ -1096,3 +1109,87 @@ resource_fetch(HASHTABLE *resources, char *key) return hashtable_fetch(resources, key); } +/** + * Normalize hostname with % wildcards to a valid IP string. + * + * Valid input values: + * a.b.c.d, a.b.c.%, a.b.%.%, a.%.%.% + * Short formats a.% and a.%.% are both converted to a.%.%.% + * Short format a.b.% is converted to a.b.%.% + * + * Last host byte is set to 1, avoiding setipadress() failure + * + * @param input_host The hostname with possible % wildcards + * @param output_host The normalized hostname (buffer must be preallocated) + * @return The calculated netmask or -1 on failure + */ +static int normalize_hostname(char *input_host, char *output_host) +{ +int netmask, bytes, bits = 0, found_wildcard = 0; +char *p, *lasts, *tmp; +int useorig = 0; + + output_host[0] = 0; + bytes = 0; + + tmp = strdup(input_host); + + if (tmp == NULL) { + return -1; + } + + p = strtok_r(tmp, ".", &lasts); + while (p != NULL) + { + + if (strcmp(p, "%")) + { + if (! isdigit(*p)) + useorig = 1; + + strcat(output_host, p); + bits += 8; + } + else if (bytes == 3) + { + found_wildcard = 1; + strcat(output_host, "1"); + } + else + { + found_wildcard = 1; + strcat(output_host, "0"); + } + bytes++; + p = strtok_r(NULL, ".", &lasts); + if (p) + strcat(output_host, "."); + } + if (found_wildcard) + { + netmask = bits; + while (bytes++ < 4) + { + if (bytes == 4) + { + strcat(output_host, ".1"); + } + else + { + strcat(output_host, ".0"); + } + } + } + else + netmask = 32; + + if (useorig == 1) + { + netmask = 32; + strcpy(output_host, input_host); + } + + free(tmp); + + return netmask; +} diff --git a/server/core/filter.c b/server/core/filter.c index 975386f2b..c697a264e 100644 --- a/server/core/filter.c +++ b/server/core/filter.c @@ -332,8 +332,7 @@ DOWNSTREAM *me; if ((filter->obj = load_module(filter->module, MODULE_FILTER)) == NULL) { - me = NULL; - goto retblock; + return NULL; } } @@ -342,8 +341,7 @@ DOWNSTREAM *me; if ((filter->filter = (filter->obj->createInstance)(filter->options, filter->parameters)) == NULL) { - me = NULL; - goto retblock; + return NULL; } } if ((me = (DOWNSTREAM *)calloc(1, sizeof(DOWNSTREAM))) == NULL) @@ -355,7 +353,7 @@ DOWNSTREAM *me; errno, strerror(errno)))); - goto retblock; + return NULL; } me->instance = filter->filter; me->routeQuery = (void *)(filter->obj->routeQuery); @@ -363,12 +361,10 @@ DOWNSTREAM *me; if ((me->session=filter->obj->newSession(me->instance, session)) == NULL) { free(me); - me = NULL; - goto retblock; + return NULL; } filter->obj->setDownstream(me->instance, me->session, downstream); -retblock: return me; } diff --git a/server/core/gateway.c b/server/core/gateway.c index 183896df4..919ca7a66 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -641,55 +641,63 @@ static bool resolve_maxscale_homedir( } check_home_dir: + if (*p_home_dir != NULL) + { + if (!file_is_readable(*p_home_dir)) + { + char* tailstr = "MaxScale doesn't have read permission " + "to MAXSCALE_HOME."; + char* logstr = (char*)malloc(strlen(log_context)+ + 1+ + strlen(tailstr)+ + 1); + snprintf(logstr, + strlen(log_context)+ + 1+ + strlen(tailstr)+1, + "%s:%s", + log_context, + tailstr); + print_log_n_stderr(true, true, logstr, logstr, 0); + free(logstr); + goto return_succp; + } + +#if WRITABLE_HOME + if (!file_is_writable(*p_home_dir)) + { + char* tailstr = "MaxScale doesn't have write permission " + "to MAXSCALE_HOME. Exiting."; + char* logstr = (char*)malloc(strlen(log_context)+ + 1+ + strlen(tailstr)+ + 1); + snprintf(logstr, + strlen(log_context)+ + 1+ + strlen(tailstr)+1, + "%s:%s", + log_context, + tailstr); + print_log_n_stderr(true, true, logstr, logstr, 0); + free(logstr); + goto return_succp; + } +#endif + if (!daemon_mode) + { + fprintf(stderr, + "Using %s as MAXSCALE_HOME = %s\n", + log_context, + tmp); + } + succp = true; + goto return_succp; + } + +return_succp: + free (tmp); - if (*p_home_dir != NULL) - { - char* errstr; - - errstr = check_dir_access(*p_home_dir); - - if (errstr != NULL) - { - char* logstr = (char*)malloc(strlen(log_context)+ - 1+ - strlen(errstr)+ - 1); - - snprintf(logstr, - strlen(log_context)+ - 1+ - strlen(errstr)+1, - "%s: %s", - log_context, - errstr); - - print_log_n_stderr(true, true, logstr, logstr, 0); - - free(errstr); - free(logstr); - succp = false; - } - else - { - succp = true; - - if (!daemon_mode) - { - fprintf(stderr, - "Using %s as MAXSCALE_HOME = %s\n", - log_context, - (tmp == NULL ? *p_home_dir : tmp)); - } - } - } - else - { - succp = false; - } - if (tmp != NULL) - { - free(tmp); - } if (log_context != NULL) { diff --git a/server/core/modutil.c b/server/core/modutil.c index b9a7aab1c..21978a740 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -293,7 +293,7 @@ GWBUF *modutil_create_mysql_err_msg( const char *msg) { uint8_t *outbuf = NULL; - uint8_t mysql_payload_size = 0; + uint32_t mysql_payload_size = 0; uint8_t mysql_packet_header[4]; uint8_t *mysql_payload = NULL; uint8_t field_count = 0; diff --git a/server/core/server.c b/server/core/server.c index e4cb0e114..61a3bdb3d 100644 --- a/server/core/server.c +++ b/server/core/server.c @@ -344,8 +344,10 @@ SERVER_PARAM *param; } } if (server->node_ts > 0) { + struct tm result; + char buf[40]; dcb_printf(dcb, "\tLast Repl Heartbeat:\t%s", - asctime(localtime(&server->node_ts))); + asctime_r(localtime_r((time_t *)(&server->node_ts), &result), buf)); } if ((param = server->parameters) != NULL) { diff --git a/server/core/service.c b/server/core/service.c index 1263c0ee1..4ef101085 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -809,13 +809,16 @@ SERVICE *service; void printService(SERVICE *service) { -SERVER *ptr = service->databases; -int i; +SERVER *ptr = service->databases; +struct tm result; +char time_buf[30]; +int i; printf("Service %p\n", service); printf("\tService: %s\n", service->name); printf("\tRouter: %s (%p)\n", service->routerModule, service->router); - printf("\tStarted: %s", asctime(localtime(&service->stats.started))); + printf("\tStarted: %s", + asctime_r(localtime_r(&service->stats.started, &result), time_buf)); printf("\tBackend databases\n"); while (ptr) { @@ -887,8 +890,10 @@ SERVICE *ptr; */ void dprintService(DCB *dcb, SERVICE *service) { -SERVER *server = service->databases; -int i; +SERVER *server = service->databases; +struct tm result; +char timebuf[30]; +int i; dcb_printf(dcb, "Service %p\n", service); dcb_printf(dcb, "\tService: %s\n", @@ -898,7 +903,7 @@ int i; if (service->router) service->router->diagnostics(service->router_instance, dcb); dcb_printf(dcb, "\tStarted: %s", - asctime(localtime(&service->stats.started))); + asctime_r(localtime_r(&service->stats.started, &result), timebuf)); dcb_printf(dcb, "\tRoot user access: %s\n", service->enable_root ? "Enabled" : "Disabled"); if (service->n_filters) @@ -1232,19 +1237,18 @@ bool service_set_param_value ( /* * Function to find a string in typelib_t * (similar to find_type() of mysys/typelib.c) - * - * SYNOPSIS - * find_type() - * lib typelib_t - * find String to find - * length Length of string to find - * part_match Allow part matching of value - * - * RETURN - * 0 error - * > 0 position in TYPELIB->type_names +1 + * + * SYNOPSIS + * find_type() + * lib typelib_t + * find String to find + * length Length of string to find + * part_match Allow part matching of value + * + * RETURN + * 0 error + * > 0 position in TYPELIB->type_names +1 */ - static int find_type( typelib_t* tl, const char* needle, diff --git a/server/core/session.c b/server/core/session.c index 3369e05bd..031780012 100644 --- a/server/core/session.c +++ b/server/core/session.c @@ -465,11 +465,15 @@ int rval = 0; void printSession(SESSION *session) { +struct tm result; +char timebuf[40]; + printf("Session %p\n", session); printf("\tState: %s\n", session_state(session->state)); printf("\tService: %s (%p)\n", session->service->name, session->service); printf("\tClient DCB: %p\n", session->client); - printf("\tConnected: %s", asctime(localtime(&session->stats.connect))); + printf("\tConnected: %s", + asctime_r(localtime_r(&session->stats.connect, &result), timebuf)); } /** @@ -566,7 +570,9 @@ int norouter = 0; void dprintAllSessions(DCB *dcb) { -SESSION *ptr; +struct tm result; +char timebuf[40]; +SESSION *ptr; spinlock_acquire(&session_spin); ptr = allSessions; @@ -578,7 +584,8 @@ SESSION *ptr; dcb_printf(dcb, "\tClient DCB: %p\n", ptr->client); if (ptr->client && ptr->client->remote) dcb_printf(dcb, "\tClient Address: %s\n", ptr->client->remote); - dcb_printf(dcb, "\tConnected: %s", asctime(localtime(&ptr->stats.connect))); + dcb_printf(dcb, "\tConnected: %s", + asctime_r(localtime_r(&ptr->stats.connect, &result), timebuf)); ptr = ptr->next; } spinlock_release(&session_spin); @@ -596,7 +603,9 @@ SESSION *ptr; void dprintSession(DCB *dcb, SESSION *ptr) { -int i; +struct tm result; +char buf[30]; +int i; dcb_printf(dcb, "Session %d (%p)\n",ptr->ses_id, ptr); dcb_printf(dcb, "\tState: %s\n", session_state(ptr->state)); @@ -604,7 +613,8 @@ int i; dcb_printf(dcb, "\tClient DCB: %p\n", ptr->client); if (ptr->client && ptr->client->remote) dcb_printf(dcb, "\tClient Address: %s\n", ptr->client->remote); - dcb_printf(dcb, "\tConnected: %s", asctime(localtime(&ptr->stats.connect))); + dcb_printf(dcb, "\tConnected: %s", + asctime_r(localtime_r(&ptr->stats.connect, &result), buf)); if (ptr->n_filters) { for (i = 0; i < ptr->n_filters; i++) diff --git a/server/core/test/test_mysql_users.c b/server/core/test/test_mysql_users.c index 090d0d3c0..ccf52dbf3 100644 --- a/server/core/test/test_mysql_users.c +++ b/server/core/test/test_mysql_users.c @@ -231,9 +231,9 @@ int set_and_get_mysql_users_wildcards(char *username, char *hostname, char *pass service->users = mysql_users; if (db_from != NULL) - strcpy(data->db, db_from); + strncpy(data->db, db_from,MYSQL_DATABASE_MAXLEN); else - strcpy(data->db, ""); + strncpy(data->db, "",MYSQL_DATABASE_MAXLEN); /* freed by dcb_free(dcb) */ dcb->data = data; @@ -392,6 +392,22 @@ int main() { if (!ret) fprintf(stderr, "\t-- Expecting ok\n"); assert(ret == 0); + ret = set_and_get_mysql_users_wildcards("pippo", "192.%", "foo", "192.254.254.242", NULL, NULL, NULL); + if (!ret) fprintf(stderr, "\t-- Expecting ok\n"); + assert(ret == 0); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.%.%", "foo", "192.254.254.242", NULL, NULL, NULL); + if (!ret) fprintf(stderr, "\t-- Expecting ok\n"); + assert(ret == 0); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.254.%", "foo", "192.254.254.242", NULL, NULL, NULL); + if (!ret) fprintf(stderr, "\t-- Expecting ok\n"); + assert(ret == 0); + + ret = set_and_get_mysql_users_wildcards("pippo", "192.254.%", "foo", "192.254.0.242", NULL, NULL, NULL); + if (!ret) fprintf(stderr, "\t-- Expecting ok\n"); + assert(ret == 0); + ret = set_and_get_mysql_users_wildcards("riccio", "192.0.0.%", "foo", "192.134.0.2", NULL, NULL, NULL); if (ret) fprintf(stderr, "\t-- Expecting no match\n"); assert(ret == 1); diff --git a/server/modules/filter/mqfilter.c b/server/modules/filter/mqfilter.c index eb69a053f..f88297e8f 100644 --- a/server/modules/filter/mqfilter.c +++ b/server/modules/filter/mqfilter.c @@ -423,8 +423,8 @@ init_conn(MQ_INSTANCE *my_instance) */ char** parse_optstr(char* str, char* tok, int* szstore) { - char* tk = str; - char** arr; + char *lasts, *tk = str; + char **arr; int i = 0, size = 1; while((tk = strpbrk(tk + 1,tok))){ size++; @@ -440,10 +440,10 @@ char** parse_optstr(char* str, char* tok, int* szstore) } *szstore = size; - tk = strtok(str,tok); + tk = strtok_r(str,tok, &lasts); while(tk && i < size){ arr[i++] = strdup(tk); - tk = strtok(NULL,tok); + tk = strtok_r(NULL,tok,&lasts); } return arr; } @@ -1052,7 +1052,8 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) for(z = 0;zshm_trg->size; i++){ if(strcmp(tmp,my_instance->shm_trg->objects[i]) == 0){ @@ -1103,8 +1104,9 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue) char* tbnm = NULL; if((strchr(sesstbls[j],'.')) != NULL){ - tbnm = strtok(sesstbls[j],"."); - tbnm = strtok(NULL,"."); + char *lasts; + tbnm = strtok_r(sesstbls[j],".",&lasts); + tbnm = strtok_r(NULL,".",&lasts); }else{ tbnm = sesstbls[j]; } diff --git a/server/modules/filter/qlafilter.c b/server/modules/filter/qlafilter.c index 301f8d3e5..74c176e02 100644 --- a/server/modules/filter/qlafilter.c +++ b/server/modules/filter/qlafilter.c @@ -58,7 +58,7 @@ extern __thread log_info_t tls_log_info; MODULE_INFO info = { MODULE_API_FILTER, - MODULE_BETA_RELEASE, + MODULE_GA, FILTER_VERSION, "A simple query logging filter" }; diff --git a/server/modules/filter/regexfilter.c b/server/modules/filter/regexfilter.c index a280c79b3..b2a8784e4 100644 --- a/server/modules/filter/regexfilter.c +++ b/server/modules/filter/regexfilter.c @@ -48,7 +48,7 @@ extern __thread log_info_t tls_log_info; MODULE_INFO info = { MODULE_API_FILTER, - MODULE_BETA_RELEASE, + MODULE_GA, FILTER_VERSION, "A query rewrite filter that uses regular expressions to rewite queries" }; diff --git a/server/modules/filter/tee.c b/server/modules/filter/tee.c index ff34e97b4..809c8c441 100644 --- a/server/modules/filter/tee.c +++ b/server/modules/filter/tee.c @@ -65,7 +65,7 @@ extern __thread log_info_t tls_log_info; MODULE_INFO info = { MODULE_API_FILTER, - MODULE_BETA_RELEASE, + MODULE_GA, FILTER_VERSION, "A tee piece in the filter plumbing" }; diff --git a/server/modules/filter/topfilter.c b/server/modules/filter/topfilter.c index 0afcb836c..71a6d86ca 100644 --- a/server/modules/filter/topfilter.c +++ b/server/modules/filter/topfilter.c @@ -55,7 +55,7 @@ extern __thread log_info_t tls_log_info; MODULE_INFO info = { MODULE_API_FILTER, - MODULE_BETA_RELEASE, + MODULE_GA, FILTER_VERSION, "A top N query logging filter" }; diff --git a/server/modules/include/blr.h b/server/modules/include/blr.h index 282bcdcdf..2225ff655 100644 --- a/server/modules/include/blr.h +++ b/server/modules/include/blr.h @@ -41,6 +41,8 @@ #define BINLOG_NAMEFMT "%s.%06d" #define BINLOG_NAME_ROOT "mysql-bin" +#define BINLOG_EVENT_HDR_LEN 19 + /* How often to call the binlog status function (seconds) */ #define BLR_STATS_FREQ 60 #define BLR_NSTATS_MINUTES 30 @@ -64,9 +66,9 @@ * BLR_MASTER_BACKOFF_TIME The increments of the back off time (seconds) * BLR_MAX_BACKOFF Maximum number of increments to backoff to */ - -#define BLR_MASTER_BACKOFF_TIME 5 +#define BLR_MASTER_BACKOFF_TIME 10 #define BLR_MAX_BACKOFF 60 + /** * Some useful macros for examining the MySQL Response packets */ @@ -128,6 +130,7 @@ typedef struct blfile { */ typedef struct { int n_events; /*< Number of events sent */ + unsigned long n_bytes; /*< Number of bytes sent */ int n_bursts; /*< Number of bursts sent */ int n_requests; /*< Number of requests received */ int n_flows; /*< Number of flow control restarts */ @@ -138,6 +141,7 @@ typedef struct { int n_above; int n_failed_read; int n_overrun; + int n_caughtup; int n_actions[3]; uint64_t lastsample; int minno; @@ -175,6 +179,7 @@ typedef struct router_slave { *router; /*< Pointer to the owning router */ struct router_slave *next; SLAVE_STATS stats; /*< Slave statistics */ + time_t connect_time; /*< Connect time of slave */ #if defined(SS_DEBUG) skygw_chk_t rses_chk_tail; #endif @@ -188,6 +193,7 @@ typedef struct { int n_slaves; /*< Number slave sessions created */ int n_reads; /*< Number of record reads */ uint64_t n_binlogs; /*< Number of binlog records from master */ + uint64_t n_binlogs_ses; /*< Number of binlog records from master */ uint64_t n_binlog_errors;/*< Number of binlog records from master */ uint64_t n_rotates; /*< Number of binlog rotate events */ uint64_t n_cachehits; /*< Number of hits on the binlog cache */ @@ -265,10 +271,12 @@ typedef struct router_instance { unsigned int short_burst; /*< Short burst for slave catchup */ unsigned int long_burst; /*< Long burst for slave catchup */ unsigned long burst_size; /*< Maximum size of burst to send */ + unsigned long heartbeat; /*< Configured heartbeat value */ ROUTER_STATS stats; /*< Statistics for this router */ int active_logs; int reconnect_pending; int retry_backoff; + time_t connect_time; int handling_threads; struct router_instance *next; @@ -278,25 +286,26 @@ typedef struct router_instance { * State machine for the master to MaxScale replication */ #define BLRM_UNCONNECTED 0x0000 -#define BLRM_AUTHENTICATED 0x0001 -#define BLRM_TIMESTAMP 0x0002 -#define BLRM_SERVERID 0x0003 -#define BLRM_HBPERIOD 0x0004 -#define BLRM_CHKSUM1 0x0005 -#define BLRM_CHKSUM2 0x0006 -#define BLRM_GTIDMODE 0x0007 -#define BLRM_MUUID 0x0008 -#define BLRM_SUUID 0x0009 -#define BLRM_LATIN1 0x000A -#define BLRM_UTF8 0x000B -#define BLRM_SELECT1 0x000C -#define BLRM_SELECTVER 0x000D -#define BLRM_REGISTER 0x000E -#define BLRM_BINLOGDUMP 0x000F +#define BLRM_CONNECTING 0x0001 +#define BLRM_AUTHENTICATED 0x0002 +#define BLRM_TIMESTAMP 0x0003 +#define BLRM_SERVERID 0x0004 +#define BLRM_HBPERIOD 0x0005 +#define BLRM_CHKSUM1 0x0006 +#define BLRM_CHKSUM2 0x0007 +#define BLRM_GTIDMODE 0x0008 +#define BLRM_MUUID 0x0009 +#define BLRM_SUUID 0x000A +#define BLRM_LATIN1 0x000B +#define BLRM_UTF8 0x000C +#define BLRM_SELECT1 0x000D +#define BLRM_SELECTVER 0x000E +#define BLRM_REGISTER 0x000F +#define BLRM_BINLOGDUMP 0x0010 -#define BLRM_MAXSTATE 0x000F +#define BLRM_MAXSTATE 0x0010 -static char *blrm_states[] = { "Unconnected", "Authenticated", "Timestamp retrieval", +static char *blrm_states[] = { "Unconnected", "Connecting", "Authenticated", "Timestamp retrieval", "Server ID retrieval", "HeartBeat Period setup", "binlog checksum config", "binlog checksum rerieval", "GTID Mode retrieval", "Master UUID retrieval", "Set Slave UUID", "Set Names latin1", "Set Names utf8", "select 1", @@ -371,6 +380,8 @@ static char *blrs_states[] = { "Created", "Unregistered", "Registered", #define ANONYMOUS_GTID_EVENT 0x22 #define PREVIOUS_GTIDS_EVENT 0x23 +#define MAX_EVENT_TYPE 0x23 + /** * Binlog event flags */ diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index f15c9e62b..b891bd7ba 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -95,6 +95,7 @@ typedef enum { MYSQL_AUTH_SENT, MYSQL_AUTH_RECV, MYSQL_AUTH_FAILED, + MYSQL_HANDSHAKE_FAILED, MYSQL_IDLE } mysql_auth_state_t; @@ -251,7 +252,7 @@ typedef enum mysql_server_cmd { typedef struct server_command_st { mysql_server_cmd_t scom_cmd; int scom_nresponse_packets; /*< packets in response */ - size_t scom_nbytes_to_read; /*< bytes left to read in current packet */ + ssize_t scom_nbytes_to_read; /*< bytes left to read in current packet */ struct server_command_st* scom_next; } server_command_t; @@ -387,8 +388,8 @@ void protocol_remove_srv_command(MySQLProtocol* p); bool protocol_waits_response(MySQLProtocol* p); mysql_server_cmd_t protocol_get_srv_command(MySQLProtocol* p,bool removep); int get_stmt_nresponse_packets(GWBUF* buf, mysql_server_cmd_t cmd); -bool protocol_get_response_status (MySQLProtocol* p, int* npackets, size_t* nbytes); -void protocol_set_response_status (MySQLProtocol* p, int npackets, size_t nbytes); +bool protocol_get_response_status (MySQLProtocol* p, int* npackets, ssize_t* nbytes); +void protocol_set_response_status (MySQLProtocol* p, int npackets, ssize_t nbytes); void protocol_archive_srv_command(MySQLProtocol* p); @@ -396,6 +397,6 @@ void init_response_status ( GWBUF* buf, mysql_server_cmd_t cmd, int* npackets, - size_t* nbytes); + ssize_t* nbytes); diff --git a/server/modules/monitor/galera_mon.c b/server/modules/monitor/galera_mon.c index 908d8895d..aa1f1fe47 100644 --- a/server/modules/monitor/galera_mon.c +++ b/server/modules/monitor/galera_mon.c @@ -61,7 +61,7 @@ static char *version_str = "V1.4.0"; MODULE_INFO info = { MODULE_API_MONITOR, - MODULE_BETA_RELEASE, + MODULE_GA, MONITOR_VERSION, "A Galera cluster monitor" }; diff --git a/server/modules/monitor/mysql_mon.c b/server/modules/monitor/mysql_mon.c index 29a56c1c2..bbf69962b 100644 --- a/server/modules/monitor/mysql_mon.c +++ b/server/modules/monitor/mysql_mon.c @@ -75,7 +75,7 @@ static char *version_str = "V1.4.0"; MODULE_INFO info = { MODULE_API_MONITOR, - MODULE_BETA_RELEASE, + MODULE_GA, MONITOR_VERSION, "A MySQL Master/Slave replication monitor" }; @@ -676,12 +676,21 @@ int log_no_master = 1; if (mon_status_changed(ptr)) { - LOGIF(LD, (skygw_log_write_flush( - LOGFILE_DEBUG, +#if defined(SS_DEBUG) + LOGIF(LT, (skygw_log_write_flush( + LOGFILE_TRACE, "Backend server %s:%d state : %s", ptr->server->name, ptr->server->port, STRSRVSTATUS(ptr->server)))); +#else + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "Backend server %s:%d state : %s", + ptr->server->name, + ptr->server->port, + STRSRVSTATUS(ptr->server)))); +#endif } if (SERVER_IS_DOWN(ptr->server)) @@ -752,7 +761,7 @@ int log_no_master = 1; /* log master detection failure od first master becomes available after failure */ if (root_master && mon_status_changed(root_master) && !(root_master->server->status & SERVER_STALE_STATUS)) { if (root_master->pending_status & (SERVER_MASTER)) { - if (!(root_master->mon_prev_status & SERVER_STALE_STATUS)) { + if (!(root_master->mon_prev_status & SERVER_STALE_STATUS) && !(root_master->server->status & SERVER_MAINT)) { LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Info: A Master Server is now available: %s:%i", diff --git a/server/modules/protocol/maxscaled.c b/server/modules/protocol/maxscaled.c index 1c564da56..052026fbe 100644 --- a/server/modules/protocol/maxscaled.c +++ b/server/modules/protocol/maxscaled.c @@ -40,7 +40,7 @@ MODULE_INFO info = { MODULE_API_PROTOCOL, - MODULE_BETA_RELEASE, + MODULE_GA, GWPROTOCOL_VERSION, "A maxscale protocol for the administration interface" }; @@ -374,8 +374,8 @@ int rc; rc = listen(listener->fd, SOMAXCONN); if (rc == 0) { - LOGIF(LD, (skygw_log_write( - LOGFILE_DEBUG, + LOGIF(LM, (skygw_log_write( + LOGFILE_MESSAGE, "Listening maxscale connections at %s\n", config))); } else { diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index 0dcec1b70..52cb14912 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -51,7 +51,7 @@ MODULE_INFO info = { MODULE_API_PROTOCOL, - MODULE_BETA_RELEASE, + MODULE_GA, GWPROTOCOL_VERSION, "The MySQL to backend server protocol" }; @@ -211,12 +211,13 @@ static int gw_read_backend_event(DCB *dcb) { /** Read cached backend handshake */ if (gw_read_backend_handshake(backend_protocol) != 0) { - backend_protocol->protocol_auth_state = MYSQL_AUTH_FAILED; + backend_protocol->protocol_auth_state = MYSQL_HANDSHAKE_FAILED; + LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_read_backend_event] after " "gw_read_backend_handshake, fd %d, " - "state = MYSQL_AUTH_FAILED.", + "state = MYSQL_HANDSHAKE_FAILED.", pthread_self(), backend_protocol->owner_dcb->fd))); } @@ -256,6 +257,7 @@ static int gw_read_backend_event(DCB *dcb) { * -- handle a previous handshake error */ if (backend_protocol->protocol_auth_state == MYSQL_AUTH_RECV || + backend_protocol->protocol_auth_state == MYSQL_HANDSHAKE_FAILED || backend_protocol->protocol_auth_state == MYSQL_AUTH_FAILED) { spinlock_acquire(&dcb->authlock); @@ -264,6 +266,7 @@ static int gw_read_backend_event(DCB *dcb) { CHK_PROTOCOL(backend_protocol); if (backend_protocol->protocol_auth_state == MYSQL_AUTH_RECV || + backend_protocol->protocol_auth_state == MYSQL_HANDSHAKE_FAILED || backend_protocol->protocol_auth_state == MYSQL_AUTH_FAILED) { ROUTER_OBJECT *router = NULL; @@ -286,7 +289,7 @@ static int gw_read_backend_event(DCB *dcb) { if (backend_protocol->protocol_auth_state == MYSQL_AUTH_RECV) { /** - * Read backed's reply to authentication message + * Read backend's reply to authentication message */ receive_rc = gw_receive_backend_auth(backend_protocol); @@ -340,7 +343,8 @@ static int gw_read_backend_event(DCB *dcb) { } /* switch */ } - if (backend_protocol->protocol_auth_state == MYSQL_AUTH_FAILED) + if (backend_protocol->protocol_auth_state == MYSQL_AUTH_FAILED || + backend_protocol->protocol_auth_state == MYSQL_HANDSHAKE_FAILED) { /** * protocol state won't change anymore, @@ -362,7 +366,9 @@ static int gw_read_backend_event(DCB *dcb) { bool succp; /* try reload users' table for next connection */ - service_refresh_users(dcb->session->service); + if (backend_protocol->protocol_auth_state == MYSQL_AUTH_FAILED) { + service_refresh_users(dcb->session->service); + } #if defined(SS_DEBUG) LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, @@ -428,7 +434,7 @@ static int gw_read_backend_event(DCB *dcb) { spinlock_release(&dcb->authlock); - } /* MYSQL_AUTH_RECV || MYSQL_AUTH_FAILED */ + } /* MYSQL_AUTH_RECV || MYSQL_AUTH_FAILED || MYSQL_HANDSHAKE_FAILED */ /* reading MySQL command output from backend and writing to the client */ { @@ -552,7 +558,7 @@ static int gw_read_backend_event(DCB *dcb) { { client_protocol = SESSION_PROTOCOL(dcb->session, MySQLProtocol); - if (client_protocol != NULL) + if (client_protocol != NULL) { CHK_PROTOCOL(client_protocol); @@ -693,6 +699,7 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue) * return 1. */ switch (backend_protocol->protocol_auth_state) { + case MYSQL_HANDSHAKE_FAILED: case MYSQL_AUTH_FAILED: { size_t len; @@ -823,18 +830,22 @@ static int gw_error_backend_event(DCB *dcb) */ if (dcb->state != DCB_STATE_POLLING) { - int error, len; - char buf[100]; + int error, len; + char buf[100]; len = sizeof(error); - if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) + + if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) == 0) { - strerror_r(error, buf, 100); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "DCB in state %s got error '%s'.", - gw_dcb_state2string(dcb->state), - buf))); + if (error != 0) + { + strerror_r(error, buf, 100); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "DCB in state %s got error '%s'.", + STRDCBSTATE(dcb->state), + buf))); + } } return 1; } @@ -862,18 +873,21 @@ static int gw_error_backend_event(DCB *dcb) if (ses_state != SESSION_STATE_ROUTER_READY) { - int error, len; - char buf[100]; + int error, len; + char buf[100]; len = sizeof(error); - if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) + if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) == 0) { - strerror_r(error, buf, 100); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error '%s' in session that is not ready for routing.", - buf))); - } + if (error != 0) + { + strerror_r(error, buf, 100); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error '%s' in session that is not ready for routing.", + buf))); + } + } gwbuf_free(errbuf); goto retblock; } @@ -948,11 +962,20 @@ static int gw_create_backend_connection( } /** Copy client flags to backend protocol */ - protocol->client_capabilities = - ((MySQLProtocol *)(backend_dcb->session->client->protocol))->client_capabilities; - /** Copy client charset to backend protocol */ - protocol->charset = - ((MySQLProtocol *)(backend_dcb->session->client->protocol))->charset; + if (backend_dcb->session->client->protocol) + { + /** Copy client flags to backend protocol */ + protocol->client_capabilities = + ((MySQLProtocol *)(backend_dcb->session->client->protocol))->client_capabilities; + /** Copy client charset to backend protocol */ + protocol->charset = + ((MySQLProtocol *)(backend_dcb->session->client->protocol))->charset; + } + else + { + protocol->client_capabilities = GW_MYSQL_CAPABILITIES_CLIENT; + protocol->charset = 0x08; + } /*< if succeed, fd > 0, -1 otherwise */ rv = gw_do_connect_to_backend(server->name, server->port, &fd); @@ -1065,18 +1088,21 @@ gw_backend_hangup(DCB *dcb) if (ses_state != SESSION_STATE_ROUTER_READY) { - int error, len; - char buf[100]; + int error, len; + char buf[100]; len = sizeof(error); - if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) + if (getsockopt(dcb->fd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len) == 0) { - strerror_r(error, buf, 100); - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Hangup in session that is not ready for routing, " - "Error reported is '%s'.", - buf))); + if (error != 0) + { + strerror_r(error, buf, 100); + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Hangup in session that is not ready for routing, " + "Error reported is '%s'.", + buf))); + } } gwbuf_free(errbuf); goto retblock; @@ -1312,12 +1338,11 @@ static int gw_change_user( /* now get the user, after 4 bytes header and 1 byte command */ client_auth_packet += 5; - strcpy(username, (char *)client_auth_packet); + strncpy(username, (char *)client_auth_packet,MYSQL_USER_MAXLEN); client_auth_packet += strlen(username) + 1; /* get the auth token len */ memcpy(&auth_token_len, client_auth_packet, 1); - ss_dassert(auth_token_len >= 0); client_auth_packet++; @@ -1333,7 +1358,7 @@ static int gw_change_user( } /* get new database name */ - strcpy(database, (char *)client_auth_packet); + strncpy(database, (char *)client_auth_packet,MYSQL_DATABASE_MAXLEN); /* get character set */ if (strlen(database)) { @@ -1346,7 +1371,7 @@ static int gw_change_user( memcpy(&backend_protocol->charset, client_auth_packet, sizeof(int)); /* save current_database name */ - strcpy(current_database, current_session->db); + strncpy(current_database, current_session->db,MYSQL_DATABASE_MAXLEN); /* * Now clear database name in dcb as we don't do local authentication on db name for change user. @@ -1466,7 +1491,7 @@ static GWBUF* process_response_data ( int nbytes_to_process) /*< number of new bytes read */ { int npackets_left = 0; /*< response's packet count */ - size_t nbytes_left = 0; /*< nbytes to be read for the packet */ + ssize_t nbytes_left = 0; /*< nbytes to be read for the packet */ MySQLProtocol* p; GWBUF* outbuf = NULL; @@ -1540,11 +1565,13 @@ static GWBUF* process_response_data ( */ else /*< nbytes_left < nbytes_to_process */ { + ss_dassert(nbytes_left >= 0); nbytes_to_process -= nbytes_left; /** Move the prefix of the buffer to outbuf from redbuf */ - outbuf = gwbuf_append(outbuf, gwbuf_clone_portion(readbuf, 0, nbytes_left)); - readbuf = gwbuf_consume(readbuf, nbytes_left); + outbuf = gwbuf_append(outbuf, + gwbuf_clone_portion(readbuf, 0, (size_t)nbytes_left)); + readbuf = gwbuf_consume(readbuf, (size_t)nbytes_left); ss_dassert(npackets_left > 0); npackets_left -= 1; nbytes_left = 0; @@ -1592,7 +1619,7 @@ static bool sescmd_response_complete( DCB* dcb) { int npackets_left; - size_t nbytes_left; + ssize_t nbytes_left; MySQLProtocol* p; bool succp; diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 26822cbdc..b99ed8cc6 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -49,7 +49,7 @@ MODULE_INFO info = { MODULE_API_PROTOCOL, - MODULE_BETA_RELEASE, + MODULE_GA, GWPROTOCOL_VERSION, "The client to MaxScale MySQL protocol implementation" }; diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 27159dc21..b24260bef 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -214,12 +214,12 @@ int gw_read_backend_handshake( if (h_len <= 4) { /* log error this exit point */ - conn->protocol_auth_state = MYSQL_AUTH_FAILED; + conn->protocol_auth_state = MYSQL_HANDSHAKE_FAILED; LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_read_backend_handshake] after " "dcb_read, fd %d, " - "state = MYSQL_AUTH_FAILED.", + "state = MYSQL_HANDSHAKE_FAILED.", dcb->fd, pthread_self()))); @@ -232,6 +232,8 @@ int gw_read_backend_handshake( uint16_t errcode = MYSQL_GET_ERRCODE(payload); char* bufstr = strndup(&((char *)payload)[7], len-3); + conn->protocol_auth_state = MYSQL_HANDSHAKE_FAILED; + LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_receive_backend_auth] Invalid " @@ -261,12 +263,14 @@ int gw_read_backend_handshake( * data in buffer less than expected in the * packet. Log error this exit point */ - conn->protocol_auth_state = MYSQL_AUTH_FAILED; + + conn->protocol_auth_state = MYSQL_HANDSHAKE_FAILED; + LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_read_backend_handshake] after " "gw_mysql_get_byte3, fd %d, " - "state = MYSQL_AUTH_FAILED.", + "state = MYSQL_HANDSHAKE_FAILED.", pthread_self(), dcb->fd, pthread_self()))); @@ -285,12 +289,13 @@ int gw_read_backend_handshake( * we cannot continue * log error this exit point */ - conn->protocol_auth_state = MYSQL_AUTH_FAILED; + conn->protocol_auth_state = MYSQL_HANDSHAKE_FAILED; + LOGIF(LD, (skygw_log_write( LOGFILE_DEBUG, "%lu [gw_read_backend_handshake] after " "gw_decode_mysql_server_handshake, fd %d, " - "state = MYSQL_AUTH_FAILED.", + "state = MYSQL_HANDSHAKE_FAILED.", pthread_self(), conn->owner_dcb->fd, pthread_self()))); @@ -577,8 +582,8 @@ int gw_send_authentication_to_backend( dcb = conn->owner_dcb; final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities); - /** Copy client's flags to backend */ - final_capabilities |= conn->client_capabilities; + /** Copy client's flags to backend but with the known capabilities mask */ + final_capabilities |= (conn->client_capabilities & GW_MYSQL_CAPABILITIES_CLIENT); /* get charset the client sent and use it for connection auth */ charset = conn->charset; @@ -964,7 +969,7 @@ GWBUF* mysql_create_custom_error( const char* msg) { uint8_t* outbuf = NULL; - uint8_t mysql_payload_size = 0; + uint32_t mysql_payload_size = 0; uint8_t mysql_packet_header[4]; uint8_t* mysql_payload = NULL; uint8_t field_count = 0; @@ -1574,7 +1579,7 @@ mysql_send_auth_error ( const char *mysql_message) { uint8_t *outbuf = NULL; - uint8_t mysql_payload_size = 0; + uint32_t mysql_payload_size = 0; uint8_t mysql_packet_header[4]; uint8_t *mysql_payload = NULL; uint8_t field_count = 0; @@ -1960,7 +1965,7 @@ void init_response_status ( GWBUF* buf, mysql_server_cmd_t cmd, int* npackets, - size_t* nbytes_left) + ssize_t* nbytes_left) { uint8_t* packet; int nparam; @@ -2022,7 +2027,7 @@ void init_response_status ( bool protocol_get_response_status ( MySQLProtocol* p, int* npackets, - size_t* nbytes) + ssize_t* nbytes) { bool succp; @@ -2030,7 +2035,7 @@ bool protocol_get_response_status ( spinlock_acquire(&p->protocol_lock); *npackets = p->protocol_command.scom_nresponse_packets; - *nbytes = p->protocol_command.scom_nbytes_to_read; + *nbytes = (ssize_t)p->protocol_command.scom_nbytes_to_read; spinlock_release(&p->protocol_lock); if (*npackets < 0 && *nbytes == 0) @@ -2048,7 +2053,7 @@ bool protocol_get_response_status ( void protocol_set_response_status ( MySQLProtocol* p, int npackets_left, - size_t nbytes) + ssize_t nbytes) { CHK_PROTOCOL(p); diff --git a/server/modules/protocol/telnetd.c b/server/modules/protocol/telnetd.c index b079a3679..f73a2942b 100644 --- a/server/modules/protocol/telnetd.c +++ b/server/modules/protocol/telnetd.c @@ -40,7 +40,7 @@ MODULE_INFO info = { MODULE_API_PROTOCOL, - MODULE_BETA_RELEASE, + MODULE_GA, GWPROTOCOL_VERSION, "A telnet deamon protocol for simple administration interface" }; diff --git a/server/modules/routing/CMakeLists.txt b/server/modules/routing/CMakeLists.txt index 8208c470b..59695b715 100644 --- a/server/modules/routing/CMakeLists.txt +++ b/server/modules/routing/CMakeLists.txt @@ -19,4 +19,7 @@ target_link_libraries(cli log_manager utils) install(TARGETS cli DESTINATION modules) add_subdirectory(readwritesplit) +if(BUILD_BINLOG) + add_subdirectory(binlog) +endif() diff --git a/server/modules/routing/binlog/blr.c b/server/modules/routing/binlog/blr.c index bded77735..1287eacf4 100644 --- a/server/modules/routing/binlog/blr.c +++ b/server/modules/routing/binlog/blr.c @@ -59,6 +59,8 @@ #include extern int lm_enabled_logfiles_bitmask; +extern size_t log_ses_count[]; +extern __thread log_info_t tls_log_info; static char *version_str = "V1.0.6"; @@ -186,6 +188,8 @@ int i; inst->long_burst = DEF_LONG_BURST; inst->burst_size = DEF_BURST_SIZE; inst->retry_backoff = 1; + inst->binlogdir = NULL; + inst->heartbeat = 300; // Default is every 5 minutes /* * We only support one server behind this router, since the server is @@ -306,6 +310,14 @@ int i; inst->burst_size = size; } + else if (strcmp(options[i], "heartbeat") == 0) + { + inst->heartbeat = atoi(value); + } + else if (strcmp(options[i], "binlogdir") == 0) + { + inst->binlogdir = strdup(value); + } else { LOGIF(LE, (skygw_log_write( @@ -416,6 +428,7 @@ ROUTER_SLAVE *slave; slave->router = inst; slave->file = NULL; strcpy(slave->binlogfile, "unassigned"); + slave->connect_time = time(0); /** * Add this session to the list of active sessions. @@ -509,9 +522,13 @@ ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session; { /* * We must be closing the master session. - * - * TODO: Handle closure of master session */ + LOGIF(LM, (skygw_log_write_flush( + LOGFILE_MESSAGE, + "%s: Master %s disconnected after %ld seconds. " + "%d events read,", + router->service->name, router->master->remote, + time(0) - router->connect_time, router->stats.n_binlogs_ses))); LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Binlog router close session with master server %s", @@ -529,6 +546,15 @@ ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session; /* decrease server registered slaves counter */ atomic_add(&router->stats.n_registered, -1); + LOGIF(LM, (skygw_log_write_flush( + LOGFILE_MESSAGE, + "%s: Slave %s, server id %d, disconnected after %ld seconds. " + "%d events sent, %lu bytes.", + router->service->name, slave->dcb->remote, + slave->serverid, + time(0) - slave->connect_time, slave->stats.n_events, + slave->stats.n_bytes))); + /* * Mark the slave as unregistered to prevent the forwarding * of any more binlog records to this slave. @@ -641,25 +667,29 @@ struct tm tm; min5 /= 5.0; - dcb_printf(dcb, "\tMaster connection DCB: %p\n", + dcb_printf(dcb, "\tMaster connection DCB: %p\n", router_inst->master); - dcb_printf(dcb, "\tMaster connection state: %s\n", + dcb_printf(dcb, "\tMaster connection state: %s\n", blrm_states[router_inst->master_state]); localtime_r(&router_inst->stats.lastReply, &tm); asctime_r(&tm, buf); - dcb_printf(dcb, "\tNumber of master connects: %d\n", + dcb_printf(dcb, "\tBinlog directory: %s\n", + router_inst->binlogdir); + dcb_printf(dcb, "\tNumber of master connects: %d\n", router_inst->stats.n_masterstarts); - dcb_printf(dcb, "\tNumber of delayed reconnects: %d\n", + dcb_printf(dcb, "\tNumber of delayed reconnects: %d\n", router_inst->stats.n_delayedreconnects); - dcb_printf(dcb, "\tCurrent binlog file: %s\n", + dcb_printf(dcb, "\tCurrent binlog file: %s\n", router_inst->binlog_name); - dcb_printf(dcb, "\tCurrent binlog position: %u\n", + dcb_printf(dcb, "\tCurrent binlog position: %u\n", router_inst->binlog_position); - dcb_printf(dcb, "\tNumber of slave servers: %u\n", + dcb_printf(dcb, "\tNumber of slave servers: %u\n", router_inst->stats.n_slaves); - dcb_printf(dcb, "\tNumber of binlog events received: %u\n", + dcb_printf(dcb, "\tNo. of binlog events received this session: %u\n", + router_inst->stats.n_binlogs_ses); + dcb_printf(dcb, "\tTotal no. of binlog events received: %u\n", router_inst->stats.n_binlogs); minno = router_inst->stats.minno - 1; if (minno == -1) @@ -668,28 +698,31 @@ struct tm tm; dcb_printf(dcb, "\tCurrent 5 10 15 30 Min Avg\n"); dcb_printf(dcb, "\t %6d %8.1f %8.1f %8.1f %8.1f\n", router_inst->stats.minavgs[minno], min5, min10, min15, min30); - dcb_printf(dcb, "\tNumber of fake binlog events: %u\n", + dcb_printf(dcb, "\tNumber of fake binlog events: %u\n", router_inst->stats.n_fakeevents); - dcb_printf(dcb, "\tNumber of artificial binlog events: %u\n", + dcb_printf(dcb, "\tNumber of artificial binlog events: %u\n", router_inst->stats.n_artificial); - dcb_printf(dcb, "\tNumber of binlog events in error: %u\n", + dcb_printf(dcb, "\tNumber of binlog events in error: %u\n", router_inst->stats.n_binlog_errors); - dcb_printf(dcb, "\tNumber of binlog rotate events: %u\n", + dcb_printf(dcb, "\tNumber of binlog rotate events: %u\n", router_inst->stats.n_rotates); - dcb_printf(dcb, "\tNumber of heartbeat events: %u\n", + dcb_printf(dcb, "\tNumber of heartbeat events: %u\n", router_inst->stats.n_heartbeats); - dcb_printf(dcb, "\tNumber of packets received: %u\n", + dcb_printf(dcb, "\tNumber of packets received: %u\n", router_inst->stats.n_reads); - dcb_printf(dcb, "\tNumber of residual data packets: %u\n", + dcb_printf(dcb, "\tNumber of residual data packets: %u\n", router_inst->stats.n_residuals); - dcb_printf(dcb, "\tAverage events per packet %.1f\n", + dcb_printf(dcb, "\tAverage events per packet %.1f\n", (double)router_inst->stats.n_binlogs / router_inst->stats.n_reads); - dcb_printf(dcb, "\tLast event from master at: %s", + dcb_printf(dcb, "\tLast event from master at: %s", buf); dcb_printf(dcb, "\t (%d seconds ago)\n", time(0) - router_inst->stats.lastReply); - dcb_printf(dcb, "\tLast event from master: 0x%x\n", - router_inst->lastEventReceived); + dcb_printf(dcb, "\tLast event from master: 0x%x (%s)\n", + router_inst->lastEventReceived, + (router_inst->lastEventReceived >= 0 && + router_inst->lastEventReceived < 0x24) ? + event_names[router_inst->lastEventReceived] : "unknown"); if (router_inst->active_logs) dcb_printf(dcb, "\tRouter processing binlog records\n"); if (router_inst->reconnect_pending) @@ -697,7 +730,7 @@ struct tm tm; dcb_printf(dcb, "\tEvents received:\n"); for (i = 0; i < 0x24; i++) { - dcb_printf(dcb, "\t\t%-38s: %u\n", event_names[i], router_inst->stats.events[i]); + dcb_printf(dcb, "\t\t%-38s %u\n", event_names[i], router_inst->stats.events[i]); } #if SPINLOCK_PROFILE @@ -739,19 +772,44 @@ struct tm tm; min15 /= 15.0; min10 /= 10.0; min5 /= 5.0; - dcb_printf(dcb, "\t\tServer-id: %d\n", session->serverid); + dcb_printf(dcb, + "\t\tServer-id: %d\n", + session->serverid); if (session->hostname) - dcb_printf(dcb, "\t\tHostname: %s\n", session->hostname); - dcb_printf(dcb, "\t\tSlave DCB: %p\n", session->dcb); - dcb_printf(dcb, "\t\tNext Sequence No: %d\n", session->seqno); - dcb_printf(dcb, "\t\tState: %s\n", blrs_states[session->state]); - dcb_printf(dcb, "\t\tBinlog file: %s\n", session->binlogfile); - dcb_printf(dcb, "\t\tBinlog position: %u\n", session->binlog_pos); + dcb_printf(dcb, "\t\tHostname: %s\n", session->hostname); + dcb_printf(dcb, + "\t\tSlave: %d\n", + session->dcb->remote); + dcb_printf(dcb, + "\t\tSlave DCB: %p\n", + session->dcb); + dcb_printf(dcb, + "\t\tNext Sequence No: %d\n", + session->seqno); + dcb_printf(dcb, + "\t\tState: %s\n", + blrs_states[session->state]); + dcb_printf(dcb, + "\t\tBinlog file: %s\n", + session->binlogfile); + dcb_printf(dcb, + "\t\tBinlog position: %u\n", + session->binlog_pos); if (session->nocrc) - dcb_printf(dcb, "\t\tMaster Binlog CRC: None\n"); - dcb_printf(dcb, "\t\tNo. requests: %u\n", session->stats.n_requests); - dcb_printf(dcb, "\t\tNo. events sent: %u\n", session->stats.n_events); - dcb_printf(dcb, "\t\tNo. bursts sent: %u\n", session->stats.n_bursts); + dcb_printf(dcb, + "\t\tMaster Binlog CRC: None\n"); + dcb_printf(dcb, + "\t\tNo. requests: %u\n", + session->stats.n_requests); + dcb_printf(dcb, + "\t\tNo. events sent: %u\n", + session->stats.n_events); + dcb_printf(dcb, + "\t\tNo. bursts sent: %u\n", + session->stats.n_bursts); + dcb_printf(dcb, + "\t\tNo. transitions to follow mode: %u\n", + session->stats.n_bursts); minno = session->stats.minno - 1; if (minno == -1) minno = 30; @@ -760,15 +818,18 @@ struct tm tm; dcb_printf(dcb, "\t\t %6d %8.1f %8.1f %8.1f %8.1f\n", session->stats.minavgs[minno], min5, min10, min15, min30); - dcb_printf(dcb, "\t\tNo. flow control: %u\n", session->stats.n_flows); - dcb_printf(dcb, "\t\tNo. up to date: %u\n", session->stats.n_upd); - dcb_printf(dcb, "\t\tNo. of drained cbs %u\n", session->stats.n_dcb); - dcb_printf(dcb, "\t\tNo. of low water cbs N/A %u\n", session->stats.n_cbna); - dcb_printf(dcb, "\t\tNo. of failed reads %u\n", session->stats.n_failed_read); - dcb_printf(dcb, "\t\tNo. of nested distribute events %u\n", session->stats.n_overrun); - dcb_printf(dcb, "\t\tNo. of distribute action 1 %u\n", session->stats.n_actions[0]); - dcb_printf(dcb, "\t\tNo. of distribute action 2 %u\n", session->stats.n_actions[1]); - dcb_printf(dcb, "\t\tNo. of distribute action 3 %u\n", session->stats.n_actions[2]); + dcb_printf(dcb, "\t\tNo. flow control: %u\n", session->stats.n_flows); + dcb_printf(dcb, "\t\tNo. up to date: %u\n", session->stats.n_upd); + dcb_printf(dcb, "\t\tNo. of drained cbs %u\n", session->stats.n_dcb); + dcb_printf(dcb, "\t\tNo. of failed reads %u\n", session->stats.n_failed_read); + +#if DETAILED_DIAG + dcb_printf(dcb, "\t\tNo. of nested distribute events %u\n", session->stats.n_overrun); + dcb_printf(dcb, "\t\tNo. of distribute action 1 %u\n", session->stats.n_actions[0]); + dcb_printf(dcb, "\t\tNo. of distribute action 2 %u\n", session->stats.n_actions[1]); + dcb_printf(dcb, "\t\tNo. of distribute action 3 %u\n", session->stats.n_actions[2]); +#endif + if ((session->cstate & CS_UPTODATE) == 0) { dcb_printf(dcb, "\t\tSlave is in catchup mode. %s%s\n", @@ -793,7 +854,7 @@ struct tm tm; dcb_printf(dcb, "\tSpinlock statistics (rses_lock):\n"); spinlock_stats(&session->rses_lock, spin_reporter, dcb); #endif - dcb_printf(dcb, "\n"); + dcb_printf(dcb, "\t\t--------------------\n\n"); session = session->next; } spinlock_release(&router_inst->lock); @@ -822,6 +883,24 @@ ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; router->stats.lastReply = time(0); } +static char * +extract_message(GWBUF *errpkt) +{ +char *rval; +int len; + + len = EXTRACT24(errpkt->start); + if ((rval = (char *)malloc(len)) == NULL) + return NULL; + memcpy(rval, (char *)(errpkt->start) + 7, 6); + rval[6] = ' '; + memcpy(&rval[7], (char *)(errpkt->start) + 13, len - 8); + rval[len-2] = 0; + return rval; +} + + + /** * Error Reply routine * @@ -841,10 +920,10 @@ errorReply(ROUTER *instance, void *router_session, GWBUF *message, DCB *backend_ { ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; int error, len; -char msg[85]; +char msg[85], *errmsg; len = sizeof(error); - if (getsockopt(router->master->fd, SOL_SOCKET, SO_ERROR, &error, &len) != 0) + if (router->master && getsockopt(router->master->fd, SOL_SOCKET, SO_ERROR, &error, &len) == 0 && error != 0) { strerror_r(error, msg, 80); strcat(msg, " "); @@ -852,10 +931,21 @@ char msg[85]; else strcpy(msg, ""); + errmsg = extract_message(message); LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, "Master connection '%s', %sattempting reconnect to master", - message, msg))); + LOGFILE_ERROR, "%s: Master connection error '%s' in state '%s', " + "%sattempting reconnect to master", + router->service->name, errmsg, + blrm_states[router->master_state], msg))); + if (errmsg) + free(errmsg); *succp = true; + LOGIF(LM, (skygw_log_write_flush( + LOGFILE_MESSAGE, + "%s: Master %s disconnected after %ld seconds. " + "%d events read.", + router->service->name, router->master->remote, + time(0) - router->connect_time, router->stats.n_binlogs_ses))); blr_master_reconnect(router); } diff --git a/server/modules/routing/binlog/blr_cache.c b/server/modules/routing/binlog/blr_cache.c index a7213aa8a..590fe55e7 100644 --- a/server/modules/routing/binlog/blr_cache.c +++ b/server/modules/routing/binlog/blr_cache.c @@ -54,6 +54,8 @@ extern int lm_enabled_logfiles_bitmask; +extern size_t log_ses_count[]; +extern __thread log_info_t tls_log_info; /** diff --git a/server/modules/routing/binlog/blr_file.c b/server/modules/routing/binlog/blr_file.c index ada392742..24be12782 100644 --- a/server/modules/routing/binlog/blr_file.c +++ b/server/modules/routing/binlog/blr_file.c @@ -51,6 +51,8 @@ #include extern int lm_enabled_logfiles_bitmask; +extern size_t log_ses_count[]; +extern __thread log_info_t tls_log_info; static void blr_file_create(ROUTER_INSTANCE *router, char *file); @@ -75,18 +77,27 @@ int root_len, i; DIR *dirp; struct dirent *dp; - strcpy(path, "/usr/local/skysql/MaxScale"); - if ((ptr = getenv("MAXSCALE_HOME")) != NULL) + if (router->binlogdir == NULL) { - strcpy(path, ptr); + strcpy(path, "/usr/local/skysql/MaxScale"); + if ((ptr = getenv("MAXSCALE_HOME")) != NULL) + { + strcpy(path, ptr); + } + strcat(path, "/"); + strcat(path, router->service->name); + + if (access(path, R_OK) == -1) + mkdir(path, 0777); + + router->binlogdir = strdup(path); + } + if (access(router->binlogdir, R_OK) == -1) + { + LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, + "%s: Unable to read the binlog directory %s.", + router->service->name, router->binlogdir))); } - strcat(path, "/"); - strcat(path, router->service->name); - - if (access(path, R_OK) == -1) - mkdir(path, 0777); - - router->binlogdir = strdup(path); /* First try to find a binlog file number by reading the directory */ root_len = strlen(router->fileroot); @@ -353,7 +364,7 @@ struct stat statb; "Short read when reading the header. " "Expected 19 bytes but got %d bytes. " "Binlog file is %s, position %d", - file->binlogname, pos, n))); + n, file->binlogname, pos))); break; } return NULL; @@ -364,6 +375,17 @@ struct stat statb; hdr->event_size = extract_field(&hdbuf[9], 32); hdr->next_pos = EXTRACT32(&hdbuf[13]); hdr->flags = EXTRACT16(&hdbuf[17]); + + if (hdr->event_type > MAX_EVENT_TYPE) + { + LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, + "Invalid event type 0x%x. " + "Binlog file is %s, position %d", + hdr->event_type, + file->binlogname, pos))); + return NULL; + } + if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT) { LOGIF(LE, (skygw_log_write(LOGFILE_ERROR, diff --git a/server/modules/routing/binlog/blr_master.c b/server/modules/routing/binlog/blr_master.c index bb43c1a27..db95cf6c5 100644 --- a/server/modules/routing/binlog/blr_master.c +++ b/server/modules/routing/binlog/blr_master.c @@ -63,6 +63,8 @@ extern int lm_enabled_logfiles_bitmask; +extern size_t log_ses_count[]; +extern __thread log_info_t tls_log_info; static GWBUF *blr_make_query(char *statement); static GWBUF *blr_make_registration(ROUTER_INSTANCE *router); @@ -91,6 +93,18 @@ blr_start_master(ROUTER_INSTANCE *router) DCB *client; GWBUF *buf; + router->stats.n_binlogs_ses = 0; + spinlock_acquire(&router->lock); + if (router->master_state != BLRM_UNCONNECTED) + { + LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, + "%s: Master Connect: Unexpected master state %s\n", + router->service->name, blrm_states[router->master_state]))); + spinlock_release(&router->lock); + return; + } + router->master_state = BLRM_CONNECTING; + spinlock_release(&router->lock); if ((client = dcb_alloc(DCB_ROLE_INTERNAL)) == NULL) { LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, @@ -98,6 +112,7 @@ GWBUF *buf; return; } router->client = client; + client->state = DCB_STATE_POLLING; /* Fake the client is reading */ client->data = CreateMySQLAuthData(router->user, router->password, ""); if ((router->session = session_alloc(router->service, client)) == NULL) { @@ -108,17 +123,27 @@ GWBUF *buf; client->session = router->session; if ((router->master = dcb_connect(router->service->databases, router->session, BLR_PROTOCOL)) == NULL) { - char *name = malloc(strlen(router->service->name) + strlen(" Master") + 1); - sprintf(name, "%s Master", router->service->name); - hktask_oneshot(name, blr_start_master, router, - BLR_MASTER_BACKOFF_TIME * router->retry_backoff++); + char *name; + if ((name = malloc(strlen(router->service->name) + + strlen(" Master") + 1)) != NULL) + { + sprintf(name, "%s Master", router->service->name); + hktask_oneshot(name, blr_start_master, router, + BLR_MASTER_BACKOFF_TIME * router->retry_backoff++); + } if (router->retry_backoff > BLR_MAX_BACKOFF) - router->retry_backoff = 1; + router->retry_backoff = BLR_MAX_BACKOFF; LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR, "Binlog router: failed to connect to master server '%s'", router->service->databases->unique_name))); return; } + router->master->remote = strdup(router->service->databases->name); + LOGIF(LM,(skygw_log_write( + LOGFILE_MESSAGE, + "%s: atempting to connect to master server %s.", + router->service->name, router->master->remote))); + router->connect_time = time(0); if (setsockopt(router->master->fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive , sizeof(keepalive ))) perror("setsockopt"); @@ -129,7 +154,6 @@ perror("setsockopt"); router->master_state = BLRM_TIMESTAMP; router->stats.n_masterstarts++; - router->retry_backoff = 1; } /** @@ -160,7 +184,27 @@ GWBUF *ptr; router->reconnect_pending = 0; router->active_logs = 0; spinlock_release(&router->lock); - blr_start_master(router); + if (router->master_state < BLRM_BINLOGDUMP) + { + char *name; + + router->master_state = BLRM_UNCONNECTED; + + if ((name = malloc(strlen(router->service->name) + + strlen(" Master")+1)) != NULL); + { + sprintf(name, "%s Master", router->service->name); + hktask_oneshot(name, blr_start_master, router, + BLR_MASTER_BACKOFF_TIME * router->retry_backoff++); + } + if (router->retry_backoff > BLR_MAX_BACKOFF) + router->retry_backoff = BLR_MAX_BACKOFF; + } + else + { + router->master_state = BLRM_UNCONNECTED; + blr_start_master(router); + } } /** @@ -225,8 +269,9 @@ char query[128]; if (router->master_state < 0 || router->master_state > BLRM_MAXSTATE) { LOGIF(LE, (skygw_log_write( - LOGFILE_ERROR, "Invalid master state machine state (%d) for binlog router.", - router->master_state))); + LOGFILE_ERROR, + "Invalid master state machine state (%d) for binlog router.", + router->master_state))); gwbuf_consume(buf, gwbuf_length(buf)); spinlock_acquire(&router->lock); if (router->reconnect_pending) @@ -234,6 +279,12 @@ char query[128]; router->active_logs = 0; spinlock_release(&router->lock); atomic_add(&router->handling_threads, -1); + LOGIF(LE, (skygw_log_write( + LOGFILE_ERROR, + "%s: Pending reconnect in state %s.", + router->service->name, + blrm_states[router->master_state] + ))); blr_restart_master(router); return; } @@ -247,8 +298,11 @@ char query[128]; { LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, - "Received error: %d, %s from master during %s phase of the master state machine.", - MYSQL_ERROR_CODE(buf), MYSQL_ERROR_MSG(buf), blrm_states[router->master_state] + "%s: Received error: %d, %s from master during %s phase " + "of the master state machine.", + router->service->name, + MYSQL_ERROR_CODE(buf), MYSQL_ERROR_MSG(buf), + blrm_states[router->master_state] ))); gwbuf_consume(buf, gwbuf_length(buf)); spinlock_acquire(&router->lock); @@ -272,12 +326,17 @@ char query[128]; buf = blr_make_query("SHOW VARIABLES LIKE 'SERVER_ID'"); router->master_state = BLRM_SERVERID; router->master->func.write(router->master, buf); + router->retry_backoff = 1; break; case BLRM_SERVERID: // Response to fetch of master's server-id router->saved_master.server_id = buf; // TODO: Extract the value of server-id and place in router->master_id - buf = blr_make_query("SET @master_heartbeat_period = 1799999979520"); + { + char str[80]; + sprintf(str, "SET @master_heartbeat_period = %lu000000000", router->heartbeat); + buf = blr_make_query(str); + } router->master_state = BLRM_HBPERIOD; router->master->func.write(router->master, buf); break; @@ -357,6 +416,12 @@ char query[128]; buf = blr_make_binlog_dump(router); router->master_state = BLRM_BINLOGDUMP; router->master->func.write(router->master, buf); + LOGIF(LM,(skygw_log_write( + LOGFILE_MESSAGE, + "%s: Request binlog records from %s at " + "position %d from master server %s.", + router->service->name, router->binlog_name, + router->binlog_position, router->master->remote))); break; case BLRM_BINLOGDUMP: // Main body, we have received a binlog record from the master @@ -618,133 +683,172 @@ static REP_HEADER phdr; n_bufs = 1; } - blr_extract_header(ptr, &hdr); - - if (hdr.event_size != len - 5) + if (len < BINLOG_EVENT_HDR_LEN) { - LOGIF(LE,(skygw_log_write( - LOGFILE_ERROR, - "Packet length is %d, but event size is %d, " - "binlog file %s position %d" - "reslen is %d and preslen is %d, " - "length of previous event %d. %s", - len, hdr.event_size, - router->binlog_name, - router->binlog_position, - reslen, preslen, prev_length, - (prev_length == -1 ? - (no_residual ? "No residual data from previous call" : "Residual data from previous call") : "") - ))); - blr_log_packet(LOGFILE_ERROR, "Packet:", ptr, len); - LOGIF(LE,(skygw_log_write( - LOGFILE_ERROR, - "This event (0x%x) was contained in %d GWBUFs, " - "the previous events was contained in %d GWBUFs", - router->lastEventReceived, n_bufs, pn_bufs))); - if (msg) + char *msg = ""; + + if (ptr[4] == 0xfe) /* EOF Packet */ { - free(msg); - msg = NULL; + msg = "end of file"; } - break; + else if (ptr[4] == 0xff) /* EOF Packet */ + { + msg = "error"; + } + LOGIF(LM,(skygw_log_write( + LOGFILE_MESSAGE, + "Non-event message (%s) from master.", + msg))); } - phdr = hdr; - if (hdr.ok == 0) + else { router->stats.n_binlogs++; + router->stats.n_binlogs_ses++; router->lastEventReceived = hdr.event_type; + blr_extract_header(ptr, &hdr); + + if (hdr.event_size != len - 5) + { + LOGIF(LE,(skygw_log_write( + LOGFILE_ERROR, + "Packet length is %d, but event size is %d, " + "binlog file %s position %d " + "reslen is %d and preslen is %d, " + "length of previous event %d. %s", + len, hdr.event_size, + router->binlog_name, + router->binlog_position, + reslen, preslen, prev_length, + (prev_length == -1 ? + (no_residual ? "No residual data from previous call" : "Residual data from previous call") : "") + ))); + blr_log_packet(LOGFILE_ERROR, "Packet:", ptr, len); + LOGIF(LE,(skygw_log_write( + LOGFILE_ERROR, + "This event (0x%x) was contained in %d GWBUFs, " + "the previous events was contained in %d GWBUFs", + router->lastEventReceived, n_bufs, pn_bufs))); + if (msg) + { + free(msg); + msg = NULL; + } + break; + } + phdr = hdr; + if (hdr.ok == 0) + { + router->stats.n_binlogs++; + router->lastEventReceived = hdr.event_type; + // #define SHOW_EVENTS #ifdef SHOW_EVENTS - printf("blr: event type 0x%02x, flags 0x%04x, event size %d", hdr.event_type, hdr.flags, hdr.event_size); + printf("blr: event type 0x%02x, flags 0x%04x, event size %d", hdr.event_type, hdr.flags, hdr.event_size); #endif - if (hdr.event_type >= 0 && hdr.event_type < 0x24) - router->stats.events[hdr.event_type]++; - if (hdr.event_type == FORMAT_DESCRIPTION_EVENT && hdr.next_pos == 0) - { - // Fake format description message - LOGIF(LD,(skygw_log_write(LOGFILE_DEBUG, - "Replication fake event. " - "Binlog %s @ %d.", - router->binlog_name, - router->binlog_position))); - router->stats.n_fakeevents++; - if (hdr.event_type == FORMAT_DESCRIPTION_EVENT) + if (hdr.event_type >= 0 && hdr.event_type < 0x24) + router->stats.events[hdr.event_type]++; + if (hdr.event_type == FORMAT_DESCRIPTION_EVENT && hdr.next_pos == 0) { - /* - * We need to save this to replay to new - * slaves that attach later. - */ - if (router->saved_master.fde_event) - free(router->saved_master.fde_event); - router->saved_master.fde_len = hdr.event_size; - router->saved_master.fde_event = malloc(hdr.event_size); - if (router->saved_master.fde_event) - memcpy(router->saved_master.fde_event, - ptr + 5, hdr.event_size); + // Fake format description message + LOGIF(LD,(skygw_log_write(LOGFILE_DEBUG, + "Replication fake event. " + "Binlog %s @ %d.", + router->binlog_name, + router->binlog_position))); + router->stats.n_fakeevents++; + if (hdr.event_type == FORMAT_DESCRIPTION_EVENT) + { + uint8_t *new_fde; + unsigned int new_fde_len; + /* + * We need to save this to replay to new + * slaves that attach later. + */ + new_fde_len = hdr.event_size; + new_fde = malloc(hdr.event_size); + if (new_fde) + { + memcpy(new_fde, ptr + 5, hdr.event_size); + if (router->saved_master.fde_event) + free(router->saved_master.fde_event); + router->saved_master.fde_event = new_fde; + router->saved_master.fde_len = new_fde_len; + } + else + { + LOGIF(LE,(skygw_log_write(LOGFILE_ERROR, + "%s: Received a format description " + "event that MaxScale was unable to " + "record. Event length is %d.", + router->service->name, + hdr.event_size))); + blr_log_packet(LOGFILE_ERROR, + "Format Description Event:", ptr, len); + } + } + } + else + { + if (hdr.event_type == HEARTBEAT_EVENT) + { +#ifdef SHOW_EVENTS + printf("Replication heartbeat\n"); +#endif + LOGIF(LD,(skygw_log_write( + LOGFILE_DEBUG, + "Replication heartbeat. " + "Binlog %s @ %d.", + router->binlog_name, + router->binlog_position))); + router->stats.n_heartbeats++; + } + else if (hdr.flags != LOG_EVENT_ARTIFICIAL_F) + { + ptr = ptr + 5; // We don't put the first byte of the payload + // into the binlog file + if (hdr.event_type == ROTATE_EVENT) + router->rotating = 1; + blr_write_binlog_record(router, &hdr, ptr); + if (hdr.event_type == ROTATE_EVENT) + { + blr_rotate_event(router, ptr, &hdr); + } + blr_distribute_binlog_record(router, &hdr, ptr); + } + else + { + router->stats.n_artificial++; + LOGIF(LD,(skygw_log_write( + LOGFILE_DEBUG, + "Artificial event not written " + "to disk or distributed. " + "Type 0x%x, Length %d, Binlog " + "%s @ %d.", + hdr.event_type, + hdr.event_size, + router->binlog_name, + router->binlog_position))); + ptr += 5; + if (hdr.event_type == ROTATE_EVENT) + { + router->rotating = 1; + blr_rotate_event(router, ptr, &hdr); + } + } } } else { - if (hdr.event_type == HEARTBEAT_EVENT) - { -#ifdef SHOW_EVENTS - printf("Replication heartbeat\n"); -#endif - LOGIF(LD,(skygw_log_write( - LOGFILE_DEBUG, - "Replication heartbeat. " - "Binlog %s @ %d.", - router->binlog_name, - router->binlog_position))); - router->stats.n_heartbeats++; - } - else if (hdr.flags != LOG_EVENT_ARTIFICIAL_F) - { - ptr = ptr + 5; // We don't put the first byte of the payload - // into the binlog file - if (hdr.event_type == ROTATE_EVENT) - router->rotating = 1; - blr_write_binlog_record(router, &hdr, ptr); - if (hdr.event_type == ROTATE_EVENT) - { - blr_rotate_event(router, ptr, &hdr); - } - blr_distribute_binlog_record(router, &hdr, ptr); - } - else - { - router->stats.n_artificial++; - LOGIF(LD,(skygw_log_write( - LOGFILE_DEBUG, - "Artificial event not written " - "to disk or distributed. " - "Type 0x%x, Length %d, Binlog " - "%s @ %d.", - hdr.event_type, - hdr.event_size, - router->binlog_name, - router->binlog_position))); - ptr += 5; - if (hdr.event_type == ROTATE_EVENT) - { - router->rotating = 1; - blr_rotate_event(router, ptr, &hdr); - } - } + LOGIF(LE,(skygw_log_write(LOGFILE_ERROR, + "Error packet in binlog stream.%s @ %d.", + router->binlog_name, + router->binlog_position))); + blr_log_packet(LOGFILE_ERROR, "Error Packet:", + ptr, len); + router->stats.n_binlog_errors++; } } - else - { - printf("Binlog router error: %s\n", &ptr[7]); - LOGIF(LE,(skygw_log_write(LOGFILE_ERROR, - "Error packet in binlog stream.%s @ %d.", - router->binlog_name, - router->binlog_position))); - blr_log_packet(LOGFILE_ERROR, "Error Packet:", - ptr, len); - router->stats.n_binlog_errors++; - } if (msg) { @@ -968,6 +1072,7 @@ int action; { blr_slave_rotate(slave, ptr); } + slave->stats.n_bytes += gwbuf_length(pkt); slave->dcb->func.write(slave->dcb, pkt); if (hdr->event_type != ROTATE_EVENT) { diff --git a/server/modules/routing/binlog/blr_slave.c b/server/modules/routing/binlog/blr_slave.c index 7a6489e02..685a6e5ae 100644 --- a/server/modules/routing/binlog/blr_slave.c +++ b/server/modules/routing/binlog/blr_slave.c @@ -65,8 +65,11 @@ int blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large); uint8_t *blr_build_header(GWBUF *pkt, REP_HEADER *hdr); int blr_slave_callback(DCB *dcb, DCB_REASON reason, void *data); static int blr_slave_fake_rotate(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); +static void blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave); extern int lm_enabled_logfiles_bitmask; +extern size_t log_ses_count[]; +extern __thread log_info_t tls_log_info; /** * Process a request packet from the slave server. @@ -544,29 +547,8 @@ uint32_t chksum; rval = slave->dcb->func.write(slave->dcb, resp); /* Send the FORMAT_DESCRIPTION_EVENT */ - if (router->saved_master.fde_event) - { - resp = gwbuf_alloc(router->saved_master.fde_len + 5); - ptr = GWBUF_DATA(resp); - encode_value(ptr, router->saved_master.fde_len + 1, 24); // Payload length - ptr += 3; - *ptr++ = slave->seqno++; - *ptr++ = 0; // OK - memcpy(ptr, router->saved_master.fde_event, router->saved_master.fde_len); - encode_value(ptr, time(0), 32); // Overwrite timestamp - /* - * Since we have changed the timestamp we must recalculate the CRC - * - * Position ptr to the start of the event header, - * calculate a new checksum - * and write it into the header - */ - ptr = GWBUF_DATA(resp) + 5 + router->saved_master.fde_len - 4; - chksum = crc32(0L, NULL, 0); - chksum = crc32(chksum, GWBUF_DATA(resp) + 5, router->saved_master.fde_len - 4); - encode_value(ptr, chksum, 32); - rval = slave->dcb->func.write(slave->dcb, resp); - } + if (slave->binlog_pos != 4) + blr_slave_send_fde(router, slave); slave->dcb->low_water = router->low_water; slave->dcb->high_water = router->high_water; @@ -575,8 +557,9 @@ uint32_t chksum; LOGIF(LM, (skygw_log_write( LOGFILE_MESSAGE, - "%s: New slave %s requested binlog file %s from position %lu", + "%s: New slave %s, server id %d, requested binlog file %s from position %lu", router->service->name, slave->dcb->remote, + slave->serverid, slave->binlogfile, slave->binlog_pos))); if (slave->binlog_pos != router->binlog_position || @@ -782,6 +765,7 @@ if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write( LOGFILE_ERROR, "blr_open_binlog took %d beats", hkheartbeat - beat1))); } + slave->stats.n_bytes += gwbuf_length(head); written = slave->dcb->func.write(slave->dcb, head); if (written && hdr.event_type != ROTATE_EVENT) { @@ -839,11 +823,23 @@ if (hkheartbeat - beat1 > 1) LOGIF(LE, (skygw_log_write( if (state_change) { - LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, - "%s: Slave %s is up to date %s, %u.", + slave->stats.n_caughtup++; + if (slave->stats.n_caughtup == 1) + { + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "%s: Slave %s is up to date %s, %u.", router->service->name, slave->dcb->remote, slave->binlogfile, slave->binlog_pos))); + } + else if ((slave->stats.n_caughtup % 50) == 0) + { + LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE, + "%s: Slave %s is up to date %s, %u.", + router->service->name, + slave->dcb->remote, + slave->binlogfile, slave->binlog_pos))); + } } } else @@ -1031,3 +1027,51 @@ uint32_t chksum; slave->dcb->func.write(slave->dcb, resp); return 1; } + +/** + * Send a "fake" format description event to the newly connected slave + * + * @param router The router instance + * @param slave The slave to send the event to + */ +static void +blr_slave_send_fde(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave) +{ +BLFILE *file; +REP_HEADER hdr; +GWBUF *record, *head; +uint8_t *ptr; +uint32_t chksum; + + if ((file = blr_open_binlog(router, slave->binlogfile)) == NULL) + return; + if ((record = blr_read_binlog(router, file, 4, &hdr)) == NULL) + { + blr_close_binlog(router, file); + return; + } + blr_close_binlog(router, file); + head = gwbuf_alloc(5); + ptr = GWBUF_DATA(head); + encode_value(ptr, hdr.event_size + 1, 24); // Payload length + ptr += 3; + *ptr++ = slave->seqno++; + *ptr++ = 0; // OK + head = gwbuf_append(head, record); + ptr = GWBUF_DATA(record); + encode_value(ptr, time(0), 32); // Overwrite timestamp + ptr += 13; + encode_value(ptr, 0, 32); // Set next position to 0 + /* + * Since we have changed the timestamp we must recalculate the CRC + * + * Position ptr to the start of the event header, + * calculate a new checksum + * and write it into the header + */ + ptr = GWBUF_DATA(record) + hdr.event_size - 4; + chksum = crc32(0L, NULL, 0); + chksum = crc32(chksum, GWBUF_DATA(record), hdr.event_size - 4); + encode_value(ptr, chksum, 32); + slave->dcb->func.write(slave->dcb, head); +} diff --git a/server/modules/routing/cli.c b/server/modules/routing/cli.c index 263e5a434..bd054f5a3 100644 --- a/server/modules/routing/cli.c +++ b/server/modules/routing/cli.c @@ -48,7 +48,7 @@ MODULE_INFO info = { MODULE_API_ROUTER, - MODULE_BETA_RELEASE, + MODULE_GA, ROUTER_VERSION, "The admin user interface" }; diff --git a/server/modules/routing/debugcli.c b/server/modules/routing/debugcli.c index 2312808ae..635847b42 100644 --- a/server/modules/routing/debugcli.c +++ b/server/modules/routing/debugcli.c @@ -47,7 +47,7 @@ MODULE_INFO info = { MODULE_API_ROUTER, - MODULE_BETA_RELEASE, + MODULE_GA, ROUTER_VERSION, "The debug user interface" }; diff --git a/server/modules/routing/readconnroute.c b/server/modules/routing/readconnroute.c index b79583999..ecce1e0b3 100644 --- a/server/modules/routing/readconnroute.c +++ b/server/modules/routing/readconnroute.c @@ -96,7 +96,7 @@ extern __thread log_info_t tls_log_info; MODULE_INFO info = { MODULE_API_ROUTER, - MODULE_BETA_RELEASE, + MODULE_GA, ROUTER_VERSION, "A connection based router to load balance based on connections" }; diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index b86db4dde..e037b16d1 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -37,7 +37,7 @@ MODULE_INFO info = { MODULE_API_ROUTER, - MODULE_BETA_RELEASE, + MODULE_GA, ROUTER_VERSION, "A Read/Write splitting router for enhancement read scalability" }; @@ -1212,24 +1212,27 @@ static bool get_dcb( * backend and update assign it to new candidate if * necessary. */ - else if (max_rlag == MAX_RLAG_UNDEFINED || + else if (SERVER_IS_SLAVE(b->backend_server)) + { + if (max_rlag == MAX_RLAG_UNDEFINED || (b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE && b->backend_server->rlag <= max_rlag)) - { - candidate_bref = check_candidate_bref( - candidate_bref, - &backend_ref[i], - rses->rses_config.rw_slave_select_criteria); - } - else - { - LOGIF(LT, (skygw_log_write( - LOGFILE_TRACE, - "Server %s:%d is too much behind the " - "master, %d s. and can't be chosen.", - b->backend_server->name, - b->backend_server->port, - b->backend_server->rlag))); + { + candidate_bref = check_candidate_bref( + candidate_bref, + &backend_ref[i], + rses->rses_config.rw_slave_select_criteria); + } + else + { + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, + "Server %s:%d is too much behind the " + "master, %d s. and can't be chosen.", + b->backend_server->name, + b->backend_server->port, + b->backend_server->rlag))); + } } } /*< for */ /** Assign selected DCB's pointer value */ @@ -1473,6 +1476,12 @@ static route_target_t get_route_target ( QUERY_IS_TYPE(qtype, QUERY_TYPE_UNKNOWN))); target = TARGET_MASTER; } +#if defined(SS_EXTRA_DEBUG) + LOGIF(LT, (skygw_log_write( + LOGFILE_TRACE, + "Selected target \"%s\"", + STRTARGET(target)))); +#endif return target; } @@ -2106,7 +2115,7 @@ static int routeQuery( rlag_max))); } } - } + } else if (TARGET_IS_SLAVE(route_target)) { btype = BE_SLAVE; @@ -2125,6 +2134,14 @@ static int routeQuery( rlag_max); if (succp) { +#if defined(SS_EXTRA_DEBUG) + LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, + "Found DCB for slave."))); +#endif + ss_dassert(get_bref_from_dcb(router_cli_ses, target_dcb) != + router_cli_ses->rses_master_ref); + ss_dassert(get_root_master_bref(router_cli_ses) == + router_cli_ses->rses_master_ref); atomic_add(&inst->stats.n_slave, 1); } else @@ -2189,7 +2206,7 @@ static int routeQuery( LOGIF(LT, (skygw_log_write( LOGFILE_TRACE, - "Route query to %s\t%s:%d <", + "Route query to %s \t%s:%d <", (SERVER_IS_MASTER(bref->bref_backend->backend_server) ? "master" : "slave"), bref->bref_backend->backend_server->name, @@ -3933,12 +3950,12 @@ static bool route_session_write( { LOGIF(LT, (skygw_log_write( LOGFILE_TRACE, - "Route query to %s\t%s:%d%s", + "Route query to %s \t%s:%d%s", (SERVER_IS_MASTER(backend_ref[i].bref_backend->backend_server) ? "master" : "slave"), backend_ref[i].bref_backend->backend_server->name, backend_ref[i].bref_backend->backend_server->port, - (i+1==router_cli_ses->rses_nbackends ? " <" : "")))); + (i+1==router_cli_ses->rses_nbackends ? " <" : " ")))); } if (BREF_IS_IN_USE((&backend_ref[i]))) @@ -3988,12 +4005,12 @@ static bool route_session_write( { LOGIF(LT, (skygw_log_write( LOGFILE_TRACE, - "Route query to %s\t%s:%d%s", + "Route query to %s \t%s:%d%s", (SERVER_IS_MASTER(backend_ref[i].bref_backend->backend_server) ? "master" : "slave"), backend_ref[i].bref_backend->backend_server->name, backend_ref[i].bref_backend->backend_server->port, - (i+1==router_cli_ses->rses_nbackends ? " <" : "")))); + (i+1==router_cli_ses->rses_nbackends ? " <" : " ")))); } scur = backend_ref_get_sescmd_cursor(&backend_ref[i]); diff --git a/server/modules/routing/readwritesplit/test/rwsplit.sh b/server/modules/routing/readwritesplit/test/rwsplit.sh index 7c9ec5edb..fa50ae6b7 100755 --- a/server/modules/routing/readwritesplit/test/rwsplit.sh +++ b/server/modules/routing/readwritesplit/test/rwsplit.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash NARGS=7 TLOG=$1 THOST=$2 diff --git a/utils/skygw_debug.h b/utils/skygw_debug.h index 7294a7ba6..1ade113a3 100644 --- a/utils/skygw_debug.h +++ b/utils/skygw_debug.h @@ -262,6 +262,12 @@ typedef enum skygw_chk_t { (SERVER_IS_RELAY_SERVER(s) ? "RUNNING RELAY" : \ (SERVER_IS_RUNNING(s) ? "RUNNING (only)" : "NO STATUS"))))))) +#define STRTARGET(t) (t == TARGET_ALL ? "TARGET_ALL" : \ + (t == TARGET_MASTER ? "TARGET_MASTER" : \ + (t == TARGET_SLAVE ? "TARGET_SLAVE" : \ + (t == TARGET_UNDEFINED ? "TARGET_UNDEFINED" : \ + "Unknown target value")))) + #define BREFSRV(b) (b->bref_backend->backend_server) diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 8e285c6a2..3e8e15e67 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -1412,7 +1412,6 @@ int simple_mutex_unlock( err, strerror(errno)); perror("simple_mutex : "); - ss_dassert(sm->sm_mutex.__data.__nusers >= 0); } else { /** * Note that these updates are not protected. @@ -1680,12 +1679,12 @@ static bool file_write_header( if (wbytes1 != 1 || wbytes2 != 1 || wbytes3 != 1 || wbytes4 != 1) { fprintf(stderr, - "* Writing header %s %s %s to %s failed.\n", + "\nError : Writing header %s %s %s %s failed.\n", header_buf1, header_buf2, header_buf3, header_buf4); - perror("Logfile header write.\n"); + perror("Logfile header write"); goto return_succp; } #endif @@ -1757,11 +1756,11 @@ static bool file_write_footer( if (wbytes1 != 1 || wbytes3 != 1 || wbytes4 != 1) { fprintf(stderr, - "* Writing header %s %s to %s failed.\n", + "\nError : Writing header %s %s to %s failed.\n", header_buf1, header_buf3, header_buf4); - perror("Logfile header write.\n"); + perror("Logfile header write"); goto return_succp; } #endif @@ -1875,7 +1874,7 @@ skygw_file_t* skygw_file_init( int eno = errno; errno = 0; fprintf(stderr, - "* Writing header of log file %s failed due %d, %s.\n", + "\nError : Writing header of log file %s failed due %d, %s.\n", file->sf_fname, eno, strerror(eno));