 f79e8b108c
			
		
	
	f79e8b108c
	
	
	
		
			
			The test appears to fail to connect to MaxScale due to unavailability of connections. Theoretically, the improvement in accept speed due to SO_REUSEPORT additions could explain the faster exhaustion of available sockets.
		
			
				
	
	
		
			147 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * @file bug601.cpp regression case for bug 601 ("COM_CHANGE_USER fails with correct user/pwd if executed
 | |
|  * during authentication")
 | |
|  * - configure Maxscale.cnf to use only one thread
 | |
|  * - in 100 parallel threads start to open/close session
 | |
|  * - do change_user 2000 times
 | |
|  * - check all change_user are ok
 | |
|  * - check Mascale is alive
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  *  Vilho Raatikka 2014-10-30 14:30:57 UTC
 | |
|  *  If COM_CHANGE_USER is executed while backend protocol's state is not yet MYSQL_AUTH_RECV it will fail in
 | |
|  * the backend.
 | |
|  *
 | |
|  *  If MaxScale uses multiple worked threads this occurs rarely and it would be possible to easily suspend
 | |
|  * execution of COM_CHANGE_USER.
 | |
|  *
 | |
|  *  If MaxScale uses one worker thread then there's currently no way to suspend execution. It would require
 | |
|  * thread to put current task on hold, complete authentication task and return to COM_CHANGE_USER execution.
 | |
|  *
 | |
|  *  In theory it is possible to add an event to client's DCB and let it become notified in the same way than
 | |
|  * events that occur in sockets. It would have to be added first (not last) and ensure that no other command
 | |
|  * is executed before it.
 | |
|  *
 | |
|  *  Since this is the only case known where this would be necessary, it could be enough to add a "pending auth
 | |
|  * change" pointer in client's protocol object which would be checked before thread returns to epoll_wait
 | |
|  * after completing the authentication.
 | |
|  *  Comment 1 Massimiliano 2014-11-07 17:01:29 UTC
 | |
|  *  Current code in develop branch let COM_CHANGE_USER work fine.
 | |
|  *
 | |
|  *  I noticed sometime a failed authentication issue using only.
 | |
|  *  This because backend protocol's state is not yet MYSQL_AUTH_RECV and necessary data for succesfull backend
 | |
|  * change user (such as scramble data from handshake) may be not available.
 | |
|  *
 | |
|  *
 | |
|  *  I put a query before change_user and the issue doesn't appear: that's another proof.
 | |
|  *  Comment 2 Vilho Raatikka 2014-11-13 15:54:15 UTC
 | |
|  *  In gw_change_user->gw_send_change_user_to_backend authentication message was sent to backend server before
 | |
|  * backend had its scramble data. That caused authentication to fail.
 | |
|  *  Comment 3 Vilho Raatikka 2014-11-13 15:58:34 UTC
 | |
|  *  if (func.auth ==)gw_change_user->gw_send_change_user_to_backend is called before backend has its scramble,
 | |
|  * auth packet is set to backend's delauqueue instead of writing it to backend. When backend_write_delayqueue
 | |
|  * is called COM_CHANGE_USER packets are rewritten with backend's current data.
 | |
|  */
 | |
| 
 | |
| 
 | |
| #include <iostream>
 | |
| #include "testconnections.h"
 | |
| 
 | |
| using namespace std;
 | |
| 
 | |
| pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
 | |
| int exit_flag = 0;
 | |
| 
 | |
| TestConnections* Test;
 | |
| 
 | |
| void* parall_traffic(void* ptr);
 | |
| 
 | |
| 
 | |
| int main(int argc, char* argv[])
 | |
| {
 | |
|     int iterations = 1000;
 | |
|     Test = new TestConnections(argc, argv);
 | |
|     if (Test->smoke)
 | |
|     {
 | |
|         iterations = 100;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     pthread_t parall_traffic1[100];
 | |
| 
 | |
|     Test->set_timeout(60);
 | |
|     Test->repl->connect();
 | |
|     Test->repl->execute_query_all_nodes((char*) "set global max_connect_errors=1000;");
 | |
|     Test->repl->execute_query_all_nodes((char*) "set global max_connections=1000;");
 | |
| 
 | |
|     Test->maxscales->connect_maxscale(0);
 | |
|     Test->tprintf("Creating one user 'user@%%'");
 | |
|     execute_query_silent(Test->maxscales->conn_rwsplit[0], (char*) "DROP USER user@'%'");
 | |
|     Test->try_query(Test->maxscales->conn_rwsplit[0], (char*) "CREATE USER user@'%%' identified by 'pass2'");
 | |
|     Test->try_query(Test->maxscales->conn_rwsplit[0], (char*) "GRANT SELECT ON test.* TO user@'%%';");
 | |
|     Test->try_query(Test->maxscales->conn_rwsplit[0], (char*) "FLUSH PRIVILEGES;");
 | |
| 
 | |
|     Test->tprintf("Starting parallel thread which opens/closes session in the loop");
 | |
| 
 | |
|     for (int j = 0; j < 25; j++)
 | |
|     {
 | |
|         pthread_create(¶ll_traffic1[j], NULL, parall_traffic, NULL);
 | |
|     }
 | |
| 
 | |
|     Test->tprintf("Doing change_user in the loop");
 | |
|     for (int i = 0; i < iterations; i++)
 | |
|     {
 | |
|         Test->set_timeout(15);
 | |
|         Test->add_result(mysql_change_user(Test->maxscales->conn_rwsplit[0], "user", "pass2", (char*) "test"),
 | |
|                          "change_user failed! %s", mysql_error(Test->maxscales->conn_rwsplit[0]));
 | |
|         Test->add_result(mysql_change_user(Test->maxscales->conn_rwsplit[0], Test->maxscales->user_name,
 | |
|                                            Test->maxscales->password,
 | |
|                                            (char*) "test"), "change_user failed! %s",
 | |
|                          mysql_error(Test->maxscales->conn_rwsplit[0]));
 | |
|     }
 | |
| 
 | |
|     Test->tprintf("Waiting for all threads to finish");
 | |
|     exit_flag = 1;
 | |
|     for (int j = 0; j < 25; j++)
 | |
|     {
 | |
|         Test->set_timeout(30);
 | |
|         pthread_join(parall_traffic1[j], NULL);
 | |
|     }
 | |
|     Test->tprintf("All threads are finished");
 | |
|     Test->repl->flush_hosts();
 | |
| 
 | |
|     Test->tprintf("Change user to '%s' in order to be able to DROP user", Test->maxscales->user_name);
 | |
|     Test->set_timeout(30);
 | |
|     mysql_change_user(Test->maxscales->conn_rwsplit[0],
 | |
|                       Test->maxscales->user_name,
 | |
|                       Test->maxscales->password,
 | |
|                       NULL);
 | |
| 
 | |
|     Test->tprintf("Dropping user", Test->maxscales->user_name);
 | |
|     Test->try_query(Test->maxscales->conn_rwsplit[0], (char*) "DROP USER user@'%%';");
 | |
| 
 | |
|     Test->maxscales->verbose = true;
 | |
|     Test->check_maxscale_alive(0);
 | |
|     Test->maxscales->verbose = false;
 | |
| 
 | |
|     int rval = Test->global_result;
 | |
|     delete Test;
 | |
|     return rval;
 | |
| }
 | |
| 
 | |
| void* parall_traffic(void* ptr)
 | |
| {
 | |
|     MYSQL* conn;
 | |
|     while (exit_flag == 0)
 | |
|     {
 | |
|         conn = Test->maxscales->open_rwsplit_connection(0);
 | |
|         mysql_close(conn);
 | |
|         if (Test->backend_ssl)
 | |
|         {
 | |
|             sleep(1);
 | |
|         }
 | |
|     }
 | |
|     return NULL;
 | |
| }
 |