├── .gitignore ├── LICENSE ├── README.rst ├── include ├── admin_sqlite.h ├── debug.h ├── external.h ├── fundadb.h ├── global_variables.h ├── lutils.h ├── mysql_protocol.h ├── proxysql.h ├── proxysql_macros.h └── structs.h ├── sqlite3 ├── shell.c ├── sqlite3.c └── sqlite3.h ├── src ├── Makefile ├── Makefile_nostatic ├── admin.c ├── admin_sqlite.c ├── debug.c ├── fundadb_hash.c ├── global_variables.c ├── l_utils.c ├── main.c ├── mysql_backend.c ├── mysql_connpool.c ├── mysql_data_stream.c ├── mysql_handler.c ├── mysql_protocol.c ├── mysql_session.c ├── network.c ├── perf.data ├── proxysqlHTTPd ├── proxysql_interactive_config.pl ├── threads.c ├── tokenizer.c └── utils.c └── tests ├── 1.c ├── Makefile ├── connect_speed ├── connect_speed.c ├── reconnect.c ├── sysbench.txt └── tests.cnf /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | 5 | # Libraries 6 | *.lib 7 | *.a 8 | 9 | # Shared objects (inc. Windows DLLs) 10 | *.dll 11 | *.so 12 | *.so.* 13 | *.dylib 14 | 15 | # Executables 16 | *.exe 17 | *.out 18 | *.app 19 | 20 | *.swp 21 | 22 | # SQLITE files 23 | *.db 24 | *.db-shm 25 | *.db-wal 26 | 27 | 28 | #pidfile and errorlog 29 | *.pid 30 | *.log 31 | *.err 32 | 33 | #binary 34 | proxysql 35 | 36 | #core 37 | core 38 | 39 | #config 40 | proxysql.cnf* 41 | 42 | jeprof*heap 43 | 44 | notes.txt 45 | architecture.txt 46 | -------------------------------------------------------------------------------- /include/admin_sqlite.h: -------------------------------------------------------------------------------- 1 | #define ADMIN_SQLITE_TABLE_SERVER_STATUS "CREATE TABLE server_status ( status INT NOT NULL PRIMARY KEY, status_desc VARCHAR NOT NULL, UNIQUE(status_desc) )" 2 | #define ADMIN_SQLITE_TABLE_SERVERS "CREATE TABLE servers ( hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 3306 , read_only INT NOT NULL DEFAULT 1, status INT NOT NULL DEFAULT ('OFFLINE') REFERENCES server_status(status) , PRIMARY KEY(hostname, port) )" 3 | #define ADMIN_SQLITE_TABLE_HOSTGROUPS "CREATE TABLE hostgroups ( hostgroup_id INT NOT NULL DEFAULT 0, hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 3306, FOREIGN KEY (hostname, port) REFERENCES servers (hostname, port) , PRIMARY KEY (hostgroup_id, hostname, port) )" 4 | #define ADMIN_SQLITE_TABLE_USERS "CREATE TABLE users ( username VARCHAR NOT NULL PRIMARY KEY , password VARCHAR , active INT NOT NULL DEFAULT 1)" 5 | #define ADMIN_SQLITE_TABLE_DEBUG_LEVELS "CREATE TABLE debug_levels (module VARCHAR NOT NULL PRIMARY KEY, verbosity INT NOT NULL DEFAULT 0)" 6 | #define ADMIN_SQLITE_TABLE_GLOBAL_VARIABLES "CREATE TABLE global_variables ( name VARCHAR NOT NULL PRIMARY KEY , value VARCHAR NOT NULL )" 7 | #define ADMIN_SQLITE_TABLE_QUERY_RULES "CREATE TABLE query_rules (rule_id INT NOT NULL PRIMARY KEY, active INT NOT NULL DEFAULT 0, username VARCHAR, schemaname VARCHAR, flagIN INT NOT NULL DEFAULT 0, match_pattern VARCHAR NOT NULL, negate_match_pattern INT NOT NULL DEFAULT 0, flagOUT INT NOT NULL DEFAULT 0, replace_pattern VARCHAR, destination_hostgroup INT NOT NULL DEFAULT 0, audit_log INT NOT NULL DEFAULT 0, performance_log INT NOT NULL DEFAULT 0, cache_tag INT NOT NULL DEFAULT 0, invalidate_cache_tag INT NOT NULL DEFAULT 0, invalidate_cache_pattern VARCHAR, cache_ttl INT NOT NULL DEFAULT 0)" 8 | #define ADMIN_SQLITE_TABLE_DEFAULT_HOSTGROUPS "CREATE TABLE default_hostgroups (username VARCHAR, schemaname VARCHAR, hostgroup_id INT NOT NULL DEFAULT 0, PRIMARY KEY(username, schemaname))" 9 | 10 | /* 11 | #define ADMIN_SQLITE_DUMP_TABLE_SERVER_STATUS "SELECT 'INSERT INTO server_status VALUES (' || quote(status) || ',' || quote(status_desc) || ')' FROM server_status" 12 | #define ADMIN_SQLITE_DUMP_TABLE_SERVERS "SELECT 'INSERT INTO servers VALUES (' || quote(hostname) || ',' || quote(port) || ',' || quote(read_only) || ',' || quote(status) || ')' FROM servers" 13 | #define ADMIN_SQLITE_DUMP_TABLE_HOSTGROUPS "SELECT 'INSERT INTO hostgroups VALUES (' || quote(hostgroup_id) || ',' || quote(hostname) || ',' || quote(port) || ')' FROM hostgroups" 14 | #define ADMIN_SQLITE_DUMP_TABLE_USERS "SELECT 'INSERT INTO users VALUES (' || quote(username) || ',' || quote(password) || ',' || quote(active) || ')' FROM users" 15 | #define ADMIN_SQLITE_DUMP_TABLE_DEBUG_LEVELS "SELECT 'INSERT INTO debug_levels VALUES (' || quote(module) || ',' || quote(verbosity) || ')' FROM debug_levels" 16 | 17 | #define ADMIN_SQLITE_DUMP_TABLE_QUERY_RULES "SELECT 'INSERT INTO query_rules VALUES (' || quote(rule_id) || ',' || quote(active) || ',' || quote(username) || ',' || quote(schemaname) || ',' || quote(flagIN) || ',' || quote(match_pattern) || ',' || quote(negate_match_pattern) || ',' || quote(flagOUT) || ',' || quote(replace_pattern) || ',' || quote(destination_hostgroup) || ',' || quote(audit_log) || ',' || quote(performance_log) || ',' || quote(cache_tag) || ',' || quote(invalidate_cache_tag) || ',' || quote(invalidate_cache_pattern) || ',' || quote(cache_ttl) || ')' FROM query_rules" 18 | */ 19 | 20 | #define ADMIN_SQLITE_TABLE_BACKUP_SERVER_STATUS "CREATE TABLE server_status ( status INT NOT NULL PRIMARY KEY, status_desc VARCHAR NOT NULL, UNIQUE(status_desc) )" 21 | #define ADMIN_SQLITE_TABLE_BACKUP_SERVERS "CREATE TABLE servers ( hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 3306 , read_only INT NOT NULL DEFAULT 1, status INT NOT NULL DEFAULT ('OFFLINE') REFERENCES server_status(status) , PRIMARY KEY(hostname, port) )" 22 | #define ADMIN_SQLITE_TABLE_BACKUP_HOSTGROUPS "CREATE TABLE hostgroups ( hostgroup_id INT NOT NULL DEFAULT 0, hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 3306, FOREIGN KEY (hostname, port) REFERENCES servers (hostname, port) , PRIMARY KEY (hostgroup_id, hostname, port) )" 23 | #define ADMIN_SQLITE_TABLE_BACKUP_USERS "CREATE TABLE users ( username VARCHAR NOT NULL PRIMARY KEY , password VARCHAR , active INT NOT NULL DEFAULT 1)" 24 | #define ADMIN_SQLITE_TABLE_BACKUP_DEBUG_LEVELS "CREATE TABLE debug_levels (module VARCHAR NOT NULL PRIMARY KEY, verbosity INT NOT NULL DEFAULT 0)" 25 | #define ADMIN_SQLITE_TABLE_BACKUP_GLOBAL_VARIABLES "CREATE TABLE global_variables ( name VARCHAR NOT NULL PRIMARY KEY , value VARCHAR NOT NULL )" 26 | #define ADMIN_SQLITE_TABLE_BACKUP_QUERY_RULES "CREATE TABLE query_rules (rule_id INT NOT NULL PRIMARY KEY, active INT NOT NULL DEFAULT 0, username VARCHAR, schemaname VARCHAR, flagIN INT NOT NULL DEFAULT 0, match_pattern VARCHAR NOT NULL, negate_match_pattern INT NOT NULL DEFAULT 0, flagOUT INT NOT NULL DEFAULT 0, replace_pattern VARCHAR, destination_hostgroup INT NOT NULL DEFAULT 0, audit_log INT NOT NULL DEFAULT 0, performance_log INT NOT NULL DEFAULT 0, cache_tag INT NOT NULL DEFAULT 0, invalidate_cache_tag INT NOT NULL DEFAULT 0, invalidate_cache_pattern VARCHAR, cache_ttl INT NOT NULL DEFAULT 0)" 27 | 28 | //#define ADMIN_SQLITE_TABLE_SERVERS "CREATE TABLE servers ( name VARCHAR NOT NULL PRIMARY KEY, hostname VARCHAR NOT NULL , port INT NOT NULL DEFAULT 3306 , read_only INT NOT NULL DEFAULT 1, status VARCHAR NOT NULL DEFAULT ('OFFLINE') REFERENCES server_status(status), hostgroup INT NOT NULL DEFAULT 0)" 29 | //#define ADMIN_SQLITE_TABLE_QUERY_RULES "CREATE TABLE query_rules (rule_id INT NOT NULL PRIMARY KEY, username VARCHAR, schemaname VARCHAR, flagIN INT NOT NULL DEFAULT 0, match_pattern VARCHAR NOT NULL, negate_match_pattern INT NOT NULL DEFAULT 0, flagOUT INT NOT NULL DEFAULT 0, replace_pattern VARCHAR, destination_hostgroup INT NOT NULL DEFAULT 0, audit_log INT NOT NULL DEFAULT 0, performance_log INT NOT NULL DEFAULT 0, caching_ttl INT NOT NULL DEFAULT 0)" 30 | //#define ADMIN_SQLITE_TABLE_QUERY_RULES "CREATE TABLE query_rules (rule_id INT NOT NULL PRIMARY KEY, username VARCHAR, schemaname VARCHAR, flagIN INT NOT NULL DEFAULT 0, match_pattern VARCHAR NOT NULL, negate_match_pattern INT NOT NULL DEFAULT 0, flagOUT INT NOT NULL DEFAULT 0, replace_pattern VARCHAR, destination_hostgroup INT NOT NULL DEFAULT 0 REFERENCES hostgroups(hostgroup_id), audit_log INT NOT NULL DEFAULT 0, performance_log INT NOT NULL DEFAULT 0, cache_tag INT NOT NULL DEFAULT 0, invalidate_cache_tag INT NOT NULL DEFAULT 0, invalidate_cache_pattern VARCHAR, cache_ttl INT NOT NULL DEFAULT 0)" 31 | 32 | #define STATSDB_QUERY_STATS "CREATE TABLE query_stats (timestamp INT NOT NULL, query_digest_md5 TEXT NOT NULL, query_digest_text TEXT NOT NULL, username TEXT NOT NULL, schemaname TEXT NOT NULL, hostgroup_id INT, server_address TEXT, server_port INT, query_time INT NOT NULL, exec_cnt INT NOT NULL, PRIMARY KEY(timestamp, query_digest_md5, username, schemaname, hostgroup_id, server_address, server_port) )" 33 | 34 | #define DEBUGDB_DEBUG_LOG "CREATE TABLE debug_log (timestamp INT NOT NULL, thread_id INT NOT NULL, module TEXT NOT NULL, filename TEXT NOT NULL, line INT NOT NULL, funct TEXT NOT NULL, level INT NOT NULL, message TEXT NOT NULL)" 35 | 36 | #define DUMP_RUNTIME_QUERY_CACHE "DUMP RUNTIME QUERY CACHE" 37 | #define DUMP_RUNTIME_QUERY_RULES "DUMP RUNTIME QUERY RULES" 38 | #define DUMP_RUNTIME_DEFAULT_HOSTGROUPS "DUMP RUNTIME DEFAULT HOSTGROUPS" 39 | 40 | #define CONFIG_SYNC_MEM_TO_DISK "CONFIG SYNC TO DISK" 41 | 42 | struct _admin_sqlite_table_def_t { 43 | char *table_name; 44 | char *table_def; 45 | // char *dumpcmd; 46 | // GPtrArray *dumps[3]; 47 | // GPtrArray *dump_configdb; 48 | // GPtrArray *dump_admindb; 49 | // GPtrArray *dump_monitordb; 50 | }; 51 | 52 | 53 | void mysql_pkt_err_from_sqlite(pkt *, const char *); 54 | int mysql_pkt_to_sqlite_exec(pkt *, mysql_session_t *); 55 | void sqlite3_exec_exit_on_failure(sqlite3 *, const char *); 56 | void sqlite3_flush_debug_levels_mem_to_db(sqlite3 *, int); 57 | int sqlite3_flush_debug_levels_db_to_mem(sqlite3 *); 58 | void sqlite3_flush_users_mem_to_db(sqlite3 *, int, int); 59 | int sqlite3_flush_users_db_to_mem(sqlite3 *); 60 | int sqlite3_flush_default_hostgroups_db_to_mem(sqlite3 *); 61 | void admin_init_sqlite3(); 62 | int sqlite3_flush_servers_db_to_mem(sqlite3 *, int); 63 | void sqlite3_flush_servers_mem_to_db(sqlite3 *, int); 64 | int sqlite3_flush_query_rules_db_to_mem(sqlite3 *); 65 | int sqlite3_dump_runtime_hostgroups(sqlite3 *); 66 | int sqlite3_dump_runtime_query_rules(sqlite3 *); 67 | int sqlite3_dump_runtime_query_cache(sqlite3 *); 68 | int sqlite3_dump_runtime_default_hostgroups(sqlite3 *); 69 | int sqlite3_config_sync_mem_to_disk(); 70 | void __sqlite3_statsdb__flush_query_stats(gpointer, gpointer); 71 | void __sqlite3_debugdb__flush_debugs(sqlite3_stmt *, dbg_msg_t *); 72 | //int sqlite3_dump_runtime_query_rules(); 73 | //int sqlite3_dump_runtime_query_cache(); 74 | 75 | 76 | struct previous_mode_data { 77 | int valid; /* Is there legit data in here? */ 78 | int mode; 79 | int showHeader; 80 | int colWidth[100]; 81 | }; 82 | 83 | 84 | struct callback_data { 85 | sqlite3 *db; /* The database */ 86 | int echoOn; /* True to echo input commands */ 87 | int statsOn; /* True to display memory stats before each finalize */ 88 | int cnt; /* Number of records displayed so far */ 89 | FILE *out; /* Write results here */ 90 | FILE *traceOut; /* Output for sqlite3_trace() */ 91 | int nErr; /* Number of errors seen */ 92 | int mode; /* An output mode setting */ 93 | int writableSchema; /* True if PRAGMA writable_schema=ON */ 94 | int showHeader; /* True to show column names in List or Column mode */ 95 | char *zDestTable; /* Name of destination table when MODE_Insert */ 96 | char separator[20]; /* Separator character for MODE_List */ 97 | int colWidth[100]; /* Requested width of each column when in column mode*/ 98 | int actualWidth[100]; /* Actual width of each column */ 99 | char nullvalue[20]; /* The text to print when a NULL comes back from 100 | ** the database */ 101 | struct previous_mode_data explainPrev; 102 | /* Holds the mode information just before 103 | ** .explain ON */ 104 | char outfile[FILENAME_MAX]; /* Filename for *out */ 105 | const char *zDbFilename; /* name of the database file */ 106 | const char *zVfs; /* Name of VFS to use */ 107 | sqlite3_stmt *pStmt; /* Current statement if any. */ 108 | FILE *pLog; /* Write log output here */ 109 | }; 110 | 111 | 112 | int do_meta_command(char *, struct callback_data *); 113 | 114 | #define sqlite3_exec_exit_on_failure(__db, __str) \ 115 | do { \ 116 | char *err=NULL; \ 117 | sqlite3_exec(__db, __str, NULL, 0, &err); \ 118 | if(err!=NULL) { \ 119 | proxy_error("SQLITE error: %s --- %s\n", err, __str); \ 120 | proxy_debug(PROXY_DEBUG_SQLITE, 1, "SQLITE: Error on %s : %s\n",__str, err); \ 121 | assert(err==NULL); \ 122 | } \ 123 | } while(0) 124 | 125 | -------------------------------------------------------------------------------- /include/debug.h: -------------------------------------------------------------------------------- 1 | struct _debug_level { 2 | enum debug_module module; 3 | int verbosity; 4 | char *name; 5 | }; 6 | 7 | 8 | #ifdef DEBUG 9 | //#define DEBUG_read_from_net 10 | //#define DEBUG_write_to_net 11 | //#define DEBUG_buffer2array 12 | //#define DEBUG_array2buffer 13 | //#define DEBUG_shutfd 14 | //#define DEBUG_mysql_rw_split 15 | //#define DEBUG_poll 16 | //#define DEBUG_COM 17 | //#define DEBUG_auth 18 | //#define DEBUG_mysql_conn 19 | //#define DEBUG_pktalloc 20 | #endif /* DEBUG */ 21 | 22 | 23 | 24 | // proxy_debug_func(module, verbosity, "%d:%s:%d:%s(): LVL#%d : " fmt, syscall(SYS_gettid), __FILE__, __LINE__, __func__ , verbosity , ## __VA_ARGS__); 25 | 26 | #ifdef DEBUG 27 | #define PROXY_TRACE() { proxy_debug(PROXY_DEBUG_GENERIC,10,"TRACE\n"); } 28 | #else 29 | #define PROXY_TRACE() 30 | #endif 31 | 32 | #ifdef DEBUG 33 | #define proxy_debug(module, verbosity, fmt, ...) \ 34 | do { if (gdbg) { \ 35 | proxy_debug_func(module, verbosity, syscall(SYS_gettid), __FILE__, __LINE__, __func__ , fmt, ## __VA_ARGS__); \ 36 | } } while (0) 37 | #else 38 | #define proxy_debug(module, verbosity, fmt, ...) 39 | #endif 40 | 41 | #ifdef DEBUG 42 | //#define proxy_error(fmt, ...) proxy_error_func("%s:%d:%s(): " fmt, __FILE__, __LINE__, __func__ , ## __VA_ARGS__); 43 | #define proxy_error(fmt, ...) \ 44 | do { \ 45 | time_t __timer; \ 46 | char __buffer[25]; \ 47 | struct tm *__tm_info; \ 48 | time(&__timer); \ 49 | __tm_info = localtime(&__timer); \ 50 | strftime(__buffer, 25, "%Y-%m-%d %H:%M:%S", __tm_info); \ 51 | proxy_error_func("%s %s:%d:%s(): " fmt, __buffer, __FILE__, __LINE__, __func__ , ## __VA_ARGS__); \ 52 | } while(0) 53 | #else 54 | #define proxy_error(fmt, ...) \ 55 | do { \ 56 | time_t __timer; \ 57 | char __buffer[25]; \ 58 | struct tm *__tm_info; \ 59 | time(&__timer); \ 60 | __tm_info = localtime(&__timer); \ 61 | strftime(__buffer, 25, "%Y-%m-%d %H:%M:%S", __tm_info); \ 62 | proxy_error_func("%s " fmt , __buffer , ## __VA_ARGS__); \ 63 | } while(0) 64 | #endif 65 | 66 | //void proxy_debug_func(enum debug_module, int, const char *, ...); 67 | void proxy_debug_func(enum debug_module, int, int, const char *, int, const char *, const char *, ...); 68 | void proxy_error_func(const char *, ...); 69 | void crash_handler(int); 70 | void init_debug_struct(); 71 | #ifdef DEBUG 72 | void *debug_logger(); 73 | #endif 74 | -------------------------------------------------------------------------------- /include/external.h: -------------------------------------------------------------------------------- 1 | #undef EXTERN 2 | #undef INITIALIZER 3 | 4 | #ifndef DEFINE_VARIABLES 5 | #define EXTERN extern 6 | #define INITIALIZER(...) /* nothing */ 7 | #else 8 | #define EXTERN /* nothing */ 9 | #define INITIALIZER(...) = __VA_ARGS__ 10 | #endif /* DEFINE_VARIABLES */ 11 | -------------------------------------------------------------------------------- /include/fundadb.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #define num_hashes 24 4 | #define EXPIRE_DROPIT 0 5 | 6 | /* 7 | #define HASH_PURGETIME_USEC 10000000 8 | #define HASH_PURGELOOP_USEC 100000 9 | #define HASH_EXPIRE_MAX 365*24*3600 10 | #define HASH_EXPIRE_DEFAULT 3600 11 | */ 12 | #define UDF_BUFFER_SIZE 255 13 | #define UDF_BUFFER_YES 1 14 | #define UDF_BUFFER_NO 2 15 | 16 | 17 | /* 18 | // the follow macro is used to verify if the connections were initialized 19 | #define CHECK_HASH_INIT if ((__sync_fetch_and_add(&hash_initialized, 0))==0) { strcpy(message, "No avaliable servers"); return 1; } 20 | #define CHECK_QUEUE_INIT if ((__sync_fetch_and_add(&queue_initialized, 0))==0) { strcpy(message, "No avaliable servers"); return 1; } 21 | */ 22 | 23 | #ifdef DEFINE_VARIABLES 24 | unsigned int hash_initialized=0; 25 | //unsigned int queue_initialized=0; 26 | #else 27 | extern unsigned int hash_initialized; 28 | //extern unsigned int queue_initialized; 29 | #endif /* DEFINE_VARIABLES */ 30 | 31 | 32 | //EXTERN fdb_system_var_t fdb_system_var; 33 | //EXTERN fdb_hash_t **fdb_hashes; 34 | 35 | struct __fdb_hash_t { 36 | pthread_rwlock_t lock; 37 | GHashTable *hash; 38 | GPtrArray *ptrArray; 39 | long long dataSize; 40 | long long purgeChunkSize; 41 | long long purgeIdx; 42 | }; 43 | 44 | struct __fdb_hashes_group_t { 45 | fdb_hash_t **fdb_hashes; 46 | int size; 47 | time_t now; 48 | unsigned int hash_expire_default; 49 | long long max_memory_size; 50 | unsigned long long cntDel; 51 | unsigned long long cntGet; 52 | unsigned long long cntGetOK; 53 | unsigned long long cntSet; 54 | unsigned long long cntSetERR; 55 | unsigned long long cntPurge; 56 | unsigned long long size_keys; 57 | unsigned long long size_values; 58 | unsigned long long size_metas; 59 | unsigned long long dataIN; 60 | unsigned long long dataOUT; 61 | }; 62 | 63 | struct __fdb_hash_entry { 64 | char *key; 65 | char *value; 66 | fdb_hash_t *hash; 67 | struct __fdb_hash_entry *self; 68 | unsigned int klen; 69 | unsigned int length; 70 | time_t expire; 71 | time_t access; 72 | int ref_count; 73 | }; 74 | 75 | pkt * fdb_get(fdb_hashes_group_t *, const char *, mysql_session_t *); 76 | gboolean fdb_set(fdb_hashes_group_t * , void *, unsigned int , void *, unsigned int , time_t, gboolean); 77 | long long fdb_del(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); 78 | inline void hash_value_destroy_func(void *); 79 | void fdb_hashes_new(fdb_hashes_group_t *, size_t, unsigned int, unsigned long long); 80 | long long fdb_truncate_all(fdb_hashes_group_t *); 81 | void *purgeHash_thread(void *); 82 | long long fdb_hash_init(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); 83 | long long fdb_hashes_group_free_mem(fdb_hashes_group_t *); 84 | int fdb_hashes_group_used_mem_pct(fdb_hashes_group_t *); 85 | 86 | 87 | // Added by chan ------- 88 | struct __qr_hash_t { 89 | pthread_rwlock_t lock; 90 | GHashTable *c_hash; 91 | GHashTable *p_hash; 92 | struct __qr_hash_entry *c_ptr_first; 93 | struct __qr_hash_entry *p_ptr_first; 94 | time_t modify; 95 | }; 96 | 97 | struct __qr_hash_entry 98 | { 99 | char *key; 100 | struct __qr_hash_entry *value; 101 | volatile unsigned int exec_cnt; 102 | char *query_digest_text; 103 | char *query_digest_md5; 104 | int hostgroup_id; 105 | char *username; 106 | char *schemaname; 107 | char *mysql_server_address; 108 | long query_time; 109 | int mysql_server_port; 110 | }; 111 | 112 | inline void qr_hash_value_destroy_func(void *); 113 | void qr_hashes_new(qr_hash_t *); 114 | void qr_set(char *, char *); 115 | inline void flush_query_stats (gpointer, gpointer); 116 | void *qr_report_thread(void *); 117 | // Added by chan end. 118 | -------------------------------------------------------------------------------- /include/global_variables.h: -------------------------------------------------------------------------------- 1 | 2 | #define CONNECTION_READING_CLIENT 1 3 | #define CONNECTION_WRITING_CLIENT 2 4 | #define CONNECTION_READING_SERVER 4 5 | #define CONNECTION_WRITING_SERVER 8 6 | 7 | 8 | //#define SQLITE_ADMINDB "proxysql.db" 9 | 10 | 11 | #ifdef DEBUG 12 | EXTERN glo_debug_t *glo_debug; 13 | #endif 14 | 15 | #ifdef PROXYMEMTRACK 16 | EXTERN long long __mem_l_alloc_size; 17 | EXTERN long long __mem_l_alloc_count; 18 | EXTERN long long __mem_l_free_size; 19 | EXTERN long long __mem_l_free_count; 20 | EXTERN long long __mem_l_memalign_size; 21 | EXTERN long long __mem_l_memalign_count; 22 | #endif 23 | 24 | //EXTERN static pthread_key_t tsd_key; 25 | extern __thread l_sfp *__thr_sfp; 26 | extern __thread myConnPools __thr_myconnpool; 27 | 28 | EXTERN global_variables glovars; 29 | EXTERN global_mysql_servers glomysrvs; 30 | 31 | EXTERN fdb_hashes_group_t QC; 32 | //EXTERN int QC_version; 33 | 34 | // Added by chan ------- 35 | EXTERN qr_hash_t QR_HASH_T; 36 | // Added by chan end. 37 | 38 | EXTERN global_query_rules_t gloQR; 39 | EXTERN global_default_hostgroups_t gloDefHG; 40 | 41 | EXTERN long long glotimenew; 42 | EXTERN long long glotimeold; 43 | EXTERN myConnPools gloconnpool; 44 | //EXTERN myBackendPools glomybepools; 45 | 46 | //EXTERN mem_superblock_t conn_queue_pool; 47 | EXTERN shared_trash_stack_t myds_pool; 48 | 49 | EXTERN sqlite3 *sqlite3configdb; 50 | EXTERN sqlite3 *sqlite3admindb; 51 | EXTERN sqlite3 *sqlite3monitordb; 52 | EXTERN sqlite3 *sqlite3statsdb; 53 | EXTERN sqlite3 *sqlite3debugdb; 54 | 55 | EXTERN time_t sqlite3admindb_lastupdate; 56 | EXTERN time_t sqlite3monitordb_lastupdate; 57 | EXTERN int sqlite3monitordb_rebuild; 58 | 59 | EXTERN ProxyIPC proxyipc; 60 | 61 | EXTERN int gdbg; // global debug 62 | EXTERN debug_level *gdbg_lvl; // global debug levels 63 | 64 | EXTERN pthread_t thread_qct; 65 | EXTERN pthread_t thread_cppt; 66 | EXTERN pthread_t thread_dbg_logger; 67 | EXTERN pthread_t thread_qr; 68 | 69 | //EXTERN admin_sqlite_table_def_t *table_defs; 70 | 71 | int init_global_variables(GKeyFile *, int); 72 | mysql_server * new_server_master(); 73 | mysql_server * new_server_slave(); 74 | void process_global_variables_from_file(GKeyFile *, int); 75 | void main_opts(const GOptionEntry *, gint *, gchar ***, gchar **); 76 | 77 | void init_glomysrvs(global_variable_entry_t *); 78 | void load_mysql_users_from_file(GKeyFile *); 79 | void load_mysql_servers_list_from_file(GKeyFile *); 80 | void pre_variable_mysql_threads(global_variable_entry_t *); 81 | void post_variable_core_dump_file_size(global_variable_entry_t *); 82 | void post_variable_net_buffer_size(global_variable_entry_t *); 83 | -------------------------------------------------------------------------------- /include/lutils.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | //#define mypkt_alloc() l_alloc(thrLD->sfp,sizeof(pkt)) 4 | //#define mypkt_free1(p) { l_free(thrLD->sfp, p->length, p->data); l_free(thrLD->sfp, sizeof(pkt), p); } 5 | //#define mypkt_free0(p) { l_free(thrLD->sfp, sizeof(pkt), p); } 6 | #define mypkt_alloc() l_alloc(sizeof(pkt)) 7 | #define mypkt_free1(p) { l_free(p->length, p->data); l_free(sizeof(pkt), p); } 8 | #define mypkt_free0(p) { l_free(sizeof(pkt), p); } 9 | 10 | 11 | #define l_ptr_array_index(array,l_index_) ((array)->pdata)[l_index_] 12 | 13 | #define l_ptr_array_free0(array) { l_free(sizeof(LPtrArray),array); } 14 | #define l_ptr_array_free1(array) { if (array->pdata) l_free(sizeof(void *)*array->size,array->pdata); l_free(sizeof(LPtrArray),array); } 15 | 16 | 17 | #define L_SFC_MIN_ELEM_SIZE 8 18 | #define L_SFC_MID_ELEM_SIZE 32 19 | #define L_SFC_MAX_ELEM_SIZE 256 20 | #define L_SFP_ARRAY_MID 3 21 | #define L_SFP_ARRAY_LEN 6 22 | //#define L_SFC_MAX_ELEM_SIZE 32 23 | //#define L_SFP_ARRAY_LEN 3 24 | #define L_SFC_MEM_BLOCK_SIZE 262144 25 | typedef struct _l_stack_t l_stack; 26 | typedef struct _l_super_free_chunk_t l_sfc; 27 | #ifndef L_SFP 28 | #define L_SFP 29 | typedef struct _l_super_free_pool_t l_sfp; 30 | typedef struct _LPtrArray LPtrArray; 31 | #endif 32 | extern __thread l_sfp *__thr_sfp; 33 | 34 | struct _LPtrArray { 35 | void **pdata; 36 | unsigned int len; 37 | unsigned int size; 38 | }; 39 | 40 | 41 | struct _l_stack_t { 42 | l_stack *n; 43 | }; 44 | 45 | struct _l_super_free_chunk_t { 46 | l_stack *stack; 47 | void **mem_blocks; 48 | size_t elem_size; 49 | size_t blocks_cnt; 50 | size_t alloc_cnt; 51 | size_t free_cnt; 52 | size_t __mem_l_free_count; 53 | 54 | }; 55 | 56 | struct _l_super_free_pool_t { 57 | l_sfc sfc[L_SFP_ARRAY_LEN]; 58 | }; 59 | 60 | static inline void l_stack_push (l_stack **s, void *p) { 61 | l_stack *d=(l_stack *)p; 62 | d->n=*s; 63 | *s=d; 64 | } 65 | 66 | static inline void * l_stack_pop (l_stack **s) { 67 | l_stack *d; 68 | d=*s; 69 | if (d) { *s=d->n; d->n=NULL; } 70 | return d; 71 | } 72 | 73 | l_sfp * l_mem_init(); 74 | void l_mem_destroy(l_sfp *); 75 | //void * l_alloc(l_sfp *, size_t); 76 | //void * l_alloc0(l_sfp *, size_t); 77 | //void l_free(l_sfp *, size_t, void *); 78 | void * l_alloc(size_t); 79 | void * l_alloc0(size_t); 80 | void l_free(size_t, void *); 81 | 82 | void * __l_alloc(l_sfp *, size_t); 83 | void __l_free(l_sfp *, size_t, void *); 84 | 85 | LPtrArray *l_ptr_array_sized_new(unsigned int); 86 | LPtrArray *l_ptr_array_new(); 87 | void l_ptr_array_add(LPtrArray *, void *); 88 | void * l_ptr_array_remove_index(LPtrArray *, unsigned int); 89 | void * l_ptr_array_remove_index_fast (LPtrArray *, unsigned int); 90 | int l_ptr_array_remove_fast(LPtrArray *, void *); 91 | -------------------------------------------------------------------------------- /include/mysql_protocol.h: -------------------------------------------------------------------------------- 1 | 2 | //mysql_data_stream_t * mysql_data_stream_init(int, mysql_session_t *); 3 | //void mysql_data_stream_close(mysql_data_stream_t *); 4 | 5 | char *user_password(char *, int); 6 | 7 | //inline int mysql_pkt_get_size(pkt *); 8 | enum MySQL_response_type mysql_response(pkt *); 9 | 10 | typedef struct _rand_struct_t { 11 | unsigned long seed1,seed2,max_value; 12 | double max_value_dbl; 13 | } rand_struct_t; 14 | 15 | 16 | void create_ok_packet(pkt *, unsigned int); 17 | void create_handshake_packet(pkt *, char *); 18 | int check_client_authentication_packet(pkt *, mysql_session_t *); 19 | void create_err_packet(pkt *, unsigned int , uint16_t,char *); 20 | void authenticate_mysql_client_send_OK(mysql_session_t *); 21 | void authenticate_mysql_client_send_ERR(mysql_session_t *, uint16_t , char *); 22 | int mysql_check_alive_and_read_only(const char *, uint16_t); 23 | void proxy_create_random_string(char *, uint , struct rand_struct *); 24 | double proxy_my_rnd(struct rand_struct *); 25 | void proxy_scramble(char *, const char *, const char *); 26 | void proxy_compute_sha1_hash_multi(uint8 *, const char *, int , const char *, int); 27 | void proxy_compute_two_stage_sha1_hash(const char *, size_t, uint8 *, uint8 *); 28 | void proxy_compute_sha1_hash(uint8 *, const char *, int); 29 | void proxy_my_crypt(char *, const uchar *, const uchar *, uint); 30 | int is_transaction_active(pkt *); 31 | int lencint(uint64_t); 32 | uint64_t lencint_unknown(uint64_t); 33 | int writeencint(void *, uint64_t); 34 | int writeencstrnull(void *, const char *); 35 | void myproto_ok_pkt(pkt *, unsigned int , uint64_t , uint64_t, uint16_t, uint16_t); 36 | void myproto_column_count(pkt *, unsigned int , uint64_t); 37 | void myproto_column_def(pkt *, unsigned int , const char *, const char *, const char *, const char *, const char *, uint32_t , uint8_t, uint16_t , uint8_t); 38 | void myproto_eof(pkt *, unsigned int , uint16_t , uint16_t); 39 | void mysql_new_payload_select(pkt *, void *, int); 40 | int parse_change_user_packet(pkt *, mysql_session_t *); 41 | void create_auth_switch_request_packet(pkt *, mysql_session_t *); 42 | int check_auth_switch_response_packet(pkt *, mysql_session_t *); 43 | /* 44 | #ifndef MYSQL_COM_END 45 | #define MYSQL_COM_END COM_END 46 | #define MYSQL_COM_QUIT COM_QUIT 47 | #define MYSQL_COM_INIT_DB COM_INIT_DB 48 | #define MYSQL_COM_QUERY COM_QUERY 49 | #define MYSQL_COM_STATISTICS COM_STATISTICS 50 | #define MYSQL_COM_STMT_PREPARE COM_STMT_PREPARE 51 | #define MYSQL_COM_STMT_EXECUTE COM_STMT_EXECUTE 52 | #define MYSQL_COM_STMT_CLOSE COM_STMT_CLOSE 53 | #define MYSQL_COM_STMT_RESET COM_STMT_RESET 54 | #define MYSQL_COM_STMT_SEND_LONG_DATA COM_STMT_SEND_LONG_DATA 55 | #endif 56 | */ 57 | -------------------------------------------------------------------------------- /include/proxysql.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "my_global.h" 35 | #include "my_pthread.h" 36 | #include "mysql.h" 37 | #include "mysql_com.h" 38 | 39 | 40 | #include 41 | 42 | #include "sqlite3.h" 43 | #include "external.h" 44 | #include "structs.h" 45 | #include "mysql_protocol.h" 46 | #include "fundadb.h" 47 | #include "global_variables.h" 48 | #include "debug.h" 49 | #include "admin_sqlite.h" 50 | 51 | #include "lutils.h" 52 | #include "proxysql_macros.h" 53 | 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | 60 | #include 61 | 62 | 63 | 64 | #undef max 65 | #define max(x,y) ((x) > (y) ? (x) : (y)) 66 | 67 | /* PANIC() is a exit with message */ 68 | void PANIC(char* msg); 69 | #define PANIC(msg) { perror(msg); exit(EXIT_FAILURE); } 70 | 71 | int listen_on_port(char *, uint16_t); 72 | int listen_on_unix(char *); 73 | int connect_socket(char *, int); 74 | gboolean write_one_pkt_to_net(mysql_data_stream_t *, pkt *); 75 | 76 | void reset_query_rule(query_rule_t *); 77 | void reset_query_rules(); 78 | void init_gloQR(); 79 | void init_query_metadata(mysql_session_t *, pkt *); 80 | void process_query_rules(mysql_session_t *); 81 | mysql_server * find_server_ptr(const char *, const uint16_t); 82 | mysql_server * mysql_server_entry_create(const char *, const uint16_t, int, enum mysql_server_status); 83 | void mysql_server_entry_add(mysql_server *); 84 | void mysql_server_entry_add_hostgroup(mysql_server *, int); 85 | MSHGE * mysql_server_random_entry_from_hostgroup__lock(int); 86 | MSHGE * mysql_server_random_entry_from_hostgroup__nolock(int); 87 | int mysql_session_create_backend_for_hostgroup(mysql_session_t *, int); 88 | int force_remove_servers(); 89 | void admin_COM_QUERY(mysql_session_t *, pkt *); 90 | 91 | 92 | void send_auth_pkt(mysql_session_t *); 93 | mysql_session_t * mysql_session_new(proxy_mysql_thread_t *, int); 94 | void mysql_session_delete(mysql_session_t *); 95 | 96 | 97 | mysql_backend_t * mysql_backend_new(); 98 | void mysql_backend_delete(mysql_backend_t *); 99 | void glomybepools_init(); 100 | 101 | void set_thread_attr(pthread_attr_t *, size_t); 102 | void start_background_threads(pthread_attr_t *, void **); 103 | void init_proxyipc(); 104 | mysql_data_stream_t * mysql_data_stream_new(mysql_session_t *, mysql_backend_t *); 105 | void mysql_data_stream_delete(mysql_data_stream_t *); 106 | 107 | 108 | gboolean reconnect_server_on_shut_fd(mysql_session_t *); 109 | void mysql_connpool_init(global_variable_entry_t *); 110 | void local_mysql_connpool_init(); 111 | void * mysql_connpool_purge_thread(); 112 | mysql_cp_entry_t *mysql_connpool_get_connection(int, mysql_connpool **, const char *, const char *, const char *, const char *, unsigned int); 113 | void mysql_connpool_detach_connection(int, mysql_connpool **, mysql_cp_entry_t *, int); 114 | mysql_connpool *mysql_connpool_exists_global(const char *, const char *, const char *, const char *, unsigned int); 115 | 116 | 117 | void term_handler(int); 118 | long monotonic_time(); 119 | 120 | 121 | char *mysql_query_digest(mysql_session_t *); 122 | void cleanup_query_stats(qr_hash_entry *); 123 | void query_statistics_set(mysql_session_t *); 124 | // Added by chan ------- 125 | //void process_query_stats(mysql_session_t *); 126 | char *str2md5(const char *); 127 | /* 128 | char is_token(char); 129 | char is_digit(char *, char *); 130 | char is_digit_char(char); 131 | char is_space_char(char); 132 | char is_token_char(char); 133 | char is_digit_string(char *, char *); 134 | */ 135 | // Added by chan end. 136 | 137 | void glo_DefHG_init(global_default_hostgroups_t *); 138 | 139 | 140 | void sighup_handler(int sig); 141 | int config_file_is_readable(char *); 142 | -------------------------------------------------------------------------------- /include/proxysql_macros.h: -------------------------------------------------------------------------------- 1 | #define MY_SESS_ADD_PKT_OUT_CLIENT(__p) l_ptr_array_add(sess->client_myds->output.pkts, __p) 2 | #define MY_SESS_ADD_PKT_OUT_SERVER(__p) l_ptr_array_add(sess->server_mybe->server_myds->output.pkts, __p) 3 | #define mysql_pkt_get_size(__p) ( __p->length-sizeof(mysql_hdr) ) 4 | #define ACTIVE_TRANSACTION(__sess) ( __sess->server_mybe->server_myds->active_transaction ) 5 | 6 | #define MS 5 7 | #define SPIN_LOCK(lock) { \ 8 | while(__sync_bool_compare_and_swap(&lock,0,1)==0) { \ 9 | usleep(MS); \ 10 | } \ 11 | } 12 | 13 | #define SPIN_UNLOCK(lock) { \ 14 | while(__sync_bool_compare_and_swap(&lock,1,0)==0) { \ 15 | usleep(MS); \ 16 | } \ 17 | } 18 | 19 | 20 | #define MEM_COPY_FWD(dst_p, src_p, bytes) \ 21 | do { \ 22 | void *__a=dst_p; \ 23 | void *__b=src_p; \ 24 | size_t __nbytes = (bytes); \ 25 | while (__nbytes > 0) { \ 26 | char __x = ((char *) __b)[0]; \ 27 | __b += 1; \ 28 | __nbytes -= 1; \ 29 | ((char *) __a)[0] = __x; \ 30 | __a += 1; \ 31 | } \ 32 | } while (0) 33 | 34 | 35 | #define ioctl_FIONBIO(fd, mode) \ 36 | { \ 37 | int ioctl_mode=mode; \ 38 | ioctl(fd, FIONBIO, (char *)&ioctl_mode); \ 39 | } 40 | 41 | #define queue_init(q,s) { \ 42 | q->size=s; \ 43 | q->buffer=l_alloc(q->size); \ 44 | q->head=0; \ 45 | q->tail=0; \ 46 | } 47 | 48 | #define queue_destroy(q) { \ 49 | l_free(q->size,q->buffer); \ 50 | } 51 | 52 | #define queue_zero(q) { \ 53 | memcpy(q->buffer, q->buffer+q->tail, q->head - q->tail); \ 54 | q->head-=q->tail; \ 55 | q->tail=0; \ 56 | } 57 | 58 | #define queue_available(q) (q->size-q->head) 59 | #define queue_data(q) (q->head-q->tail) 60 | 61 | #define queue_r(q, s) { \ 62 | q->tail+=s; \ 63 | if (q->tail==q->head) { \ 64 | q->head=0; \ 65 | q->tail=0; \ 66 | } \ 67 | } 68 | 69 | #define queue_w(q,s) (q->head+=s) 70 | 71 | #define queue_r_ptr(q) (q->buffer+q->tail) 72 | #define queue_w_ptr(q) (q->buffer+q->head) 73 | 74 | -------------------------------------------------------------------------------- /include/structs.h: -------------------------------------------------------------------------------- 1 | #define MAX_FDS_PER_SESSION 2 2 | #define MIN_FDS_PER_THREAD 1024 3 | 4 | 5 | #ifndef L_SFP 6 | #define L_SFP 7 | typedef struct _LPtrArray LPtrArray; 8 | typedef struct _l_super_free_pool_t l_sfp; 9 | #endif 10 | typedef struct __fdb_hash_t fdb_hash_t; 11 | typedef struct __fdb_hashes_group_t fdb_hashes_group_t; 12 | typedef struct __fdb_hash_entry fdb_hash_entry; 13 | 14 | typedef struct _mysql_backend_t mysql_backend_t; 15 | 16 | // Added by chan 17 | typedef struct __qr_hash_t qr_hash_t; 18 | typedef struct __qr_hash_entry qr_hash_entry; 19 | // Added by chan end. 20 | 21 | 22 | 23 | typedef struct __fdb_system_var_t { 24 | long long hash_purge_time; 25 | long long hash_purge_loop; 26 | unsigned int hash_expire_max; 27 | unsigned int hash_expire_default; 28 | int purge_threshold_pct_min; 29 | int purge_threshold_pct_max; 30 | } fdb_system_var_t; 31 | 32 | 33 | enum debug_module { 34 | PROXY_DEBUG_GENERIC, 35 | PROXY_DEBUG_NET, 36 | PROXY_DEBUG_PKT_ARRAY, 37 | PROXY_DEBUG_POLL, 38 | PROXY_DEBUG_MYSQL_COM, 39 | PROXY_DEBUG_MYSQL_SERVER, 40 | PROXY_DEBUG_MYSQL_CONNECTION, 41 | PROXY_DEBUG_MYSQL_RW_SPLIT, 42 | PROXY_DEBUG_MYSQL_AUTH, 43 | PROXY_DEBUG_MEMORY, 44 | PROXY_DEBUG_ADMIN, 45 | PROXY_DEBUG_SQLITE, 46 | PROXY_DEBUG_IPC, 47 | PROXY_DEBUG_QUERY_CACHE, 48 | PROXY_DEBUG_QUERY_STATISTICS, 49 | PROXY_DEBUG_UNKNOWN 50 | }; 51 | 52 | 53 | typedef struct _dbg_msg_t { 54 | enum debug_module module; 55 | struct timeval tv; 56 | int thr; 57 | char *file; 58 | int line; 59 | char *func; 60 | int verb; 61 | char *msg; 62 | } dbg_msg_t; 63 | 64 | EXTERN fdb_system_var_t fdb_system_var; 65 | EXTERN fdb_hash_t **fdb_hashes; 66 | 67 | 68 | typedef struct _glo_debug_t { 69 | int glock; 70 | int status; 71 | GAsyncQueue *async_queue; 72 | l_sfp *sfp; 73 | int msg_count; 74 | } glo_debug_t; 75 | 76 | enum enum_resultset_progress { 77 | RESULTSET_WAITING, 78 | RESULTSET_COLUMN_COUNT, 79 | RESULTSET_COLUMN_DEFINITIONS, 80 | RESULTSET_EOF1, 81 | RESULTSET_ROWS, 82 | RESULTSET_COMPLETED 83 | }; 84 | 85 | enum mysql_server_status { 86 | MYSQL_SERVER_STATUS_OFFLINE_HARD = 0, 87 | MYSQL_SERVER_STATUS_OFFLINE_SOFT = 1, 88 | MYSQL_SERVER_STATUS_SHUNNED = 2, 89 | MYSQL_SERVER_STATUS_ONLINE = 3, 90 | }; 91 | 92 | typedef struct _queue_t { 93 | void *buffer; 94 | int size; 95 | int head; 96 | int tail; 97 | } queue_t; 98 | 99 | 100 | // structure that defines mysql protocol header 101 | typedef struct _mysql_hdr { 102 | u_int pkt_length:24, pkt_id:8; 103 | } mysql_hdr; 104 | 105 | typedef struct _pkt { 106 | int length; 107 | void *data; 108 | } pkt; 109 | 110 | typedef struct _mysql_server { 111 | // char *name; 112 | char *address; 113 | uint16_t port; 114 | int read_only; 115 | enum mysql_server_status status; 116 | uint16_t flags; 117 | unsigned int connections; 118 | unsigned char alive; 119 | } mysql_server; 120 | 121 | typedef struct _bytes_stats { 122 | uint64_t bytes_recv; 123 | uint64_t bytes_sent; 124 | } bytes_stats; 125 | 126 | typedef struct _mysql_uni_ds_t { 127 | queue_t queue; 128 | LPtrArray *pkts; 129 | int partial; 130 | pkt *mypkt; 131 | mysql_hdr hdr; 132 | } mysql_uni_ds_t; 133 | 134 | typedef struct _mysql_cp_entry_t { 135 | MYSQL *conn; 136 | unsigned long long expire; 137 | int reusable; 138 | } mysql_cp_entry_t; 139 | 140 | typedef struct _mysql_connpool { 141 | char *hostname; 142 | char *username; 143 | char *password; 144 | char *db; 145 | unsigned int port; 146 | // GPtrArray *used_conns; // temporary (?) disabled 147 | GPtrArray *free_conns; 148 | } mysql_connpool; 149 | 150 | 151 | 152 | #define MAX_USERNAME_LENGTH 16*3 153 | #define MAX_PASSWORD_LENGTH 40 154 | #define MAX_SCHEMA_LENGTH 64*3 155 | 156 | typedef struct _mysql_backend_pool_t { 157 | //char user[MAX_USERNAME_LENGTH+1]; 158 | //char pass[MAX_PASSWORD_LENGTH+1]; 159 | //char schema[MAX_SCHEMA_LENGTH+1]; 160 | char *username; 161 | char *password; 162 | char *schema; 163 | int hostgroup; 164 | LPtrArray *free_backends; 165 | } mysql_backend_pool_t; 166 | 167 | typedef struct _mysql_data_stream_t mysql_data_stream_t; 168 | typedef struct _mysql_session_t mysql_session_t; 169 | typedef struct _shared_trash_stack_t shared_trash_stack_t; 170 | 171 | struct _mysql_data_stream_t { 172 | mysql_session_t *sess; // this MUST always the first, because will be overwritten when pushed in a trash stack 173 | mysql_backend_t *mybe; 174 | uint64_t pkts_recv; 175 | uint64_t pkts_sent; 176 | bytes_stats bytes_info; 177 | mysql_uni_ds_t input; 178 | mysql_uni_ds_t output; 179 | int fd; 180 | int active_transaction; 181 | gboolean active; 182 | // mysql_server *server_ptr; 183 | // mysql_cp_entry_t *mycpe; 184 | void (*setfd) (mysql_data_stream_t *, int); 185 | void (*shut_soft) (mysql_data_stream_t *); 186 | void (*shut_hard) (mysql_data_stream_t *); 187 | int (*array2buffer) (mysql_data_stream_t *); 188 | int (*buffer2array) (mysql_data_stream_t *); 189 | int (*read_from_net) (mysql_data_stream_t *); 190 | int (*write_to_net) (mysql_data_stream_t *); 191 | }; 192 | 193 | 194 | // this structure is shared amount backends, and it contains global metadata and stats 195 | typedef struct _mysql_server_hostgroup_entry_t MSHGE; 196 | struct _mysql_server_hostgroup_entry_t { 197 | int hostgroup_id; 198 | mysql_server *MSptr; 199 | unsigned long weight; 200 | long long connections_created; 201 | long long connections_active; 202 | bytes_stats server_bytes; 203 | }; 204 | 205 | struct _shared_trash_stack_t { 206 | pthread_mutex_t mutex; 207 | GTrashStack *stack; 208 | LPtrArray *blocks; 209 | int size; 210 | int incremental; 211 | }; 212 | 213 | typedef struct _query_rule_t { // use g_slice_alloc 214 | GRegex *regex; 215 | int rule_id; 216 | int flagIN; 217 | char *username; 218 | char *schemaname; 219 | char *match_pattern; // use g_malloc/g_free 220 | int negate_match_pattern; 221 | int flagOUT; 222 | char *replace_pattern; // use g_malloc/g_free 223 | int destination_hostgroup; 224 | int audit_log; 225 | int performance_log; 226 | int cache_tag; 227 | int invalidate_cache_tag; 228 | char *invalidate_cache_pattern; // use g_malloc/g_free 229 | int cache_ttl; 230 | unsigned int hits; 231 | } query_rule_t; 232 | 233 | 234 | typedef struct _global_query_rules_t { 235 | pthread_rwlock_t rwlock; 236 | GPtrArray *query_rules; 237 | } global_query_rules_t; 238 | 239 | typedef struct _default_hostgroup_t { 240 | char *username; 241 | char *schemaname; 242 | int hostgroup_id; 243 | } default_hostgroup_t; 244 | 245 | typedef struct _global_default_hostgroups_t global_default_hostgroups_t; 246 | 247 | struct _global_default_hostgroups_t { 248 | int version; 249 | pthread_rwlock_t rwlock; 250 | GPtrArray *default_hostgroups; 251 | void (*add_defHG) (global_default_hostgroups_t *, const unsigned char *, const unsigned char *, int); 252 | int (*find_defHG) (global_default_hostgroups_t *, const unsigned char *, const unsigned char *); 253 | void (*delete_all) (global_default_hostgroups_t *); 254 | }; 255 | 256 | typedef struct _proxysql_mysql_thread_t { 257 | int thread_id; 258 | // GPtrArray *QC_rules; // regex should be thread-safe, use just a global one 259 | // int QCRver; 260 | LPtrArray *sessions; 261 | } proxy_mysql_thread_t; 262 | 263 | 264 | typedef struct _mysql_query_metadata_t { 265 | pkt *p; 266 | GChecksum *query_checksum; 267 | int flagOUT; 268 | int rewritten; 269 | int cache_ttl; 270 | int destination_hostgroup; 271 | int audit_log; 272 | int performance_log; 273 | int mysql_query_cache_hit; 274 | char *query; 275 | int prepared_statement; 276 | int query_len; 277 | qr_hash_entry *query_stats; 278 | } mysql_query_metadata_t ; 279 | 280 | 281 | struct _mysql_backend_t { 282 | // attributes 283 | int fd; 284 | MSHGE *mshge; 285 | mysql_connpool *last_mysql_connpool; 286 | mysql_data_stream_t *server_myds; 287 | mysql_cp_entry_t *server_mycpe; 288 | bytes_stats server_bytes_at_cmd; 289 | // methods 290 | void (*bedetach) (mysql_backend_t *, mysql_connpool **, int); 291 | void (*bereset) (mysql_backend_t *, mysql_connpool **, int); 292 | }; 293 | 294 | typedef struct __change_user_info_t change_user_info_t; 295 | 296 | struct __change_user_info_t { 297 | char *mysql_username; 298 | char *mysql_schema; 299 | char scramble_buf[21]; 300 | }; 301 | 302 | struct _mysql_session_t { 303 | proxy_mysql_thread_t *handler_thread; 304 | int healthy; 305 | int admin; 306 | int client_fd; 307 | int server_fd; 308 | int status; 309 | int force_close_backends; 310 | int ret; // generic return status 311 | struct pollfd fds[MAX_FDS_PER_SESSION]; 312 | int nfds; 313 | int last_server_poll_fd; 314 | bytes_stats server_bytes_at_cmd; 315 | enum enum_server_command client_command; 316 | enum enum_resultset_progress resultset_progress; 317 | unsigned long long resultset_size; 318 | mysql_query_metadata_t query_info; 319 | gboolean query_to_cache; // must go into query_info 320 | LPtrArray *resultset; 321 | // mysql_server *server_ptr; 322 | mysql_data_stream_t *client_myds; 323 | // mysql_data_stream_t *server_myds; 324 | // mysql_cp_entry_t *server_mycpe; 325 | mysql_backend_t *server_mybe; 326 | // mysql_connpool *last_mysql_connpool; 327 | LPtrArray *mybes; 328 | 329 | // mysql_cp_entry_t *idle_server_mycpe; 330 | char *mysql_username; 331 | char *mysql_password; 332 | char *mysql_schema_cur; 333 | char *mysql_schema_new; 334 | int default_hostgroup; 335 | int default_hostgroup_version; 336 | char scramble_buf[21]; 337 | int waiting_change_user_response; 338 | change_user_info_t *change_user; 339 | gboolean mysql_query_cache_hit; // must go into query_info 340 | gboolean mysql_server_reconnect; 341 | int net_failure; 342 | gboolean send_to_slave; // must go into query_info 343 | 344 | // public methods 345 | void (*conn_poll) (mysql_session_t *); 346 | gboolean (*sync_net) (mysql_session_t *, int); 347 | //void (*buffer2array_2) (mysql_session_t *); 348 | //void (*array2buffer_2) (mysql_session_t *); 349 | void (*check_fds_errors) (mysql_session_t *); 350 | // int (*process_client_pkts) (mysql_session_t *); 351 | // void (*process_server_pkts) (mysql_session_t *); 352 | int (*remove_all_backends_offline_soft) (mysql_session_t *); 353 | void (*close) (mysql_session_t *); 354 | int (*handler) (mysql_session_t *); 355 | void (*process_authentication_pkt) (mysql_session_t *); 356 | // private methods 357 | // void (*read_from_net_2) (mysql_session_t *); 358 | // void (*write_to_net_2) (mysql_session_t *, int); 359 | int (*default_hostgroup_func) (mysql_session_t *); 360 | }; 361 | 362 | 363 | 364 | typedef struct _global_variables { 365 | //pthread_rwlock_t rwlock_global; 366 | pthread_rwlock_t rwlock_usernames; 367 | 368 | int shutdown; 369 | 370 | unsigned char protocol_version; 371 | char *mysql_server_version; 372 | uint16_t server_capabilities; 373 | uint8_t server_language; 374 | uint16_t server_status; 375 | 376 | uint32_t thread_id; 377 | 378 | 379 | int merge_configfile_db; 380 | 381 | gint core_dump_file_size; 382 | int stack_size; 383 | char *proxy_mysql_bind; 384 | char *proxy_admin_bind; 385 | char *proxy_monitor_bind; 386 | gint proxy_mysql_port; 387 | gint proxy_admin_port; 388 | gint proxy_monitor_port; 389 | int proxy_admin_refresh_status_interval; 390 | int proxy_monitor_refresh_status_interval; 391 | //int proxy_flush_status_interval; 392 | int backlog; 393 | //int print_statistics_interval; 394 | 395 | int admin_sync_disk_on_flush; 396 | int admin_sync_disk_on_shutdown; 397 | 398 | int mysql_poll_timeout; 399 | int mysql_poll_timeout_maintenance; 400 | 401 | int mysql_maintenance_timeout; 402 | 403 | int mysql_threads; 404 | gboolean mysql_auto_reconnect_enabled; 405 | gboolean mysql_query_cache_enabled; 406 | gboolean mysql_query_cache_precheck; 407 | gboolean mysql_query_statistics; 408 | gboolean mysql_query_statistics_interval; 409 | int mysql_query_cache_partitions; 410 | int mysql_parse_trx_cmds; 411 | int mysql_share_connections; 412 | 413 | unsigned int mysql_query_cache_default_timeout; 414 | unsigned long long mysql_wait_timeout; 415 | unsigned long long mysql_query_cache_size; 416 | unsigned long long mysql_max_resultset_size; 417 | int mysql_max_query_size; 418 | 419 | int mysql_hostgroups; 420 | 421 | // this user needs only USAGE grants 422 | // and it is use only to create a connection 423 | char *mysql_usage_user; 424 | char *mysql_usage_password; 425 | 426 | char *proxy_admin_user; 427 | char *proxy_admin_password; 428 | char *proxy_monitor_user; 429 | char *proxy_monitor_password; 430 | 431 | char *mysql_default_schema; 432 | char *mysql_socket; 433 | char *proxy_datadir; 434 | char *proxy_admin_pathdb; 435 | char *persistent_statistics_pathdb; 436 | char *debug_pathdb; 437 | char *proxy_pidfile; 438 | char *proxy_errorlog; 439 | char *proxy_debuglog; 440 | char *proxy_configfile; 441 | int proxy_restart_on_error; 442 | int proxy_restart_delay; 443 | int http_start; 444 | // unsigned int count_masters; 445 | // unsigned int count_slaves; 446 | // GPtrArray *servers_masters; 447 | // GPtrArray *servers_slaves; 448 | // gchar **mysql_servers_name; // used to parse config file 449 | GHashTable *usernames; 450 | // gchar **mysql_users_name; // used to parse config file 451 | // gchar **mysql_users_pass; // used to parse config file 452 | GPtrArray *mysql_users_name; 453 | GPtrArray *mysql_users_pass; 454 | // unsigned int mysql_connections_max; 455 | // unsigned int mysql_connections_cur; 456 | // GPtrArray *mysql_connections; 457 | unsigned int net_buffer_size; 458 | // unsigned int conn_queue_allocator_blocks; 459 | // GPtrArray *conn_queue_allocator; 460 | // GPtrArray *QC_rules; 461 | // int QCRver; 462 | } global_variables; 463 | 464 | 465 | typedef struct _global_mysql_servers { 466 | pthread_rwlock_t rwlock; 467 | unsigned int mysql_connections_max; 468 | unsigned int mysql_connections_cur; 469 | unsigned int servers_count; 470 | unsigned int count_masters; 471 | unsigned int count_slaves; 472 | gchar **mysql_servers_name; // used to parse config file 473 | GPtrArray *servers; 474 | GPtrArray *servers_masters; 475 | GPtrArray *servers_slaves; 476 | GPtrArray *mysql_connections; 477 | gboolean mysql_use_masters_for_reads; 478 | GPtrArray *mysql_hostgroups; 479 | } global_mysql_servers; 480 | 481 | 482 | enum MySQL_response_type { 483 | OK_Packet, 484 | ERR_Packet, 485 | EOF_Packet, 486 | UNKNOWN_Packet, 487 | }; 488 | 489 | 490 | typedef struct _mem_block_t { 491 | LPtrArray *used; 492 | LPtrArray *free; 493 | void *mem; 494 | } mem_block_t; 495 | 496 | 497 | typedef struct _mem_superblock_t { 498 | pthread_mutex_t mutex; 499 | GPtrArray *blocks; 500 | int size; 501 | int incremental; 502 | } mem_superblock_t; 503 | 504 | 505 | /* ProxyIPC is a struct used for inter-thread communication between the admin thread and the the mysql threads 506 | because mysql threads are normally blocked on poll(), the best way to wake them up is to send them a signal on a pipe 507 | fdIn and fdOut represents the two endpoints of the pipe 508 | The data should should be the follow: 509 | - admin thread sends a message in each mysql thread queue 510 | - admin thread sends a byte to all fdIn 511 | - all the mysql threads will wake up reading from fdOut 512 | - all the mysql threads will read the message from their async queue 513 | - all the mysql threads will perform an action and send an ack to the admin thread 514 | - all the mysql threads may enter in a maintenance mode and just wait on async queue, or go back in the main loop 515 | */ 516 | typedef struct _ProxyIPC { 517 | int *fdIn; 518 | int *fdOut; 519 | GAsyncQueue **queue; 520 | } ProxyIPC; 521 | 522 | 523 | typedef struct _debug_level debug_level; 524 | typedef struct _admin_sqlite_table_def_t admin_sqlite_table_def_t; 525 | 526 | typedef struct _global_variable_entry_t global_variable_entry_t; 527 | 528 | struct _global_variable_entry_t { 529 | const char *group_name; 530 | const char *key_name; 531 | int dynamic; 532 | GOptionArg arg; 533 | void *arg_data; 534 | const char *description; 535 | long long value_min; 536 | long long value_max; 537 | long long value_round; 538 | int value_multiplier; 539 | long long int_default; 540 | const char *char_default; 541 | void (*func_pre)(global_variable_entry_t *); 542 | void (*func_post)(global_variable_entry_t *); 543 | }; 544 | 545 | 546 | //#define MYSQL_SERVER_STATUS_OFFLINE 0 547 | 548 | /* 549 | typedef struct _myBackendPools { 550 | //int mutex; 551 | pthread_mutex_t mutex; 552 | GPtrArray *mybepools; 553 | int enabled; 554 | mysql_backend_t * (*get) (const char *, const char *, const char *, int); 555 | void (*detach) (mysql_backend_t *, int, int); 556 | 557 | } myBackendPools; 558 | */ 559 | 560 | typedef struct _myConnPools { 561 | pthread_mutex_t mutex; 562 | GPtrArray *connpools; 563 | int enabled; 564 | struct timeval tv; 565 | } myConnPools; 566 | 567 | #define MYSQL_CONNPOOL_LOCAL 0 568 | #define MYSQL_CONNPOOL_GLOBAL 1 569 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | MARIADBLIBPATH=../../mariadb-native-client 2 | DAEMONPATH=../../libdaemon-0.14 3 | GLIBPATH=../../glib-2.40.0 4 | JEMALLOCPATH=../../jemalloc-3.6.0 5 | GLIB_IDIRS=-I$(GLIBPATH) -I$(GLIBPATH)/glib 6 | GLIB_LDIRS=-L$(GLIBPATH)/glib/.libs -L$(GLIBPATH)/gthread/.libs 7 | GLIB_LIB=-pthread -lgthread-2.0 -lglib-2.0 8 | ODIR= obj 9 | LDIR=../lib 10 | IDIR=../include 11 | IDIRS=-I../include -I../sqlite3 -I$(MARIADBLIBPATH)/include -I$(DAEMONPATH) -I$(JEMALLOCPATH)/include $(GLIB_IDIRS) 12 | LDIRS=-L$(MARIADBLIBPATH)/libmysql -L$(DAEMONPATH)/libdaemon/.libs -L$(JEMALLOCPATH)/lib $(GLIB_LDIRS) 13 | CC=gcc 14 | #CC=clang 15 | MARIADBAR=$(MARIADBLIBPATH)/libmysql/libmariadbclient.a 16 | DAEMONAR=$(DAEMONPATH)/libdaemon/.libs/libdaemon.a 17 | GLIBAR=$(GLIBPATH)/glib/.libs/libglib-2.0.a $(GLIBPATH)/gthread/.libs/libgthread-2.0.a 18 | JEMALLOCAR=$(JEMALLOCPATH)/lib/libjemalloc.a 19 | DEBUG=-ggdb -DDEBUG 20 | O0=-O0 21 | O2=-O2 22 | O1=-O1 23 | O3=-O3 -mtune=native 24 | OPTZ=$(O0) 25 | CFLAGS=$(IDIRS) $(OPTZ) $(DEBUG) 26 | LIBS=-rdynamic -Wl,-Bstatic -lmariadbclient -ldaemon -ljemalloc -lssl -lcrypto $(GLIB_LIB) -Wl,-Bdynamic -lkrb5 -ldl -lpthread -lm -lz -lrt 27 | SQLITEOBJ=../sqlite3/sqlite3.o 28 | EXECUTABLE=proxysql 29 | 30 | _OBJ = tokenizer.o utils.o l_utils.o mysql_session.o mysql_data_stream.o mysql_backend.o main.o debug.o fundadb_hash.o global_variables.o mysql_connpool.o mysql_protocol.o mysql_handler.o network.o threads.o admin.o admin_sqlite.o 31 | OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) 32 | 33 | $(ODIR)/%.o: %.c 34 | $(CC) -c -o $@ $< $(CFLAGS) -Wall 35 | 36 | 37 | proxysql: $(ODIR) $(OBJ) $(SQLITEOBJ) $(MARIADBAR) $(DAEMONAR) $(GLIBAR) $(JEMALLOCAR) 38 | gcc -o $@ $(MARIADBAR) $(DAEMONAR) $(GLIBAR) $(SQLITEOBJ) $(OBJ) $(CFLAGS) $(LDIRS) $(LIBS) 39 | 40 | $(SQLITEOBJ): ../sqlite3/sqlite3.c 41 | $(CC) -c -o $@ $(CFLAGS) ../sqlite3/sqlite3.c 42 | 43 | $(ODIR): 44 | mkdir $(ODIR) 45 | 46 | all: $(EXECUTABLE) 47 | 48 | .PHONY: clean resetdb clean_sqlite3 cleanall 49 | 50 | 51 | cleanall: clean clean_sqlite3 52 | 53 | clean: 54 | rm -f *.pid $(ODIR)/*.o *~ core $(INCDIR)/*~ $(EXECUTABLE) 55 | 56 | clean_sqlite3: 57 | rm -f ../sqlite3/*.o 58 | 59 | resetdb: 60 | rm -rf $(EXECUTABLE).db* 61 | -------------------------------------------------------------------------------- /src/Makefile_nostatic: -------------------------------------------------------------------------------- 1 | MARIADBLIBPATH=../../mariadb-native-client 2 | DAEMONPATH=../../libdaemon-0.14 3 | GLIB_CFLAGS=`pkg-config --cflags gthread-2.0` 4 | GLIB_LIB=`pkg-config --libs gthread-2.0` 5 | ODIR= obj 6 | LDIR=../lib 7 | IDIR=../include 8 | IDIRS=-I../include -I../sqlite3 -I$(MARIADBLIBPATH)/include -I$(DAEMONPATH) $(GLIB_CFLAGS) 9 | LDIRS=-L$(MARIADBLIBPATH)/libmysql -L$(DAEMONPATH)/libdaemon/.libs $(GLIB_CFLAGS) 10 | CC=gcc 11 | MARIADBAR=$(MARIADBLIBPATH)/libmysql/libmariadbclient.a 12 | DAEMONAR=$(DAEMONPATH)/libdaemon/.libs/libdaemon.a 13 | DEBUG=-ggdb -DDEBUG 14 | O0=-O0 15 | O2=-O2 16 | O1=-O1 17 | O3=-O3 -mtune=native 18 | OPTZ=$(O0) 19 | CFLAGS=$(IDIRS) -rdynamic $(OPTZ) $(DEBUG) 20 | LIBS=-Wl,-Bstatic -lmariadbclient -ldaemon -lcrypto -Wl,-Bdynamic -lssl -ldl -lpthread -lm -lz $(GLIB_LIB) 21 | SQLITEOBJ=../sqlite3/sqlite3.o 22 | EXECUTABLE=proxysql 23 | 24 | _OBJ = tokenizer.o utils.o l_utils.o mysql_session.o mysql_data_stream.o mysql_backend.o main.o debug.o fundadb_hash.o global_variables.o mysql_connpool.o mysql_protocol.o mysql_handler.o network.o threads.o admin.o admin_sqlite.o 25 | OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ)) 26 | 27 | $(ODIR)/%.o: %.c 28 | $(CC) -c -o $@ $< $(CFLAGS) -Wall 29 | 30 | 31 | proxysql: $(ODIR) $(OBJ) $(SQLITEOBJ) $(MARIADBAR) $(DAEMONAR) 32 | gcc -o $@ $(MARIADBAR) $(DAEMONAR) $(SQLITEOBJ) $(OBJ) $(CFLAGS) $(LDIRS) $(LIBS) 33 | 34 | $(SQLITEOBJ): ../sqlite3/sqlite3.c 35 | $(CC) -c -o $@ $(CFLAGS) ../sqlite3/sqlite3.c 36 | 37 | $(ODIR): 38 | mkdir $(ODIR) 39 | 40 | all: $(EXECUTABLE) 41 | 42 | .PHONY: clean resetdb clean_sqlite3 cleanall 43 | 44 | 45 | cleanall: clean clean_sqlite3 46 | 47 | clean: 48 | rm -f *.pid $(ODIR)/*.o *~ core $(INCDIR)/*~ $(EXECUTABLE) 49 | 50 | clean_sqlite3: 51 | rm -f ../sqlite3/*.o 52 | 53 | resetdb: 54 | rm -rf $(EXECUTABLE).db* 55 | -------------------------------------------------------------------------------- /src/admin.c: -------------------------------------------------------------------------------- 1 | #include "proxysql.h" 2 | 3 | //int static_lock=0; 4 | 5 | 6 | void sighup_handler(int sig) { 7 | GKeyFile *keyfile; 8 | GError *error = NULL; 9 | int rc; 10 | // TODO: handle also closing/reopening of log files and databases 11 | proxy_error("Received HUP signal: reloading config file...\n"); 12 | #ifdef DEBUG 13 | //g_mem_profile(); 14 | malloc_stats_print(NULL, NULL, ""); 15 | #endif 16 | char *config_file=glovars.proxy_configfile; 17 | rc=config_file_is_readable(config_file); 18 | if (rc==0) { 19 | proxy_error("Config file %s is not readable\n", config_file); 20 | return; 21 | } 22 | 23 | keyfile = g_key_file_new(); 24 | if (!g_key_file_load_from_file(keyfile, config_file, G_KEY_FILE_NONE, &error)) { 25 | proxy_error("Error loading configuration from config file %s\n", config_file); 26 | g_key_file_free(keyfile); 27 | return; 28 | } 29 | 30 | // initialize variables and process config file 31 | init_global_variables(keyfile,1); 32 | 33 | g_key_file_free(keyfile); 34 | 35 | } 36 | 37 | void term_handler(int sig) { 38 | proxy_error("Received TERM signal: shutdown in progress...\n"); 39 | #ifdef DEBUG 40 | //g_mem_profile(); 41 | malloc_stats_print(NULL, NULL, ""); 42 | #endif 43 | glovars.shutdown=1; 44 | sleep(5); 45 | exit(0); 46 | } 47 | 48 | static inline pkt * admin_version_comment_pkt(mysql_session_t *sess) { 49 | pkt *p; 50 | //proxy_mysql_thread_t *thrLD=pthread_getspecific(tsd_key); 51 | p=mypkt_alloc(); 52 | // hardcoded, we send " (ProxySQL) " 53 | p->length=81; 54 | //p->data=l_alloc(thrLD->sfp, p->length); 55 | p->data=l_alloc(p->length); 56 | //p->data=g_slice_alloc0(p->length); 57 | memcpy(p->data,"\x01\x00\x00\x01\x01\x27\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x11\x40\x40\x76\x65\x72\x73\x69\x6f\x6e\x5f\x63\x6f\x6d\x6d\x65\x6e\x74\x00\x0c\x21\x00\x18\x00\x00\x00\xfd\x00\x00\x1f\x00\x00\x05\x00\x00\x03\xfe\x00\x00\x02\x00\x0b\x00\x00\x04\x0a\x28\x50\x72\x6f\x78\x79\x53\x51\x4c\x29\x05\x00\x00\x05\xfe\x00\x00\x02\x00",p->length); 58 | return p; 59 | } 60 | 61 | static void update_runtime_statistics(int admin) { 62 | sqlite3 *db=NULL; 63 | time_t t=time(NULL); 64 | // SPIN_LOCK(static_lock); 65 | 66 | if (admin==1) { 67 | if (tadmin==1) { 93 | defaultdb=sqlite3admindb; 94 | } else { //admin==2 95 | defaultdb=sqlite3monitordb; 96 | } 97 | //proxy_mysql_thread_t *thrLD=pthread_getspecific(tsd_key); 98 | // enter admin mode 99 | // configure the session to not send data to servers using a hack: pretend the result set is cached 100 | sess->mysql_query_cache_hit=TRUE; 101 | sess->query_to_cache=FALSE; 102 | update_runtime_statistics(sess->admin); 103 | if (strncasecmp("SHOW TABLES", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 104 | char *str="SELECT name AS tables FROM sqlite_master WHERE type='table'"; 105 | //g_slice_free1(p->length, p->data); 106 | //l_free(thrLD->sfp,p->length, p->data); 107 | l_free(p->length, p->data); 108 | int l=strlen(str); 109 | //p->data=l_alloc(thrLD->sfp, l+sizeof(mysql_hdr)+1); 110 | p->data=l_alloc(l+sizeof(mysql_hdr)+1); 111 | //p->data=g_slice_alloc0(l+sizeof(mysql_hdr)+1); 112 | p->length=l+sizeof(mysql_hdr)+1; 113 | memset(p->data+sizeof(mysql_hdr), MYSQL_COM_QUERY, 1); 114 | memcpy(p->data+sizeof(mysql_hdr)+1,str,l); 115 | } 116 | { 117 | static char *strA="SHOW CREATE TABLE "; 118 | static char *strB="SELECT name AS 'table' , sql AS 'Create Table' FROM sqlite_master WHERE type='table' AND name='%s'"; 119 | int strAl=strlen(strA); 120 | if (strncasecmp("SHOW CREATE TABLE ", p->data+sizeof(mysql_hdr)+1, strAl)==0) { 121 | int strBl=strlen(strB); 122 | int tblnamelen=p->length-sizeof(mysql_hdr)-1-strAl; 123 | int l=strBl+tblnamelen-2; 124 | char *buff=g_malloc0(l); 125 | snprintf(buff,l,strB,p->data+sizeof(mysql_hdr)+1+strAl); 126 | buff[l-1]='\''; 127 | //g_slice_free1(p->length, p->data); 128 | //l_free(thrLD->sfp,p->length, p->data); 129 | l_free(p->length, p->data); 130 | //p->data=l_alloc(thrLD->sfp, l+sizeof(mysql_hdr)+1); 131 | p->data=l_alloc(l+sizeof(mysql_hdr)+1); 132 | //p->data=g_slice_alloc0(l+sizeof(mysql_hdr)+1); 133 | p->length=l+sizeof(mysql_hdr)+1; 134 | memset(p->data+sizeof(mysql_hdr), MYSQL_COM_QUERY, 1); 135 | memcpy(p->data+sizeof(mysql_hdr)+1,buff,l); 136 | g_free(buff); 137 | } 138 | } 139 | if (strncmp("select @@version_comment limit 1", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 140 | // mysql client in interactive mode sends "select @@version_comment limit 1" : we treat this as a special case 141 | 142 | // drop the packet from client 143 | mypkt_free1(p); 144 | 145 | // prepare a new packet to send to the client 146 | pkt *np=NULL; 147 | np=admin_version_comment_pkt(sess); 148 | MY_SESS_ADD_PKT_OUT_CLIENT(np); 149 | //l_ptr_array_add(sess->client_myds->output.pkts, np); 150 | return; 151 | } 152 | if (sess->admin==1) { // in the admin module, not in the monitoring module 153 | if (strncasecmp("FLUSH QUERY CACHE", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 154 | int affected_rows=fdb_truncate_all(&QC); 155 | pkt *ok=mypkt_alloc(); 156 | myproto_ok_pkt(ok,1,affected_rows,0,2,0); 157 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 158 | return; 159 | } 160 | if (strncasecmp("FLUSH DEBUG", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 161 | int affected_rows=sqlite3_flush_debug_levels_db_to_mem(sqlite3admindb); 162 | pkt *ok=mypkt_alloc(); 163 | myproto_ok_pkt(ok,1,affected_rows,0,2,0); 164 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 165 | if (glovars.admin_sync_disk_on_flush==1) sqlite3_config_sync_mem_to_disk(); 166 | //l_ptr_array_add(sess->client_myds->output.pkts, ok); 167 | return; 168 | } 169 | if (strncasecmp("FLUSH USERS", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 170 | int affected_rows=sqlite3_flush_users_db_to_mem(sqlite3admindb); 171 | pkt *ok=mypkt_alloc(); 172 | myproto_ok_pkt(ok,1,affected_rows,0,2,0); 173 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 174 | if (glovars.admin_sync_disk_on_flush==1) sqlite3_config_sync_mem_to_disk(); 175 | //l_ptr_array_add(sess->client_myds->output.pkts, ok); 176 | return; 177 | } 178 | if (strncasecmp("FLUSH QUERY RULES", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 179 | int affected_rows=sqlite3_flush_query_rules_db_to_mem(sqlite3admindb); 180 | pkt *ok=mypkt_alloc(); 181 | myproto_ok_pkt(ok,1,affected_rows,0,2,0); 182 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 183 | if (glovars.admin_sync_disk_on_flush==1) sqlite3_config_sync_mem_to_disk(); 184 | //l_ptr_array_add(sess->client_myds->output.pkts, ok); 185 | return; 186 | } 187 | if (strncasecmp("FLUSH DEFAULT HOSTGROUPS", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 188 | int affected_rows=sqlite3_flush_default_hostgroups_db_to_mem(sqlite3admindb); 189 | pkt *ok=mypkt_alloc(); 190 | myproto_ok_pkt(ok,1,affected_rows,0,2,0); 191 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 192 | if (glovars.admin_sync_disk_on_flush==1) sqlite3_config_sync_mem_to_disk(); 193 | //l_ptr_array_add(sess->client_myds->output.pkts, ok); 194 | return; 195 | } 196 | if (strncasecmp("FLUSH HOSTGROUPS", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 197 | int affected_rows=sqlite3_flush_servers_db_to_mem(sqlite3admindb,0); 198 | int warnings=force_remove_servers(); 199 | if ( affected_rows>=0 ) { 200 | pkt *ok=mypkt_alloc(); 201 | myproto_ok_pkt(ok,1,affected_rows,0,2,warnings); 202 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 203 | if (glovars.admin_sync_disk_on_flush==1) sqlite3_config_sync_mem_to_disk(); 204 | //l_ptr_array_add(sess->client_myds->output.pkts, ok); 205 | } else { 206 | // TODO: send some error 207 | } 208 | return; 209 | } 210 | // if (strncasecmp("REMOVE SERVERS", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 211 | // } 212 | if (strncasecmp("SHUTDOWN", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 213 | glovars.shutdown=1; 214 | pkt *ok=mypkt_alloc(); 215 | myproto_ok_pkt(ok,1,0,0,2,0); 216 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 217 | if (glovars.admin_sync_disk_on_shutdown==1) sqlite3_config_sync_mem_to_disk(); 218 | //l_ptr_array_add(sess->client_myds->output.pkts, ok); 219 | return; 220 | } 221 | /* 222 | if (strncasecmp("DUMP RUNTIME HOSTGROUPS", p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 223 | //int affected_rows=sqlite3_dump_runtime_hostgroups(); 224 | int affected_rows; 225 | affected_rows=sqlite3_dump_runtime_hostgroups(sqlite3configdb); 226 | affected_rows=sqlite3_dump_runtime_hostgroups(sqlite3admindb); 227 | affected_rows=sqlite3_dump_runtime_hostgroups(sqlite3monitordb); 228 | pkt *ok=mypkt_alloc(); 229 | myproto_ok_pkt(ok,1,affected_rows,0,2,0); 230 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 231 | //l_ptr_array_add(sess->client_myds->output.pkts, ok); 232 | return; 233 | } 234 | */ 235 | 236 | if (strncasecmp(DUMP_RUNTIME_QUERY_RULES, p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 237 | int affected_rows; 238 | //int affected_rows=sqlite3_dump_runtime_query_rules(); 239 | affected_rows=sqlite3_dump_runtime_query_rules(defaultdb); 240 | //affected_rows=sqlite3_dump_runtime_query_rules(sqlite3configdb); 241 | //affected_rows=sqlite3_dump_runtime_query_rules(sqlite3admindb); 242 | //affected_rows=sqlite3_dump_runtime_query_rules(sqlite3monitordb); 243 | pkt *ok=mypkt_alloc(); 244 | myproto_ok_pkt(ok,1,affected_rows,0,2,0); 245 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 246 | //l_ptr_array_add(sess->client_myds->output.pkts, ok); 247 | return; 248 | } 249 | 250 | if (strncasecmp(DUMP_RUNTIME_QUERY_CACHE, p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 251 | int affected_rows; 252 | affected_rows=sqlite3_dump_runtime_query_cache(defaultdb); 253 | //affected_rows=sqlite3_dump_runtime_query_cache(sqlite3configdb); 254 | //affected_rows=sqlite3_dump_runtime_query_cache(sqlite3admindb); 255 | //affected_rows=sqlite3_dump_runtime_query_cache(sqlite3monitordb); 256 | pkt *ok=mypkt_alloc(); 257 | myproto_ok_pkt(ok,1,affected_rows,0,2,0); 258 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 259 | //l_ptr_array_add(sess->client_myds->output.pkts, ok); 260 | return; 261 | } 262 | if (strncasecmp(DUMP_RUNTIME_DEFAULT_HOSTGROUPS, p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 263 | int affected_rows=sqlite3_dump_runtime_default_hostgroups(defaultdb); 264 | pkt *ok=mypkt_alloc(); 265 | myproto_ok_pkt(ok,1,affected_rows,0,2,0); 266 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 267 | return; 268 | } 269 | if (strncasecmp(CONFIG_SYNC_MEM_TO_DISK, p->data+sizeof(mysql_hdr)+1, p->length-sizeof(mysql_hdr)-1)==0) { 270 | int affected_rows=sqlite3_config_sync_mem_to_disk(); 271 | pkt *ok=mypkt_alloc(); 272 | myproto_ok_pkt(ok,1,affected_rows,0,2,0); 273 | MY_SESS_ADD_PKT_OUT_CLIENT(ok); 274 | //l_ptr_array_add(sess->client_myds->output.pkts, ok); 275 | return; 276 | } 277 | 278 | } 279 | rc=mysql_pkt_to_sqlite_exec(p, sess); 280 | mypkt_free1(p); 281 | 282 | if (rc==-1) { 283 | sess->healthy=0; 284 | } 285 | // sess->healthy=0; // for now, always 286 | return; 287 | } 288 | 289 | 290 | int force_remove_servers() { 291 | int i; 292 | int warnings=0; 293 | // temporary change poll_timeout 294 | int default_mysql_poll_timeout=glovars.mysql_poll_timeout; 295 | glovars.mysql_poll_timeout=glovars.mysql_poll_timeout_maintenance; 296 | for (i=0; i=1); 308 | } 309 | for (i=0; iclient_myds->output.pkts, ok); 325 | return warnings; 326 | } 327 | 328 | 329 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | #include "proxysql.h" 2 | 3 | 4 | extern gboolean foreground; 5 | 6 | void crash_handler(int sig) { 7 | #ifdef DEBUG 8 | //g_mem_profile(); 9 | malloc_stats_print(NULL, NULL, ""); 10 | #endif 11 | void *arr[20]; 12 | size_t s; 13 | 14 | s = backtrace(arr, 20); 15 | 16 | fprintf(stderr, "Error: signal %d:\n", sig); 17 | backtrace_symbols_fd(arr, s, STDERR_FILENO); 18 | exit(EXIT_FAILURE); 19 | } 20 | 21 | /* 22 | void proxy_debug_func(enum debug_module module, int verbosity, const char *fmt, ...) { 23 | assert(moduleglock); 49 | //dbg_msg_t *dbg_msg=g_malloc(sizeof(dbg_msg_t)); 50 | dbg_msg_t *dbg_msg=__l_alloc(glo_debug->sfp,sizeof(dbg_msg_t)); 51 | SPIN_UNLOCK(glo_debug->glock); 52 | gettimeofday(&dbg_msg->tv, NULL); 53 | dbg_msg->thr=thr; 54 | //dbg_msg->file=g_strdup(__file); 55 | dbg_msg->module=module; 56 | dbg_msg->file=(char *)__file; 57 | dbg_msg->line=__line; 58 | //dbg_msg->func=g_strdup(__func); 59 | dbg_msg->func=(char *)__func; 60 | dbg_msg->verb=verbosity; 61 | while (__sync_fetch_and_add(&glo_debug->msg_count,0)>9000) {usleep(10000); } 62 | SPIN_LOCK(glo_debug->glock); 63 | __sync_fetch_and_add(&glo_debug->msg_count,1); 64 | dbg_msg->msg=__l_alloc(glo_debug->sfp,DEBUG_MSG_MAXSIZE); 65 | SPIN_UNLOCK(glo_debug->glock); 66 | va_list ap; 67 | va_start(ap, fmt); 68 | //vfprintf(stderr, fmt, ap); 69 | vsnprintf(dbg_msg->msg,DEBUG_MSG_MAXSIZE,fmt,ap); 70 | va_end(ap); 71 | //memcpy(dbg_msg->msg,debugbuff,DEBUG_MSG_MAXSIZE); 72 | //dbg_msg->msg=g_strdup(debugbuff); 73 | g_async_queue_push(glo_debug->async_queue,dbg_msg); 74 | }; 75 | #endif 76 | 77 | void proxy_error_func(const char *fmt, ...) { 78 | va_list ap; 79 | va_start(ap, fmt); 80 | vfprintf(stderr, fmt, ap); 81 | va_end(ap); 82 | }; 83 | 84 | void init_debug_struct() { 85 | int i; 86 | gdbg_lvl=g_malloc0_n(PROXY_DEBUG_UNKNOWN,sizeof(debug_level)); 87 | for (i=0;iasync_queue,1000000); 134 | if (dbg_msg) { 135 | // char __buffer[25]; 136 | // char __buffer2[35]; 137 | // struct timeval tv; 138 | // gettimeofday(&tv, NULL); 139 | // struct tm *__tm_info=localtime(&dbg_msg->tv.tv_sec); 140 | // strftime(__buffer, 25, "%Y-%m-%d %H:%M:%S", __tm_info); 141 | // sprintf(__buffer2, "%s:06%d", __buffer, (int)dbg_msg->tv.tv_usec); 142 | // sqlite3_exec_exit_on_failure(db,"BEGIN TRANSACTION"); 143 | __sqlite3_debugdb__flush_debugs(statement, dbg_msg); 144 | // sqlite3_exec_exit_on_failure(db,"COMMIT"); 145 | // fprintf(debugfile, "%s:%06d %d:%s:%d:%s(): LVL#%d : %s" , __buffer, (int)dbg_msg->tv.tv_usec, dbg_msg->thr, dbg_msg->file, dbg_msg->line, dbg_msg->func, dbg_msg->verb, dbg_msg->msg); 146 | if (dbg_msg->tv.tv_sec > lt + 4) { 147 | lt=dbg_msg->tv.tv_sec; 148 | fflush(debugfile); 149 | sqlite3_exec_exit_on_failure(db,"COMMIT"); 150 | sqlite3_exec_exit_on_failure(db,"BEGIN TRANSACTION"); 151 | } 152 | //g_free(dbg_msg->file); 153 | //g_free(dbg_msg->func); 154 | SPIN_LOCK(glo_debug->glock); 155 | __l_free(glo_debug->sfp,DEBUG_MSG_MAXSIZE, dbg_msg->msg); 156 | __sync_fetch_and_sub(&glo_debug->msg_count,1); 157 | __l_free(glo_debug->sfp, sizeof(dbg_msg_t), dbg_msg); 158 | //g_free(dbg_msg); 159 | SPIN_UNLOCK(glo_debug->glock); 160 | // g_free(dbg_msg->msg); 161 | //g_slice_free1(sizeof(dbg_msg_t *),dbg_msg); 162 | } else { 163 | //sqlite3_exec_exit_on_failure(db,"COMMIT"); 164 | } 165 | } 166 | return NULL; 167 | } 168 | #endif 169 | -------------------------------------------------------------------------------- /src/fundadb_hash.c: -------------------------------------------------------------------------------- 1 | #include "proxysql.h" 2 | 3 | 4 | 5 | pkt * fdb_get(fdb_hashes_group_t *hg, const char *kp, mysql_session_t *sess) { 6 | //void *value=NULL; 7 | pkt * result=NULL; 8 | //unsigned int vl=0; 9 | int kl=strlen(kp); 10 | unsigned char i; 11 | i=*((unsigned char *)kp); 12 | if((kl)>2) i=i+(*((unsigned char *)kp+1))+(*((unsigned char *)kp+2)); 13 | i=i%hg->size; 14 | pthread_rwlock_rdlock(&hg->fdb_hashes[i]->lock); 15 | fdb_hash_entry *entry=g_hash_table_lookup(hg->fdb_hashes[i]->hash, kp); 16 | if (entry!=NULL) { __sync_fetch_and_add(&entry->ref_count,1); } 17 | pthread_rwlock_unlock(&hg->fdb_hashes[i]->lock); 18 | if (entry!=NULL) { 19 | time_t t=hg->now; 20 | if (entry->expire > t) { 21 | //proxy_mysql_thread_t *thrLD=pthread_getspecific(tsd_key); 22 | result=mypkt_alloc(); 23 | //result=mypkt_alloc(sess); 24 | //result->data=g_slice_alloc(entry->length); 25 | //result->data=l_alloc(thrLD->sfp, entry->length); 26 | result->data=l_alloc(entry->length); 27 | memcpy(result->data,entry->value,entry->length); 28 | result->length=entry->length; 29 | __sync_fetch_and_add(&hg->cntGetOK,1); 30 | __sync_fetch_and_add(&hg->dataOUT,result->length); 31 | if (t > entry->access) entry->access=t; 32 | } else { 33 | // this was a bug. Altering an entry should not possible when the lock is rdlock 34 | // g_hash_table_remove (hg->fdb_hashes[i]->hash,kp); 35 | } 36 | __sync_fetch_and_sub(&entry->ref_count,1); 37 | } 38 | __sync_fetch_and_add(&hg->cntGet,1); 39 | return result; 40 | } 41 | 42 | gboolean fdb_set(fdb_hashes_group_t *hg, void *kp, unsigned int kl, void *vp, unsigned int vl, time_t expire, gboolean copy) { 43 | //fdb_hash_entry *entry = g_malloc(sizeof(fdb_hash_entry)); 44 | fdb_hash_entry *entry = g_slice_alloc(sizeof(fdb_hash_entry)); 45 | entry->klen=kl; 46 | entry->length=vl; 47 | entry->ref_count=0; 48 | if (copy) { 49 | //entry->key=g_malloc(kl); 50 | entry->key=g_slice_alloc(kl); 51 | //memcpy(entry->key,kp,kl); 52 | MEM_COPY_FWD(entry->key,kp,kl); 53 | entry->value=g_malloc(vl); 54 | memcpy(entry->value,vp,vl); 55 | } else { 56 | entry->key=kp; 57 | entry->value=vp; 58 | } 59 | entry->self=entry; 60 | entry->access=hg->now; 61 | 62 | if (expire>0) { 63 | if (expire > fdb_system_var.hash_expire_max) { 64 | entry->expire=expire; // expire is a unix timestamp 65 | } else { 66 | entry->expire=hg->now+expire; // expire is seconds 67 | } 68 | } else entry->expire=hg->now+hg->hash_expire_default; // set default expire 69 | 70 | unsigned char i; 71 | i=*((unsigned char *)kp); 72 | if((kl)>2) i=i+(*((unsigned char *)kp+1))+(*((unsigned char *)kp+2)); 73 | i=i%hg->size; 74 | pthread_rwlock_wrlock(&hg->fdb_hashes[i]->lock); 75 | g_ptr_array_add(hg->fdb_hashes[i]->ptrArray, entry); 76 | g_hash_table_replace(hg->fdb_hashes[i]->hash, entry->key, entry); 77 | pthread_rwlock_unlock(&hg->fdb_hashes[i]->lock); 78 | //int s; 79 | __sync_fetch_and_add(&hg->cntSet,1); 80 | __sync_fetch_and_add(&hg->size_keys,kl); 81 | __sync_fetch_and_add(&hg->size_values,vl); 82 | __sync_fetch_and_add(&hg->dataIN,vl); 83 | __sync_fetch_and_add(&hg->size_metas,sizeof(fdb_hash_entry)+sizeof(fdb_hash_entry *)); 84 | // __sync_fetch_and_add(&fdb_system_var.cntSet,1); 85 | return 0; 86 | } 87 | 88 | my_bool fdb_del_init(UDF_INIT *initid, UDF_ARGS *args, char *message) 89 | { 90 | if (args->arg_count != 1 || args->arg_type[0] != STRING_RESULT) 91 | { 92 | strcpy(message, "fdb_del() can only accept one string argument"); 93 | return 1; 94 | } 95 | return 0; 96 | } 97 | 98 | long long fdb_del(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) 99 | { 100 | long long ret; 101 | unsigned char i=(unsigned char) (args->args[0][0]); 102 | if((args->lengths[0])>2) i=i+args->args[0][1]+args->args[0][2]; 103 | i=i%num_hashes; 104 | pthread_rwlock_wrlock(&fdb_hashes[i]->lock); 105 | ret=g_hash_table_remove (fdb_hashes[i]->hash,args->args[0]); 106 | // __sync_fetch_and_add(&fdb_hashes[i]->cntDel,1); 107 | pthread_rwlock_unlock(&fdb_hashes[i]->lock); 108 | return ret; 109 | } 110 | 111 | void hash_value_destroy_func(void * hash_entry) { 112 | fdb_hash_entry *entry= (fdb_hash_entry *) hash_entry; 113 | entry->expire=EXPIRE_DROPIT; 114 | } 115 | 116 | 117 | 118 | void fdb_hashes_new(fdb_hashes_group_t *hg, size_t size, unsigned int hash_expire_default, unsigned long long max_memory_size) { 119 | unsigned int i; 120 | hg->now=time(NULL); 121 | hg->size=size; 122 | hg->hash_expire_default=hash_expire_default; 123 | hg->max_memory_size=max_memory_size; 124 | hg->fdb_hashes=g_slice_alloc(sizeof(fdb_hash_t)*hg->size); 125 | for (i=0; isize; i++) { 126 | hg->fdb_hashes[i]=malloc(sizeof(fdb_hash_t)); 127 | hg->fdb_hashes[i]->hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, hash_value_destroy_func); 128 | hg->fdb_hashes[i]->purgeIdx = 0; 129 | pthread_rwlock_init(&(hg->fdb_hashes[i]->lock), NULL); 130 | hg->fdb_hashes[i]->ptrArray=g_ptr_array_new(); 131 | hg->fdb_hashes[i]->purgeChunkSize=0; // unnecessary. Here to avoid errors in valgrind 132 | } 133 | hg->cntDel = 0; 134 | hg->cntGet = 0; 135 | hg->cntGetOK = 0; 136 | hg->cntSet = 0; 137 | hg->cntSetERR = 0; 138 | hg->size_keys=0; 139 | hg->size_values=0; 140 | hg->size_metas=0; 141 | hg->dataIN=0; 142 | hg->dataOUT=0; 143 | } 144 | 145 | 146 | long long fdb_truncate_all(fdb_hashes_group_t *hg) { 147 | unsigned char i; 148 | long long totsize=0; 149 | for (i=0; isize; i++) { 150 | pthread_rwlock_wrlock(&hg->fdb_hashes[i]->lock); 151 | } 152 | for (i=0; isize; i++) { 153 | totsize+=g_hash_table_size(hg->fdb_hashes[i]->hash); 154 | g_hash_table_remove_all(hg->fdb_hashes[i]->hash); 155 | } 156 | for (i=0; isize; i++) { 157 | pthread_rwlock_unlock(&hg->fdb_hashes[i]->lock); 158 | } 159 | return totsize; 160 | } 161 | 162 | void *purgeHash_thread(void *arg) { 163 | long long min_idx=0; 164 | fdb_hashes_group_t *hg=arg; 165 | while(glovars.shutdown==0) { 166 | usleep(fdb_system_var.hash_purge_loop); 167 | hg->now=time(NULL); 168 | if ( fdb_hashes_group_used_mem_pct(hg) < fdb_system_var.purge_threshold_pct_min ) continue; 169 | unsigned char i; 170 | for (i=0; isize; i++) { 171 | pthread_rwlock_wrlock(&hg->fdb_hashes[i]->lock); 172 | if (hg->fdb_hashes[i]->purgeIdx==0) { 173 | if (hg->fdb_hashes[i]->ptrArray->len) { 174 | hg->fdb_hashes[i]->purgeIdx=hg->fdb_hashes[i]->ptrArray->len; 175 | hg->fdb_hashes[i]->purgeChunkSize=hg->fdb_hashes[i]->ptrArray->len*fdb_system_var.hash_purge_loop/fdb_system_var.hash_purge_time; 176 | if (hg->fdb_hashes[i]->purgeChunkSize < 10) { hg->fdb_hashes[i]->purgeChunkSize=hg->fdb_hashes[i]->ptrArray->len; } // this should prevent a bug with few entries left in the cache 177 | } 178 | } 179 | time_t t=hg->now; 180 | min_idx=( hg->fdb_hashes[i]->purgeIdx > hg->fdb_hashes[i]->purgeChunkSize ? hg->fdb_hashes[i]->purgeIdx - hg->fdb_hashes[i]->purgeChunkSize : 0 ) ; 181 | if (min_idx < hg->fdb_hashes[i]->purgeChunkSize ) min_idx=0; 182 | if (hg->fdb_hashes[i]->purgeIdx) while( --hg->fdb_hashes[i]->purgeIdx > min_idx) { 183 | fdb_hash_entry *entry=g_ptr_array_index(hg->fdb_hashes[i]->ptrArray,hg->fdb_hashes[i]->purgeIdx); 184 | if (( entry->expire!=EXPIRE_DROPIT) && entry->expire <= t) { 185 | g_hash_table_remove(hg->fdb_hashes[i]->hash,entry->key); 186 | } 187 | if ( (entry->expire==EXPIRE_DROPIT) 188 | && (__sync_fetch_and_add(&entry->ref_count,0)==0) 189 | ) { 190 | __sync_fetch_and_sub(&hg->size_keys,entry->klen); 191 | __sync_fetch_and_sub(&hg->size_values,entry->length); 192 | __sync_fetch_and_sub(&hg->size_metas,sizeof(fdb_hash_entry)+sizeof(fdb_hash_entry *)); 193 | g_free(entry->key); 194 | //g_slice_free1(entry->klen,entry->key); 195 | g_free(entry->value); 196 | entry->self=NULL; 197 | //g_free(entry); 198 | g_slice_free1(sizeof(fdb_hash_entry),entry); 199 | 200 | __sync_fetch_and_add(&hg->cntPurge,1); 201 | g_ptr_array_remove_index_fast(hg->fdb_hashes[i]->ptrArray,hg->fdb_hashes[i]->purgeIdx); 202 | } 203 | } 204 | 205 | pthread_rwlock_unlock(&hg->fdb_hashes[i]->lock); 206 | } 207 | } 208 | proxy_error("Shutdown purgeHash_thread\n"); 209 | return NULL; 210 | } 211 | 212 | long long fdb_hashes_group_free_mem(fdb_hashes_group_t *hg) { 213 | // note: this check is not 100% accurate as it is performed before locking any structure 214 | // this lack of accuracy is by design and not a bug 215 | long long cur_size=hg->size_keys+hg->size_values+hg->size_metas; 216 | long long max_size=hg->max_memory_size; 217 | return (cur_size > max_size ? 0 : max_size-cur_size); 218 | } 219 | 220 | int fdb_hashes_group_used_mem_pct(fdb_hashes_group_t *hg) { 221 | long long cur_size=hg->size_keys+hg->size_values+hg->size_metas; 222 | long long max_size=hg->max_memory_size; 223 | float pctf = (float) cur_size*100/max_size; 224 | if (pctf > 100) return 100; 225 | int pct=pctf; 226 | return pct; 227 | } 228 | 229 | // Added by chan ---------------------------------- 230 | // Destory hash value function 231 | void qr_hash_value_destroy_func(void * hash_entry) { 232 | cleanup_query_stats(hash_entry); 233 | /* qr_hash_entry *entry= (qr_hash_entry *) hash_entry; 234 | g_free(entry->key); 235 | g_free(entry->value); 236 | g_free(entry);*/ 237 | } 238 | 239 | // Create new hash struct 240 | void qr_hashes_new(qr_hash_t *ht){ 241 | ht->modify = time(NULL); 242 | ht->c_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, qr_hash_value_destroy_func); 243 | ht->p_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, qr_hash_value_destroy_func); 244 | pthread_rwlock_init(&(ht->lock), NULL); 245 | } 246 | 247 | // Increase executed count - search target with hash key 248 | // last changed at 20140418 - by chan 249 | void qr_set(char * key, char * value){ 250 | qr_hash_t *ht = &QR_HASH_T; 251 | pthread_rwlock_wrlock(&(ht->lock)); 252 | qr_hash_entry *entry = g_hash_table_lookup(ht->c_hash, key); 253 | if(entry == NULL){ 254 | entry = g_malloc(sizeof(qr_hash_entry)); 255 | entry->key = key; 256 | //FIXME: entry->value = value; 257 | entry->exec_cnt = 0; 258 | g_hash_table_insert(ht->c_hash, entry->key, entry); 259 | }else{ 260 | // free duplicated key and value 261 | // added by chan 262 | g_free(key); 263 | g_free(value); 264 | } 265 | entry->exec_cnt++; 266 | pthread_rwlock_unlock(&(ht->lock)); 267 | } 268 | 269 | // Print query stats - needed to write on log file 270 | // last changed at 20140418 - by chan 271 | void flush_query_stats (gpointer key, gpointer user_data){ 272 | #ifdef DEBUG 273 | qr_hash_t *ht = &QR_HASH_T; 274 | qr_hash_entry *entry = g_hash_table_lookup(ht->p_hash, key); 275 | //fprintf(stderr, "%s\t%d\t%p\n", entry->key, entry->exec_cnt, entry->value); 276 | //fprintf(stderr, "%d\t%s\t%s\t%s\t%s\t%d\t%s\t%d\n" , entry->exec_cnt, entry->key, key, entry->query_digest_md5, entry->query_digest_text, entry->hostgroup_id, entry->mysql_server_address, entry->mysql_server_port); 277 | proxy_debug(PROXY_DEBUG_QUERY_STATISTICS, 4, "%d\t%s\t%s\t%s\t%s\t%d\t%s\t%d\t%ld\n" , entry->exec_cnt, entry->username, entry->schemaname, entry->query_digest_md5, entry->query_digest_text, entry->hostgroup_id, ( entry->mysql_server_address ? entry->mysql_server_address : "NULL" ) , entry->mysql_server_port, entry->query_time); 278 | #endif 279 | } 280 | 281 | // Report query stat result 282 | // last changed at 20140418 - by chan 283 | void *qr_report_thread(void *arg){ 284 | qr_hash_t *ht = arg; 285 | while(glovars.shutdown==0) { 286 | sleep(glovars.mysql_query_statistics_interval); 287 | if (glovars.mysql_query_statistics) { 288 | char __buffer[25]; 289 | time_t curtime = time (NULL); 290 | struct tm *__tm_info=localtime(&curtime); 291 | strftime(__buffer, 25, "%Y-%m-%d %H:%M:%S", __tm_info); 292 | //fprintf(stderr, "%s\n", __buffer); 293 | proxy_debug(PROXY_DEBUG_QUERY_STATISTICS, 7, "Reporting queries\n"); 294 | pthread_rwlock_wrlock(&(ht->lock)); 295 | GHashTable *t_hash = ht->p_hash; 296 | ht->p_hash = ht->c_hash; 297 | ht->c_hash = t_hash; 298 | pthread_rwlock_unlock(&(ht->lock)); 299 | 300 | // Print current stats 301 | GList *keysList = g_hash_table_get_keys(ht->p_hash); 302 | #ifdef DEBUG 303 | g_list_foreach (keysList, flush_query_stats, NULL); 304 | #endif 305 | g_list_foreach (keysList, __sqlite3_statsdb__flush_query_stats, &curtime); 306 | g_list_free(keysList); 307 | 308 | // Remove all entry in p_hash 309 | g_hash_table_remove_all(ht->p_hash); 310 | } 311 | } 312 | return NULL; 313 | } 314 | // Added by chan end. 315 | -------------------------------------------------------------------------------- /src/l_utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "lutils.h" 5 | #include 6 | 7 | #ifdef PROXYMEMTRACK 8 | extern long long __mem_l_alloc_size; 9 | extern long long __mem_l_alloc_count; 10 | extern long long __mem_l_free_size; 11 | extern long long __mem_l_free_count; 12 | extern long long __mem_l_memalign_size; 13 | extern long long __mem_l_memalign_count; 14 | #endif 15 | 16 | extern __thread l_sfp *__thr_sfp; 17 | 18 | static unsigned int l_near_pow_2 (int n) { 19 | unsigned int i = 1; 20 | while (i < n) i <<= 1; 21 | return i ? i : n; 22 | } 23 | 24 | 25 | static inline void __sort_stack(l_sfc *sfc, void **sort_buff) { 26 | int i; 27 | sfc->stack=NULL; 28 | for (i=0;ifree_cnt;i++) { 29 | void *n=sort_buff[i]; 30 | if (n) { 31 | l_stack_push(&sfc->stack,n); 32 | } 33 | } 34 | } 35 | 36 | 37 | static void *__x_malloc(size_t size) { 38 | void *m=malloc(size); 39 | assert(m); 40 | return m; 41 | } 42 | 43 | static void * __x_memalign(size_t size) { 44 | int rc; 45 | void *m; 46 | rc=posix_memalign(&m, L_SFC_MEM_BLOCK_SIZE, size); 47 | assert(rc==0); 48 | #ifdef PROXYMEMTRACK 49 | __sync_fetch_and_add(&__mem_l_memalign_size,size); 50 | __sync_fetch_and_add(&__mem_l_memalign_count,1); 51 | #endif 52 | return m; 53 | } 54 | 55 | 56 | static void l_ptr_array_expand(LPtrArray *array, unsigned int more) { 57 | if ( (array->len+more) > array->size ) { 58 | unsigned int new_size=l_near_pow_2(array->len+more); 59 | void *new_pdata=l_alloc(new_size*sizeof(void *)); 60 | if (array->pdata) { 61 | memcpy(new_pdata,array->pdata,array->size*sizeof(void *)); 62 | l_free(array->size*sizeof(void *),array->pdata); 63 | } 64 | array->size=new_size; 65 | array->pdata=new_pdata; 66 | } 67 | } 68 | 69 | 70 | LPtrArray *l_ptr_array_sized_new(unsigned int size) { 71 | LPtrArray *array=l_alloc(sizeof(LPtrArray)); 72 | array->pdata=NULL; 73 | array->len=0; 74 | array->size=0; 75 | if (size) { 76 | l_ptr_array_expand(array, size); 77 | } 78 | return array; 79 | } 80 | 81 | LPtrArray *l_ptr_array_new() { 82 | return l_ptr_array_sized_new(0); 83 | } 84 | 85 | void l_ptr_array_add(LPtrArray *array, void *p) { 86 | if (array->len==array->size) { 87 | l_ptr_array_expand(array,1); 88 | } 89 | array->pdata[array->len++]=p; 90 | } 91 | 92 | void * l_ptr_array_remove_index(LPtrArray *array, unsigned int i) { 93 | void *r=array->pdata[i]; 94 | if (i != (array->len-1)) { 95 | int j; 96 | for (j=i; jlen-1; j++) { 97 | array->pdata[j]=array->pdata[j+1]; 98 | } 99 | } 100 | array->len--; 101 | return r; 102 | } 103 | 104 | void * l_ptr_array_remove_index_fast (LPtrArray *array, unsigned int i) { 105 | void *r=array->pdata[i]; 106 | if (i != (array->len-1)) 107 | array->pdata[i]=array->pdata[array->len-1]; 108 | array->len--; 109 | return r; 110 | } 111 | 112 | int l_ptr_array_remove_fast(LPtrArray *array, void *p) { 113 | unsigned int i; 114 | unsigned len=array->len; 115 | for (i=0; ipdata[i]==p) { 117 | l_ptr_array_remove_index_fast(array, i); 118 | return 1; 119 | } 120 | } 121 | return 0; 122 | } 123 | 124 | 125 | static void __add_mem_block(l_sfc *sfc, void *m) { 126 | void *nmp=__x_malloc(sizeof(void *)*(sfc->blocks_cnt+1)); 127 | if (sfc->mem_blocks) { 128 | memcpy(nmp,sfc->mem_blocks,sizeof(void *)*(sfc->blocks_cnt)); 129 | free(sfc->mem_blocks); 130 | } 131 | sfc->mem_blocks=nmp; 132 | sfc->mem_blocks[sfc->blocks_cnt++]=m; 133 | } 134 | 135 | 136 | l_sfp * l_mem_init() { 137 | l_sfp *s=__x_malloc(sizeof(l_sfp)); 138 | int i; 139 | for (i=0; isfc[i].stack=NULL; 141 | s->sfc[i].mem_blocks=NULL; 142 | s->sfc[i].elem_size=L_SFC_MIN_ELEM_SIZE * (1 << i) ; 143 | s->sfc[i].alloc_cnt=0; 144 | s->sfc[i].free_cnt=0; 145 | s->sfc[i].blocks_cnt=0; 146 | s->sfc[i].__mem_l_free_count=0; 147 | } 148 | return s; 149 | } 150 | 151 | void l_mem_destroy(l_sfp *s) { 152 | int i,j; 153 | for (i=0; isfc[i].blocks_cnt;j++) { 155 | free(s->sfc[i].mem_blocks[j]); 156 | } 157 | if (s->sfc[i].mem_blocks) { 158 | free(s->sfc[i].mem_blocks); 159 | } 160 | } 161 | } 162 | static inline void __push_mem_block(l_sfc *sfc, void *m) { 163 | int j; 164 | void *n; 165 | for (j=0; jelem_size; j++) { 166 | n=m+j*sfc->elem_size; 167 | l_stack_push(&sfc->stack,n); 168 | } 169 | } 170 | 171 | 172 | void * __l_alloc(l_sfp *sfp, size_t size) { 173 | if (size>L_SFC_MAX_ELEM_SIZE) { 174 | return __x_malloc(size); 175 | } 176 | #ifdef PROXYMEMTRACK 177 | __sync_fetch_and_add(&__mem_l_alloc_size,size); 178 | __sync_fetch_and_add(&__mem_l_alloc_count,1); 179 | #endif 180 | void *p; 181 | int i; 182 | i=L_SFP_ARRAY_LEN-1; 183 | if (size<=L_SFC_MID_ELEM_SIZE) 184 | i=L_SFP_ARRAY_MID-1; 185 | for ( ; i>=0 ; i-- ) { 186 | if (size*2>sfp->sfc[i].elem_size || i==0) { 187 | p=l_stack_pop(&sfp->sfc[i].stack); 188 | if (p) { 189 | return p; 190 | } 191 | void *m=__x_memalign(L_SFC_MEM_BLOCK_SIZE); 192 | __add_mem_block(&sfp->sfc[i],m); 193 | __push_mem_block(&sfp->sfc[i],m); 194 | sfp->sfc[i].alloc_cnt+=L_SFC_MEM_BLOCK_SIZE/sfp->sfc[i].elem_size; 195 | sfp->sfc[i].free_cnt+=L_SFC_MEM_BLOCK_SIZE/sfp->sfc[i].elem_size; 196 | p=l_stack_pop(&sfp->sfc[i].stack); 197 | return p; 198 | } 199 | } 200 | return NULL; 201 | } 202 | 203 | void * l_alloc(size_t size) { 204 | // return malloc(size); 205 | return __l_alloc(__thr_sfp,size); 206 | } 207 | 208 | 209 | void * l_alloc0(size_t size) { 210 | void *p=l_alloc(size); 211 | memset(p,0,size); 212 | return p; 213 | } 214 | 215 | 216 | int cmpptr(const void *a, const void *b) { 217 | int d= *(intptr_t *)a - *(intptr_t *)b; 218 | return d; 219 | } 220 | 221 | 222 | static void inline __compact_mem_copy_to_sort_buff(l_sfc *sfc, int elems, void **sort_buff) { 223 | int i; 224 | l_stack *p=sfc->stack; 225 | for (i=0;in; 228 | } 229 | } 230 | 231 | void compact_mem(l_sfc *sfc) { 232 | int elems=sfc->free_cnt; 233 | int i; 234 | int fbi=0; 235 | void **sort_buff=__x_malloc(elems*sizeof(void *)); 236 | void **free_block=__x_malloc(sfc->blocks_cnt*sizeof(void *)); 237 | __compact_mem_copy_to_sort_buff(sfc,elems,sort_buff); 238 | 239 | qsort(sort_buff,elems,sizeof(void *),cmpptr); 240 | for (i=0;ielem_size;i++) { 241 | intptr_t v1=(intptr_t)sort_buff[i]; 242 | if (v1%L_SFC_MEM_BLOCK_SIZE) { 243 | continue; 244 | } 245 | //unsigned long v2=*(unsigned long *)sort_buff[i+L_SFC_MEM_BLOCK_SIZE/sfc->elem_size-1]; 246 | intptr_t v2=(intptr_t)sort_buff[i+L_SFC_MEM_BLOCK_SIZE/sfc->elem_size-1]; 247 | //if (v2!=v1+sizeof(void *)*(L_SFC_MEM_BLOCK_SIZE/sfc->elem_size-1)) { 248 | if (v2!=v1+L_SFC_MEM_BLOCK_SIZE-sfc->elem_size) { 249 | continue; 250 | } 251 | free_block[fbi]=sort_buff[i]; 252 | memset(sort_buff+i,0,sizeof(void *)*L_SFC_MEM_BLOCK_SIZE/sfc->elem_size); 253 | fbi++; 254 | i+=L_SFC_MEM_BLOCK_SIZE/sfc->elem_size-1; 255 | } 256 | fprintf(stderr, "block_size: %d, blocks_cnt: %d, free_blocks: %d\n", (int)sfc->elem_size, (int)sfc->blocks_cnt, fbi); 257 | if (fbi) { 258 | int k=0; 259 | int j=0; 260 | void **new_mem_blocks=__x_malloc((sfc->blocks_cnt-fbi)*sizeof(void *)); 261 | for (i=0;iblocks_cnt;i++) { 262 | for (j=0;jmem_blocks[i]) { 264 | // found 265 | free(sfc->mem_blocks[i]); 266 | sfc->mem_blocks[i]=NULL; 267 | j=fbi; 268 | } 269 | } 270 | if (sfc->mem_blocks[i]) { 271 | new_mem_blocks[k]=sfc->mem_blocks[i]; 272 | k++; 273 | } 274 | } 275 | free(sfc->mem_blocks); 276 | sfc->mem_blocks=new_mem_blocks; 277 | sfc->blocks_cnt-=fbi; 278 | __sort_stack(sfc, sort_buff); 279 | 280 | fprintf(stderr, "block_size: %d, blocks_cnt: %d\n", (int)sfc->elem_size, (int)sfc->blocks_cnt); 281 | sfc->alloc_cnt-=fbi*L_SFC_MEM_BLOCK_SIZE/sfc->elem_size; 282 | sfc->free_cnt-=fbi*L_SFC_MEM_BLOCK_SIZE/sfc->elem_size; 283 | } 284 | __sort_stack(sfc, sort_buff); 285 | free(free_block); 286 | free(sort_buff); 287 | } 288 | 289 | 290 | void __l_free(l_sfp *sfp, size_t size, void *p) { 291 | if (size>L_SFC_MAX_ELEM_SIZE) { 292 | free(p); 293 | return; 294 | } 295 | #ifdef PROXYMEMTRACK 296 | __sync_fetch_and_add(&__mem_l_free_size,size); 297 | __sync_fetch_and_add(&__mem_l_free_count,1); 298 | #endif 299 | int i; 300 | i=L_SFP_ARRAY_LEN-1; 301 | if (size<=L_SFC_MID_ELEM_SIZE) 302 | i=L_SFP_ARRAY_MID-1; 303 | for ( ; i>=0 ; i-- ) { 304 | if (size*2>sfp->sfc[i].elem_size || i==0) { 305 | l_stack_push(&sfp->sfc[i].stack,p); 306 | sfp->sfc[i].free_cnt++; 307 | sfp->sfc[i].__mem_l_free_count++; 308 | 309 | if ((sfp->sfc[i].__mem_l_free_count%(L_SFC_MEM_BLOCK_SIZE)==0) && (sfp->sfc[i].blocks_cnt>16) && (sfp->sfc[i].free_cnt > sfp->sfc[i].alloc_cnt * 990/1000)) { 310 | compact_mem(&sfp->sfc[i]); 311 | fprintf(stderr,"%d\n",(int)sfp->sfc[i].__mem_l_free_count); 312 | sfp->sfc[i].__mem_l_free_count=0; 313 | } 314 | 315 | return; 316 | } 317 | } 318 | } 319 | 320 | void l_free(size_t size, void *p) { 321 | // free(p); 322 | __l_free(__thr_sfp,size,p); 323 | } 324 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #define DEFINE_VARIABLES 2 | #include "proxysql.h" 3 | 4 | //static pthread_key_t tsd_key; 5 | 6 | 7 | 8 | #ifdef DEBUG 9 | const char *malloc_conf = "xmalloc:true,lg_tcache_max:17,prof_accum:true,prof_gdump:true,lg_prof_sample:16,lg_prof_interval:20,prof_leak:true,prof_final:true"; 10 | #else 11 | const char *malloc_conf = "xmalloc:true,lg_tcache_max:17"; 12 | #endif 13 | 14 | static gint proxy_admin_port = 0; 15 | static gint proxy_mysql_port = 0; 16 | //static gchar *config_file="proxysql.cnf"; 17 | static gchar *config_file=NULL; 18 | gboolean foreground = 0; 19 | 20 | pthread_key_t tsd_key; 21 | 22 | int outfd=0; 23 | int errfd=0; 24 | 25 | static GOptionEntry entries[] = 26 | { 27 | { "admin-port", 0, 0, G_OPTION_ARG_INT, &proxy_admin_port, "Administration port", NULL }, 28 | { "mysql-port", 0, 0, G_OPTION_ARG_INT, &proxy_mysql_port, "MySQL proxy port", NULL }, 29 | { "foreground", 'f', 0, G_OPTION_ARG_NONE, &foreground, "Run in foreground", NULL }, 30 | { "debug", 'd', 0, G_OPTION_ARG_INT, &gdbg, "debug", NULL }, 31 | { "config", 'c', 0, G_OPTION_ARG_FILENAME, &config_file, "Configuration file", NULL }, 32 | { NULL } 33 | }; 34 | 35 | pthread_attr_t attr; 36 | int conn_cnt=0; 37 | 38 | time_t laststart; 39 | 40 | __thread l_sfp *__thr_sfp=NULL; 41 | __thread myConnPools __thr_myconnpool; 42 | 43 | int listen_tcp_fd; 44 | int listen_tcp_admin_fd; 45 | int listen_tcp_monitor_fd; 46 | int listen_unix_fd; 47 | 48 | pthread_t *glo_mysql_thrarr=NULL; 49 | 50 | 51 | static const char * proxysql_pid_file() { 52 | static char fn[512]; 53 | snprintf(fn, sizeof(fn), "%s", daemon_pid_file_ident); 54 | return fn; 55 | } 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | void *mysql_thread(void *arg) { 64 | int admin=0; 65 | mysql_thread_init(); 66 | int rc; 67 | int removing_hosts=0; 68 | long maintenance_ends=0; 69 | proxy_mysql_thread_t thrLD; 70 | rc=pthread_setspecific(tsd_key, &thrLD); 71 | assert(rc==0); 72 | thrLD.thread_id=*(int *)arg; 73 | 74 | // Initialize local memory allocator 75 | __thr_sfp=l_mem_init(); 76 | 77 | // Initialize local connection pool 78 | local_mysql_connpool_init(); 79 | 80 | if (thrLD.thread_id>=glovars.mysql_threads) { // starting admin or monitoring thread 81 | proxy_debug(PROXY_DEBUG_GENERIC, 4, "Started %s Thread with thread_id = %d\n", (thrLD.thread_id==glovars.mysql_threads ? "Admin" : "Monitoring") , thrLD.thread_id); 82 | admin=thrLD.thread_id+1-glovars.mysql_threads; 83 | } else { // starting normal mysql thread 84 | proxy_debug(PROXY_DEBUG_GENERIC, 4, "Started MySQL Thread with thread_id = %d\n", thrLD.thread_id); 85 | } 86 | 87 | // Initialize local array of sessions 88 | thrLD.sessions=l_ptr_array_new(); 89 | // thr.QC_rules=NULL; 90 | // thr.QCRver=0; 91 | // if (admin==0) { // no need for QC rules in 92 | // reset_QC_rules(thr.QC_rules); 93 | // } 94 | 95 | int i, nfds, r, max_fds; 96 | mysql_session_t *sess=NULL; 97 | 98 | struct pollfd *fds; 99 | max_fds = MIN_FDS_PER_THREAD; 100 | 101 | // preallocate an array of fds 102 | fds=(void *)g_malloc0(sizeof(struct pollfd)*MIN_FDS_PER_THREAD); 103 | 104 | 105 | // listen on various ports, different depending from admin status 106 | switch (admin) { 107 | case 0: // normal mysql thread 108 | fds[0].fd=listen_tcp_fd; 109 | fds[1].fd=listen_unix_fd; // Unix Domain Socket 110 | fds[2].fd=proxyipc.fdIn[thrLD.thread_id]; // IPC pipe 111 | break; 112 | case 1: // admin thread 113 | fds[0].fd=listen_tcp_admin_fd; 114 | break; 115 | case 2: // monitoring thread 116 | fds[0].fd=listen_tcp_monitor_fd; 117 | break; 118 | } 119 | while(glovars.shutdown==0) { 120 | // always wait for new connections 121 | fds[0].events=POLLIN; 122 | fds[0].revents=0; 123 | if (admin==0) { // normal mysql thread 124 | // Unix Domain Socket 125 | fds[1].events=POLLIN; 126 | fds[1].revents=0; 127 | // IPC pipe 128 | fds[2].events=POLLIN; 129 | fds[2].revents=0; 130 | nfds=3; 131 | } else { nfds=1;} // admin and monitoring thread 132 | 133 | 134 | // cycle through all healthy sessions 135 | // if the session has an active backend, prepare that fd 136 | for (i=0; i < thrLD.sessions->len; i++) { 137 | sess=l_ptr_array_index(thrLD.sessions, i); 138 | if (sess->healthy==1) { 139 | if (sess->admin==0 && sess->server_mybe && sess->server_mybe->server_myds && sess->server_mybe->server_mycpe) { 140 | sess->fds[1].fd=sess->server_mybe->server_myds->fd; 141 | sess->last_server_poll_fd=sess->server_mybe->server_myds->fd; 142 | sess->nfds=2; 143 | } else { 144 | sess->nfds=1; 145 | } 146 | sess->status=CONNECTION_READING_CLIENT|CONNECTION_WRITING_CLIENT|CONNECTION_READING_SERVER|CONNECTION_WRITING_SERVER; 147 | sess->conn_poll(sess); 148 | int j; 149 | // copy pollfd from the session into the thread 150 | for (j=0; jnfds; j++) { 151 | if (sess->fds[j].events) { 152 | sess->fds[j].revents=0; 153 | //memcpy(&fds[nfds],&sess->fds[j],sizeof(struct pollfd)); 154 | MEM_COPY_FWD(&fds[nfds],&sess->fds[j],sizeof(struct pollfd)); 155 | nfds++; 156 | } 157 | } 158 | } 159 | } 160 | 161 | // poll() for all the fds of all the sessions 162 | r=poll(fds,nfds,glovars.mysql_poll_timeout); 163 | if (r == -1 && errno == EINTR) 164 | continue; 165 | if (r == -1) { 166 | PANIC("poll()"); 167 | } 168 | 169 | 170 | if (admin==0 && fds[2].revents==POLLIN) { // admin thread is calling 171 | char c; 172 | int r; 173 | r=read(fds[2].fd,&c,sizeof(char)); 174 | assert(r>=1); 175 | proxy_debug(PROXY_DEBUG_IPC, 4, "Got byte on thr %d from FD %d\n", thrLD.thread_id, fds[2].fd); 176 | gchar *admincmd=g_async_queue_pop(proxyipc.queue[thrLD.thread_id]); 177 | proxy_debug(PROXY_DEBUG_IPC, 4, "Got command %s on thr %d\n", admincmd, thrLD.thread_id); 178 | if (strncmp(admincmd,"REMOVE SERVER",20)==0) { 179 | 180 | removing_hosts=1; 181 | maintenance_ends=monotonic_time()+glovars.mysql_maintenance_timeout*1000; 182 | } 183 | g_free(admincmd); 184 | } 185 | 186 | 187 | if (admin==0 && removing_hosts==1) { // admin thread is forcing the mysql thread to remove a server 188 | int i; 189 | int cnt=0; 190 | for (i=0; i < thrLD.sessions->len; i++) { 191 | sess=l_ptr_array_index(thrLD.sessions, i); 192 | cnt+=sess->remove_all_backends_offline_soft(sess); 193 | } 194 | if (cnt==0) { 195 | removing_hosts=0; 196 | gchar *ack=g_malloc0(20); 197 | sprintf(ack,"%d",cnt); 198 | proxy_debug(PROXY_DEBUG_IPC, 4, "Sending ACK from thr %d\n", thrLD.thread_id); 199 | g_async_queue_push(proxyipc.queue[glovars.mysql_threads],ack); 200 | } else { 201 | long ct=monotonic_time(); 202 | if (ct > maintenance_ends) { 203 | // drop all connections that aren't switched yet 204 | int i; 205 | int t=0; 206 | for (i=0; i < thrLD.sessions->len; i++) { 207 | int c=0; 208 | sess=l_ptr_array_index(thrLD.sessions, i); 209 | c=sess->remove_all_backends_offline_soft(sess); 210 | if (c) { 211 | t+=c; 212 | sess->force_close_backends=1; 213 | sess->close(sess); 214 | } 215 | } 216 | removing_hosts=0; 217 | gchar *ack=g_malloc0(20); 218 | sprintf(ack,"%d",t); 219 | proxy_debug(PROXY_DEBUG_IPC, 4, "Sending ACK from thr %d\n", thrLD.thread_id); 220 | g_async_queue_push(proxyipc.queue[glovars.mysql_threads],ack); 221 | } 222 | } 223 | } 224 | 225 | 226 | if (admin==0) {nfds=3;} else {nfds=1;} // define the starting point for fds array 227 | 228 | 229 | // cycle through all healthy sessions 230 | // and copy the fds back the the sessions 231 | for (i=0; i < thrLD.sessions->len; i++) { 232 | // copy pollfd back from the thread into the session 233 | sess=l_ptr_array_index(thrLD.sessions, i); 234 | if (sess->healthy==1) { 235 | int j; 236 | for (j=0; jnfds; j++) { 237 | if (sess->fds[j].events) { 238 | //memcpy(&sess->fds[j],&fds[nfds],sizeof(struct pollfd)); 239 | MEM_COPY_FWD(&sess->fds[j],&fds[nfds],sizeof(struct pollfd)); 240 | nfds++; 241 | } 242 | } 243 | // handle the session. This is the real core of the proxy 244 | sess->check_fds_errors(sess); 245 | sess->handler(sess); 246 | } 247 | } 248 | 249 | // cycle through all sessions 250 | // remove and delete all unhealthy session 251 | for (i=0; i < thrLD.sessions->len; i++) { 252 | sess=l_ptr_array_index(thrLD.sessions, i); 253 | if (sess->healthy==0) { 254 | l_ptr_array_remove_index_fast(thrLD.sessions,i); 255 | i--; 256 | mysql_session_delete(sess); 257 | } 258 | } 259 | 260 | 261 | // This section manages new incoming connections via TCP 262 | if (fds[0].revents==POLLIN) { 263 | int c=0; 264 | switch (admin) { 265 | case 0: // mysql thread 266 | c=accept(listen_tcp_fd, NULL, NULL); 267 | break; 268 | case 1: // admin thread 269 | c=accept(listen_tcp_admin_fd, NULL, NULL); 270 | break; 271 | case 2: // monitoring thread 272 | c=accept(listen_tcp_monitor_fd, NULL, NULL); 273 | break; 274 | } 275 | if (c>0) { // this thread got the new connection 276 | int arg_on=1; 277 | setsockopt(c, IPPROTO_TCP, TCP_NODELAY, (char *) &arg_on, sizeof(int)); 278 | mysql_session_t *ses=mysql_session_new(&thrLD, c); 279 | ses->admin=admin; // make the session aware of what sort of session is 280 | send_auth_pkt(ses); 281 | l_ptr_array_add(thrLD.sessions,ses); 282 | } 283 | } 284 | if (admin==0 && fds[1].revents==POLLIN) { 285 | int c=accept(listen_unix_fd, NULL, NULL); 286 | if (c>0) { 287 | mysql_session_t *ses=mysql_session_new(&thrLD, c); 288 | send_auth_pkt(ses); 289 | l_ptr_array_add(thrLD.sessions,ses); 290 | } 291 | } 292 | if ( ( (thrLD.sessions->len + 10) * MAX_FDS_PER_SESSION ) > max_fds ) { 293 | // allocate more fds 294 | max_fds+=MIN_FDS_PER_THREAD; 295 | struct pollfd *fds_tmp=(void *)g_malloc0(sizeof(struct pollfd)*max_fds); 296 | memcpy(fds_tmp,fds,sizeof(struct pollfd)*nfds); 297 | g_free(fds); 298 | fds=fds_tmp; 299 | } 300 | } 301 | g_free(fds); 302 | l_mem_destroy(__thr_sfp); 303 | return NULL; 304 | } 305 | 306 | 307 | 308 | 309 | int main(int argc, char **argv) { 310 | gdbg=0; 311 | pid_t pid; 312 | int i, rc; 313 | 314 | //g_thread_init(NULL); 315 | 316 | 317 | 318 | #ifdef DEBUG 319 | glo_debug=g_slice_alloc(sizeof(glo_debug_t)); 320 | glo_debug->glock=0; 321 | glo_debug->msg_count=0; 322 | glo_debug->async_queue=g_async_queue_new(); 323 | glo_debug->sfp=l_mem_init(); 324 | #endif 325 | 326 | #ifdef DEBUG 327 | //g_mem_set_vtable(glib_mem_profiler_table); 328 | #endif 329 | 330 | #ifdef PROXYMEMTRACK 331 | __mem_l_alloc_size=0; 332 | __mem_l_alloc_count=0; 333 | __mem_l_free_size=0; 334 | __mem_l_free_count=0; 335 | __mem_l_memalign_size=0; 336 | __mem_l_memalign_count=0; 337 | #endif 338 | 339 | #ifdef DEBUG 340 | mtrace(); 341 | #endif 342 | 343 | rc=pthread_key_create(&tsd_key, NULL); 344 | assert(rc==0); 345 | // parse all the arguments and the config file 346 | main_opts(entries, &argc, &argv, &config_file); 347 | 348 | 349 | if (foreground==0) { 350 | daemon_pid_file_ident=glovars.proxy_pidfile; 351 | daemon_log_ident=daemon_ident_from_argv0(argv[0]); 352 | 353 | rc=chdir(glovars.proxy_datadir); 354 | if (rc) { 355 | daemon_log(LOG_ERR, "Could not chdir into datadir: %s . Error: %s", glovars.proxy_datadir, strerror(errno)); 356 | return EXIT_FAILURE; 357 | } 358 | 359 | 360 | daemon_pid_file_proc=proxysql_pid_file; 361 | 362 | pid=daemon_pid_file_is_running(); 363 | if (pid>=0) { 364 | daemon_log(LOG_ERR, "Daemon already running on PID file %u", pid); 365 | return EXIT_FAILURE; 366 | } 367 | if (daemon_retval_init() < 0) { 368 | daemon_log(LOG_ERR, "Failed to create pipe."); 369 | return EXIT_FAILURE; 370 | } 371 | 372 | 373 | /* Do the fork */ 374 | if ((pid = daemon_fork()) < 0) { 375 | /* Exit on error */ 376 | daemon_retval_done(); 377 | return EXIT_FAILURE; 378 | 379 | } else if (pid) { /* The parent */ 380 | int ret; 381 | /* Wait for 20 seconds for the return value passed from the daemon process */ 382 | if ((ret = daemon_retval_wait(20)) < 0) { 383 | daemon_log(LOG_ERR, "Could not recieve return value from daemon process: %s", strerror(errno)); 384 | return EXIT_FAILURE; 385 | } 386 | 387 | if (ret) { 388 | daemon_log(LOG_ERR, "Daemon returned %i as return value.", ret); 389 | } 390 | return ret; 391 | } else { /* The daemon */ 392 | 393 | /* Close FDs */ 394 | if (daemon_close_all(-1) < 0) { 395 | daemon_log(LOG_ERR, "Failed to close all file descriptors: %s", strerror(errno)); 396 | 397 | /* Send the error condition to the parent process */ 398 | daemon_retval_send(1); 399 | goto finish; 400 | } 401 | 402 | rc=chdir(glovars.proxy_datadir); 403 | if (rc) { 404 | daemon_log(LOG_ERR, "Could not chdir into datadir: %s . Error: %s", glovars.proxy_datadir, strerror(errno)); 405 | return EXIT_FAILURE; 406 | } 407 | /* Create the PID file */ 408 | if (daemon_pid_file_create() < 0) { 409 | daemon_log(LOG_ERR, "Could not create PID file (%s).", strerror(errno)); 410 | daemon_retval_send(2); 411 | goto finish; 412 | } 413 | 414 | 415 | 416 | /* Send OK to parent process */ 417 | daemon_retval_send(0); 418 | outfd=open(glovars.proxy_errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 419 | assert(outfd>0); 420 | dup2(outfd, STDOUT_FILENO); 421 | close(outfd); 422 | errfd=open(glovars.proxy_errorlog, O_WRONLY | O_APPEND | O_CREAT , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); 423 | assert(errfd>0); 424 | dup2(errfd, STDERR_FILENO); 425 | close(errfd); 426 | 427 | proxy_error("Starting ProxySQL\n"); 428 | daemon_log(LOG_INFO, "Sucessfully started"); 429 | 430 | } 431 | } 432 | laststart=0; 433 | if (glovars.proxy_restart_on_error) { 434 | gotofork: 435 | if (laststart) { 436 | proxy_error("Angel process is waiting %d seconds before starting a new ProxySQL process\n", glovars.proxy_restart_delay); 437 | sleep(glovars.proxy_restart_delay); 438 | } 439 | laststart=time(NULL); 440 | pid = fork(); 441 | if (pid < 0) { 442 | proxy_error("[FATAL]: Error in fork()\n"); 443 | return EXIT_FAILURE; 444 | } 445 | 446 | if (pid) { 447 | int status; 448 | proxy_error("Angel process started ProxySQL process %d\n", pid); 449 | rc=waitpid(pid, &status, 0); 450 | if (rc==-1) { 451 | perror("waitpid"); 452 | //proxy_error("[FATAL]: waitpid: %s\n", perror("waitpid")); 453 | return EXIT_FAILURE; 454 | } 455 | rc=WIFEXITED(status); 456 | if (rc) { // client exit()ed 457 | rc=WEXITSTATUS(status); 458 | if (rc==0) { 459 | proxy_error("Shutdown angel process\n"); 460 | if (glovars.http_start) { 461 | int rc=system("pkill -f proxysqlHTTPd"); 462 | assert(rc>=0); 463 | } 464 | return 0; 465 | } else { 466 | proxy_error("ProxySQL exited with code %d . Restarting!\n", rc); 467 | goto gotofork; 468 | } 469 | } else { 470 | proxy_error("ProxySQL crashed. Restarting!\n"); 471 | goto gotofork; 472 | } 473 | } 474 | } 475 | if (glovars.http_start) { 476 | pid = fork(); 477 | if (!pid) { 478 | { 479 | int rc=system("pkill -f proxysqlHTTPd"); 480 | assert(rc>=0); 481 | } 482 | sleep(1); 483 | //execlp("perl", "perl", "-f", "./proxysqlHTTPd",NULL); 484 | char *execbin="./proxysqlHTTPd"; 485 | char *newargv[] = { NULL, NULL, NULL }; 486 | char *newenviron[] = { NULL }; 487 | newargv[0]=execbin; 488 | newargv[1]=glovars.proxy_configfile; 489 | int rc; 490 | rc=chdir(glovars.proxy_datadir); 491 | if (rc) { 492 | daemon_log(LOG_ERR, "Could not chdir into datadir: %s . Error: %s", glovars.proxy_datadir, strerror(errno)); 493 | return EXIT_FAILURE; 494 | } 495 | execve(execbin,newargv,newenviron); 496 | //execve(execbin,NULL,NULL); 497 | } 498 | } 499 | 500 | 501 | glo_DefHG_init(&gloDefHG); 502 | 503 | admin_init_sqlite3(); 504 | 505 | if (glovars.merge_configfile_db==1) { 506 | sqlite3_flush_users_mem_to_db(sqlite3admindb,0,1); 507 | sqlite3_flush_debug_levels_mem_to_db(sqlite3admindb,0); 508 | } 509 | // copying back and forth should merge the data 510 | sqlite3_flush_debug_levels_db_to_mem(sqlite3admindb); 511 | sqlite3_flush_users_db_to_mem(sqlite3admindb); 512 | sqlite3_flush_query_rules_db_to_mem(sqlite3admindb); 513 | sqlite3_flush_default_hostgroups_db_to_mem(sqlite3admindb); 514 | 515 | sqlite3_flush_servers_mem_to_db(sqlite3admindb,0); 516 | sqlite3_flush_servers_db_to_mem(sqlite3admindb,1); 517 | 518 | 519 | // command line options take precedences over config file 520 | if (proxy_admin_port) { glovars.proxy_admin_port=proxy_admin_port; } 521 | if (proxy_mysql_port) { glovars.proxy_mysql_port=proxy_mysql_port; } 522 | 523 | if (glovars.proxy_admin_port==glovars.proxy_mysql_port) { 524 | proxy_error("Fatal error: proxy_admin_port (%d) matches proxy_mysql_port (%d) . Configure them to use different ports\n", glovars.proxy_admin_port, glovars.proxy_mysql_port); 525 | exit(EXIT_FAILURE); 526 | } 527 | 528 | 529 | //glomybepools_init(); 530 | 531 | 532 | proxy_error("Opening Sockets\n"); 533 | listen_tcp_fd=listen_on_port(glovars.proxy_mysql_bind, (uint16_t)glovars.proxy_mysql_port); 534 | listen_tcp_admin_fd=listen_on_port(glovars.proxy_admin_bind, (uint16_t)glovars.proxy_admin_port); 535 | listen_tcp_monitor_fd=listen_on_port(glovars.proxy_monitor_bind, (uint16_t)glovars.proxy_monitor_port); 536 | listen_unix_fd=listen_on_unix(glovars.mysql_socket); 537 | ioctl_FIONBIO(listen_tcp_fd, 1); 538 | ioctl_FIONBIO(listen_tcp_admin_fd, 1); 539 | ioctl_FIONBIO(listen_unix_fd, 1); 540 | //mysql_library_init(0, NULL, NULL); 541 | //pthread_init(); 542 | my_init(); 543 | mysql_server_init(0, NULL, NULL); 544 | 545 | 546 | 547 | // Set threads attributes . For now only setstacksize is defined 548 | rc=pthread_attr_init(&attr); 549 | rc=pthread_attr_setstacksize(&attr, glovars.stack_size); 550 | assert(rc==0); 551 | //set_thread_attr(&attr,glovars.stack_size); 552 | { 553 | size_t ss; 554 | rc=pthread_attr_getstacksize(&attr,&ss); 555 | // fprintf(stderr,"stack size=%d (%d)\n", ss, glovars.stack_size); 556 | } 557 | 558 | 559 | void **stackspts=g_malloc0(sizeof(void *)*(glovars.mysql_threads+2+4)); 560 | // start background threads: 561 | // - mysql QC purger ( purgeHash_thread ) 562 | // - mysql connection pool purger ( mysql_connpool_purge_thread ) 563 | start_background_threads(&attr, stackspts); 564 | 565 | 566 | init_proxyipc(); 567 | 568 | // Note: glovars.mysql_threads+1 threads are created. The +2 is for the admin and monitoring module 569 | glo_mysql_thrarr=g_malloc0(sizeof(pthread_t)*(glovars.mysql_threads+2)); 570 | int *args=g_malloc0(sizeof(int)*(glovars.mysql_threads+2)); 571 | // while all other threads are detachable, the mysql connections handlers are not 572 | // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 573 | for (i=0; i< glovars.mysql_threads+2; i++) { 574 | args[i]=i; 575 | int rc; 576 | void *sp; 577 | rc=posix_memalign(&sp, sysconf(_SC_PAGESIZE), glovars.stack_size); 578 | assert(rc==0); 579 | stackspts[i]=sp; 580 | rc = pthread_attr_setstack(&attr, sp, glovars.stack_size); 581 | assert(rc==0); 582 | rc=pthread_create(&glo_mysql_thrarr[i], &attr, mysql_thread , &args[i]); 583 | assert(rc==0); 584 | } 585 | 586 | 587 | // wait for graceful shutdown 588 | for (i=0; isfp); 615 | g_async_queue_unref(glo_debug->async_queue); 616 | g_slice_free1(sizeof(glo_debug_t),glo_debug); 617 | 618 | #endif 619 | 620 | for (i=0; iserver_bytes_at_cmd,0,sizeof(bytes_stats)); } 4 | 5 | static inline void backend_reset_server_mycpe(mysql_backend_t *mybe, mysql_connpool **mcp, int fc) { 6 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 7, "Reset server_mycpe for MySQL backend %p\n", mybe); 7 | if (mybe->server_mycpe) { 8 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 7, "Reset server_mycpe %p for MySQL backend %p\n", mybe->server_mycpe, mybe); 9 | mysql_connpool_detach_connection(MYSQL_CONNPOOL_LOCAL, mcp, mybe->server_mycpe, fc); 10 | } 11 | mybe->server_mycpe=NULL; 12 | } 13 | 14 | static inline int backend_reset_server_myds(mysql_backend_t *mybe) { 15 | int rc=0; 16 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 7, "Reset myds for MySQL backend %p\n", mybe); 17 | if (mybe->server_myds) { 18 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 7, "Reset myds %p for MySQL backend %p\n", mybe->server_myds, mybe); 19 | if (mybe->server_myds->active==FALSE) rc=1; 20 | mysql_data_stream_delete(mybe->server_myds); 21 | mybe->server_myds=NULL; 22 | } 23 | return rc; 24 | } 25 | 26 | static void backend_detach(mysql_backend_t *mybe, mysql_connpool **mcp, int fc) { 27 | mybe->last_mysql_connpool=NULL; 28 | /* for optimization, the calling function should check that 29 | if (glovars.mysql_share_connections==0) { 30 | return; 31 | } */ 32 | if (mybe->server_mycpe && mybe->server_mycpe->reusable) { 33 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 7, "Detach MySQL backend, server %s:%d , fd %d\n", mybe->mshge->MSptr->address, mybe->mshge->MSptr->port, mybe->fd); 34 | backend_reset_server_mycpe(mybe, mcp, fc); 35 | } 36 | } 37 | 38 | static void backend_reset(mysql_backend_t *mybe, mysql_connpool **mcp, int force_close) { 39 | mybe->fd=0; 40 | int fc=force_close; 41 | int rc; 42 | mybe->last_mysql_connpool=NULL; 43 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 7, "Reset MySQL backend %p\n", mybe); 44 | if (mybe->mshge && mybe->mshge->MSptr) { 45 | // without the IF , this can cause SIGSEGV 46 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 7, "Reset MySQL backend, server %s:%d , fd %d\n", mybe->mshge->MSptr->address, mybe->mshge->MSptr->port, mybe->fd); 47 | __sync_fetch_and_sub(&mybe->mshge->connections_active,1); 48 | } 49 | //if (mybe->mshge) mybe->mshge->MSptr=NULL; 50 | mybe->mshge=NULL; 51 | 52 | rc=backend_reset_server_myds(mybe); 53 | if (fc==0 && rc==1) { fc=1; } // close an inactive data stream 54 | backend_reset_server_mycpe(mybe, mcp, fc); 55 | RESET_MYBE_STATS(mybe); 56 | mybe->fd=0; 57 | //memset(&mybe->server_bytes_at_cmd,0,sizeof(bytes_stats)); 58 | } 59 | 60 | mysql_backend_t *mysql_backend_new() { 61 | mysql_backend_t *mybe=g_slice_alloc0(sizeof(mysql_backend_t)); 62 | mybe->bereset=backend_reset; 63 | mybe->bedetach=backend_detach; 64 | mybe->last_mysql_connpool=NULL; 65 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 7, "Created new MySQL backend, addr %p\n", mybe); 66 | return mybe; 67 | } 68 | 69 | void mysql_backend_delete(mysql_backend_t *mybe) { 70 | g_slice_free1(sizeof(mysql_backend_t),mybe); 71 | } 72 | 73 | 74 | 75 | /* 76 | static mysql_backend_pool_t * mysql_backend_pool_create(const char *username, const char *password, const char *schema, int hostgroup) { 77 | // NOTE: the calling function must lock the mutex 78 | mysql_backend_pool_t *mbep; 79 | mbep=g_malloc0(sizeof(mysql_backend_pool_t)); 80 | mbep->username=g_strdup(username); 81 | mbep->password=g_strdup(password); 82 | mbep->schema=g_strdup(schema); 83 | mbep->hostgroup=hostgroup; 84 | mbep->free_backends=g_ptr_array_new(); 85 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Created mysql backend pool for user u=%s p=%s D=%s hg=%d\n", username, password, schema, hostgroup); 86 | return mbep; 87 | } 88 | 89 | static mysql_backend_pool_t * mysql_backend_pool_find(const char *username, const char *password, const char *schema, int hostgroup) { 90 | // NOTE: the calling function must lock the mutex 91 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Searching a backend for u=%s p=%s D=%s hg=%d\n", username, password, schema, hostgroup); 92 | guint l; 93 | for (l=0; llen; l++) { 94 | mysql_backend_pool_t *mbep=g_ptr_array_index(glomybepools.mybepools,l); 95 | if ( 96 | (strcmp(username,mbep->username)==0) && 97 | (strcmp(password,mbep->password)==0) && 98 | (strcmp(schema,mbep->schema)==0) && 99 | (hostgroup==mbep->hostgroup) 100 | ) { // we found the matching username/password/schema/hostgroup 101 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Found a backend for u=%s p=%s D=%s hg=%d\n", username, password, schema, hostgroup); 102 | return mbep; 103 | }// else { 104 | // return NULL; 105 | //} 106 | } 107 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Found a backend for u=%s p=%s D=%s hg=%d\n", username, password, schema, hostgroup); 108 | return NULL; // no match found 109 | } 110 | 111 | static mysql_backend_t * mysql_backend_pool_get(const char *username, const char *password, const char *schema, int hostgroup) { 112 | // NOTE: this function locks the mutex 113 | return NULL; 114 | mysql_backend_t *mybe=NULL; 115 | pthread_mutex_lock(&glomybepools.mutex); 116 | mysql_backend_pool_t *mbep=mysql_backend_pool_find(username, password, schema, hostgroup); 117 | if (mbep==NULL) { 118 | mbep=mysql_backend_pool_create(username, password, schema, hostgroup); 119 | g_ptr_array_add(glomybepools.mybepools,mbep); 120 | } 121 | if (mbep->free_backends->len) { 122 | mybe=g_ptr_array_index(mbep->free_backends,0); 123 | g_ptr_array_remove_index_fast(mbep->free_backends,0); 124 | } 125 | pthread_mutex_unlock(&glomybepools.mutex); 126 | return mybe; 127 | } 128 | 129 | static void mysql_backend_pool_detach(mysql_backend_t *mybe, int hostgroup, int force_close) { 130 | return; 131 | if (mybe->mshge==NULL || mybe->mshge->MSptr==NULL) return; 132 | pthread_mutex_lock(&glomybepools.mutex); 133 | mysql_backend_pool_t *mbep=mysql_backend_pool_find(mybe->server_mycpe->conn->user, mybe->server_mycpe->conn->passwd, mybe->server_mycpe->conn->db, hostgroup); 134 | g_ptr_array_add(mbep->free_backends,mybe); 135 | pthread_mutex_unlock(&glomybepools.mutex); 136 | } 137 | 138 | void glomybepools_init() { 139 | //glomybepools.mutex=0; 140 | pthread_mutex_init(&glomybepools.mutex, NULL); 141 | glomybepools.enabled=1; 142 | glomybepools.mybepools=g_ptr_array_new(); 143 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 4, "Main mysql backend pool struct created\n"); 144 | glomybepools.get=mysql_backend_pool_get; 145 | glomybepools.detach=mysql_backend_pool_detach; 146 | // glomybepools.create=mysql_backend_pool_create; 147 | } 148 | */ 149 | -------------------------------------------------------------------------------- /src/mysql_connpool.c: -------------------------------------------------------------------------------- 1 | #include "proxysql.h" 2 | 3 | /* 4 | * need to add several 5 | * #ifdef DEBUG_mysql_conn 6 | * for debugging 7 | */ 8 | 9 | 10 | 11 | int glock; 12 | 13 | 14 | 15 | static inline void close_expired_mysql_connection(mysql_cp_entry_t *mc) { 16 | mysql_close(mc->conn); // ... close it 17 | g_free(mc); 18 | } 19 | 20 | 21 | static mysql_cp_entry_t *create_new_mysql_connection(const char *hostname, const char *username, const char *password, const char *db, unsigned int port) { 22 | mysql_cp_entry_t *mycpe=NULL; 23 | MYSQL *mysql_con = mysql_init(NULL); 24 | // my_bool my_true = 1; 25 | // mysql_options(mysql_con, MYSQL_OPT_RECONNECT, &my_true); 26 | if (mysql_real_connect(mysql_con, hostname, username, password, db, port, NULL, 0) == NULL) { 27 | // we aren't able to connect 28 | fprintf(stderr, "%s\n", mysql_error(mysql_con)); 29 | // we don't abort because the called may decide to connect to another slave if available 30 | } else { 31 | mycpe=g_malloc(sizeof(mysql_cp_entry_t)); 32 | mycpe->reusable=1; 33 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Created new connection for %s %s %s %d\n", hostname, username, db, port); 34 | mycpe->conn=mysql_con; 35 | unsigned long long curr_time=(unsigned long long) (gloconnpool.tv.tv_sec) * 1000000 + (gloconnpool.tv.tv_usec); 36 | mycpe->expire = curr_time + glovars.mysql_wait_timeout; 37 | /* NEW STUFF 38 | While testing auto-reconnect features with hundreds of connections being constantly killed, 39 | I noticed that connections stay in CLOSE_WAIT state for very long time 40 | It should be related to the fact that mysql_real_connect() call setsockopt() with SO_KEEPALIVE 41 | By default a keepalive is sent every tcp_keepalive_time seconds (defaults to 2 hours). 42 | We are not changing it to 10 minutes, hardcoded for now, configurable later on. 43 | We also need to add error control. 44 | */ 45 | int tcp_keepalive_time=600; 46 | setsockopt(mysql_con->net.fd, SOL_TCP, TCP_KEEPIDLE, (char *)&tcp_keepalive_time, sizeof(tcp_keepalive_time)); 47 | ioctl_FIONBIO(mysql_con->net.fd, 1); 48 | } 49 | return mycpe; 50 | } 51 | 52 | 53 | //gboolean reconnect_server_on_shut_fd(mysql_session_t *sess, mysql_cp_entry_t **myc) { 54 | gboolean reconnect_server_on_shut_fd(mysql_session_t *sess) { 55 | if ( (sess->server_mybe==NULL) || (sess->server_mybe->server_myds==NULL) // the backend is not initialized, return 56 | || ( sess->server_mybe->server_myds->active==TRUE )) { 57 | return TRUE; 58 | } 59 | if ( 60 | ( sess->server_mybe->server_myds->active==FALSE ) // connection is not active 61 | && (glovars.mysql_auto_reconnect_enabled==FALSE) // auto-reconnect is globally disabled 62 | ) { 63 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "mysql_auto_reconnect_enabled is OFF\n"); 64 | return FALSE; 65 | } 66 | 67 | // FIXME: temporary workaround for issue #57 68 | return FALSE; 69 | 70 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Entering reconnect_server_on_shut_fd\n"); 71 | mysql_cp_entry_t *mycpe=NULL; 72 | if ( 73 | ( sess->server_mybe->server_myds->active==FALSE ) // connection is not active 74 | && ( sess->mysql_server_reconnect==TRUE ) // the session is configured to reconnect 75 | && ( sess->server_bytes_at_cmd.bytes_sent==sess->server_mybe->server_myds->bytes_info.bytes_sent) // no bytes sent so far 76 | && ( sess->server_bytes_at_cmd.bytes_recv==sess->server_mybe->server_myds->bytes_info.bytes_recv) // no bytes recv so far 77 | ) { 78 | int tries=10; 79 | while (tries--) { 80 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Trying to reconnect...\n"); 81 | if (sess->server_mybe && sess->server_mybe->server_mycpe) { 82 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Closing mysql connection on fd %d\n", sess->server_mybe->server_mycpe->conn->net.fd); 83 | if (sess->server_mybe->server_mycpe->conn->net.fd==0) { 84 | // for some unknown reason, conn->net.fd may be 0. This seems a bug! 85 | sess->server_mybe->server_mycpe->conn->net.vio=0; 86 | } 87 | mysql_close(sess->server_mybe->server_mycpe->conn); // drop the connection 88 | sess->server_mybe->server_mycpe=NULL; 89 | } 90 | // mycpe=mysql_connpool_get_connection(MYSQL_CONNPOOL_LOCAL, &sess->last_mysql_connpool, sess->server_mybe->mshge->MSptr->address, sess->mysql_username, sess->mysql_password, sess->mysql_schema_cur, sess->server_mybe->mshge->MSptr->port); // re-establish a new connection --- FIXME: bugged 91 | // try it 92 | if (mycpe) { 93 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Obtained mysql connection on fd %d\n", mycpe->conn->net.fd); 94 | ioctl_FIONBIO(mycpe->conn->net.fd, 0); 95 | if (mysql_query(mycpe->conn,"SELECT 1")) { 96 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 3, "SELECT 1 failed on fd %d\n", mycpe->conn->net.fd); 97 | //shutdown(mycpe->conn->net.fd, SHUT_RDWR); 98 | close_expired_mysql_connection(mycpe); 99 | mycpe=NULL; 100 | continue; 101 | } 102 | MYSQL_RES *result = mysql_store_result(mycpe->conn); 103 | mysql_free_result(result); 104 | tries=0; 105 | ioctl_FIONBIO(mycpe->conn->net.fd, 1); 106 | continue; 107 | } 108 | } 109 | if (mycpe==NULL) { 110 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 3, "Unable to return a connection. Reconnection FAILED\n"); 111 | // maybe is better if the calling function sends the error to the client. The follow 3 lines should be moved out of here 112 | pkt *hs; 113 | hs=mypkt_alloc(); 114 | create_err_packet(hs, 2, 1045, "#28000Access denied for user"); 115 | write_one_pkt_to_net(sess->client_myds,hs); 116 | return FALSE; 117 | } else { 118 | sess->fds[1].fd=sess->server_mybe->server_myds->fd; 119 | sess->server_mybe->server_myds->active=TRUE; 120 | } 121 | } 122 | return TRUE; 123 | } 124 | 125 | 126 | void local_mysql_connpool_init() { 127 | __thr_myconnpool.connpools=g_ptr_array_new(); 128 | __thr_myconnpool.enabled=gloconnpool.enabled; 129 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 4, "Per-thread connection pool struct created\n"); 130 | } 131 | 132 | void mysql_connpool_init(global_variable_entry_t *gve) { 133 | glock=0; 134 | pthread_mutex_init(&gloconnpool.mutex, NULL); 135 | gloconnpool.connpools=g_ptr_array_new(); 136 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 4, "Main connection pool struct created\n"); 137 | } 138 | 139 | 140 | static mysql_connpool *mysql_connpool_find(myConnPools *cp, const char *hostname, const char *username, const char *password, const char *db, unsigned int port) { 141 | // NOTE: the calling function must lock the mutex 142 | //myConnPools *cp=&__thr_myconnpool; 143 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Searching a connection for %s %s %s %d\n", hostname, username, db, port); 144 | guint l; 145 | for (l=0; lconnpools->len; l++) { 146 | mysql_connpool *mcp=g_ptr_array_index(cp->connpools,l); 147 | if (db == NULL || mcp->db == NULL) 148 | return NULL; 149 | if ( 150 | (strcmp(hostname,mcp->hostname)==0) && 151 | (strcmp(username,mcp->username)==0) && 152 | (strcmp(password,mcp->password)==0) && 153 | (strcmp(db,mcp->db)==0) && 154 | (port==mcp->port) 155 | ) { // we found the matching hostname/username/password/port 156 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Found connection for %s %s %s %d\n", hostname, username, db, port); 157 | return mcp; 158 | } 159 | } 160 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "NOT found connection for %s %s %s %d\n", hostname, username, db, port); 161 | return NULL; // no match found 162 | } 163 | 164 | static mysql_connpool *mysql_connpool_create(myConnPools *cp, const char *hostname, const char *username, const char *password, const char *db, unsigned int port) { 165 | // NOTE: the calling function must lock the mutex 166 | //myConnPools *cp=&__thr_myconnpool; 167 | mysql_connpool *mcp; 168 | mcp=g_malloc(sizeof(mysql_connpool)); 169 | mcp->hostname=g_strdup(hostname); 170 | mcp->username=g_strdup(username); 171 | mcp->password=g_strdup(password); 172 | mcp->db=g_strdup(db); 173 | mcp->port=port; 174 | mcp->free_conns=g_ptr_array_new(); 175 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Created connection pool for %s %s %s %d\n", hostname, username, db, port); 176 | g_ptr_array_add(cp->connpools,mcp); 177 | return mcp; 178 | } 179 | 180 | static inline mysql_connpool *mysql_connpool_find_or_create(myConnPools *cp, const char *hostname, const char *username, const char *password, const char *db, unsigned int port) { 181 | mysql_connpool *mcp=mysql_connpool_find(cp, hostname, username, password, db, port); 182 | if (mcp==NULL) { 183 | mcp=mysql_connpool_create(cp, hostname, username, password, db, port); 184 | } 185 | return mcp; 186 | } 187 | 188 | 189 | mysql_connpool *mysql_connpool_exists_global(const char *hostname, const char *username, const char *password, const char *db, unsigned int port) { 190 | mysql_connpool *mcp=mysql_connpool_find(&__thr_myconnpool, hostname, username, password, db, port); 191 | if (mcp) return mcp; 192 | SPIN_LOCK(glock); 193 | mcp=mysql_connpool_find(&gloconnpool, hostname, username, password, db, port); 194 | SPIN_UNLOCK(glock); 195 | return mcp; 196 | } 197 | 198 | mysql_cp_entry_t *mysql_connpool_get_connection(int cp_scope, mysql_connpool **mcp_ref, const char *hostname, const char *username, const char *password, const char *db, unsigned int port) { 199 | myConnPools *cp; 200 | if (cp_scope==MYSQL_CONNPOOL_GLOBAL) { 201 | cp=&gloconnpool; 202 | } else { 203 | cp=&__thr_myconnpool; 204 | } 205 | // NOTE: this function locks the mutex 206 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Getting a connection for %s %s %s %d\n", hostname, username, db, port); 207 | //guint l; 208 | mysql_cp_entry_t *mycpe=NULL; 209 | //struct timeval tv; 210 | //gettimeofday(&tv, NULL); 211 | unsigned long long curr_time=(unsigned long long) (gloconnpool.tv.tv_sec) * 1000000 + (gloconnpool.tv.tv_usec); 212 | if (cp->enabled==TRUE) { 213 | if (cp_scope==MYSQL_CONNPOOL_GLOBAL) SPIN_LOCK(glock); 214 | //pthread_mutex_lock(&cp->mutex); 215 | mysql_connpool *mcp=NULL; 216 | if ((cp_scope==MYSQL_CONNPOOL_LOCAL) && (*mcp_ref)) { 217 | mcp=*mcp_ref; 218 | } else { 219 | mcp=mysql_connpool_find_or_create(cp, hostname, username, password, db, port); 220 | } 221 | while (mcp->free_conns->len) { 222 | mysql_cp_entry_t *mc=g_ptr_array_index(mcp->free_conns,0); // get the first connection 223 | g_ptr_array_remove_index_fast(mcp->free_conns,0); // remove it 224 | if (mc->expire <= curr_time) { // if the connection is expired ... 225 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Closing expired connection for %s %s %s %d\n", hostname, username, db, port); 226 | close_expired_mysql_connection(mc); 227 | } else { // found a potential good connection 228 | mc->expire=curr_time + glovars.mysql_wait_timeout; 229 | // mysql_ping seems bugged, as it re-establishes a connection, disabling 230 | // if (mysql_ping(mc->conn)!=0) { // the conn is dead 231 | // fprintf(stderr, "%s\n", mysql_error(mc->conn)); 232 | // mysql_close(mc->conn); 233 | // free(mc); 234 | // } else { // the connection is really good 235 | // g_ptr_array_add(mcp->used_conns,mc); 236 | mycpe=mc; 237 | break; 238 | // } 239 | } 240 | } 241 | if (cp_scope==MYSQL_CONNPOOL_LOCAL) { *mcp_ref=mcp; } 242 | if (cp_scope==MYSQL_CONNPOOL_GLOBAL) SPIN_UNLOCK(glock); 243 | //pthread_mutex_unlock(&cp->mutex); // free the lock now! 244 | } /* (cp->enabled==TRUE) */ 245 | if (mycpe==NULL) { 246 | // if we reached here it means we couldn't find any connection 247 | 248 | // try to get a connection from global pool 249 | if (cp_scope==MYSQL_CONNPOOL_LOCAL) { 250 | mycpe=mysql_connpool_get_connection(MYSQL_CONNPOOL_GLOBAL, NULL, hostname, username, password, db, port); 251 | if (mycpe==NULL) { 252 | mycpe=create_new_mysql_connection(hostname, username, password, db, port); 253 | } 254 | } 255 | } 256 | if (mycpe) { 257 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Returning connection with fd %d\n", mycpe->conn->net.fd); 258 | } 259 | return mycpe; 260 | } 261 | 262 | void mysql_connpool_detach_connection(int cp_scope, mysql_connpool **mcp_ref, mysql_cp_entry_t *mc, int force_close) { 263 | myConnPools *cp; 264 | if (cp_scope==MYSQL_CONNPOOL_GLOBAL) { 265 | cp=&gloconnpool; 266 | } else { 267 | cp=&__thr_myconnpool; 268 | } 269 | if (cp->enabled==FALSE) { 270 | close_expired_mysql_connection(mc); 271 | return; 272 | } 273 | if (mc->reusable==0) { 274 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 3, "Detaching not reusable connection for %s %s %s %d\n", mc->conn->host, mc->conn->user, mc->conn->db, mc->conn->port); 275 | close_expired_mysql_connection(mc); 276 | return; 277 | } 278 | if (force_close==1) { 279 | // we assume the connection is not healthy, drop it immediately 280 | //MYSQL *mysql_con; 281 | //mysql_con=mc->conn; 282 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 3, "Detaching unhealthy(?) connection for %s %s %s %d\n", mc->conn->host, mc->conn->user, mc->conn->db, mc->conn->port); 283 | close_expired_mysql_connection(mc); 284 | return; 285 | } 286 | MYSQL *mysql_con; 287 | mysql_con=mc->conn; 288 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Detaching connection for %s %s %s %d, fd %d\n", mc->conn->host, mc->conn->user, mc->conn->db, mc->conn->port, mc->conn->net.fd); 289 | mysql_connpool *mcp=NULL; 290 | if (*mcp_ref==NULL) { 291 | mcp=mysql_connpool_find_or_create(cp, mysql_con->host, mysql_con->user, mysql_con->passwd, mysql_con->db, mysql_con->port); 292 | } else { 293 | mcp=*mcp_ref; 294 | } 295 | // we may not find the connection if an INITDB was issued, thus changing db 296 | // we temporary (?) remove the used_conns array 297 | // maybe a global implementation is better 298 | if (mcp) { 299 | //g_ptr_array_remove_fast(mcp->used_conns,mc); 300 | struct timeval tv; 301 | gettimeofday(&tv, NULL); 302 | unsigned long long curr_time=(unsigned long long) (tv.tv_sec) * 1000000 + (tv.tv_usec); 303 | if (mc->expire <= curr_time) { // if the connection is expired ... 304 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Closing expired connection for %s %s %s %d\n", mysql_con->host, mysql_con->user, mysql_con->db, mysql_con->port); 305 | close_expired_mysql_connection(mc); 306 | } else { 307 | g_ptr_array_add(mcp->free_conns,mc); 308 | } 309 | } 310 | // *mcp_ref=mcp; 311 | } 312 | 313 | 314 | void * mysql_connpool_purge_thread() { 315 | my_init(); 316 | mysql_server_init(0, NULL, NULL); 317 | // GPtrArray *conns=g_ptr_array_new(); 318 | while(glovars.shutdown==0) { 319 | usleep(1000000); 320 | if (gloconnpool.enabled==0) { 321 | continue; 322 | } 323 | // unsigned int i; 324 | //struct timeval tv; 325 | gettimeofday(&gloconnpool.tv, NULL); 326 | /* 327 | unsigned long long curr_time=(unsigned long long)(gloconnpool.tv.tv_sec) * 1000000 + (gloconnpool.tv.tv_usec); 328 | spin_lock(glock); 329 | //pthread_mutex_lock(&gloconnpool.mutex); 330 | for (i=0; ilen; i++) { 331 | mysql_connpool *mcp=g_ptr_array_index(gloconnpool.connpools,i); 332 | int l=mcp->free_conns->len; 333 | while (l>0) { 334 | l--; 335 | mysql_cp_entry_t *mc=g_ptr_array_index(mcp->free_conns,l); 336 | if (mc->expire <= curr_time) { 337 | g_ptr_array_add(conns,mc); 338 | g_ptr_array_remove_index_fast(mcp->free_conns,l); 339 | l--; 340 | } 341 | } 342 | } 343 | spin_unlock(glock); 344 | //pthread_mutex_unlock(&gloconnpool.mutex); 345 | for (i=0; ilen; i++) { 346 | mysql_cp_entry_t *mc=g_ptr_array_index(conns,0); 347 | g_ptr_array_remove_index_fast(conns,0); 348 | mysql_close(mc->conn); 349 | free(mc); 350 | } 351 | */ 352 | } 353 | proxy_error("Shutdown mysql_connpool_purge_thread\n"); 354 | return NULL; 355 | } 356 | -------------------------------------------------------------------------------- /src/mysql_data_stream.c: -------------------------------------------------------------------------------- 1 | #include "proxysql.h" 2 | 3 | 4 | static void mysql_data_stream_setfd(mysql_data_stream_t *myds, int fd) { 5 | myds->fd=fd; 6 | } 7 | 8 | void mysql_data_stream_delete(mysql_data_stream_t *my) { 9 | //proxy_mysql_thread_t *thrLD=pthread_getspecific(tsd_key); 10 | queue_t *q=NULL; 11 | q=&my->input.queue; 12 | queue_destroy(q); 13 | q=&my->output.queue; 14 | queue_destroy(q); 15 | 16 | pkt *p; 17 | /* 18 | if(my->input.mypkt) { 19 | if(my->input.mypkt->data) 20 | g_slice_free1(my->input.mypkt->length, my->input.mypkt->data); 21 | mypkt_free(my->input.mypkt,my->sess); 22 | } 23 | 24 | if(my->output.mypkt) { 25 | if(my->output.mypkt->data) 26 | g_slice_free1(my->output.mypkt->length, my->output.mypkt->data); 27 | // g_slice_free1(sizeof(pkt), my->output.mypkt); 28 | } 29 | */ 30 | if(my->input.mypkt) { 31 | l_free(my->input.mypkt->length, my->input.mypkt->data); 32 | } 33 | if(my->output.mypkt) { 34 | l_free(my->output.mypkt->length, my->output.mypkt->data); 35 | } 36 | while (my->input.pkts->len) { 37 | p=l_ptr_array_remove_index(my->input.pkts, 0); 38 | mypkt_free1(p); 39 | } 40 | while (my->output.pkts->len) { 41 | p=l_ptr_array_remove_index(my->output.pkts, 0); 42 | mypkt_free1(p); 43 | } 44 | 45 | 46 | l_ptr_array_free1(my->input.pkts); 47 | l_ptr_array_free1(my->output.pkts); 48 | g_slice_free1(sizeof(mysql_data_stream_t),my); 49 | //stack_free(my,&myds_pool); 50 | } 51 | 52 | static void shut_soft(mysql_data_stream_t *myds) { 53 | proxy_debug(PROXY_DEBUG_NET, 4, "Shutdown soft %d\n", myds->fd); 54 | myds->active=FALSE; 55 | myds->sess->net_failure=1; 56 | } 57 | 58 | static void shut_hard(mysql_data_stream_t *myds) { 59 | proxy_debug(PROXY_DEBUG_NET, 4, "Shutdown hard %d\n", myds->fd); 60 | if (myds->fd >= 0) { 61 | shutdown(myds->fd, SHUT_RDWR); 62 | close(myds->fd); 63 | myds->fd = -1; 64 | } 65 | } 66 | 67 | 68 | static int buffer2array(mysql_data_stream_t *myds) { 69 | int ret=0; 70 | //proxy_mysql_thread_t *thrLD=pthread_getspecific(tsd_key); 71 | queue_t *qin = &myds->input.queue; 72 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "BEGIN : bytes in buffer = %d\n", queue_data(qin)); 73 | if (queue_data(qin)==0) return ret; 74 | if (myds->input.mypkt==NULL) { 75 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Allocating a new packet\n"); 76 | myds->input.mypkt=mypkt_alloc(); 77 | //myds->input.mypkt=mypkt_alloc(myds->sess); 78 | myds->input.mypkt->length=0; 79 | } 80 | if ((myds->input.mypkt->length==0) && queue_data(qin)input.mypkt->length==0) && queue_data(qin)>=sizeof(mysql_hdr)) { 84 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Reading the header of a new packet\n"); 85 | memcpy(&myds->input.hdr,queue_r_ptr(qin),sizeof(mysql_hdr)); 86 | queue_r(qin,sizeof(mysql_hdr)); 87 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Allocating %d bytes for a new packet\n", myds->input.hdr.pkt_length+sizeof(mysql_hdr)); 88 | myds->input.mypkt->length=myds->input.hdr.pkt_length+sizeof(mysql_hdr); 89 | //myds->input.mypkt->data=l_alloc(thrLD->sfp, myds->input.mypkt->length); 90 | myds->input.mypkt->data=l_alloc(myds->input.mypkt->length); 91 | //myds->input.mypkt->data=g_slice_alloc(myds->input.mypkt->length); 92 | 93 | //void *__a=myds->input.mypkt->data; 94 | //void *__b=&myds->input.hdr; 95 | //MEM_COPY_FWD(__a, __b, sizeof(mysql_hdr)); 96 | MEM_COPY_FWD(myds->input.mypkt->data, &myds->input.hdr, sizeof(mysql_hdr)); // immediately copy the header into the packet 97 | //memcpy(myds->input.mypkt->data, &myds->input.hdr, sizeof(mysql_hdr)); // immediately copy the header into the packet 98 | myds->input.partial=sizeof(mysql_hdr); 99 | ret+=sizeof(mysql_hdr); 100 | } 101 | if ((myds->input.mypkt->length>0) && queue_data(qin)) { 102 | int b= ( queue_data(qin) > (myds->input.mypkt->length-myds->input.partial) ? myds->input.mypkt->length-myds->input.partial : queue_data(qin) ); 103 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Copied %d bytes into packet\n", b); 104 | memcpy(myds->input.mypkt->data + myds->input.partial, queue_r_ptr(qin),b); 105 | queue_r(qin,b); 106 | myds->input.partial+=b; 107 | ret+=b; 108 | } 109 | if ((myds->input.mypkt->length>0) && (myds->input.mypkt->length==myds->input.partial) ) { 110 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Packet (%d bytes) completely read, moving into input.pkts array\n", myds->input.mypkt->length); 111 | l_ptr_array_add(myds->input.pkts, myds->input.mypkt); 112 | myds->pkts_recv+=1; 113 | myds->input.mypkt=NULL; 114 | } 115 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "END : bytes in buffer = %d\n", queue_data(qin)); 116 | return ret; 117 | } 118 | 119 | static int array2buffer(mysql_data_stream_t *myds) { 120 | int ret=0; 121 | //proxy_mysql_thread_t *thrLD=pthread_getspecific(tsd_key); 122 | queue_t *qout = &myds->output.queue; 123 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Entering array2buffer with partial_send = %d and queue_available = %d\n", myds->output.partial, queue_available(qout)); 124 | if (queue_available(qout)==0) return ret; // no space to write 125 | if (myds->output.partial==0) { // read a new packet 126 | if (myds->output.pkts->len) { 127 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "%s\n", "Removing a packet from array"); 128 | if (myds->output.mypkt) { 129 | //mypkt_free0(myds->output.mypkt); 130 | mypkt_free1(myds->output.mypkt); 131 | myds->output.mypkt=NULL; 132 | } 133 | myds->output.mypkt=l_ptr_array_remove_index(myds->output.pkts, 0); 134 | } else { 135 | return ret; 136 | } 137 | } 138 | int b= ( queue_available(qout) > (myds->output.mypkt->length - myds->output.partial) ? (myds->output.mypkt->length - myds->output.partial) : queue_available(qout) ); 139 | memcpy(queue_w_ptr(qout), myds->output.mypkt->data + myds->output.partial, b); 140 | queue_w(qout,b); 141 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Copied %d bytes into send buffer\n", b); 142 | myds->output.partial+=b; 143 | ret=b; 144 | if (myds->output.partial==myds->output.mypkt->length) { 145 | //g_slice_free1(myds->output.mypkt->length, myds->output.mypkt->data); 146 | //l_free(thrLD->sfp,myds->output.mypkt->length, myds->output.mypkt->data); 147 | if (myds->output.mypkt) { 148 | //l_free(myds->output.mypkt->length, myds->output.mypkt->data); 149 | mypkt_free1(myds->output.mypkt); 150 | myds->output.mypkt=NULL; 151 | } 152 | proxy_debug(PROXY_DEBUG_PKT_ARRAY, 5, "Packet completely written into send buffer\n"); 153 | myds->output.partial=0; 154 | myds->pkts_sent+=1; 155 | } 156 | return ret; 157 | } 158 | 159 | static int read_from_net(mysql_data_stream_t *myds) { 160 | int r; 161 | queue_t *q=&myds->input.queue; 162 | int s=queue_available(q); 163 | r = recv(myds->fd, queue_w_ptr(q), s, 0); 164 | proxy_debug(PROXY_DEBUG_NET, 5, "read %d bytes from fd %d into a buffer of %d bytes free\n", r, myds->fd, s); 165 | if (r < 1) { 166 | // if (r==-1) { 167 | myds->shut_soft(myds); 168 | // } //else { printf("%d\n",errno); } 169 | // if ((r==0) && (!errno)) { mysql_data_stream_shut_soft(myds); } 170 | } 171 | else { 172 | queue_w(q,r); 173 | myds->bytes_info.bytes_recv+=r; 174 | if (myds->mybe) { 175 | __sync_fetch_and_add(&myds->mybe->mshge->server_bytes.bytes_recv,r); 176 | } 177 | } 178 | return r; 179 | } 180 | 181 | static int write_to_net(mysql_data_stream_t *myds) { 182 | int r=0; 183 | queue_t *q=&myds->output.queue; 184 | // r = write(myds->fd, queue_r_ptr(&myds->output.queue), queue_data(&myds->output.queue)); 185 | int s = queue_data(q); 186 | if (s==0) return 0; 187 | r = send(myds->fd, queue_r_ptr(q), s, 0); 188 | proxy_debug(PROXY_DEBUG_NET, 5, "wrote %d bytes to fd %d from a buffer with %d bytes of data\n", r, myds->fd, s); 189 | if (r < 0) { 190 | myds->shut_soft(myds); 191 | } 192 | else { 193 | queue_r(q,r); 194 | myds->bytes_info.bytes_sent+=r; 195 | if (myds->mybe) { 196 | __sync_fetch_and_add(&myds->mybe->mshge->server_bytes.bytes_sent,r); 197 | } 198 | } 199 | return r; 200 | } 201 | 202 | 203 | 204 | 205 | mysql_data_stream_t * mysql_data_stream_new(mysql_session_t *sess, mysql_backend_t *mybe) { 206 | mysql_data_stream_t *my=g_slice_new(mysql_data_stream_t); 207 | // mysql_data_stream_t *my=stack_alloc(&myds_pool); 208 | my->mybe=mybe; 209 | if (mybe) { 210 | __sync_fetch_and_add(&mybe->mshge->connections_created,1); 211 | __sync_fetch_and_add(&mybe->mshge->connections_active,1); 212 | } 213 | my->bytes_info.bytes_recv=0; 214 | my->bytes_info.bytes_sent=0; 215 | my->pkts_recv=0; 216 | my->pkts_sent=0; 217 | queue_t *q=NULL; 218 | //pthread_mutex_lock(&conn_queue_pool.mutex); 219 | q=&my->input.queue; 220 | queue_init(q,glovars.net_buffer_size); 221 | q=&my->output.queue; 222 | queue_init(q,glovars.net_buffer_size); 223 | //pthread_mutex_unlock(&conn_queue_pool.mutex); 224 | my->input.pkts=l_ptr_array_new(); 225 | my->output.pkts=l_ptr_array_new(); 226 | my->input.mypkt=NULL; 227 | my->output.mypkt=NULL; 228 | my->input.partial=0; 229 | my->output.partial=0; 230 | // my->fd=fd; 231 | my->active_transaction=0; 232 | my->active=TRUE; 233 | my->sess=sess; 234 | my->shut_soft = shut_soft; 235 | my->shut_hard = shut_hard; 236 | my->array2buffer = array2buffer; 237 | my->buffer2array = buffer2array; 238 | my->read_from_net = read_from_net; 239 | my->write_to_net = write_to_net; 240 | my->setfd=mysql_data_stream_setfd; 241 | return my; 242 | } 243 | 244 | 245 | -------------------------------------------------------------------------------- /src/mysql_handler.c: -------------------------------------------------------------------------------- 1 | #include "proxysql.h" 2 | 3 | 4 | 5 | 6 | void reset_query_rule(query_rule_t *qr) { 7 | if (qr->regex) { 8 | g_regex_unref(qr->regex); 9 | } 10 | if (qr->username) { 11 | g_free(qr->username); 12 | } 13 | if (qr->schemaname) { 14 | g_free(qr->schemaname); 15 | } 16 | if (qr->match_pattern) { 17 | g_free(qr->match_pattern); 18 | } 19 | if (qr->replace_pattern) { 20 | g_free(qr->replace_pattern); 21 | } 22 | if (qr->invalidate_cache_pattern) { 23 | g_free(qr->invalidate_cache_pattern); 24 | } 25 | g_slice_free1(sizeof(query_rule_t), qr); 26 | } 27 | 28 | void reset_query_rules() { 29 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "Resetting query rules\n"); 30 | if (gloQR.query_rules == NULL) { 31 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "Initializing query rules\n"); 32 | gloQR.query_rules=g_ptr_array_new(); 33 | return; 34 | } 35 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "%d query rules to reset\n", gloQR.query_rules->len); 36 | while ( gloQR.query_rules->len ) { 37 | query_rule_t *qr = g_ptr_array_remove_index_fast(gloQR.query_rules,0); 38 | reset_query_rule(qr); 39 | } 40 | } 41 | 42 | void init_gloQR() { 43 | pthread_rwlock_init(&gloQR.rwlock, NULL); 44 | gloQR.query_rules=NULL; 45 | reset_query_rules(); 46 | } 47 | 48 | 49 | void init_query_metadata(mysql_session_t *sess, pkt *p) { 50 | sess->query_info.p=p; 51 | if (sess->query_info.query_checksum) { 52 | g_checksum_free(sess->query_info.query_checksum); 53 | sess->query_info.query_checksum=NULL; 54 | } 55 | sess->query_info.flagOUT=0; 56 | sess->query_info.rewritten=0; 57 | sess->query_info.cache_ttl=0; 58 | sess->query_info.destination_hostgroup=-1; // the destination hostgroup is set to unknown 59 | sess->query_info.audit_log=0; 60 | sess->query_info.performance_log=0; 61 | sess->query_info.mysql_query_cache_hit=0; 62 | if (sess->query_info.query_stats) { 63 | // If we hit here, the query statistics were disabled while being processed or this session is being closed. 64 | // We need to clean up 65 | cleanup_query_stats(sess->query_info.query_stats); 66 | } 67 | if (p) { 68 | sess->query_info.query=p->data+sizeof(mysql_hdr)+1; 69 | sess->query_info.query_len=p->length-sizeof(mysql_hdr)-1; 70 | // Added by chan 71 | if (glovars.mysql_query_statistics) { 72 | sess->query_info.query_stats=g_malloc0(sizeof(qr_hash_entry)); 73 | sess->query_info.query_stats->query_time=monotonic_time(); 74 | //process_query_stats(sess); 75 | sess->query_info.query_stats->query_digest_text=mysql_query_digest(sess); 76 | sess->query_info.query_stats->query_digest_md5=str2md5(sess->query_info.query_stats->query_digest_text); 77 | sess->query_info.query_stats->username=g_strdup(sess->mysql_username); 78 | sess->query_info.query_stats->schemaname=g_strdup(sess->mysql_schema_cur); 79 | } 80 | } else { 81 | sess->query_info.query=NULL; 82 | sess->query_info.query_len=0; 83 | } 84 | } 85 | 86 | void process_query_rules(mysql_session_t *sess) { 87 | int i; 88 | int flagIN=0; 89 | gboolean rc; 90 | //GMatchInfo *match_info; 91 | pthread_rwlock_rdlock(&gloQR.rwlock); 92 | for (i=0;ilen;i++) { 93 | query_rule_t *qr=g_ptr_array_index(gloQR.query_rules, i); 94 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 6, "Processing rule %d\n", qr->rule_id); 95 | if (qr->flagIN != flagIN) { 96 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "query rule %d has no matching flagIN\n", qr->rule_id); 97 | continue; 98 | } 99 | if (qr->username) { 100 | if (strcmp(qr->username,sess->mysql_username)!=0) { 101 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "query rule %d has no matching username\n", qr->rule_id); 102 | continue; 103 | } 104 | } 105 | if (qr->schemaname) { 106 | if (strcmp(qr->schemaname,sess->mysql_schema_cur)!=0) { 107 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "query rule %d has no matching schema\n", qr->rule_id); 108 | continue; 109 | } 110 | } 111 | //rc = g_regex_match_full(qr->regex, sess->query_info.query , sess->query_info.query_len, 0, 0, &match_info, NULL); 112 | rc = g_regex_match_full(qr->regex, sess->query_info.query , sess->query_info.query_len, 0, 0, NULL, NULL); 113 | if ( 114 | (rc==TRUE && qr->negate_match_pattern==1) || ( rc==FALSE && qr->negate_match_pattern==0 ) 115 | ) { 116 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "query rule %d has no matching pattern\n", qr->rule_id); 117 | //g_match_info_free(match_info); 118 | continue; 119 | } 120 | // if we arrived here, we have a match 121 | __sync_fetch_and_add(&qr->hits,1); 122 | if (qr->replace_pattern) { 123 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "query rule %d on match_pattern \"%s\" has a replace_pattern \"%s\" to apply\n", qr->rule_id, qr->match_pattern, qr->replace_pattern); 124 | GError *error=NULL; 125 | char *new_query; 126 | new_query=g_regex_replace(qr->regex, sess->query_info.query , sess->query_info.query_len, 0, qr->replace_pattern, 0, &error); 127 | if (error) { 128 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 3, "g_regex_replace() on query rule %d generated error %d\n", qr->rule_id, error->message); 129 | g_error_free(error); 130 | if (new_query) { 131 | g_free(new_query); 132 | } 133 | //g_match_info_free(match_info); 134 | continue; 135 | } 136 | sess->query_info.rewritten=1; 137 | if (sess->query_info.query_checksum) { 138 | g_checksum_free(sess->query_info.query_checksum); // remove checksum, as it may needs to be computed again 139 | sess->query_info.query_checksum=NULL; 140 | } 141 | mysql_new_payload_select(sess->query_info.p, new_query, -1); 142 | pkt *p=sess->query_info.p; 143 | sess->query_info.query=p->data+sizeof(mysql_hdr)+1; 144 | sess->query_info.query_len=p->length-sizeof(mysql_hdr)-1; 145 | g_free(new_query); 146 | } 147 | if (qr->flagOUT) { 148 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "query rule %d has changed flagOUT\n", qr->rule_id); 149 | flagIN=qr->flagOUT; 150 | sess->query_info.flagOUT=flagIN; 151 | } 152 | if (qr->cache_ttl) { 153 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "query rule %d has non-zero cache_ttl: %d. Query will%s hit the cache\n", qr->rule_id, qr->cache_ttl, (qr->cache_ttl < 0 ? " NOT" : "" )); 154 | sess->query_info.cache_ttl=qr->cache_ttl; 155 | } 156 | //g_match_info_free(match_info); 157 | //sess->query_info.destination_hostgroup=-1; // default 158 | if (qr->destination_hostgroup>=0) { 159 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "query rule %d has changed destination_hostgroup %d\n", qr->rule_id, qr->destination_hostgroup); 160 | sess->query_info.destination_hostgroup=qr->destination_hostgroup; 161 | } 162 | if (qr->audit_log==1) { 163 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "query rule %d has set audit_log\n", qr->rule_id); 164 | sess->query_info.audit_log=qr->audit_log; 165 | } 166 | if (qr->performance_log==1) { 167 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 5, "query rule %d has set performance_log\n", qr->rule_id); 168 | sess->query_info.performance_log=qr->performance_log; 169 | } 170 | if (sess->query_info.cache_ttl) { 171 | goto exit_process_query_rules; 172 | } 173 | } 174 | exit_process_query_rules: 175 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 6, "End processing query rules\n"); 176 | pthread_rwlock_unlock(&gloQR.rwlock); 177 | // if the query reached this point with cache_ttl==0 , we set it to the default 178 | if (sess->query_info.cache_ttl==0) { 179 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 6, "Query has no caching TTL, setting the default\n"); 180 | sess->query_info.cache_ttl=glovars.mysql_query_cache_default_timeout; 181 | } 182 | // if the query reached this point with cache_ttl==-1 , we set it to 0 183 | if (sess->query_info.cache_ttl==-1) { 184 | proxy_debug(PROXY_DEBUG_QUERY_CACHE, 6, "Query won't be cached\n"); 185 | sess->query_info.cache_ttl=0; 186 | } 187 | // if the query is flagged to be cached but mysql_query_cache_enabled=0 , the query needs to be flagged to NOT be cached 188 | if (glovars.mysql_query_cache_enabled==FALSE) { 189 | sess->query_info.cache_ttl=0; 190 | } 191 | // if destination_hostgroup didn't change from default (-1) we apply sess->default_hostgroup, setting it first if necessary 192 | if (sess->query_info.destination_hostgroup==-1) { 193 | if ( (sess->default_hostgroup==-1) || (sess->default_hostgroup_version != __sync_fetch_and_add(&gloDefHG.version,0)) ) { 194 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 5, "Session %p has default_hostgroup %d version %d, system version %d\n", sess, sess->default_hostgroup, sess->default_hostgroup_version, gloDefHG.version); 195 | sess->query_info.destination_hostgroup=sess->default_hostgroup_func(sess); 196 | } else { 197 | sess->query_info.destination_hostgroup=sess->default_hostgroup; 198 | } 199 | } 200 | } 201 | 202 | 203 | mysql_server * find_server_ptr(const char *address, const uint16_t port) { 204 | mysql_server *ms=NULL; 205 | int i; 206 | // if (lock) pthread_rwlock_wrlock(&glomysrvs.rwlock); 207 | for (i=0;ilen;i++) { 208 | mysql_server *mst=g_ptr_array_index(glomysrvs.servers,i); 209 | if (mst->port==port && (strcmp(mst->address,address)==0)) { 210 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 6, "MySQL server %s:%d found in servers list\n", address, port); 211 | i=glomysrvs.servers->len; 212 | ms=mst; 213 | } 214 | } 215 | // if (lock) pthread_rwlock_unlock(&glomysrvs.rwlock); 216 | return ms; 217 | } 218 | 219 | 220 | mysql_server * mysql_server_entry_create(const char *address, const uint16_t port, int read_only, enum mysql_server_status status) { 221 | mysql_server *ms=g_slice_alloc0(sizeof(mysql_server)); 222 | ms->address=g_strdup(address); 223 | ms->port=port; 224 | ms->read_only=read_only; 225 | ms->status=status; 226 | return ms; 227 | } 228 | 229 | inline void mysql_server_entry_add(mysql_server *ms) { 230 | g_ptr_array_add(glomysrvs.servers,ms); 231 | } 232 | 233 | void mysql_server_entry_add_hostgroup(mysql_server *MSptr, int hostgroup_id) { 234 | if (hostgroup_id >= glovars.mysql_hostgroups) { 235 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 4, "Server %s:%d not inserted in hostgroup %d as this is an invalid hostgroup\n", MSptr->address, MSptr->port, hostgroup_id); 236 | return; 237 | } 238 | GPtrArray *hg=g_ptr_array_index(glomysrvs.mysql_hostgroups, hostgroup_id); 239 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 5, "Adding server %s:%d in hostgroup %d\n", MSptr->address, MSptr->port, hostgroup_id); 240 | MSHGE *ms; 241 | ms=g_malloc0(sizeof(MSHGE)); 242 | ms->MSptr=MSptr; 243 | g_ptr_array_add(hg,ms); 244 | } 245 | 246 | MSHGE * mysql_server_random_entry_from_hostgroup__lock(int hostgroup_id) { 247 | MSHGE *ms; 248 | pthread_rwlock_wrlock(&glomysrvs.rwlock); 249 | ms=mysql_server_random_entry_from_hostgroup__nolock(hostgroup_id); 250 | pthread_rwlock_unlock(&glomysrvs.rwlock); 251 | return ms; 252 | } 253 | 254 | MSHGE * mysql_server_random_entry_from_hostgroup__nolock(int hostgroup_id) { 255 | assert(hostgroup_id < glovars.mysql_hostgroups); 256 | GPtrArray *hg=g_ptr_array_index(glomysrvs.mysql_hostgroups, hostgroup_id); 257 | if (hg->len==0) { 258 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 3, "Server not found from hostgroup %d\n", hostgroup_id); 259 | return NULL; 260 | } 261 | int i=rand()%hg->len; 262 | MSHGE *ms; 263 | ms=g_ptr_array_index(hg,i); 264 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 5, "Returning server %s:%d from hostgroup %d\n", ms->MSptr->address, ms->MSptr->port, hostgroup_id); 265 | return ms; 266 | } 267 | 268 | int mysql_session_create_backend_for_hostgroup(mysql_session_t *sess, int hostgroup_id) { 269 | assert(hostgroup_id < glovars.mysql_hostgroups); 270 | mysql_backend_t *mybe=NULL; 271 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 5, "Retrieving backend for session %p hostgroup %d\n", sess, hostgroup_id); 272 | mybe=l_ptr_array_index(sess->mybes,hostgroup_id); 273 | int retries=10; 274 | //mysql_backend_t *tmp_mybe=NULL; 275 | //tmp_mybe=glomybepools.get(sess->mysql_username, sess->mysql_password, sess->mysql_schema_cur, hostgroup_id); 276 | //if (tmp_mybe) { 277 | // mysql_backend_delete(mybe); 278 | // *mybe=*tmp_mybe; 279 | // proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 4, "Recycling backend for sess %p , hostgroup %d , fd %d\n", sess , hostgroup_id , mybe->fd); 280 | // mybe->server_myds->sess=sess; 281 | // return 1; 282 | //} 283 | mysql_session_create_backend_for_hostgroup__label1: 284 | if (mybe->mshge == NULL || mybe->mshge->MSptr==NULL) { 285 | //--mysql_server *ms=NULL; 286 | //ms=mysql_server_random_entry_from_hostgroup__lock(hostgroup_id); 287 | if (mybe->mshge==NULL) 288 | { // FIXME : temporary hack, and memory leak 289 | //--mybe->mshge=g_malloc0(sizeof(MSHGE)); 290 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 5, "Backend for session %p hostgroup %d has no mshge\n", sess, hostgroup_id); 291 | mybe->mshge=mysql_server_random_entry_from_hostgroup__lock(hostgroup_id); 292 | } 293 | //--mybe->mshge->MSptr=ms; 294 | if (mybe->mshge==NULL) { 295 | // this is a severe condition, needs to be handled 296 | return 0; 297 | } 298 | } 299 | if (mybe->server_mycpe==NULL) { 300 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 5, "Backend for session %p hostgroup %d has no server_mycpe\n", sess, hostgroup_id); 301 | mybe->server_mycpe=mysql_connpool_get_connection(MYSQL_CONNPOOL_LOCAL, &mybe->last_mysql_connpool, mybe->mshge->MSptr->address, sess->mysql_username, sess->mysql_password, sess->mysql_schema_cur, mybe->mshge->MSptr->port); 302 | if (mybe->server_mycpe==NULL) { 303 | if (--retries) { 304 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 5, "Unable to connect to %s:%d from hostgroup %d, %s retries left\n", mybe->mshge->MSptr->address, mybe->mshge->MSptr->port, hostgroup_id, retries); 305 | mybe->mshge=NULL; 306 | goto mysql_session_create_backend_for_hostgroup__label1; 307 | } 308 | // handle error!! 309 | proxy_debug(PROXY_DEBUG_MYSQL_SERVER, 5, "We failed to created a server_mycpe for backend for session %p hostgroup %d, we will generate an error\n", sess, hostgroup_id); 310 | authenticate_mysql_client_send_ERR(sess, 1045, "#28000Access denied for user"); 311 | // this is a severe condition, needs to be handled 312 | return -1; 313 | } 314 | } 315 | mybe->fd=mybe->server_mycpe->conn->net.fd; 316 | //mybe->server_myds=mysql_data_stream_init(mybe->fd, sess); 317 | if (mybe->server_myds==NULL) { 318 | mybe->server_myds=mysql_data_stream_new(sess,mybe); 319 | } else { 320 | mybe->server_myds->mybe=mybe; 321 | } 322 | mysql_data_stream_t *myds=mybe->server_myds; 323 | myds->setfd(myds, mybe->fd); 324 | // mybe->server_myds->fd=mybe->fd; 325 | proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 4, "Created new connection for sess %p , hostgroup %d , fd %d\n", sess , hostgroup_id , mybe->fd); 326 | return 1; 327 | } 328 | -------------------------------------------------------------------------------- /src/network.c: -------------------------------------------------------------------------------- 1 | #include "proxysql.h" 2 | 3 | int listen_on_port(char *ip, uint16_t port) { 4 | int rc, arg_on=1; 5 | struct sockaddr_in addr; 6 | int sd; 7 | if ( (sd = socket(PF_INET, SOCK_STREAM, 0)) < 0 ) 8 | PANIC("Socket - TCP"); 9 | rc = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&arg_on, sizeof(arg_on)); 10 | if (rc < 0) { 11 | PANIC("setsockopt() failed"); 12 | } 13 | addr.sin_family = AF_INET; 14 | addr.sin_port = htons(port); 15 | addr.sin_addr.s_addr = inet_addr(ip); 16 | if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ) { 17 | proxy_error("Error on Bind , address %s:%d\n", ip, port); 18 | exit(EXIT_FAILURE); 19 | } 20 | if ( listen(sd, glovars.backlog) != 0 ) { 21 | proxy_error("Error on Listen , address %s:%d\n", ip, port); 22 | exit(EXIT_FAILURE); 23 | } 24 | return sd; 25 | } 26 | 27 | int listen_on_unix(char *path) { 28 | struct sockaddr_un serveraddr; 29 | int sd; 30 | int r; 31 | r=unlink(path); 32 | if ( (r==-1) && (errno!=ENOENT) ) { 33 | PANIC("Error unlink Unix Socket"); 34 | } 35 | if ( ( sd = socket(AF_UNIX, SOCK_STREAM, 0)) <0 ) 36 | PANIC("Socket - Unix"); 37 | memset(&serveraddr, 0, sizeof(serveraddr)); 38 | serveraddr.sun_family = AF_UNIX; 39 | strncpy(serveraddr.sun_path, path, sizeof(serveraddr.sun_path) - 1); 40 | if ( bind(sd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr_un)) != 0 ) 41 | PANIC("Bind - Unix"); 42 | if ( listen(sd, glovars.backlog) != 0 ) 43 | PANIC("Listen - Unix"); 44 | r=chmod(path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH); 45 | return sd; 46 | } 47 | 48 | 49 | int connect_socket(char *address, int connect_port) 50 | { 51 | struct sockaddr_in a; 52 | int s; 53 | 54 | if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 55 | perror("socket"); 56 | close(s); 57 | return -1; 58 | } 59 | 60 | memset(&a, 0, sizeof(a)); 61 | a.sin_port = htons(connect_port); 62 | a.sin_family = AF_INET; 63 | 64 | if (!inet_aton(address, (struct in_addr *) &a.sin_addr.s_addr)) { 65 | perror("bad IP address format"); 66 | close(s); 67 | return -1; 68 | } 69 | 70 | if (connect(s, (struct sockaddr *) &a, sizeof(a)) == -1) { 71 | perror("connect()"); 72 | shutdown(s, SHUT_RDWR); 73 | close(s); 74 | return -1; 75 | } 76 | return s; 77 | } 78 | 79 | gboolean write_one_pkt_to_net(mysql_data_stream_t *myds, pkt *p) {// this should be used ONLY when sure that only 1 packet is expected, for example during authentication 80 | l_ptr_array_add(myds->output.pkts, p); 81 | myds->array2buffer(myds); 82 | queue_t *q=&myds->output.queue; 83 | while (queue_data(q) && (myds->active==TRUE)) { 84 | myds->write_to_net(myds); 85 | } 86 | if (myds->active==FALSE) { 87 | return FALSE; 88 | } 89 | return TRUE; 90 | } 91 | -------------------------------------------------------------------------------- /src/perf.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renecannao/proxysql-old/f9a91fe77bf9bf82335bc3a4a95bd973f3c0d507/src/perf.data -------------------------------------------------------------------------------- /src/proxysqlHTTPd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | { 3 | package MyWebServer; 4 | 5 | use Config::IniFiles; 6 | use Proc::Daemon; 7 | use Net::Server::PreFork; 8 | use HTTP::Server::Simple::CGI; 9 | use HTTP::Server::Simple::CGI::PreFork; 10 | #use base qw(HTTP::Server::Simple::CGI); 11 | use base qw(HTTP::Server::Simple::CGI::PreFork); 12 | #use base qw(HTTP::Server); 13 | 14 | 15 | 16 | my %dispatch = ( 17 | '/test' => \&resp_test, 18 | # ... 19 | ); 20 | 21 | sub handle_request { 22 | my $self = shift; 23 | my $cgi = shift; 24 | 25 | my $path = $cgi->path_info(); 26 | my $handler = $dispatch{$path}; 27 | 28 | if (ref($handler) eq "CODE") { 29 | print "HTTP/1.0 200 OK\r\n"; 30 | $handler->($cgi); 31 | 32 | } else { 33 | print "HTTP/1.0 404 Not found\r\n"; 34 | print $cgi->header, 35 | $cgi->start_html('Not found'), 36 | $cgi->h1('Not found'), 37 | $cgi->end_html; 38 | } 39 | } 40 | 41 | sub resp_test { 42 | my $cgi = shift; # CGI.pm object 43 | return if !ref $cgi; 44 | 45 | my $val1 = $cgi->param('param1'); 46 | 47 | print $cgi->header, 48 | $cgi->start_html("Test Page"), 49 | $cgi->h1("Test page, param1 = $val1!"), 50 | $cgi->end_html; 51 | } 52 | 53 | } 54 | 55 | 56 | 57 | my $num_args = $#ARGV + 1; 58 | #if ($num_args != 1) { 59 | # print "\nUsage: ./proxysqlHTTPd configfile\n"; 60 | # exit 0; 61 | #} 62 | 63 | 64 | ## open the config file passed as argument 65 | my $inicfg; 66 | $inicfg=Config::IniFiles->new( -file => $ARGV[0] ) || die "Could not open config file"; 67 | #$inicfg=Config::IniFiles->new( -file => "proxysql.cnf" ) || die "Could not open config file"; 68 | 69 | 70 | my $datadir; 71 | $datadir=$inicfg->val( "http", "datadir"); 72 | if (length($datadir)==0) { $datadir=$inicfg->val( "global", "datadir"); } 73 | 74 | my $pidfile; 75 | $pidfile=$inicfg->val( "http", "pid_file"); 76 | if (length($pidfile)==0) { $pidfile="proxysqlHTTPd.pid"; } 77 | 78 | my $errorlog; 79 | $errorlog=$inicfg->val( "http", "error_log"); 80 | if (length($errorlog)==0) { $errorlog="proxysqlHTTPd.log"; } 81 | 82 | my $port; 83 | $port=$inicfg->val( "http", "port"); 84 | if (length($port)==0) { $port="8080"; } 85 | 86 | 87 | 88 | my $daemon = Proc::Daemon->new( 89 | work_dir => $datadir, 90 | child_STDOUT => ">>$errorlog", 91 | child_STDERR => ">>$errorlog", 92 | pid_file => $pidfile, 93 | ); 94 | 95 | my $pidd=$daemon->Init(); 96 | if ($pidd>0) { 97 | exit 0; 98 | } 99 | #my $ppid=$daemon->Init(); 100 | print "datadir = $datadir , pidfile = $pidfile , errorlog = $errorlog , port = $port\n"; 101 | #my $pid = MyWebServer->new($port)->background(); 102 | #my $pid = MyWebServer->new($port)->run(); 103 | 104 | 105 | 106 | 107 | #MyWebServer->new($port)->run(); 108 | #print "Use 'kill $pid' to stop server.\n"; 109 | #exit 0; 110 | 111 | 112 | my $server = MyWebServer->new($port); 113 | #$server::Prefork 114 | #$server->net_server(user => "nobody" , group => "nobody" , min_spare_servers => 8, max_servers => 10); 115 | #$server->net_server(PreFork); 116 | #$server->prefork(4); 117 | #$server::Simple::CGI->run(prefork=>1); 118 | #$server->run(prefork=>1); 119 | $server->run(ipv =>4 , prefork=>1 , user => "nobody" , group => "nogroup", min_spare_servers => 8, min_servers => 8, max_servers => 20); 120 | 121 | -------------------------------------------------------------------------------- /src/proxysql_interactive_config.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use warnings; 4 | 5 | my %proxycfg = () ; 6 | 7 | my $bool=""; 8 | my $dbglvl=0; 9 | my $configfile=""; 10 | 11 | my %defaults = ( 12 | datadir => "/var/run/proxysql", 13 | error_log => "proxysql.log", 14 | debug_log => "debug.log", 15 | pid_file => "proxysql.pid", 16 | restart_on_error => 1, 17 | restart_delay => 5, 18 | core_dump_file_size => 0, 19 | debug => 0, 20 | stack_size => 524288, 21 | proxy_admin_pathdb => "proxysql.db", 22 | proxy_mysql_port => 6033, 23 | proxy_admin_port => 6032, 24 | proxy_admin_user => "admin", 25 | proxy_admin_password => "admin", 26 | proxy_admin_refresh_status_interval => 600, 27 | sync_to_disk_on_flush_command => 1, 28 | sync_to_disk_on_shutdown => 1, 29 | mysql_socket => "/tmp/proxysql.sock", 30 | mysql_query_cache_partitions => 16, 31 | mysql_query_cache_precheck => 1, 32 | mysql_query_cache_default_timeout => 1, 33 | mysql_query_cache_size => 67108864 , 34 | mysql_threads => 1, 35 | mysql_hostgroups => 8, 36 | mysql_poll_timeout => 10000, 37 | mysql_maintenance_timeout => 1000, 38 | mysql_poll_timeout_maintenance => 100, 39 | mysql_max_query_size => 1048576, 40 | mysql_max_resultset_size => 1048576, 41 | net_buffer_size => 8192, 42 | backlog => 2000, 43 | mysql_connection_pool_enabled => 1, 44 | mysql_wait_timeout => 28800 , 45 | mysql_usage_user => "proxy", 46 | mysql_usage_password => "proxy", 47 | fundadb_hash_purge_threshold_pct_min => 50, 48 | fundadb_hash_purge_threshold_pct_max => 90, 49 | config_file => "proxysql.cnf" 50 | ); 51 | my $cpus=`cat /proc/cpuinfo | egrep '^processor' | wc -l`; 52 | $defaults{'mysql_threads'}=$cpus*2; 53 | 54 | 55 | sub intro { 56 | print " 57 | 58 | Interactive script to configure ProxySQL, 59 | High Performance and High Availability proxy for MySQL 60 | 61 | 62 | "; 63 | } 64 | 65 | 66 | sub config_to_string { 67 | $configfile=" 68 | # 69 | # ProxySQL config file 70 | # 71 | # Generated using proxysql_interactive_config.pl 72 | # 73 | [global] 74 | datadir=$proxycfg{'datadir'} 75 | core_dump_file_size=$proxycfg{'core_dump_file_size'} 76 | debug=$proxycfg{'debug'} 77 | stack_size=$proxycfg{'stack_size'} 78 | net_buffer_size=$proxycfg{'net_buffer_size'} 79 | backlog=$proxycfg{'backlog'} 80 | error_log=$proxycfg{'error_log'} 81 | debug_log=$proxycfg{'debug_log'} 82 | pid_file=$proxycfg{'pid_file'} 83 | restart_on_error=$proxycfg{'restart_on_error'} 84 | restart_delay=$proxycfg{'restart_delay'} 85 | 86 | 87 | [admin] 88 | proxy_admin_pathdb=$proxycfg{'proxy_admin_pathdb'} 89 | proxy_admin_port=$proxycfg{'proxy_admin_port'} 90 | proxy_admin_user=$proxycfg{'proxy_admin_user'} 91 | proxy_admin_password=$proxycfg{'proxy_admin_password'} 92 | proxy_admin_refresh_status_interval=$proxycfg{'proxy_admin_refresh_status_interval'} 93 | sync_to_disk_on_flush_command=$defaults{'sync_to_disk_on_flush_command'} 94 | sync_to_disk_on_shutdown=$defaults{'sync_to_disk_on_shutdown'} 95 | 96 | 97 | [mysql] 98 | mysql_threads=$proxycfg{'mysql_threads'} 99 | proxy_mysql_port=$proxycfg{'proxy_mysql_port'} 100 | mysql_socket=$proxycfg{'mysql_socket'} 101 | mysql_query_cache_partitions=$proxycfg{'mysql_query_cache_partitions'} 102 | mysql_query_cache_default_timeout=$proxycfg{'mysql_query_cache_default_timeout'} 103 | mysql_query_cache_size=$proxycfg{'mysql_query_cache_size'} 104 | mysql_query_cache_precheck=$proxycfg{'mysql_query_cache_precheck'} 105 | mysql_hostgroups=$proxycfg{'mysql_hostgroups'} 106 | mysql_poll_timeout=$proxycfg{'mysql_poll_timeout'} 107 | mysql_maintenance_timeout=$proxycfg{'mysql_maintenance_timeout'} 108 | mysql_poll_timeout_maintenance=$proxycfg{'mysql_poll_timeout_maintenance'} 109 | mysql_max_query_size=$proxycfg{'mysql_max_query_size'} 110 | mysql_max_resultset_size=$proxycfg{'mysql_max_resultset_size'} 111 | mysql_connection_pool_enabled=$proxycfg{'mysql_connection_pool_enabled'} 112 | mysql_wait_timeout=$proxycfg{'mysql_wait_timeout'} 113 | mysql_servers=$proxycfg{'mysql_servers'} 114 | mysql_usage_user=$proxycfg{'mysql_usage_user'} 115 | mysql_usage_password=$proxycfg{'mysql_usage_password'} 116 | $proxycfg{'mysql_users'} 117 | 118 | [fundadb] 119 | fundadb_hash_purge_threshold_pct_min=$defaults{'fundadb_hash_purge_threshold_pct_min'} 120 | fundadb_hash_purge_threshold_pct_max=$defaults{'fundadb_hash_purge_threshold_pct_max'} 121 | 122 | [debug] 123 | debug_generic=$dbglvl 124 | debug_net=$dbglvl 125 | debug_pkt_array=$dbglvl 126 | debug_memory=$dbglvl 127 | debug_mysql_com=$dbglvl 128 | debug_mysql_connection=$dbglvl 129 | debug_mysql_server=$dbglvl 130 | debug_admin=$dbglvl 131 | debug_mysql_auth=$dbglvl 132 | " 133 | }; 134 | 135 | 136 | sub conf_general { 137 | print " 138 | 139 | Generic options: 140 | - core_dump_file_size : maximum size of core dump in case of crash 141 | - stack_size : stack size allocated for each thread 142 | - datadir : default directory for ProxySQL files, like error log, debug log, internal db 143 | - error_log : log file for error messages 144 | - debug_log : log file for debug messages 145 | - pid_file : PID file 146 | - restart_on_error : defines if proxysql is automatically restarted on crash or critical error 147 | - restart_delay : limits the frequency of automatic restarts 148 | 149 | "; 150 | do { 151 | print "\tcore_dump_file_size [$defaults{'core_dump_file_size'}]: "; 152 | my $input = ; 153 | chomp $input; 154 | if ( $input =~ /^\d+$/ ) { $proxycfg{'core_dump_file_size'}=$input } 155 | if ( $input =~ /^$/ ) { $proxycfg{'core_dump_file_size'}=$defaults{'core_dump_file_size'} } 156 | } until (defined $proxycfg{'core_dump_file_size'}); 157 | do { 158 | print "\tstack_size (65536-8388608) [$defaults{'stack_size'}]: "; 159 | my $input = ; 160 | chomp $input; 161 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 65536 ) && ( $input <= 8388608) ) { $proxycfg{'stack_size'}=$input } 162 | if ( $input =~ /^$/ ) { $proxycfg{'stack_size'}=$defaults{'stack_size'} } 163 | } until (defined $proxycfg{'stack_size'}); 164 | { 165 | print "\tdatadir [$defaults{'datadir'}]: "; 166 | my $input = ; 167 | chomp $input; 168 | if ( $input =~ /^$/ ) { $proxycfg{'datadir'}=$defaults{'datadir'} } 169 | else { $proxycfg{'datadir'}=$input; } 170 | }; 171 | { 172 | print "\terror_log [$defaults{'error_log'}]: "; 173 | my $input = ; 174 | chomp $input; 175 | if ( $input =~ /^$/ ) { $proxycfg{'error_log'}=$defaults{'error_log'} } 176 | else { $proxycfg{'error_log'}=$input; } 177 | }; 178 | { 179 | print "\tdebug_log [$defaults{'debug_log'}]: "; 180 | my $input = ; 181 | chomp $input; 182 | if ( $input =~ /^$/ ) { $proxycfg{'debug_log'}=$defaults{'debug_log'} } 183 | else { $proxycfg{'debug_log'}=$input; } 184 | }; 185 | { 186 | print "\tpid_file [$defaults{'pid_file'}]: "; 187 | my $input = ; 188 | chomp $input; 189 | if ( $input =~ /^$/ ) { $proxycfg{'pid_file'}=$defaults{'pid_file'} } 190 | else { $proxycfg{'pid_file'}=$input; } 191 | }; 192 | do { 193 | print "\trestart_on_error (0-1) [$defaults{'restart_on_error'}]: "; 194 | my $input = ; 195 | chomp $input; 196 | if ( ( $input =~ /^\d+$/ ) && ( $input <= 1 ) ) { $proxycfg{'restart_on_error'}=$input } 197 | if ( $input =~ /^$/ ) { $proxycfg{'restart_on_error'}=$defaults{'restart_on_error'} } 198 | } until (defined $proxycfg{'restart_on_error'}); 199 | do { 200 | print "\trestart_delay (0-600) [$defaults{'restart_delay'}]: "; 201 | my $input = ; 202 | chomp $input; 203 | if ( ( $input =~ /^\d+$/ ) && ( $input <= 600 ) ) { $proxycfg{'restart_delay'}=$input } 204 | if ( $input =~ /^$/ ) { $proxycfg{'restart_delay'}=$defaults{'restart_delay'} } 205 | } until (defined $proxycfg{'restart_delay'}); 206 | } 207 | 208 | sub conf_sockets { 209 | print " 210 | 211 | Clients can communicate with ProxySQL through 2 different sockets: 212 | - proxy_mysql_port : TCP socket for MySQL traffic : default is 6033 213 | - mysql_socket : Unix Domanin socket : default is /tmp/proxysql.sock 214 | 215 | "; 216 | do { 217 | print "\tproxy_mysql_port [$defaults{'proxy_mysql_port'}]: "; 218 | my $input = ; 219 | chomp $input; 220 | if ( $input =~ /^\d+$/ ) { $proxycfg{'proxy_mysql_port'}=$input } 221 | if ( $input =~ /^$/ ) { $proxycfg{'proxy_mysql_port'}=$defaults{'proxy_mysql_port'} } 222 | } until (defined $proxycfg{'proxy_mysql_port'}); 223 | { 224 | print "\tmysql_socket [$defaults{'mysql_socket'}]: "; 225 | my $input = ; 226 | chomp $input; 227 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_socket'}=$defaults{'mysql_socket'} } 228 | else { $proxycfg{'mysql_socket'}=$input; } 229 | }; 230 | } 231 | 232 | sub conf_admin { 233 | print " 234 | 235 | ProxySQL uses an admin interface for runtime configuration and to export statistics. 236 | Such interface uses the MySQL protocol and can be used by any MySQL client. 237 | Options: 238 | - proxy_admin_pathdb : path to the built-in database file that stores advanced configuration 239 | - proxy_admin_port : TCP socket for Administration : default is proxy_mysql_port-1 (6032) 240 | - proxy_admin_user : username for authentication ( this is not a mysql user ) 241 | - proxy_admin_password : password for the user specified in proxy_admin_user 242 | - proxy_admin_refresh_status_interval : how often internal statistics are updated 243 | 244 | "; 245 | { 246 | print "\tproxy_admin_pathdb [$defaults{'proxy_admin_pathdb'}]: "; 247 | my $input = ; 248 | chomp $input; 249 | if ( $input =~ /^$/ ) { $proxycfg{'proxy_admin_pathdb'}=$defaults{'proxy_admin_pathdb'} } 250 | else { $proxycfg{'proxy_admin_pathdb'}=$input; } 251 | }; 252 | $defaults{'proxy_admin_port'}=$proxycfg{'proxy_mysql_port'}-1; 253 | do { 254 | print "\tproxy_admin_port [$defaults{'proxy_admin_port'}]: "; 255 | my $input = ; 256 | chomp $input; 257 | if ( $input =~ /^\d+$/ ) { $proxycfg{'proxy_admin_port'}=$input } 258 | if ( $input =~ /^$/ ) { $proxycfg{'proxy_admin_port'}=$defaults{'proxy_admin_port'} } 259 | } until (defined $proxycfg{'proxy_admin_port'}); 260 | { 261 | print "\tproxy_admin_user [$defaults{'proxy_admin_user'}]: "; 262 | my $input = ; 263 | chomp $input; 264 | if ( $input =~ /^$/ ) { $proxycfg{'proxy_admin_user'}=$defaults{'proxy_admin_user'} } 265 | else { $proxycfg{'proxy_admin_user'}=$input; } 266 | }; 267 | { 268 | print "\tproxy_admin_password [$defaults{'proxy_admin_password'}]: "; 269 | my $input = ; 270 | chomp $input; 271 | if ( $input =~ /^$/ ) { $proxycfg{'proxy_admin_password'}=$defaults{'proxy_admin_password'} } 272 | else { $proxycfg{'proxy_admin_password'}=$input; } 273 | }; 274 | do { 275 | print "\tproxy_admin_refresh_status_interval (0-3600) [$defaults{'proxy_admin_refresh_status_interval'}]: "; 276 | my $input = ; 277 | chomp $input; 278 | if ( ( $input =~ /^\d+$/ ) && ( $input <= 3600 ) ) { $proxycfg{'proxy_admin_refresh_status_interval'}=$input } 279 | if ( $input =~ /^$/ ) { $proxycfg{'proxy_admin_refresh_status_interval'}=$defaults{'proxy_admin_refresh_status_interval'} } 280 | } until (defined $proxycfg{'proxy_admin_refresh_status_interval'}); 281 | 282 | } 283 | 284 | sub conf_query_cache { 285 | print " 286 | 287 | ProxySQL allows to cache SELECT statements executed by the application. 288 | Query cache is configured through: 289 | - mysql_query_cache_partitions : defines the number of partitions, reducing contention 290 | - mysql_query_cache_default_timeout : defaults TTL for queries without explicit TTL 291 | - mysql_query_cache_size : total amount of memory allocable for query cache 292 | - mysql_query_cache_precheck : check the query cache before processing the query 293 | 294 | "; 295 | do { 296 | print "\tmysql_query_cache_partitions (1-64) [$defaults{'mysql_query_cache_partitions'}]: "; 297 | my $input = ; 298 | chomp $input; 299 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 1 ) && ( $input <= 64 ) ) { $proxycfg{'mysql_query_cache_partitions'}=$input } 300 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_query_cache_partitions'}=$defaults{'mysql_query_cache_partitions'} } 301 | } until (defined $proxycfg{'mysql_query_cache_partitions'}); 302 | do { 303 | print "\tmysql_query_cache_default_timeout (0-315360000) [$defaults{'mysql_query_cache_default_timeout'}]: "; 304 | my $input = ; 305 | chomp $input; 306 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 0 ) && ( $input <= 315360000 ) ) { $proxycfg{'mysql_query_cache_default_timeout'}=$input } 307 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_query_cache_default_timeout'}=$defaults{'mysql_query_cache_default_timeout'} } 308 | } until (defined $proxycfg{'mysql_query_cache_default_timeout'}); 309 | do { 310 | print "\tmysql_query_cache_size (1048576-10737418240) [$defaults{'mysql_query_cache_size'}]: "; 311 | my $input = ; 312 | chomp $input; 313 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 1048576 ) && ( $input <= 10737418240 ) ) { $proxycfg{'mysql_query_cache_size'}=$input } 314 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_query_cache_size'}=$defaults{'mysql_query_cache_size'} } 315 | } until (defined $proxycfg{'mysql_query_cache_size'}); 316 | do { 317 | print "\tmysql_query_cache_precheck (0-1) [$defaults{'mysql_query_cache_precheck'}]: "; 318 | my $input = ; 319 | chomp $input; 320 | if ( ( $input =~ /^\d+$/ ) && ( $input <= 1 ) ) { $proxycfg{'mysql_query_cache_precheck'}=$input } 321 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_query_cache_precheck'}=$defaults{'mysql_query_cache_precheck'} } 322 | } until (defined $proxycfg{'mysql_query_cache_precheck'}); 323 | } 324 | 325 | 326 | sub conf_network { 327 | print " 328 | 329 | Several options define the network behaviour of ProxySQL: 330 | - mysql_threads : defines how many threads will process MySQL traffic 331 | - mysql_hostgroups : number of possible hostgroups configurable as backends 332 | - mysql_poll_timeout : poll() timeout (millisecond) 333 | - mysql_max_query_size : maximum length of a query to be analyzed 334 | - mysql_max_resultset_size : maximum size of resultset for caching and buffering 335 | - net_buffer_size : internal buffer for network I/O 336 | - backlog : listen() backlog 337 | - mysql_maintenance_timeout : timeout (millisecond) before terminating connections to servers in maintenance 338 | - mysql_poll_timeout_maintenance : poll() timeout (millisecond) during maintenance 339 | "; 340 | do { 341 | print "\tmysql_threads (1-128) [$defaults{'mysql_threads'}]: "; 342 | my $input = ; 343 | chomp $input; 344 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 1 ) && ( $input <= 128 ) ) { $proxycfg{'mysql_threads'}=$input } 345 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_threads'}=$defaults{'mysql_threads'} } 346 | } until (defined $proxycfg{'mysql_threads'}); 347 | do { 348 | print "\tmysql_hostgroups (2-64) [$defaults{'mysql_hostgroups'}]: "; 349 | my $input = ; 350 | chomp $input; 351 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 2 ) && ( $input <= 64 ) ) { $proxycfg{'mysql_hostgroups'}=$input } 352 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_hostgroups'}=$defaults{'mysql_hostgroups'} } 353 | } until (defined $proxycfg{'mysql_hostgroups'}); 354 | do { 355 | print "\tmysql_poll_timeout (100-1000000) [$defaults{'mysql_poll_timeout'}]: "; 356 | my $input = ; 357 | chomp $input; 358 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 100 ) && ( $input <= 1000000 ) ) { $proxycfg{'mysql_poll_timeout'}=$input } 359 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_poll_timeout'}=$defaults{'mysql_poll_timeout'} } 360 | } until (defined $proxycfg{'mysql_poll_timeout'}); 361 | do { 362 | print "\tmysql_max_query_size (1-16777210) [$defaults{'mysql_max_query_size'}]: "; 363 | my $input = ; 364 | chomp $input; 365 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 1 ) && ( $input <= 16777210 ) ) { $proxycfg{'mysql_max_query_size'}=$input } 366 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_max_query_size'}=$defaults{'mysql_max_query_size'} } 367 | } until (defined $proxycfg{'mysql_max_query_size'}); 368 | do { 369 | print "\tmysql_max_resultset_size (1-1073741824) [$defaults{'mysql_max_resultset_size'}]: "; 370 | my $input = ; 371 | chomp $input; 372 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 1 ) && ( $input <= 1073741824 ) ) { $proxycfg{'mysql_max_resultset_size'}=$input } 373 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_max_resultset_size'}=$defaults{'mysql_max_resultset_size'} } 374 | } until (defined $proxycfg{'mysql_max_resultset_size'}); 375 | do { 376 | print "\tnet_buffer_size (1024-16777216) [$defaults{'net_buffer_size'}]: "; 377 | my $input = ; 378 | chomp $input; 379 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 1024 ) && ( $input <= 16777216 ) ) { $proxycfg{'net_buffer_size'}=$input } 380 | if ( $input =~ /^$/ ) { $proxycfg{'net_buffer_size'}=$defaults{'net_buffer_size'} } 381 | } until (defined $proxycfg{'net_buffer_size'}); 382 | $proxycfg{'net_buffer_size'}=int($proxycfg{'net_buffer_size'}/1024)*1024; 383 | do { 384 | print "\tbacklog (50-10000) [$defaults{'backlog'}]: "; 385 | my $input = ; 386 | chomp $input; 387 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 50 ) && ( $input <= 10000 ) ) { $proxycfg{'backlog'}=$input } 388 | if ( $input =~ /^$/ ) { $proxycfg{'backlog'}=$defaults{'backlog'} } 389 | } until (defined $proxycfg{'backlog'}); 390 | do { 391 | print "\tmysql_maintenance_timeout (100-60000) [$defaults{'mysql_maintenance_timeout'}]: "; 392 | my $input = ; 393 | chomp $input; 394 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 100 ) && ( $input <= 60000 ) ) { $proxycfg{'mysql_maintenance_timeout'}=$input } 395 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_maintenance_timeout'}=$defaults{'mysql_maintenance_timeout'} } 396 | } until (defined $proxycfg{'mysql_maintenance_timeout'}); 397 | do { 398 | print "\tmysql_poll_timeout_maintenance (100-1000) [$defaults{'mysql_poll_timeout_maintenance'}]: "; 399 | my $input = ; 400 | chomp $input; 401 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 100 ) && ( $input <= 1000 ) ) { $proxycfg{'mysql_poll_timeout_maintenance'}=$input } 402 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_poll_timeout_maintenance'}=$defaults{'mysql_poll_timeout_maintenance'} } 403 | } until (defined $proxycfg{'mysql_poll_timeout_maintenance'}); 404 | } 405 | 406 | 407 | 408 | sub conf_conn_poll { 409 | print " 410 | 411 | ProxySQL implements an internal connection pool. Configurable with: 412 | - mysql_connection_pool_enabled : enables the connection pool if set to 1 413 | - mysql_wait_timeout : timeout to drop unused connections 414 | 415 | "; 416 | do { 417 | print "\tmysql_connection_pool_enabled (0-1) [$defaults{'mysql_connection_pool_enabled'}]: "; 418 | my $input = ; 419 | chomp $input; 420 | if ( ( $input =~ /^\d+$/ ) && ( $input <= 1 ) ) { $proxycfg{'mysql_connection_pool_enabled'}=$input } 421 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_connection_pool_enabled'}=$defaults{'mysql_connection_pool_enabled'} } 422 | } until (defined $proxycfg{'mysql_connection_pool_enabled'}); 423 | do { 424 | print "\tmysql_wait_timeout (1-31536000) [$defaults{'mysql_wait_timeout'}]: "; 425 | my $input = ; 426 | chomp $input; 427 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 1 ) && ( $input <= 31536000 ) ) { $proxycfg{'mysql_wait_timeout'}=$input } 428 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_wait_timeout'}=$defaults{'mysql_wait_timeout'} } 429 | } until (defined $proxycfg{'mysql_wait_timeout'}); 430 | } 431 | 432 | 433 | sub conf_mysql_backends1 { 434 | print " 435 | 436 | ProxySQL connects to various mysqld instances that form the backend. 437 | - mysql_servers : list of mysqld servers in the format host:port;host:port;... 438 | 439 | "; 440 | $proxycfg{'mysql_servers'}=""; 441 | my $cont=0; 442 | my $srvcnt=1; 443 | while ($cont==0) { 444 | my $input; 445 | print "\tHostname[:port] of backend#$srvcnt : "; 446 | $input = ; 447 | chomp $input; 448 | if ( $input =~ /^$/ ) { next; } 449 | my $server=$input; 450 | my $port=""; 451 | if ( $input !~ /:\d+$/ ) { 452 | do { 453 | print "\t\tport for server $server [3306]: "; 454 | $input = ; 455 | chomp $input; 456 | if ( $input =~ /^\d+$/ ) { $port=$input } 457 | if ( $input =~ /^$/ ) { $port=3306 } 458 | } until ($port ne ""); 459 | } 460 | if ($port ne "") { $server=$server.":".$port; } 461 | if ($srvcnt>1) { $server=";".$server; } 462 | $proxycfg{'mysql_servers'}.=$server; 463 | $srvcnt+=1; 464 | if ($proxycfg{'mysql_servers'} ne "") { 465 | my $more=""; 466 | do { 467 | print "\tWould you like to add another backend server (Y-N) [N]: "; 468 | my $input = ; 469 | chomp $input; 470 | if ( $input =~ /^$/ ) { $more="N" } 471 | if ( $input =~ /^N(o|)$/i ) { $more="N" } 472 | if ( $input =~ /^Y(es|)$/i ) { $more="Y" } 473 | } until ($more ne ""); 474 | if ($more eq "N") { $cont=1; } 475 | } 476 | } 477 | } 478 | 479 | 480 | sub conf_mysql_users { 481 | $proxycfg{'mysql_users'}="[mysql users]\n"; 482 | print " 483 | 484 | ProxySQL authenticates clients' connections, and then uses the same credentials to connect to the backends. 485 | ProxySQL needs to know clients' usernames and passwords because a single client connection can generate multiple connections to the backend. 486 | 487 | "; 488 | my $cont=0; 489 | my $usercnt=1; 490 | while ($cont==0) { 491 | my $input; 492 | print "\tUsername for user#$usercnt : "; 493 | $input = ; 494 | chomp $input; 495 | if ( $input =~ /^$/ ) { next; } 496 | my $user=$input; 497 | print "\tPassword for user $user : "; 498 | $input = ; 499 | chomp $input; 500 | my $password=$input; 501 | $proxycfg{'mysql_users'}.="$user=$password\n"; 502 | $usercnt+=1; 503 | my $more=""; 504 | do { 505 | print "\tWould you like to add another user (Y-N) [N]: "; 506 | my $input = ; 507 | chomp $input; 508 | if ( $input =~ /^$/ ) { $more="N" } 509 | if ( $input =~ /^N(o|)$/i ) { $more="N" } 510 | if ( $input =~ /^Y(es|)$/i ) { $more="Y" } 511 | } until ($more ne ""); 512 | if ($more eq "N") { $cont=1; } 513 | } 514 | } 515 | 516 | sub conf_mysql_backends2 { 517 | print " 518 | 519 | Few options specify how to connect to the backend: 520 | - mysql_usage_user : user used by ProxySQL to connect to the backend to verify its status 521 | - mysql_usage_password : password for user specified in mysql_usage_user 522 | 523 | Note: 524 | the user specified in mysql_usage_user needs only USAGE privilege, and you can create the user with GRANT USAGE 525 | 526 | "; 527 | { 528 | print "\tmysql_usage_user [$defaults{'mysql_usage_user'}]: "; 529 | my $input = ; 530 | chomp $input; 531 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_usage_user'}=$defaults{'mysql_usage_user'} } 532 | else { $proxycfg{'mysql_usage_user'}=$input; } 533 | }; 534 | { 535 | print "\tmysql_usage_password [$defaults{'mysql_usage_password'}]: "; 536 | my $input = ; 537 | chomp $input; 538 | if ( $input =~ /^$/ ) { $proxycfg{'mysql_usage_password'}=$defaults{'mysql_usage_password'} } 539 | else { $proxycfg{'mysql_usage_password'}=$input; } 540 | }; 541 | print " 542 | Note (again!): 543 | The user specified in mysql_usage_user needs only USAGE privilege 544 | You can create the user with GRANT USAGE ON *.* TO '$proxycfg{'mysql_usage_user'}'\@'' IDENTIFIED BY '$proxycfg{'mysql_usage_password'}'; 545 | 546 | "; 547 | 548 | } 549 | 550 | sub enable_verb { 551 | $bool=""; 552 | print "\nIf you compiled ProxySQL with debug information (enabled by default) you can enable debug verbosity.\n\n"; 553 | do { 554 | print "\tWould you like to enable debug verbosity? (Y-N) [N]: "; 555 | my $input = ; 556 | chomp $input; 557 | if ( $input =~ /^$/ ) { $proxycfg{'debug'}=0 } 558 | if ( $input =~ /^N(o|)$/i ) { $proxycfg{'debug'}=0 } 559 | if ( $input =~ /^Y(es|)$/i ) { $proxycfg{'debug'}=1 } 560 | } until (defined $proxycfg{'debug'}); 561 | if ($proxycfg{'debug'}==1) { 562 | print " 563 | 564 | Several modules can be debugged and each of them can be configured with a different verbosity level. 565 | You can now configure the default verbosity level, and you can fine tune it later on 566 | 567 | "; 568 | do { 569 | print "\tdefault debug level (0-9) [$dbglvl]: "; 570 | my $input = ; 571 | chomp $input; 572 | if ( ( $input =~ /^\d+$/ ) && ( $input >= 0 ) && ($input <= 9 ) ) { $proxycfg{'dbglvl'}=$input } 573 | if ( $input =~ /^$/ ) { $proxycfg{'dbglvl'}=$dbglvl; } 574 | } until (defined $proxycfg{'dbglvl'}); 575 | $dbglvl=$proxycfg{'dbglvl'}; 576 | } 577 | } 578 | 579 | 580 | sub save_to_file { 581 | $bool=""; 582 | do { 583 | print "\n\nWould you like to write a configuration file? (Y-N) [Y]: "; 584 | my $input = ; 585 | chomp $input; 586 | if ( $input =~ /^$/ ) { $bool="Y" } 587 | if ( $input =~ /^N(o|)$/i ) { $bool="N" } 588 | if ( $input =~ /^Y(es|)$/i ) { $bool="Y" } 589 | } until ($bool ne ""); 590 | if ($bool eq "Y") { 591 | my $filewritten=0; 592 | do { 593 | print "\tconfig filename [$defaults{'config_file'}]: "; 594 | my $input = ; 595 | chomp $input; 596 | if ( $input =~ /^$/ ) { $proxycfg{'config_file'}=$defaults{'config_file'} } 597 | else { $proxycfg{'config_file'}=$input; } 598 | if (-e $proxycfg{'config_file'}) { 599 | print "Error: file $proxycfg{'config_file'} Exists!\n"; 600 | } else { 601 | open (MYFILE, "> $proxycfg{'config_file'}"); 602 | print MYFILE $configfile; 603 | close (MYFILE); 604 | $filewritten=1; 605 | } 606 | } until ($filewritten==1 ); 607 | } 608 | 609 | } 610 | 611 | sub main { 612 | intro(); 613 | conf_general(); 614 | conf_sockets(); 615 | conf_admin(); 616 | conf_query_cache(); 617 | conf_network(); 618 | conf_conn_poll(); 619 | conf_mysql_backends1(); 620 | conf_mysql_backends2(); 621 | conf_mysql_users; 622 | print "\nBasic configuration completed!\n\n"; 623 | enable_verb(); 624 | config_to_string(); 625 | print "\n\n$configfile"; 626 | save_to_file(); 627 | print "\nConfiguration completed!\nQuit\n\n"; 628 | } 629 | 630 | if(!caller) { exit(main(@ARGV)); } 631 | -------------------------------------------------------------------------------- /src/threads.c: -------------------------------------------------------------------------------- 1 | #include "proxysql.h" 2 | void set_thread_attr(pthread_attr_t *attr, size_t stacksize) { 3 | // int rc; 4 | //assert(rc==0); 5 | // pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED); 6 | // int ss; 7 | } 8 | 9 | void start_background_threads(pthread_attr_t *attra, void **stackspts) { 10 | pthread_attr_t attr; 11 | 12 | int r; 13 | r=pthread_attr_init(&attr); 14 | assert(r==0); 15 | void *sp; 16 | #ifdef DEBUG 17 | r=posix_memalign(&sp, sysconf(_SC_PAGESIZE), glovars.stack_size); 18 | assert(r==0); 19 | stackspts[glovars.mysql_threads+2+0]=sp; 20 | r=pthread_attr_setstack(&attr, sp, glovars.stack_size); 21 | assert(r==0); 22 | r=pthread_create(&thread_dbg_logger, &attr, debug_logger , NULL); 23 | assert(r==0); 24 | #endif 25 | if (glovars.mysql_query_cache_enabled==TRUE) { 26 | PROXY_TRACE(); 27 | fdb_hashes_new(&QC,glovars.mysql_query_cache_partitions, glovars.mysql_query_cache_default_timeout, glovars.mysql_query_cache_size); 28 | // pthread_t qct; 29 | r=posix_memalign(&sp, sysconf(_SC_PAGESIZE), glovars.stack_size); 30 | assert(r==0); 31 | stackspts[glovars.mysql_threads+2+1]=sp; 32 | r=pthread_attr_setstack(&attr, sp, glovars.stack_size); 33 | assert(r==0); 34 | r=pthread_create(&thread_qct, &attr, purgeHash_thread, &QC); 35 | assert(r==0); 36 | } 37 | 38 | // Added by chan 39 | //printf("=> create new qr_hash\n"); 40 | qr_hashes_new(&QR_HASH_T); 41 | //printf("=> end\n"); 42 | 43 | r=posix_memalign(&sp, sysconf(_SC_PAGESIZE), glovars.stack_size); 44 | assert(r==0); 45 | stackspts[glovars.mysql_threads+2+2]=sp; 46 | r=pthread_attr_setstack(&attr, sp, glovars.stack_size); 47 | assert(r==0); 48 | r=pthread_create(&thread_qr, &attr, qr_report_thread, &QR_HASH_T); 49 | assert(r==0); 50 | // Added by chan end. 51 | 52 | 53 | // pthread_t cppt; 54 | r=posix_memalign(&sp, sysconf(_SC_PAGESIZE), glovars.stack_size); 55 | assert(r==0); 56 | stackspts[glovars.mysql_threads+2+3]=sp; 57 | r=pthread_attr_setstack(&attr, sp, glovars.stack_size); 58 | assert(r==0); 59 | r=pthread_create(&thread_cppt, &attr, mysql_connpool_purge_thread , NULL); 60 | assert(r==0); 61 | } 62 | 63 | void init_proxyipc() { 64 | int i; 65 | PROXY_TRACE(); 66 | proxyipc.fdIn=g_malloc0_n(glovars.mysql_threads,sizeof(int)); 67 | proxyipc.fdOut=g_malloc0_n(glovars.mysql_threads,sizeof(int)); 68 | proxyipc.queue=g_malloc0_n(glovars.mysql_threads+1,sizeof(GAsyncQueue *)); 69 | // create pipes 70 | for (i=0; i= 'a' && c <= 'z') 11 | return 1; 12 | if(c >= 'A' && c <= 'Z') 13 | return 1; 14 | if(c >= '0' && c <= '9') 15 | return 1; 16 | if(c == '$' || c == '_') 17 | return 1; 18 | return 0; 19 | } 20 | 21 | // token char - not table name string 22 | static inline char is_token_char(char c) 23 | { 24 | return !is_normal_char(c); 25 | } 26 | 27 | // space - it's much easy to remove duplicated space chars 28 | static inline char is_space_char(char c) 29 | { 30 | if(c == ' ' || c == '\t' || c == '\n' || c == '\r') 31 | return 1; 32 | return 0; 33 | } 34 | 35 | // check digit 36 | static inline char is_digit_char(char c) 37 | { 38 | if(c >= '0' && c <= '9') 39 | return 1; 40 | return 0; 41 | } 42 | 43 | // check if it can be HEX char 44 | static inline char is_hex_char(char c) 45 | { 46 | if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) 47 | return 1; 48 | return 0; 49 | } 50 | 51 | // between pointer, check string is number - need to be changed more functions 52 | static char is_digit_string(char *f, char *t) 53 | { 54 | if(f == t) 55 | { 56 | if(is_digit_char(*f)) 57 | return 1; 58 | else 59 | return 0; 60 | } 61 | 62 | int is_hex = 0; 63 | int i = 0; 64 | 65 | // 0x, 0X 66 | while(f != t) 67 | { 68 | if(i == 1 && *(f-1) == '0' && (*f == 'x' || *f == 'X')) 69 | { 70 | is_hex = 1; 71 | } 72 | 73 | // none hex 74 | else if(!is_hex && !is_digit_char(*f)) 75 | { 76 | return 0; 77 | } 78 | 79 | // hex 80 | else if(is_hex && !is_hex_char(*f)) 81 | { 82 | return 0; 83 | } 84 | f++; 85 | i++; 86 | } 87 | 88 | // need to be added function ---------------- 89 | // 23e 90 | // 23e+1 91 | 92 | return 1; 93 | } 94 | 95 | // need to be changed - I've got this code from google result. :) 96 | char *str2md5(const char *str) { 97 | int n; 98 | MD5_CTX c; 99 | unsigned char digest[16]; 100 | char *out = (char*)g_malloc(33); 101 | MD5_Init(&c); 102 | int length = strlen(str); 103 | 104 | while (length > 0) { 105 | if (length > 512) { 106 | MD5_Update(&c, str, 512); 107 | } else { 108 | MD5_Update(&c, str, length); 109 | } 110 | length -= 512; 111 | str += 512; 112 | } 113 | 114 | MD5_Final(digest, &c); 115 | for (n = 0; n < 16; ++n) { 116 | snprintf(&(out[n*2]), 16*2, "%02x", (unsigned int)digest[n]); 117 | } 118 | return out; 119 | } 120 | 121 | // Added by chan end -------------------------------------------- 122 | 123 | 124 | 125 | // Added by chan 126 | //void process_query_stats(mysql_session_t *sess){ 127 | char *mysql_query_digest(mysql_session_t *sess){ 128 | char *s = sess->query_info.query; 129 | int len = sess->query_info.query_len; 130 | 131 | int i = 0; 132 | 133 | char *r = (char *) g_malloc(len + SIZECHAR); 134 | 135 | char *p_r = r; 136 | char *p_r_t = r; 137 | 138 | char prev_char = 0; 139 | char qutr_char = 0; 140 | 141 | char flag = 0; 142 | 143 | while(i < len) 144 | { 145 | // ================================================= 146 | // START - read token char and set flag what's going on. 147 | // ================================================= 148 | if(flag == 0) 149 | { 150 | // store current position 151 | p_r_t = p_r; 152 | 153 | // comment type 1 - start with '/*' 154 | if(prev_char == '/' && *s == '*') 155 | { 156 | flag = 1; 157 | } 158 | 159 | // comment type 2 - start with '#' 160 | else if(*s == '#') 161 | { 162 | flag = 2; 163 | } 164 | 165 | // string - start with ' 166 | else if(*s == '\'' || *s == '"') 167 | { 168 | flag = 3; 169 | qutr_char = *s; 170 | } 171 | 172 | // may be digit - start with digit 173 | else if(is_token_char(prev_char) && is_digit_char(*s)) 174 | { 175 | flag = 4; 176 | if(len == i+1) 177 | continue; 178 | } 179 | 180 | // not above case - remove duplicated space char 181 | else 182 | { 183 | flag = 0; 184 | if(is_space_char(prev_char) && is_space_char(*s)){ 185 | prev_char = ' '; 186 | *p_r = ' '; 187 | s++; 188 | i++; 189 | continue; 190 | } 191 | } 192 | } 193 | 194 | // ================================================= 195 | // PROCESS and FINISH - do something on each case 196 | // ================================================= 197 | else 198 | { 199 | // -------- 200 | // comment 201 | // -------- 202 | if( 203 | // comment type 1 - /* .. */ 204 | (flag == 1 && prev_char == '*' && *s == '/') || 205 | 206 | // comment type 2 - # ... \n 207 | (flag == 2 && (*s == '\n' || *s == '\r')) 208 | ) 209 | { 210 | p_r = flag == 1 ? p_r_t - SIZECHAR : p_r_t; 211 | prev_char = ' '; 212 | flag = 0; 213 | s++; 214 | i++; 215 | continue; 216 | } 217 | 218 | // -------- 219 | // string 220 | // -------- 221 | else if(flag == 3) 222 | { 223 | // Last char process 224 | if(len == i + 1) 225 | { 226 | p_r = p_r_t; 227 | *p_r++ = '?'; 228 | flag = 0; 229 | break; 230 | } 231 | 232 | // need to be ignored case 233 | if(p_r > p_r_t + SIZECHAR) 234 | { 235 | if( 236 | (prev_char == '\\' && *s == '\\') || // to process '\\\\', '\\' 237 | (prev_char == '\\' && *s == qutr_char) || // to process '\'' 238 | (prev_char == qutr_char && *s == qutr_char) // to process '''' 239 | ) 240 | { 241 | prev_char = 'X'; 242 | s++; 243 | i++; 244 | continue; 245 | } 246 | } 247 | 248 | // satisfied closing string - swap string to ? 249 | if(*s == qutr_char && (len == i+1 || *(s + SIZECHAR) != qutr_char)) 250 | { 251 | p_r = p_r_t; 252 | *p_r++ = '?'; 253 | flag = 0; 254 | if(i < len) 255 | s++; 256 | i++; 257 | continue; 258 | } 259 | } 260 | 261 | // -------- 262 | // digit 263 | // -------- 264 | else if(flag == 4) 265 | { 266 | // last single char 267 | if(p_r_t == p_r) 268 | { 269 | *p_r++ = '?'; 270 | i++; 271 | continue; 272 | } 273 | 274 | // token char or last char 275 | if(is_token_char(*s) || len == i+1) 276 | { 277 | if(is_digit_string(p_r_t, p_r)) 278 | { 279 | p_r = p_r_t; 280 | *p_r++ = '?'; 281 | if(len == i+1) 282 | { 283 | if(is_token_char(*s)) 284 | *p_r++ = *s; 285 | i++; 286 | continue; 287 | } 288 | 289 | 290 | } 291 | flag = 0; 292 | } 293 | } 294 | } 295 | 296 | // ================================================= 297 | // COPY CHAR 298 | // ================================================= 299 | // convert every space char to ' ' 300 | *p_r++ = !is_space_char(*s) ? *s : ' '; 301 | prev_char = *s++; 302 | 303 | i++; 304 | } 305 | *p_r = 0; 306 | 307 | // process query stats 308 | // last changed at 20140418 - by chan 309 | return r; 310 | } 311 | 312 | 313 | /* 314 | if(*r){ 315 | // to save memory usage 316 | int slen = len + strlen(sess->mysql_username) + strlen(sess->mysql_schema_cur)+ SIZECHAR + 2; 317 | char *r2 = (char *) g_malloc(slen); 318 | 319 | snprintf(r2, slen, "%s\t%s\t%s", sess->mysql_username, sess->mysql_schema_cur, r); 320 | g_free(r); 321 | 322 | char *md5 = str2md5(r2); 323 | proxy_debug(PROXY_DEBUG_GENERIC, 1, "%s => %s\n", md5, r2); 324 | qr_set(md5, r2); 325 | } 326 | } 327 | */ 328 | // Added by chan end. 329 | 330 | 331 | void cleanup_query_stats(qr_hash_entry *query_stats) { 332 | if (query_stats->key) 333 | g_free(query_stats->key); 334 | if (query_stats->mysql_server_address) 335 | g_free(query_stats->mysql_server_address); 336 | if (query_stats->query_digest_text) 337 | g_free(query_stats->query_digest_text); 338 | if (query_stats->query_digest_md5) 339 | g_free(query_stats->query_digest_md5); 340 | if (query_stats->username) 341 | g_free(query_stats->username); 342 | if (query_stats->schemaname) 343 | g_free(query_stats->schemaname); 344 | g_free(query_stats); 345 | } 346 | 347 | 348 | static void __generate_qr_hash_entry__key(qr_hash_entry *entry) { 349 | int i; 350 | char *sa=""; 351 | i=strlen(entry->query_digest_md5); 352 | i+=3; //length hostgroup_id 353 | if (entry->mysql_server_address) { 354 | i+=strlen(entry->mysql_server_address); 355 | sa=entry->mysql_server_address; 356 | } 357 | i+=strlen(entry->username); 358 | i+=strlen(entry->schemaname); 359 | i+=5; //length port 360 | i+=5*strlen("__")+5; //spacers + extra buffer 361 | entry->key=g_malloc0(i); 362 | sprintf(entry->key,"%s__%s__%s__%d__%s__%d",entry->query_digest_md5, entry->username, entry->schemaname, entry->hostgroup_id, sa, entry->mysql_server_port); 363 | } 364 | 365 | void query_statistics_set(mysql_session_t *sess) { 366 | // FIXME: placeholder 367 | qr_hash_entry *query_stats=sess->query_info.query_stats; 368 | __generate_qr_hash_entry__key(query_stats); 369 | query_stats->value=query_stats; 370 | query_stats->exec_cnt=1; 371 | qr_hash_t *ht = &QR_HASH_T; 372 | long total_time=monotonic_time()-query_stats->query_time; 373 | query_stats->query_time=total_time; 374 | pthread_rwlock_wrlock(&(ht->lock)); 375 | qr_hash_entry *entry = g_hash_table_lookup(ht->c_hash, query_stats->key); 376 | if(entry == NULL){ 377 | g_hash_table_insert(ht->c_hash, query_stats->key, query_stats); 378 | // fprintf(stderr, "INSERTING %p\t%p\t%d\t%s\t%s\t%s\t%d\t%s\t%d\n" , query_stats->key, query_stats, query_stats->exec_cnt, query_stats->key, query_stats->query_digest_md5, query_stats->query_digest_text, query_stats->hostgroup_id, query_stats->mysql_server_address, query_stats->mysql_server_port); 379 | }else{ 380 | cleanup_query_stats(query_stats); 381 | entry->exec_cnt++; 382 | entry->query_time+=total_time; 383 | // fprintf(stderr, "REPLACING %p\t%p\t%d\t%s\t%s\t%s\t%d\t%s\t%d\n" , entry->key, entry, entry->exec_cnt, entry->key, entry->query_digest_md5, entry->query_digest_text, entry->hostgroup_id, entry->mysql_server_address, entry->mysql_server_port); 384 | } 385 | sess->query_info.query_stats=NULL; 386 | pthread_rwlock_unlock(&(ht->lock)); 387 | return; 388 | } 389 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include "proxysql.h" 2 | 3 | long monotonic_time() { 4 | struct timespec ts; 5 | clock_gettime(CLOCK_MONOTONIC, &ts); 6 | return (((long) ts.tv_sec) * 1000000) + (ts.tv_nsec / 1000); 7 | } 8 | 9 | -------------------------------------------------------------------------------- /tests/1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | 9 | int main() { 10 | int i; 11 | char query[24]; 12 | MYSQL *mysql=mysql_init(NULL); 13 | //if (!mysql_real_connect(mysql,"127.0.0.1","root","","test",3306,NULL,0)) 14 | if (!mysql_real_connect(mysql,"127.0.0.1","root","","test",3306,NULL,0)) 15 | { 16 | fprintf(stderr, "Failed to connect to database: Error: %s\n", mysql_error(mysql)); 17 | return 0; 18 | } 19 | mysql_query(mysql,"SELECT id FROM INFORMATION_SCHEMA.PROCESSLIST WHERE COMMAND='Sleep' AND USER='vegaicm'"); 20 | // fprintf(stderr,"%s\n", mysql_error(mysql)); 21 | mysql_close(mysql); 22 | sleep(100); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-lpthread -ggdb `mysql_config --libs_r --cflags` `pkg-config --libs --cflags glib-2.0` -O2 3 | 4 | reconnect.o: reconnect.c 5 | $(CC) -c -o reconnect.o reconnect.c $(CFLAGS) 6 | reconnect: reconnect.o 7 | $(CC) -o reconnect reconnect.o $(CFLAGS) 8 | connect_speed.o: connect_speed.c 9 | $(CC) -c -o connect_speed.o connect_speed.c $(CFLAGS) 10 | connect_speed: connect_speed.o 11 | $(CC) -o connect_speed connect_speed.o $(CFLAGS) 12 | 13 | all: reconnect connect_speed 14 | -------------------------------------------------------------------------------- /tests/connect_speed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/renecannao/proxysql-old/f9a91fe77bf9bf82335bc3a4a95bd973f3c0d507/tests/connect_speed -------------------------------------------------------------------------------- /tests/connect_speed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | pthread_attr_t attr; 8 | 9 | int qnum=0; 10 | 11 | typedef struct _setting_reconnect_t { 12 | int enabled; 13 | int threads; 14 | int queries; 15 | int slave_pct; 16 | int min_sleep; 17 | int max_sleep; 18 | int kill_interval; 19 | int select_OK; 20 | int select_ERR; 21 | int kills; 22 | } setting_reconnect_t; 23 | 24 | 25 | setting_reconnect_t reconnect; 26 | 27 | 28 | /* 29 | void * mysql_client_reconnect_kill() { 30 | int i; 31 | char query[24]; 32 | MYSQL *mysql=mysql_init(NULL); 33 | //if (!mysql_real_connect(mysql,"127.0.0.1","root","","test",3306,NULL,0)) 34 | if (!mysql_real_connect(mysql,"127.0.0.1","root","","test",3306,NULL,0)) 35 | { 36 | fprintf(stderr, "Failed to connect to database: Error: %s\n", mysql_error(mysql)); 37 | return NULL; 38 | } 39 | while (reconnect.enabled==1) { 40 | usleep(reconnect.kill_interval*1000); 41 | if (mysql_query(mysql,"SELECT id FROM INFORMATION_SCHEMA.PROCESSLIST WHERE COMMAND='Sleep' AND USER='vegaicm'")) { 42 | fprintf(stderr,"%s\n", mysql_error(mysql)); 43 | mysql_close(mysql); 44 | return NULL; 45 | } 46 | MYSQL_RES *result = mysql_store_result(mysql); 47 | MYSQL_ROW row; 48 | // for (i=0; ireconnect.slave_pct ? " FOR UPDATE" : "")); 85 | // printf("%s\n", query); 86 | 87 | /* 88 | if (mysql_query(mysql,query)) { 89 | fprintf(stderr,"%s %s\n", mysql_error(mysql), query); 90 | __sync_fetch_and_add(&reconnect.select_ERR,1); 91 | return NULL; 92 | } else { 93 | MYSQL_RES *result = mysql_store_result(mysql); 94 | mysql_free_result(result); 95 | __sync_fetch_and_add(&reconnect.select_OK,1); 96 | } 97 | */ 98 | mysql_close(mysql); 99 | } 100 | return NULL; 101 | } 102 | int main(int argc, char **argv) { 103 | int i; 104 | mysql_library_init(0,NULL,NULL); 105 | pthread_attr_init(&attr); 106 | pthread_attr_setstacksize (&attr, 64*1024); 107 | // reconnect.enabled=1; 108 | reconnect.threads=1; 109 | reconnect.queries=1000; 110 | reconnect.slave_pct=70; 111 | reconnect.min_sleep=1000; 112 | reconnect.max_sleep=3000; 113 | reconnect.kill_interval=10000; 114 | reconnect.kills=0; 115 | reconnect.select_OK=0; 116 | reconnect.select_ERR=0; 117 | 118 | pthread_t *thi=malloc(sizeof(pthread_t)*reconnect.threads); 119 | if (thi==NULL) exit(EXIT_FAILURE); 120 | // pthread_t kill; 121 | // if ( pthread_create(&kill, &attr, mysql_client_reconnect_kill, NULL ) != 0 ) 122 | // perror("Thread creation"); 123 | for (i=0; i 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | pthread_attr_t attr; 8 | 9 | int qnum=0; 10 | 11 | typedef struct _setting_reconnect_t { 12 | int enabled; 13 | int threads; 14 | int queries; 15 | int slave_pct; 16 | int min_sleep; 17 | int max_sleep; 18 | int kill_interval; 19 | int select_OK; 20 | int select_ERR; 21 | int kills; 22 | } setting_reconnect_t; 23 | 24 | 25 | setting_reconnect_t reconnect; 26 | 27 | 28 | 29 | void * mysql_client_reconnect_kill() { 30 | int i; 31 | char query[24]; 32 | MYSQL *mysql=mysql_init(NULL); 33 | //if (!mysql_real_connect(mysql,"127.0.0.1","root","","test",3306,NULL,0)) 34 | if (!mysql_real_connect(mysql,"127.0.0.1","root","","test",3306,NULL,0)) 35 | { 36 | fprintf(stderr, "Failed to connect to database: Error: %s\n", mysql_error(mysql)); 37 | return NULL; 38 | } 39 | while (reconnect.enabled==1) { 40 | usleep(reconnect.kill_interval*1000); 41 | if (mysql_query(mysql,"SELECT id FROM INFORMATION_SCHEMA.PROCESSLIST WHERE COMMAND='Sleep' AND USER='vegaicm'")) { 42 | fprintf(stderr,"%s\n", mysql_error(mysql)); 43 | mysql_close(mysql); 44 | return NULL; 45 | } 46 | MYSQL_RES *result = mysql_store_result(mysql); 47 | MYSQL_ROW row; 48 | // for (i=0; ireconnect.slave_pct ? " FOR UPDATE" : "")); 81 | // printf("%s\n", query); 82 | if (mysql_query(mysql,query)) { 83 | fprintf(stderr,"%s %s\n", mysql_error(mysql), query); 84 | __sync_fetch_and_add(&reconnect.select_ERR,1); 85 | mysql_close(mysql); 86 | return NULL; 87 | } 88 | MYSQL_RES *result = mysql_store_result(mysql); 89 | mysql_free_result(result); 90 | __sync_fetch_and_add(&reconnect.select_OK,1); 91 | } 92 | return NULL; 93 | } 94 | int main(int argc, char **argv) { 95 | int i; 96 | /* int thrnum; 97 | if (argc < 3) exit(EXIT_FAILURE); 98 | thrnum=atoi(argv[1]); 99 | if (thrnum==0) exit(EXIT_FAILURE); 100 | qnum=atoi(argv[2]); 101 | if (qnum==0) exit(EXIT_FAILURE);*/ 102 | mysql_library_init(0,NULL,NULL); 103 | pthread_attr_init(&attr); 104 | //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 105 | pthread_attr_setstacksize (&attr, 64*1024); 106 | reconnect.enabled=1; 107 | reconnect.threads=64; 108 | reconnect.queries=1000; 109 | reconnect.slave_pct=70; 110 | reconnect.min_sleep=1000; 111 | reconnect.max_sleep=3000; 112 | reconnect.kill_interval=10000; 113 | reconnect.kills=0; 114 | reconnect.select_OK=0; 115 | reconnect.select_ERR=0; 116 | pthread_t *thi=malloc(sizeof(pthread_t)*reconnect.threads); 117 | if (thi==NULL) exit(EXIT_FAILURE); 118 | pthread_t kill; 119 | if ( pthread_create(&kill, &attr, mysql_client_reconnect_kill, NULL ) != 0 ) 120 | perror("Thread creation"); 121 | for (i=0; i