From 91e0bb2b8220114829b59dc0f54947b7f6e9e971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sat, 4 Jun 2016 10:10:27 +0000 Subject: [PATCH] Preparing more test case for prepared stataments --- include/MySQL_PreparedStatement.h | 15 +- lib/MySQL_PreparedStatement.cpp | 14 ++ test/PrepStmt/.gitignore | 2 + test/PrepStmt/Makefile | 6 + test/PrepStmt/client3.cpp | 4 +- test/PrepStmt/client5.cpp | 267 ++++++++++++++++++++++++++++++ 6 files changed, 305 insertions(+), 3 deletions(-) create mode 100644 test/PrepStmt/client5.cpp diff --git a/include/MySQL_PreparedStatement.h b/include/MySQL_PreparedStatement.h index 9c7203fb3..3e6909c24 100644 --- a/include/MySQL_PreparedStatement.h +++ b/include/MySQL_PreparedStatement.h @@ -5,16 +5,23 @@ #include "cpp.h" +// class MySQL_STMTs_local assiciates a global statement ID with a local statement ID for a specific connection class MySQL_STMTs_local { private: + unsigned int num_entries; std::map m; public: MySQL_STMTs_local() { + num_entries=0; } ~MySQL_STMTs_local(); // we declare it here to be inline void insert(uint32_t global_statement_id, MYSQL_STMT *stmt) { - m.insert(std::make_pair(global_statement_id, stmt)); + std::pair::iterator,bool> ret; + ret=m.insert(std::make_pair(global_statement_id, stmt)); + if (ret.second==true) { + num_entries++; + } } // we declare it here to be inline MYSQL_STMT * find(uint32_t global_statement_id) { @@ -24,9 +31,15 @@ class MySQL_STMTs_local { } return NULL; // not found } + bool erase(uint32_t global_statement_id); uint64_t compute_hash(unsigned int hostgroup, char *user, char *schema, char *query, unsigned int query_length); }; + + +// class MySQL_STMT_Global_info represents information about a MySQL Prepared Statement +// it is an internal representation of prepared statement +// it include all metadata associated with it class MySQL_STMT_Global_info { private: void compute_hash(); diff --git a/lib/MySQL_PreparedStatement.cpp b/lib/MySQL_PreparedStatement.cpp index 879c01829..d86a5f093 100644 --- a/lib/MySQL_PreparedStatement.cpp +++ b/lib/MySQL_PreparedStatement.cpp @@ -71,6 +71,20 @@ MySQL_STMTs_local::~MySQL_STMTs_local() { m.erase(m.begin(),m.end()); } +bool MySQL_STMTs_local::erase(uint32_t global_statement_id) { + auto s=m.find(global_statement_id); + if (s!=m.end()) { // found + if (num_entries>1000) { + MYSQL_STMT *stmt=s->second; + mysql_stmt_close(stmt); + m.erase(s); + num_entries--; + return true; // we truly removed the prepared statement + } + } + return false; // we don't really remove the prepared statement +} + MySQL_STMT_Manager::MySQL_STMT_Manager() { spinlock_rwlock_init(&rwlock); next_statement_id=1; // we initialize this as 1 because we 0 is not allowed diff --git a/test/PrepStmt/.gitignore b/test/PrepStmt/.gitignore index 79629c502..290524341 100644 --- a/test/PrepStmt/.gitignore +++ b/test/PrepStmt/.gitignore @@ -2,3 +2,5 @@ client1 client2 client3 client4 +client5 +client6 diff --git a/test/PrepStmt/Makefile b/test/PrepStmt/Makefile index 05b2f4066..1bcd6bdb8 100644 --- a/test/PrepStmt/Makefile +++ b/test/PrepStmt/Makefile @@ -62,6 +62,12 @@ client2: client2.cpp client3: client3.cpp $(CXX) -o $@ $@.cpp $(LIBPROXYSQLAR) $(MYCPPFLAGS) $(CPPFLAGS) $(LDIRS) $(LIBS) $(LDFLAGS) $(MYLIBS) +client4: client4.cpp + $(CXX) -o $@ $@.cpp $(LIBPROXYSQLAR) $(MYCPPFLAGS) $(CPPFLAGS) $(LDIRS) $(LIBS) $(LDFLAGS) $(MYLIBS) + +client5: client5.cpp + $(CXX) -o $@ $@.cpp $(LIBPROXYSQLAR) $(MYCPPFLAGS) $(CPPFLAGS) $(LDIRS) $(LIBS) $(LDFLAGS) $(MYLIBS) + default: $(EXECUTABLE) diff --git a/test/PrepStmt/client3.cpp b/test/PrepStmt/client3.cpp index d6f8fe34e..51c8c2043 100644 --- a/test/PrepStmt/client3.cpp +++ b/test/PrepStmt/client3.cpp @@ -101,8 +101,8 @@ void * mysql_thread(int tid) { bl=strlen(buff); uint64_t hash=local_stmts->compute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); - if (a==NULL) { - if (mysql_stmt_prepare(stmt[i], buff, bl)) { + if (a==NULL) { // no prepared statement was found in global + if (mysql_stmt_prepare(stmt[i], buff, bl)) { // the prepared statement is created fprintf(stderr, " mysql_stmt_prepare(), failed: %s\n" , mysql_stmt_error(stmt[i])); exit(EXIT_FAILURE); } diff --git a/test/PrepStmt/client5.cpp b/test/PrepStmt/client5.cpp new file mode 100644 index 000000000..9c878cdf5 --- /dev/null +++ b/test/PrepStmt/client5.cpp @@ -0,0 +1,267 @@ +#include "proxysql.h" +#include "cpp.h" + +#include +#include + +#define QUERY1 "SELECT ?" +#define NUMPREP 100000 +#define NUMPRO 20000 +//#define NUMPREP 160 +//#define NUMPRO 4 +#define LOOPS 1 +#define USER "root" +#define SCHEMA "test" + +#define NTHREADS 4 + +MySQL_STMT_Manager *GloMyStmt; + +typedef struct _thread_data_t { + std::thread *thread; + MYSQL *mysql; + MYSQL_STMT **stmt; +} thread_data_t; + + +thread_data_t **GloThrData; + +struct cpu_timer +{ + ~cpu_timer() + { + auto end = std::clock() ; + std::cout << double( end - begin ) / CLOCKS_PER_SEC << " secs.\n" ; + }; + + const std::clock_t begin = std::clock() ; +}; + + +int run_stmt(MYSQL_STMT *stmt, int int_data) { + MYSQL_BIND bind[1]; + MYSQL_RES *prepare_meta_result; + bind[0].buffer_type= MYSQL_TYPE_LONG; + bind[0].buffer= (char *)&int_data; + bind[0].is_null= 0; + bind[0].length= 0; + if (mysql_stmt_bind_param(stmt, bind)) { + fprintf(stderr, " mysql_stmt_bind_param() failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + prepare_meta_result = mysql_stmt_result_metadata(stmt); // FIXME: no error check + if (mysql_stmt_execute(stmt)) { + fprintf(stderr, " mysql_stmt_execute(), 1 failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } +// memset(bind, 0, sizeof(bind)); + if (mysql_stmt_store_result(stmt)) { + fprintf(stderr, " mysql_stmt_store_result() failed\n"); + fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + mysql_free_result(prepare_meta_result); + return 0; +} + + +void * mysql_thread(int tid) { + std::mt19937 mt_rand(time(0)); + + thread_data_t *THD; + THD=GloThrData[tid]; + + // in this version, each mysql thread has just ONE connection + // for now we use blocking API + MYSQL *mysql; + + //MYSQL_STMT **stmt; + + // we intialize the local mapping : MySQL_STMTs_local() + MySQL_STMTs_local *local_stmts=new MySQL_STMTs_local(); + + // we initialize a MYSQL structure + THD->mysql = mysql_init(NULL); + mysql=THD->mysql; + + char buff[128]; + unsigned int bl=0; + + // we establish a connection to the database + if (!mysql_real_connect(mysql,"127.0.0.1",USER,"",SCHEMA,3306,NULL,0)) { + fprintf(stderr, "Failed to connect to database: Error: %s\n", mysql_error(mysql)); + exit(EXIT_FAILURE); + } + int i; + + // array of (MYSQL_STMT *) ; we don't use it in this version + //stmt=(MYSQL_STMT **)malloc(sizeof(MYSQL_STMT*)*NUMPREP); + + MYSQL_STMT *stmt; + { + cpu_timer t; + // in this loop we create only some the prepared statements + for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); + MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); + if (a==NULL) { // no prepared statement was found in global + stmt = mysql_stmt_init(mysql); + if (!stmt) { + fprintf(stderr, " mysql_stmt_init(), out of memory\n"); + exit(EXIT_FAILURE); + } + if (mysql_stmt_prepare(stmt, buff, bl)) { // the prepared statement is created + fprintf(stderr, " mysql_stmt_prepare(), failed: %s\n" , mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + uint32_t stmid=GloMyStmt->add_prepared_statement(0,(char *)USER,(char *)SCHEMA,buff,bl,stmt); + if (NUMPRO < 32) + fprintf(stdout, "SERVER_statement_id=%lu , PROXY_statement_id=%u\n", stmt->stmt_id, stmid); + local_stmts->insert(stmid,stmt); + } + } + fprintf(stdout, "Prepared statements: %u client, %u proxy/server. ", i, GloMyStmt->total_prepared_statements()); + fprintf(stdout, "Created in: "); + } + { + unsigned int founds=0; + cpu_timer t; + for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); + //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); + //if (a) founds++; + } + fprintf(stdout, "Computed %u random strings in: ", i); + } + { + unsigned int founds=0; + cpu_timer t; + for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); + //MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); + //if (a) founds++; + } + fprintf(stdout, "Computed %u hashes in: ", i); + } + { + unsigned int founds=0; + cpu_timer t; + for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); + MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); + if (a) founds++; + } + fprintf(stdout, "Found %u prepared statements searching by hash in: ", founds); + } + + { + unsigned int founds=0; + unsigned int created=0; + unsigned int executed=0; + cpu_timer t; + for (i=0; icompute_hash(0,(char *)USER,(char *)SCHEMA,buff,bl); + MySQL_STMT_Global_info *a=GloMyStmt->find_prepared_statement_by_hash(hash); + if (a) { + // we have a prepared statement, we can run it + MYSQL_STMT *stm=local_stmts->find(a->statement_id); + if (stm) { // the statement exists in local + run_stmt(stm,(uint32_t)mt_rand()); + founds++; + executed++; + local_stmts->erase(a->statement_id); + } else { // the statement doesn't exist locally + stmt = mysql_stmt_init(mysql); + if (!stmt) { + fprintf(stderr, " mysql_stmt_init(), out of memory\n"); + exit(EXIT_FAILURE); + } + if (mysql_stmt_prepare(stmt, buff, bl)) { // the prepared statement is created + fprintf(stderr, " mysql_stmt_prepare(), failed: %s\n" , mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + local_stmts->insert(a->statement_id,stmt); + run_stmt(stmt,(uint32_t)mt_rand()); + created++; + executed++; + local_stmts->erase(a->statement_id); + } + } else { // no prepared statement was found in global + stmt = mysql_stmt_init(mysql); + if (!stmt) { + fprintf(stderr, " mysql_stmt_init(), out of memory\n"); + exit(EXIT_FAILURE); + } + if (mysql_stmt_prepare(stmt, buff, bl)) { // the prepared statement is created + fprintf(stderr, " mysql_stmt_prepare(), failed: %s\n" , mysql_stmt_error(stmt)); + exit(EXIT_FAILURE); + } + uint32_t stmid=GloMyStmt->add_prepared_statement(0,(char *)USER,(char *)SCHEMA,buff,bl,stmt); + if (NUMPRO < 32) + fprintf(stdout, "SERVER_statement_id=%lu , PROXY_statement_id=%u\n", stmt->stmt_id, stmid); + local_stmts->insert(stmid,stmt); + run_stmt(stmt,(uint32_t)mt_rand()); + created++; + executed++; + local_stmts->erase(stmid); + } + } + fprintf(stdout, "Found %u , created %u and executed %u prepared statements in: ", founds, created, executed); + } +/* + { + // for comparison, we run also queries in TEXT protocol + cpu_timer t; + for (i=0; ithread = new std::thread(&mysql_thread,i); + } + // wait for the threads to complete + for (i=0; ithread->join(); + } + return 0; +}