From 517ecd9a12471e662c877abe100bc8dfc29db630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 8 Feb 2017 14:18:56 +0200 Subject: [PATCH 01/10] Remove unnecessary spinlocks from random_jkiss Removing the locks will increase the randomness of the random number generation by introducing race conditions into the code. --- server/core/random_jkiss.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/server/core/random_jkiss.c b/server/core/random_jkiss.c index bc81b9fcc..b21ce42f4 100644 --- a/server/core/random_jkiss.c +++ b/server/core/random_jkiss.c @@ -31,7 +31,6 @@ #include #include #include -#include #include /* Public domain code for JKISS RNG - Comment header added */ @@ -42,8 +41,6 @@ static unsigned int x = 123456789, y = 987654321, z = 43219876, c = 6543217; /* Seed variables */ static bool init = false; -static SPINLOCK random_jkiss_spinlock = SPINLOCK_INIT; - static unsigned int random_jkiss_devrand(void); static void random_init_jkiss(void); @@ -60,14 +57,11 @@ random_jkiss(void) unsigned long long t; unsigned int result; - spinlock_acquire(&random_jkiss_spinlock); if (!init) { /* Must set init first because initialisation calls this function */ init = true; - spinlock_release(&random_jkiss_spinlock); random_init_jkiss(); - spinlock_acquire(&random_jkiss_spinlock); } x = 314527869 * x + 1234567; y ^= y << 5; @@ -77,7 +71,6 @@ random_jkiss(void) c = t >> 32; z = t; result = x + y + z; - spinlock_release(&random_jkiss_spinlock); return result; } @@ -120,7 +113,6 @@ random_init_jkiss(void) { int newrand, i; - spinlock_acquire(&random_jkiss_spinlock); if ((newrand = random_jkiss_devrand()) != 0) { x = newrand; @@ -140,7 +132,6 @@ random_init_jkiss(void) { c = newrand % 698769068 + 1; /* Should be less than 698769069 */ } - spinlock_release(&random_jkiss_spinlock); /* "Warm up" our random number generator */ for (i = 0; i < 100; i++) From ce5cd69eb3d6d2aa0f04457d5acb8beb282c1a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Fri, 3 Feb 2017 17:26:55 +0200 Subject: [PATCH 02/10] Remove unused locks and variables Removed unused spinlocks from DCBs, sessions and the MySQL protocol structs. They were used in a context where only one thread has access to the structure. Removed unused member variables from DCBs. --- include/maxscale/dcb.h | 20 +------ include/maxscale/protocol/mysql.h | 1 - include/maxscale/session.h | 1 - server/core/dcb.c | 50 ++++------------ server/core/maxscale/session.h | 2 +- server/core/poll.c | 27 +-------- server/core/session.c | 4 -- server/modules/filter/tee/tee.c | 2 - .../MySQL/MySQLBackend/mysql_backend.c | 57 +------------------ .../protocol/MySQL/MySQLClient/mysql_client.c | 6 -- server/modules/protocol/MySQL/mysql_common.c | 50 +++++----------- server/modules/routing/maxinfo/maxinfo.c | 7 +-- .../routing/readconnroute/readconnroute.c | 18 +----- .../routing/readwritesplit/readwritesplit.c | 2 - .../routing/schemarouter/schemarouter.c | 6 -- 15 files changed, 36 insertions(+), 217 deletions(-) diff --git a/include/maxscale/dcb.h b/include/maxscale/dcb.h index 8df97832a..169d25c2f 100644 --- a/include/maxscale/dcb.h +++ b/include/maxscale/dcb.h @@ -190,7 +190,6 @@ typedef struct dcb bool draining_flag; /**< Set while write queue is drained */ bool drain_called_while_busy; /**< Set as described */ dcb_role_t dcb_role; - SPINLOCK dcb_initlock; DCBEVENTQ evq; /**< The event queue for this DCB */ int fd; /**< The descriptor */ dcb_state_t state; /**< Current descriptor state */ @@ -207,32 +206,20 @@ typedef struct dcb struct servlistener *listener; /**< For a client DCB, the listener data */ MXS_PROTOCOL func; /**< The protocol functions for this descriptor */ MXS_AUTHENTICATOR authfunc; /**< The authenticator functions for this descriptor */ - int writeqlen; /**< Current number of byes in the write queue */ - SPINLOCK writeqlock; /**< Write Queue spinlock */ GWBUF *writeq; /**< Write Data Queue */ - SPINLOCK delayqlock; /**< Delay Backend Write Queue spinlock */ GWBUF *delayq; /**< Delay Backend Write Data Queue */ GWBUF *dcb_readqueue; /**< read queue for storing incomplete reads */ GWBUF *dcb_fakequeue; /**< Fake event queue for generated events */ DCBSTATS stats; /**< DCB related statistics */ - unsigned int dcb_server_status; /*< the server role indicator from SERVER */ struct dcb *nextpersistent; /**< Next DCB in the persistent pool for SERVER */ time_t persistentstart; /**< Time when DCB placed in persistent pool */ struct service *service; /**< The related service */ void *data; /**< Specific client data, shared between DCBs of this session */ void *authenticator_data; /**< The authenticator data for this DCB */ DCBMM memdata; /**< The data related to DCB memory management */ - SPINLOCK cb_lock; /**< The lock for the callbacks linked list */ DCB_CALLBACK *callbacks; /**< The list of callbacks for the DCB */ - SPINLOCK pollinlock; - int pollinbusy; - int readcheck; - - SPINLOCK polloutlock; - int polloutbusy; - int writecheck; long last_read; /*< Last time the DCB received data */ int high_water; /**< High water mark */ int low_water; /**< Low water mark */ @@ -242,7 +229,6 @@ typedef struct dcb bool ssl_read_want_write; /*< Flag */ bool ssl_write_want_read; /*< Flag */ bool ssl_write_want_write; /*< Flag */ - int dcb_port; /**< port of target server */ bool was_persistent; /**< Whether this DCB was in the persistent pool */ struct { @@ -253,13 +239,11 @@ typedef struct dcb skygw_chk_t dcb_chk_tail; } DCB; -#define DCB_INIT {.dcb_chk_top = CHK_NUM_DCB, .dcb_initlock = SPINLOCK_INIT, \ +#define DCB_INIT {.dcb_chk_top = CHK_NUM_DCB, \ .evq = DCBEVENTQ_INIT, .ipv4 = {0}, .func = {0}, .authfunc = {0}, \ - .writeqlock = SPINLOCK_INIT, .delayqlock = SPINLOCK_INIT, \ .stats = {0}, .memdata = DCBMM_INIT, \ - .cb_lock = SPINLOCK_INIT, .pollinlock = SPINLOCK_INIT, \ .fd = DCBFD_CLOSED, .stats = DCBSTATS_INIT, .ssl_state = SSL_HANDSHAKE_UNKNOWN, \ - .state = DCB_STATE_ALLOC, .polloutlock = SPINLOCK_INIT, .dcb_chk_tail = CHK_NUM_DCB, \ + .state = DCB_STATE_ALLOC, .dcb_chk_tail = CHK_NUM_DCB, \ .authenticator_data = NULL, .thread = {0}} /** diff --git a/include/maxscale/protocol/mysql.h b/include/maxscale/protocol/mysql.h index b75ecdf9f..6212ca9c5 100644 --- a/include/maxscale/protocol/mysql.h +++ b/include/maxscale/protocol/mysql.h @@ -269,7 +269,6 @@ typedef struct #endif int fd; /*< The socket descriptor */ struct dcb* owner_dcb; /*< The DCB of the socket we are running on */ - SPINLOCK protocol_lock; /*< Protocol lock */ mysql_server_cmd_t current_command; /*< Current command being executed */ server_command_t protocol_command; /*< session command list */ server_command_t* protocol_cmd_history; /*< session command history */ diff --git a/include/maxscale/session.h b/include/maxscale/session.h index b21b87350..363d0df6f 100644 --- a/include/maxscale/session.h +++ b/include/maxscale/session.h @@ -126,7 +126,6 @@ typedef struct mxs_upstream typedef struct session { skygw_chk_t ses_chk_top; - SPINLOCK ses_lock; mxs_session_state_t state; /*< Current descriptor state */ size_t ses_id; /*< Unique session identifier */ int enabled_log_priorities; /*< Bitfield of enabled syslog priorities */ diff --git a/server/core/dcb.c b/server/core/dcb.c index bf01c7a2c..de32eb2c7 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -429,13 +429,13 @@ dcb_free_all_memory(DCB *dcb) gwbuf_free(dcb->dcb_fakequeue); dcb->dcb_fakequeue = NULL; } - spinlock_acquire(&dcb->cb_lock); + while ((cb_dcb = dcb->callbacks) != NULL) { dcb->callbacks = cb_dcb->next; MXS_FREE(cb_dcb); } - spinlock_release(&dcb->cb_lock); + if (dcb->ssl) { SSL_free(dcb->ssl); @@ -757,10 +757,6 @@ dcb_connect(SERVER *server, MXS_SESSION *session, const char *protocol) */ dcb->server = server; - /** Copy status field to DCB */ - dcb->dcb_server_status = server->status; - dcb->dcb_port = server->port; - dcb->was_persistent = false; /** @@ -1721,13 +1717,13 @@ dcb_maybe_add_persistent(DCB *dcb) session_put_ref(local_session); } } - spinlock_acquire(&dcb->cb_lock); + while ((loopcallback = dcb->callbacks) != NULL) { dcb->callbacks = loopcallback->next; MXS_FREE(loopcallback); } - spinlock_release(&dcb->cb_lock); + dcb->nextpersistent = dcb->server->persistent[dcb->thread.id]; dcb->server->persistent[dcb->thread.id] = dcb; atomic_add(&dcb->server->stats.n_persistent, 1); @@ -2070,20 +2066,7 @@ dprintDCB(DCB *pdcb, DCB *dcb) { dcb_printf(pdcb, "\t\tDCB is a clone.\n"); } -#if SPINLOCK_PROFILE - dcb_printf(pdcb, "\tInitlock Statistics:\n"); - spinlock_stats(&dcb->dcb_initlock, spin_reporter, pdcb); - dcb_printf(pdcb, "\tWrite Queue Lock Statistics:\n"); - spinlock_stats(&dcb->writeqlock, spin_reporter, pdcb); - dcb_printf(pdcb, "\tDelay Queue Lock Statistics:\n"); - spinlock_stats(&dcb->delayqlock, spin_reporter, pdcb); - dcb_printf(pdcb, "\tPollin Lock Statistics:\n"); - spinlock_stats(&dcb->pollinlock, spin_reporter, pdcb); - dcb_printf(pdcb, "\tPollout Lock Statistics:\n"); - spinlock_stats(&dcb->polloutlock, spin_reporter, pdcb); - dcb_printf(pdcb, "\tCallback Lock Statistics:\n"); - spinlock_stats(&dcb->cb_lock, spin_reporter, pdcb); -#endif + if (dcb->persistentstart) { char buff[20]; @@ -2337,8 +2320,8 @@ dcb_add_callback(DCB *dcb, ptr->cb = callback; ptr->userdata = userdata; ptr->next = NULL; - spinlock_acquire(&dcb->cb_lock); cb = dcb->callbacks; + while (cb) { if (cb->reason == reason && cb->cb == callback && @@ -2346,7 +2329,6 @@ dcb_add_callback(DCB *dcb, { /* Callback is a duplicate, abandon it */ MXS_FREE(ptr); - spinlock_release(&dcb->cb_lock); return 0; } lastcb = cb; @@ -2360,7 +2342,7 @@ dcb_add_callback(DCB *dcb, { lastcb->next = ptr; } - spinlock_release(&dcb->cb_lock); + return 1; } @@ -2384,9 +2366,8 @@ dcb_remove_callback(DCB *dcb, { DCB_CALLBACK *cb, *pcb = NULL; int rval = 0; - - spinlock_acquire(&dcb->cb_lock); cb = dcb->callbacks; + if (cb == NULL) { rval = 0; @@ -2407,7 +2388,7 @@ dcb_remove_callback(DCB *dcb, { dcb->callbacks = cb->next; } - spinlock_release(&dcb->cb_lock); + MXS_FREE(cb); rval = 1; break; @@ -2416,10 +2397,7 @@ dcb_remove_callback(DCB *dcb, cb = cb->next; } } - if (!rval) - { - spinlock_release(&dcb->cb_lock); - } + return rval; } @@ -2433,22 +2411,19 @@ static void dcb_call_callback(DCB *dcb, DCB_REASON reason) { DCB_CALLBACK *cb, *nextcb; - - spinlock_acquire(&dcb->cb_lock); cb = dcb->callbacks; + while (cb) { if (cb->reason == reason) { nextcb = cb->next; - spinlock_release(&dcb->cb_lock); MXS_DEBUG("%lu [dcb_call_callback] %s", pthread_self(), STRDCBREASON(reason)); cb->cb(dcb, reason, cb->userdata); - spinlock_acquire(&dcb->cb_lock); cb = nextcb; } else @@ -2456,7 +2431,6 @@ dcb_call_callback(DCB *dcb, DCB_REASON reason) cb = cb->next; } } - spinlock_release(&dcb->cb_lock); } /** @@ -2488,13 +2462,11 @@ dcb_hangup_foreach(struct server* server) for (DCB *dcb = all_dcbs[i]; dcb; dcb = dcb->thread.next) { - spinlock_acquire(&dcb->dcb_initlock); if (dcb->state == DCB_STATE_POLLING && dcb->server && dcb->server == server) { poll_fake_hangup_event(dcb); } - spinlock_release(&dcb->dcb_initlock); } spinlock_release(&all_dcbs_lock[i]); diff --git a/server/core/maxscale/session.h b/server/core/maxscale/session.h index 5508ae97c..423579fd9 100644 --- a/server/core/maxscale/session.h +++ b/server/core/maxscale/session.h @@ -25,7 +25,7 @@ MXS_BEGIN_DECLS #define MXS_UPSTREAM_INIT {0} #define SESSION_FILTER_INIT {0} -#define SESSION_INIT {.ses_chk_top = CHK_NUM_SESSION, .ses_lock = SPINLOCK_INIT, \ +#define SESSION_INIT {.ses_chk_top = CHK_NUM_SESSION, \ .stats = SESSION_STATS_INIT, .head = MXS_DOWNSTREAM_INIT, .tail = MXS_UPSTREAM_INIT, \ .state = SESSION_STATE_ALLOC, .ses_chk_tail = CHK_NUM_SESSION} diff --git a/server/core/poll.c b/server/core/poll.c index 1088b2993..f6884d55f 100644 --- a/server/core/poll.c +++ b/server/core/poll.c @@ -337,7 +337,6 @@ int poll_add_dcb(DCB *dcb) /*< * Choose new state according to the role of dcb. */ - spinlock_acquire(&dcb->dcb_initlock); if (dcb->dcb_role == DCB_ROLE_CLIENT_HANDLER || dcb->dcb_role == DCB_ROLE_BACKEND_HANDLER) { new_state = DCB_STATE_POLLING; @@ -388,7 +387,6 @@ int poll_add_dcb(DCB *dcb) } dcb->thread.id = owner; - spinlock_release(&dcb->dcb_initlock); dcb_add_to_list(dcb); @@ -396,7 +394,6 @@ int poll_add_dcb(DCB *dcb) if (dcb->dcb_role == DCB_ROLE_SERVICE_LISTENER) { - spinlock_acquire(&dcb->dcb_initlock); /** Listeners are added to all epoll instances */ int nthr = config_threadcount(); @@ -413,7 +410,6 @@ int poll_add_dcb(DCB *dcb) break; } } - spinlock_release(&dcb->dcb_initlock); } else { @@ -448,12 +444,10 @@ int poll_remove_dcb(DCB *dcb) struct epoll_event ev; CHK_DCB(dcb); - spinlock_acquire(&dcb->dcb_initlock); /*< It is possible that dcb has already been removed from the set */ if (dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ZOMBIE) { - spinlock_release(&dcb->dcb_initlock); return 0; } if (DCB_STATE_POLLING != dcb->state @@ -478,7 +472,6 @@ int poll_remove_dcb(DCB *dcb) * DCB_STATE_NOPOLLING. */ dcbfd = dcb->fd; - spinlock_release(&dcb->dcb_initlock); if (dcbfd > 0) { @@ -486,7 +479,6 @@ int poll_remove_dcb(DCB *dcb) if (dcb->dcb_role == DCB_ROLE_SERVICE_LISTENER) { - spinlock_acquire(&dcb->dcb_initlock); /** Listeners are added to all epoll instances */ int nthr = config_threadcount(); @@ -502,7 +494,6 @@ int poll_remove_dcb(DCB *dcb) ss_dassert(error_num); } } - spinlock_release(&dcb->dcb_initlock); } else { @@ -925,15 +916,12 @@ process_pollq(int thread_id, struct epoll_event *event) thread_data[thread_id].event = ev; } - ss_debug(spinlock_acquire(&dcb->dcb_initlock)); - ss_dassert(dcb->state != DCB_STATE_ALLOC); /* It isn't obvious that this is impossible */ /* ss_dassert(dcb->state != DCB_STATE_DISCONNECTED); */ if (DCB_STATE_DISCONNECTED == dcb->state) { return 0; } - ss_debug(spinlock_release(&dcb->dcb_initlock)); MXS_DEBUG("%lu [poll_waitevents] event %d dcb %p " "role %s", @@ -1059,11 +1047,10 @@ process_pollq(int thread_id, struct epoll_event *event) eno, strerror_r(eno, errbuf, sizeof(errbuf))); ts_stats_increment(pollStats.n_hup, thread_id); - spinlock_acquire(&dcb->dcb_initlock); if ((dcb->flags & DCBF_HUNG) == 0) { dcb->flags |= DCBF_HUNG; - spinlock_release(&dcb->dcb_initlock); + /** Read session id to thread's local storage */ dcb_get_ses_log_info(dcb, &mxs_log_tls.li_sesid, @@ -1074,10 +1061,6 @@ process_pollq(int thread_id, struct epoll_event *event) dcb->func.hangup(dcb); } } - else - { - spinlock_release(&dcb->dcb_initlock); - } } #ifdef EPOLLRDHUP @@ -1094,11 +1077,11 @@ process_pollq(int thread_id, struct epoll_event *event) eno, strerror_r(eno, errbuf, sizeof(errbuf))); ts_stats_increment(pollStats.n_hup, thread_id); - spinlock_acquire(&dcb->dcb_initlock); + if ((dcb->flags & DCBF_HUNG) == 0) { dcb->flags |= DCBF_HUNG; - spinlock_release(&dcb->dcb_initlock); + /** Read session id to thread's local storage */ dcb_get_ses_log_info(dcb, &mxs_log_tls.li_sesid, @@ -1109,10 +1092,6 @@ process_pollq(int thread_id, struct epoll_event *event) dcb->func.hangup(dcb); } } - else - { - spinlock_release(&dcb->dcb_initlock); - } } #endif diff --git a/server/core/session.c b/server/core/session.c index 4db17b9bd..06bcd37b2 100644 --- a/server/core/session.c +++ b/server/core/session.c @@ -230,7 +230,6 @@ session_set_dummy(DCB *client_dcb) session->ses_chk_top = CHK_NUM_SESSION; session->ses_chk_tail = CHK_NUM_SESSION; session->ses_is_child = false; - spinlock_init(&session->ses_lock); session->service = NULL; session->client_dcb = NULL; session->n_filters = 0; @@ -285,20 +284,17 @@ void session_disable_log_priority(MXS_SESSION* session, int priority) bool session_link_dcb(MXS_SESSION *session, DCB *dcb) { - spinlock_acquire(&session->ses_lock); ss_info_dassert(session->state != SESSION_STATE_FREE, "If session->state is SESSION_STATE_FREE then this attempt to " "access freed memory block."); if (session->state == SESSION_STATE_FREE) { - spinlock_release(&session->ses_lock); return false; } atomic_add(&session->refcount, 1); dcb->session = session; /** Move this DCB under the same thread */ dcb->thread.id = session->client_dcb->thread.id; - spinlock_release(&session->ses_lock); return true; } diff --git a/server/modules/filter/tee/tee.c b/server/modules/filter/tee/tee.c index c1768042d..3bcb6cc25 100644 --- a/server/modules/filter/tee/tee.c +++ b/server/modules/filter/tee/tee.c @@ -536,7 +536,6 @@ closeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session) if ((bsession = my_session->branch_session) != NULL) { CHK_SESSION(bsession); - spinlock_acquire(&bsession->ses_lock); if (bsession->state != SESSION_STATE_STOPPING) { @@ -545,7 +544,6 @@ closeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session) router = bsession->service->router; router_instance = bsession->service->router_instance; rsession = bsession->router_session; - spinlock_release(&bsession->ses_lock); /** Close router session and all its connections */ router->closeSession(router_instance, rsession); diff --git a/server/modules/protocol/MySQL/MySQLBackend/mysql_backend.c b/server/modules/protocol/MySQL/MySQLBackend/mysql_backend.c index be6cb5a0a..17fe201fe 100644 --- a/server/modules/protocol/MySQL/MySQLBackend/mysql_backend.c +++ b/server/modules/protocol/MySQL/MySQLBackend/mysql_backend.c @@ -638,9 +638,7 @@ gw_reply_on_error(DCB *dcb, mxs_auth_state_t state) session->router_session, errbuf, dcb, ERRACT_REPLY_CLIENT, &succp); - spinlock_acquire(&session->ses_lock); session->state = SESSION_STATE_STOPPING; - spinlock_release(&session->ses_lock); ss_dassert(dcb->dcb_errhandle_called); } else @@ -737,9 +735,7 @@ gw_read_and_write(DCB *dcb) if (!succp) { - spinlock_acquire(&session->ses_lock); session->state = SESSION_STATE_STOPPING; - spinlock_release(&session->ses_lock); } return 0; } @@ -1174,22 +1170,7 @@ static int gw_error_backend_event(DCB *dcb) 0, "Lost connection to backend server."); - spinlock_acquire(&session->ses_lock); ses_state = session->state; - spinlock_release(&session->ses_lock); - - /** - * Session might be initialized when DCB already is in the poll set. - * Thus hangup can occur in the middle of session initialization. - * Only complete and successfully initialized sessions allow for - * calling error handler. - */ - while (ses_state == SESSION_STATE_READY) - { - spinlock_acquire(&session->ses_lock); - ses_state = session->state; - spinlock_release(&session->ses_lock); - } if (ses_state != SESSION_STATE_ROUTER_READY) { @@ -1226,9 +1207,7 @@ static int gw_error_backend_event(DCB *dcb) */ if (!succp) { - spinlock_acquire(&session->ses_lock); session->state = SESSION_STATE_STOPPING; - spinlock_release(&session->ses_lock); } retblock: @@ -1278,22 +1257,7 @@ static int gw_backend_hangup(DCB *dcb) 0, "Lost connection to backend server."); - spinlock_acquire(&session->ses_lock); ses_state = session->state; - spinlock_release(&session->ses_lock); - - /** - * Session might be initialized when DCB already is in the poll set. - * Thus hangup can occur in the middle of session initialization. - * Only complete and successfully initialized sessions allow for - * calling error handler. - */ - while (ses_state == SESSION_STATE_READY) - { - spinlock_acquire(&session->ses_lock); - ses_state = session->state; - spinlock_release(&session->ses_lock); - } if (ses_state != SESSION_STATE_ROUTER_READY) { @@ -1331,9 +1295,7 @@ static int gw_backend_hangup(DCB *dcb) /** There are no required backends available, close session. */ if (!succp) { - spinlock_acquire(&session->ses_lock); session->state = SESSION_STATE_STOPPING; - spinlock_release(&session->ses_lock); } retblock: @@ -1372,7 +1334,7 @@ static int gw_backend_close(DCB *dcb) * but client's close and adding client's DCB to zombies list is executed * only if client's DCB's state does _not_ change in parallel. */ - spinlock_acquire(&session->ses_lock); + /** * If session->state is STOPPING, start closing client session. * Otherwise only this backend connection is closed. @@ -1382,19 +1344,9 @@ static int gw_backend_close(DCB *dcb) { if (session->client_dcb->state == DCB_STATE_POLLING) { - spinlock_release(&session->ses_lock); - /** Close client DCB */ dcb_close(session->client_dcb); } - else - { - spinlock_release(&session->ses_lock); - } - } - else - { - spinlock_release(&session->ses_lock); } } return 1; @@ -1472,9 +1424,7 @@ static int backend_write_delayqueue(DCB *dcb, GWBUF *buffer) if (!succp) { - spinlock_acquire(&session->ses_lock); session->state = SESSION_STATE_STOPPING; - spinlock_release(&session->ses_lock); } } @@ -1565,8 +1515,6 @@ static int gw_change_user(DCB *backend, memcpy(&backend_protocol->charset, client_auth_packet, sizeof(int)); } - spinlock_acquire(&in_session->ses_lock); - /* save current_database name */ strcpy(current_database, current_session->db); @@ -1586,7 +1534,6 @@ static int gw_change_user(DCB *backend, sizeof(client_protocol->scramble), username, client_sha1); strcpy(current_session->db, current_database); - spinlock_release(&in_session->ses_lock); if (auth_ret != 0) { @@ -1594,7 +1541,6 @@ static int gw_change_user(DCB *backend, { /* Try authentication again with new repository data */ /* Note: if no auth client authentication will fail */ - spinlock_acquire(&in_session->ses_lock); *current_session->db = 0; auth_ret = gw_check_mysql_scramble_data( backend->session->client_dcb, @@ -1603,7 +1549,6 @@ static int gw_change_user(DCB *backend, sizeof(client_protocol->scramble), username, client_sha1); strcpy(current_session->db, current_database); - spinlock_release(&in_session->ses_lock); } } diff --git a/server/modules/protocol/MySQL/MySQLClient/mysql_client.c b/server/modules/protocol/MySQL/MySQLClient/mysql_client.c index 210c52938..37a8c6715 100644 --- a/server/modules/protocol/MySQL/MySQLClient/mysql_client.c +++ b/server/modules/protocol/MySQL/MySQLClient/mysql_client.c @@ -1362,7 +1362,6 @@ gw_client_close(DCB *dcb) if (session != NULL && SESSION_STATE_DUMMY != session->state) { CHK_SESSION(session); - spinlock_acquire(&session->ses_lock); if (session->state != SESSION_STATE_STOPPING) { @@ -1376,14 +1375,9 @@ gw_client_close(DCB *dcb) */ if (session->router_session != NULL) { - spinlock_release(&session->ses_lock); /** Close router session and all its connections */ router->closeSession(router_instance, session->router_session); } - else - { - spinlock_release(&session->ses_lock); - } } return 1; } diff --git a/server/modules/protocol/MySQL/mysql_common.c b/server/modules/protocol/MySQL/mysql_common.c index ed008ca5f..754a8d115 100644 --- a/server/modules/protocol/MySQL/mysql_common.c +++ b/server/modules/protocol/MySQL/mysql_common.c @@ -132,27 +132,21 @@ void mysql_protocol_done(DCB* dcb) p = (MySQLProtocol *)dcb->protocol; - spinlock_acquire(&p->protocol_lock); - - if (p->protocol_state != MYSQL_PROTOCOL_ACTIVE) + if (p->protocol_state == MYSQL_PROTOCOL_ACTIVE) { - goto retblock; + scmd = p->protocol_cmd_history; + + while (scmd != NULL) + { + scmd2 = scmd->scom_next; + MXS_FREE(scmd); + scmd = scmd2; + } + + gwbuf_free(p->stored_query); + + p->protocol_state = MYSQL_PROTOCOL_DONE; } - scmd = p->protocol_cmd_history; - - while (scmd != NULL) - { - scmd2 = scmd->scom_next; - MXS_FREE(scmd); - scmd = scmd2; - } - - gwbuf_free(p->stored_query); - - p->protocol_state = MYSQL_PROTOCOL_DONE; - -retblock: - spinlock_release(&p->protocol_lock); } /** @@ -660,8 +654,6 @@ void protocol_archive_srv_command(MySQLProtocol* p) CHK_PROTOCOL(p); - spinlock_acquire(&p->protocol_lock); - if (p->protocol_state != MYSQL_PROTOCOL_ACTIVE) { goto retblock; @@ -710,7 +702,6 @@ void protocol_archive_srv_command(MySQLProtocol* p) } retblock: - spinlock_release(&p->protocol_lock); CHK_PROTOCOL(p); } @@ -725,11 +716,10 @@ void protocol_add_srv_command(MySQLProtocol* p, #if defined(EXTRA_SS_DEBUG) server_command_t* c; #endif - spinlock_acquire(&p->protocol_lock); if (p->protocol_state != MYSQL_PROTOCOL_ACTIVE) { - goto retblock; + return; } /** this is the only server command in protocol */ if (p->protocol_command.scom_cmd == MYSQL_COM_UNDEFINED) @@ -758,8 +748,6 @@ void protocol_add_srv_command(MySQLProtocol* p, c = c->scom_next; } #endif -retblock: - spinlock_release(&p->protocol_lock); } @@ -772,7 +760,7 @@ retblock: void protocol_remove_srv_command(MySQLProtocol* p) { server_command_t* s; - spinlock_acquire(&p->protocol_lock); + s = &p->protocol_command; #if defined(EXTRA_SS_DEBUG) MXS_INFO("Removed command %s from fd %d.", @@ -788,8 +776,6 @@ void protocol_remove_srv_command(MySQLProtocol* p) p->protocol_command = *(s->scom_next); MXS_FREE(s->scom_next); } - - spinlock_release(&p->protocol_lock); } mysql_server_cmd_t protocol_get_srv_command(MySQLProtocol* p, @@ -889,10 +875,8 @@ bool protocol_get_response_status(MySQLProtocol* p, CHK_PROTOCOL(p); - spinlock_acquire(&p->protocol_lock); *npackets = p->protocol_command.scom_nresponse_packets; *nbytes = (ssize_t)p->protocol_command.scom_nbytes_to_read; - spinlock_release(&p->protocol_lock); if (*npackets < 0 && *nbytes == 0) { @@ -912,14 +896,10 @@ void protocol_set_response_status(MySQLProtocol* p, { CHK_PROTOCOL(p); - spinlock_acquire(&p->protocol_lock); - p->protocol_command.scom_nbytes_to_read = nbytes; ss_dassert(p->protocol_command.scom_nbytes_to_read >= 0); p->protocol_command.scom_nresponse_packets = npackets_left; - - spinlock_release(&p->protocol_lock); } char* create_auth_failed_msg(GWBUF*readbuf, diff --git a/server/modules/routing/maxinfo/maxinfo.c b/server/modules/routing/maxinfo/maxinfo.c index 7a56d46c9..c4c18a3f6 100644 --- a/server/modules/routing/maxinfo/maxinfo.c +++ b/server/modules/routing/maxinfo/maxinfo.c @@ -291,20 +291,15 @@ static void handleError(MXS_ROUTER *instance, { backend_dcb->dcb_errhandle_called = true; } - spinlock_acquire(&session->ses_lock); + sesstate = session->state; client_dcb = session->client_dcb; if (sesstate == SESSION_STATE_ROUTER_READY) { CHK_DCB(client_dcb); - spinlock_release(&session->ses_lock); client_dcb->func.write(client_dcb, gwbuf_clone(errbuf)); } - else - { - spinlock_release(&session->ses_lock); - } /** false because connection is not available anymore */ dcb_close(backend_dcb); diff --git a/server/modules/routing/readconnroute/readconnroute.c b/server/modules/routing/readconnroute/readconnroute.c index c2c4e46ff..77ca1e23f 100644 --- a/server/modules/routing/readconnroute/readconnroute.c +++ b/server/modules/routing/readconnroute/readconnroute.c @@ -82,7 +82,6 @@ #include #include #include -#include #include #include #include @@ -105,9 +104,6 @@ static void rses_end_locked_router_action(ROUTER_CLIENT_SES* rses); static SERVER_REF *get_root_master(SERVER_REF *servers); static int handle_state_switch(DCB* dcb, DCB_REASON reason, void * routersession); -static SPINLOCK instlock; -static ROUTER_INSTANCE *instances; - /** * The module entry point routine. It is this routine that * must populate the structure that is referred to as the @@ -119,8 +115,6 @@ static ROUTER_INSTANCE *instances; MXS_MODULE* MXS_CREATE_MODULE() { MXS_NOTICE("Initialise readconnroute router module."); - spinlock_init(&instlock); - instances = NULL; static MXS_ROUTER_OBJECT MyObject = { @@ -252,10 +246,6 @@ createInstance(SERVICE *service, char **options) * insert this router instance into the linked list of routers * that have been created with this module. */ - spinlock_acquire(&instlock); - inst->next = instances; - instances = inst; - spinlock_release(&instlock); return (MXS_ROUTER *) inst; } @@ -710,20 +700,16 @@ static void handleError(MXS_ROUTER *instance, void *router_session, GWBUF *errbu { problem_dcb->dcb_errhandle_called = true; } - spinlock_acquire(&session->ses_lock); + sesstate = session->state; client_dcb = session->client_dcb; if (sesstate == SESSION_STATE_ROUTER_READY) { CHK_DCB(client_dcb); - spinlock_release(&session->ses_lock); + client_dcb->func.write(client_dcb, gwbuf_clone(errbuf)); } - else - { - spinlock_release(&session->ses_lock); - } if (DCB_ROLE_CLIENT_HANDLER == problem_dcb->dcb_role) { diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 71a979717..5f700c5e8 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1535,10 +1535,8 @@ static void handle_error_reply_client(MXS_SESSION *ses, ROUTER_CLIENT_SES *rses, DCB *client_dcb; backend_ref_t *bref; - spinlock_acquire(&ses->ses_lock); sesstate = ses->state; client_dcb = ses->client_dcb; - spinlock_release(&ses->ses_lock); if ((bref = get_bref_from_dcb(rses, backend_dcb)) != NULL) { diff --git a/server/modules/routing/schemarouter/schemarouter.c b/server/modules/routing/schemarouter/schemarouter.c index 6f26a9497..809251d69 100644 --- a/server/modules/routing/schemarouter/schemarouter.c +++ b/server/modules/routing/schemarouter/schemarouter.c @@ -860,8 +860,6 @@ static void* newSession(MXS_ROUTER* router_inst, MXS_SESSION* session) bool using_db = false; bool have_db = false; - spinlock_acquire(&session->ses_lock); - /* To enable connecting directly to a sharded database we first need * to disable it for the client DCB's protocol so that we can connect to them*/ if (protocol->client_capabilities & GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB && @@ -880,8 +878,6 @@ static void* newSession(MXS_ROUTER* router_inst, MXS_SESSION* session) MXS_INFO("Client'%s' connecting with empty database.", data->user); } - spinlock_release(&session->ses_lock); - client_rses = (ROUTER_CLIENT_SES *)MXS_CALLOC(1, sizeof(ROUTER_CLIENT_SES)); if (client_rses == NULL) @@ -3647,10 +3643,8 @@ static void handle_error_reply_client(MXS_SESSION* ses, DCB* client_dcb; backend_ref_t* bref; - spinlock_acquire(&ses->ses_lock); sesstate = ses->state; client_dcb = ses->client_dcb; - spinlock_release(&ses->ses_lock); /** * If bref exists, mark it closed From ac0679a9d5e8421e65a2bfdd5d97768fcfa4a5fb Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 8 Feb 2017 13:02:45 +0200 Subject: [PATCH 03/10] Update filter documentation - Add note about filters having become available in 2.1. - Add insertstream to release notes and change log. - Add complete example to cache documentation. --- Documentation/Changelog.md | 2 +- Documentation/Filters/CCRFilter.md | 2 + Documentation/Filters/Cache.md | 48 +++++++++++++++++++ Documentation/Filters/Insert-Stream-Filter.md | 3 ++ Documentation/Filters/Masking.md | 2 + Documentation/Filters/Maxrows.md | 2 + .../MaxScale-2.1.0-Release-Notes.md | 11 +++++ 7 files changed, 69 insertions(+), 1 deletion(-) diff --git a/Documentation/Changelog.md b/Documentation/Changelog.md index 3ea72cc1a..489611c9e 100644 --- a/Documentation/Changelog.md +++ b/Documentation/Changelog.md @@ -12,7 +12,7 @@ * MySQL Monitor now has a failover mode. * Named Server Filter now supports wildcards for source option. * Binlog Server can now be configured to encrypt binlog files. -* New filters, _cache, _ccrfilter_, _masking_, and _maxrows_ are introduced. +* New filters, _cache_, _ccrfilter_, _insertstream_, _masking_, and _maxrows_ are introduced. For more details, please refer to: * [MariaDB MaxScale 2.1.0 Release Notes](Release-Notes/MaxScale-2.1.0-Release-Notes.md) diff --git a/Documentation/Filters/CCRFilter.md b/Documentation/Filters/CCRFilter.md index f2ea452d8..74499a2bc 100644 --- a/Documentation/Filters/CCRFilter.md +++ b/Documentation/Filters/CCRFilter.md @@ -1,5 +1,7 @@ # Consistent Critical Read Filter +This filter was introduced in MariaDB MaxScale 2.1. + ## Overview The Consistent Critical Read (CCR) filter allows consistent critical reads to be diff --git a/Documentation/Filters/Cache.md b/Documentation/Filters/Cache.md index 6c5c27576..02ecd71b8 100644 --- a/Documentation/Filters/Cache.md +++ b/Documentation/Filters/Cache.md @@ -1,5 +1,7 @@ # Cache +This filter was introduced in MariaDB MaxScale 2.1. + ## Overview The cache filter is a simple cache that is capable of caching the result of SELECTs, so that subsequent identical SELECTs are served directly by MaxScale, @@ -8,6 +10,11 @@ without the queries being routed to any server. _Note that the cache is still experimental and that non-backward compatible changes may be made._ +Note that installing the cache causes all statements to be parsed. The +implication of that is that unless statements _already_ need to be parsed, +e.g. due to the presence of another filter or the chosen router, then adding +the cache will not necessarily improve the performance, but may decrease it. + ## Limitations All of these limitations may be addressed in forthcoming releases. @@ -639,3 +646,44 @@ The value is a boolean and the default is `false`. ``` storage_options=collect_statistics=true ``` + +# Example + +In the following we define a cache _MyCache_ that uses the cache storage module +`storage_inmemory` and whose _soft ttl_ is `30` seconds and whose _hard ttl_ is +`45` seconds. The cached data is shared between all threads and the maximum size +of the cached data is `50` mebibytes. The rules for the cache are in the file +`cache_rules.json`. + +### Configuration +``` +[MyCache] +type=filter +module=cache +storage=storage_inmemory +soft_ttl=30 +hard_ttl=45 +cached_data=shared +max_size=50Mi +rules=cache_rules.json + +[MyService] +type=service +... +filters=MyCache +``` + +### `cache_rules.json` +The rules specify that the data of the table `sbtest` should be cached. + +``` +{ + "store": [ + { + "attribute": "table", + "op": "=", + "value": "sbtest" + } + ] +} +``` diff --git a/Documentation/Filters/Insert-Stream-Filter.md b/Documentation/Filters/Insert-Stream-Filter.md index f17a9d829..2f508cbbf 100644 --- a/Documentation/Filters/Insert-Stream-Filter.md +++ b/Documentation/Filters/Insert-Stream-Filter.md @@ -1,5 +1,8 @@ # Insert Stream Filter +This filter was introduced in MariaDB MaxScale 2.1. + +## Overview The _insertstream_ filter converts bulk inserts into CSV data streams that are consumed by the backend server via the LOAD DATA LOCAL INFILE mechanism. This leverages the speed advantage of LOAD DATA LOCAL INFILE over regular inserts diff --git a/Documentation/Filters/Masking.md b/Documentation/Filters/Masking.md index 9cadf6085..5d8f1a4cc 100644 --- a/Documentation/Filters/Masking.md +++ b/Documentation/Filters/Masking.md @@ -1,5 +1,7 @@ # Masking +This filter was introduced in MariaDB MaxScale 2.1. + ## Overview With the _masking_ filter it is possible to obfuscate the returned value of a particular column. diff --git a/Documentation/Filters/Maxrows.md b/Documentation/Filters/Maxrows.md index 47edb688f..f36269564 100644 --- a/Documentation/Filters/Maxrows.md +++ b/Documentation/Filters/Maxrows.md @@ -1,5 +1,7 @@ # Maxrows +This filter was introduced in MariaDB MaxScale 2.1. + ## Overview The maxrows filter is capable of restricting the amount of rows that a SELECT, a prepared statement or stored procedure could return to the client application. diff --git a/Documentation/Release-Notes/MaxScale-2.1.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-2.1.0-Release-Notes.md index d45dee83d..5afd369bf 100644 --- a/Documentation/Release-Notes/MaxScale-2.1.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-2.1.0-Release-Notes.md @@ -271,6 +271,17 @@ to large sets of data with a single query. For more information, refer to the [Maxrows](../Filters/Maxrows.md) documentation. +### Insert stream filter + +The _insertstream_ filter converts bulk inserts into CSV data streams that are +consumed by the backend server via the LOAD DATA LOCAL INFILE mechanism. This +leverages the speed advantage of LOAD DATA LOCAL INFILE over regular inserts +while also reducing the overall network traffic by condensing the inserted +values into CSV. + +For more information, refer to the [Insert Stream Filter](../Filters/Insert-Stream-Filter.md) +documentation. + ### Galeramon Monitor new option The `set_donor_nodes` option allows the setting of _global variable_ _wsrep_sst_donor_ with a list the preferred donor nodes (among slave ones). From 0e037e4005354c91dbe86de704d2178b1596ade5 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 8 Feb 2017 13:49:26 +0200 Subject: [PATCH 04/10] Add full masking example --- Documentation/Filters/Masking.md | 44 ++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/Documentation/Filters/Masking.md b/Documentation/Filters/Masking.md index 5d8f1a4cc..75daea123 100644 --- a/Documentation/Filters/Masking.md +++ b/Documentation/Filters/Masking.md @@ -304,3 +304,47 @@ MaxScale> call command masking reload MyMaskingFilter ``` `MyMaskingFilter` refers to a particular filter section in the MariaDB MaxScale configuration file. + +# Example + +In the following we configure a masking filter _MyMasking_ that should always log a +warning if a masking rule matches a column that is of a type that cannot be masked, +and that should abort the client connection if a resultset package is larger than +16MB. The rules for the masking filter are in the file `masking_rules.json`. + +### Configuration +``` +[MyMasking] +type=filter +module=masking +warn_type_mismatch=always +large_payload=abort +rules=masking_rules.json + +[MyService] +type=service +... +filters=MyMasking +``` + +### `masking_rules.json` + +The rules specify that the data of a column whose name is `ssn`, should +be replaced with the string _012345-ABCD_. If the length of the data is +not exactly the same as the length of the replacement value, then the +data should be replaced with as many _X_ characters as needed. +``` +{ + "rules": [ + { + "replace": { + "column": "ssn" + }, + "with": { + "value": "012345-ABCD", + "fill": "X" + } + } + ] +} +``` From b46bc47c879766f20f45c8216ab86b2444c61c58 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Tue, 7 Feb 2017 15:14:17 +0200 Subject: [PATCH 05/10] Add NullFilter filter module NullFilter is a filter module that does nothing, except reports capabilities as defined in the configuration file. It's purpose is only to make it simple to benchmark the performance impact various routing capabilities have. Note that since getCapabilities() currently does *not* take an instance pointer as parameter, all NullFilter instances will report the same capabilities, the ones specified for the last filter to have been loaded. --- server/modules/filter/CMakeLists.txt | 1 + .../modules/filter/nullfilter/CMakeLists.txt | 7 + .../modules/filter/nullfilter/nullfilter.cc | 133 ++++++++++++++++++ .../modules/filter/nullfilter/nullfilter.hh | 36 +++++ .../filter/nullfilter/nullfiltersession.cc | 31 ++++ .../filter/nullfilter/nullfiltersession.hh | 35 +++++ 6 files changed, 243 insertions(+) create mode 100644 server/modules/filter/nullfilter/CMakeLists.txt create mode 100644 server/modules/filter/nullfilter/nullfilter.cc create mode 100644 server/modules/filter/nullfilter/nullfilter.hh create mode 100644 server/modules/filter/nullfilter/nullfiltersession.cc create mode 100644 server/modules/filter/nullfilter/nullfiltersession.hh diff --git a/server/modules/filter/CMakeLists.txt b/server/modules/filter/CMakeLists.txt index f2ffcb730..7e046f9a2 100644 --- a/server/modules/filter/CMakeLists.txt +++ b/server/modules/filter/CMakeLists.txt @@ -6,6 +6,7 @@ add_subdirectory(hintfilter) add_subdirectory(luafilter) add_subdirectory(mqfilter) add_subdirectory(namedserverfilter) +add_subdirectory(nullfilter) add_subdirectory(qlafilter) add_subdirectory(regexfilter) add_subdirectory(tee) diff --git a/server/modules/filter/nullfilter/CMakeLists.txt b/server/modules/filter/nullfilter/CMakeLists.txt new file mode 100644 index 000000000..f72e4888b --- /dev/null +++ b/server/modules/filter/nullfilter/CMakeLists.txt @@ -0,0 +1,7 @@ +add_library(nullfilter SHARED + nullfilter.cc + nullfiltersession.cc + ) +target_link_libraries(nullfilter maxscale-common) +set_target_properties(nullfilter PROPERTIES VERSION "1.0.0") +install_module(nullfilter core) diff --git a/server/modules/filter/nullfilter/nullfilter.cc b/server/modules/filter/nullfilter/nullfilter.cc new file mode 100644 index 000000000..07e2b662f --- /dev/null +++ b/server/modules/filter/nullfilter/nullfilter.cc @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#define MXS_MODULE_NAME "nullfilter" +#include "nullfilter.hh" +#include +#include + +using std::string; + +namespace +{ + +#define VERSION_STRING "V1.0.0" + +const char CAPABILITIES_PARAM[] = "capabilities"; + +MXS_ENUM_VALUE capability_values[] = +{ + { "RCAP_TYPE_STMT_INPUT", RCAP_TYPE_STMT_INPUT }, + { "RCAP_TYPE_CONTIGUOUS_INPUT", RCAP_TYPE_CONTIGUOUS_INPUT }, + { "RCAP_TYPE_TRANSACTION_TRACKING", RCAP_TYPE_TRANSACTION_TRACKING }, + { "RCAP_TYPE_STMT_OUTPUT", RCAP_TYPE_STMT_OUTPUT }, + { "RCAP_TYPE_CONTIGUOUS_OUTPUT", RCAP_TYPE_CONTIGUOUS_OUTPUT }, + { "RCAP_TYPE_RESULTSET_OUTPUT", RCAP_TYPE_RESULTSET_OUTPUT }, + { NULL, 0 } +}; + +struct unit_variables +{ + uint64_t capabilities; + bool capabilities_set; +} this_unit = +{ + 0, + false +}; + +} + +// +// Global symbols of the Module +// + +extern "C" MXS_MODULE* MXS_CREATE_MODULE() +{ + MXS_NOTICE("Nullfilter module %s initialized.", VERSION_STRING); + + static MXS_MODULE info = + { + MXS_MODULE_API_FILTER, + MXS_MODULE_IN_DEVELOPMENT, + MXS_FILTER_VERSION, + "A null filter that does nothing.", + VERSION_STRING, + &NullFilter::s_object, + NULL, /* Process init. */ + NULL, /* Process finish. */ + NULL, /* Thread init. */ + NULL, /* Thread finish. */ + { + { CAPABILITIES_PARAM, MXS_MODULE_PARAM_ENUM, NULL, MXS_MODULE_OPT_REQUIRED, capability_values }, + { MXS_END_MODULE_PARAMS } + } + }; + + return &info; +} + +// +// NullFilter +// + +NullFilter::NullFilter(const char* zName) +{ + MXS_NOTICE("Null filter [%s] created.", zName); +} + +NullFilter::~NullFilter() +{ +} + +// static +NullFilter* NullFilter::create(const char* zName, char**, MXS_CONFIG_PARAMETER* pParams) +{ + NullFilter* pFilter = NULL; + + uint64_t capabilities = config_get_enum(pParams, CAPABILITIES_PARAM, capability_values); + + if (this_unit.capabilities_set) + { + MXS_WARNING("The capabilities reported by NullFilter are currently global, " + "and not specific for a particular NullFilter instance."); + } + + this_unit.capabilities = capabilities; + this_unit.capabilities_set = true; + + return new NullFilter(zName); +} + + +NullFilterSession* NullFilter::newSession(MXS_SESSION* pSession) +{ + return NullFilterSession::create(pSession, this); +} + +// static +void NullFilter::diagnostics(DCB* pDcb) +{ + dcb_printf(pDcb, "Hello, World!\n"); +} + +// static +uint64_t NullFilter::getCapabilities() +{ + if (!this_unit.capabilities_set) + { + MXS_ERROR("getCapabilities() called before they have been set."); + } + + return this_unit.capabilities; +} diff --git a/server/modules/filter/nullfilter/nullfilter.hh b/server/modules/filter/nullfilter/nullfilter.hh new file mode 100644 index 000000000..459a1bcff --- /dev/null +++ b/server/modules/filter/nullfilter/nullfilter.hh @@ -0,0 +1,36 @@ +#pragma once +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include +#include +#include "nullfiltersession.hh" + +class NullFilter : public maxscale::Filter +{ +public: + ~NullFilter(); + static NullFilter* create(const char* zName, char** pzOptions, MXS_CONFIG_PARAMETER* pParams); + + NullFilterSession* newSession(MXS_SESSION* pSession); + + void diagnostics(DCB* pDcb); + + static uint64_t getCapabilities(); + +private: + NullFilter(const char* zName); + + NullFilter(const NullFilter&); + NullFilter& operator = (const NullFilter&); +}; diff --git a/server/modules/filter/nullfilter/nullfiltersession.cc b/server/modules/filter/nullfilter/nullfiltersession.cc new file mode 100644 index 000000000..5fbc48472 --- /dev/null +++ b/server/modules/filter/nullfilter/nullfiltersession.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#define MXS_MODULE_NAME "nullfilter" +#include "nullfiltersession.hh" + +NullFilterSession::NullFilterSession(MXS_SESSION* pSession, const NullFilter* pFilter) + : maxscale::FilterSession(pSession) + , m_filter(*pFilter) +{ +} + +NullFilterSession::~NullFilterSession() +{ +} + +//static +NullFilterSession* NullFilterSession::create(MXS_SESSION* pSession, const NullFilter* pFilter) +{ + return new NullFilterSession(pSession, pFilter); +} diff --git a/server/modules/filter/nullfilter/nullfiltersession.hh b/server/modules/filter/nullfilter/nullfiltersession.hh new file mode 100644 index 000000000..3102b8cb3 --- /dev/null +++ b/server/modules/filter/nullfilter/nullfiltersession.hh @@ -0,0 +1,35 @@ +#pragma once +/* + * Copyright (c) 2016 MariaDB Corporation Ab + * + * Use of this software is governed by the Business Source License included + * in the LICENSE.TXT file and at www.mariadb.com/bsl. + * + * Change Date: 2019-07-01 + * + * On the date above, in accordance with the Business Source License, use + * of this software will be governed by version 2 or later of the General + * Public License. + */ + +#include +#include + +class NullFilter; + +class NullFilterSession : public maxscale::FilterSession +{ +public: + ~NullFilterSession(); + + static NullFilterSession* create(MXS_SESSION* pSession, const NullFilter* pFilter); + +private: + NullFilterSession(MXS_SESSION* pSession, const NullFilter* pFilter); + + NullFilterSession(const NullFilterSession&); + NullFilterSession& operator = (const NullFilterSession&); + +private: + const NullFilter& m_filter; +}; From ec238a168d3ce4c43969cb00e8e043805918ca99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 8 Feb 2017 14:46:52 +0200 Subject: [PATCH 06/10] Add example to CCRFilter documentation The example has a filter configuration for a minimal setup. It also describes how to tune the filter for greater performance. --- Documentation/Filters/CCRFilter.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/Filters/CCRFilter.md b/Documentation/Filters/CCRFilter.md index 74499a2bc..6de13ce3b 100644 --- a/Documentation/Filters/CCRFilter.md +++ b/Documentation/Filters/CCRFilter.md @@ -70,3 +70,24 @@ An optional parameter that can be used to control which statements don't trigger the statement re-routing. This does the opposite of the _match_ parameter. The parameter value is a regular expression that is used to match against the SQL text. Only non-SELECT statements are inspected. + +## Example Configuration + +Here is a minimal filter configuration for the CCRFilter which should solve most +problems with critical reads after writes. + +``` +[CCRFilter] +type=filter +module=ccrfilter +time=5 +``` + +This configuration will force all read queries after a write to the master for 5 +seconds, preventing read scaling until the modifications have been replicated to +the slaves. + +For best performance, the value of _time_ should be slightly greater than the +actual replication lag between the master and its slaves. If the number of +critical read statements is known, the _count_ parameter could be used to +control the number reads that are sent to the master. From fbe7885ecb5be94dc22ed73a19509fdb0057e8af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 8 Feb 2017 15:20:29 +0200 Subject: [PATCH 07/10] Add changes to router_options to release notes Added a section to the release notes that tells the fact that the readwritesplit, schemarouter and binlogrouter now also accept the values of router_options as parameters. --- .../MaxScale-2.1.0-Release-Notes.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/Release-Notes/MaxScale-2.1.0-Release-Notes.md b/Documentation/Release-Notes/MaxScale-2.1.0-Release-Notes.md index 5afd369bf..bb88c7684 100644 --- a/Documentation/Release-Notes/MaxScale-2.1.0-Release-Notes.md +++ b/Documentation/Release-Notes/MaxScale-2.1.0-Release-Notes.md @@ -10,6 +10,38 @@ report at [Jira](https://jira.mariadb.org). ## Changed Features +### `router_options` to Parameters + +The `router_options` values can also be given as parameters to the service for +the _readwritesplit_, _schemarouter_ and _binlogrouter_ modules. + +What this means is that in MaxScale 2.1 the following _readwritesplit_ +configration. + +``` +[RW Split Router] +type=service +router=readwritesplit +servers=server1 +user=maxuser +passwd=maxpwd +router_options=slave_selection_criteria=LEAST_ROUTER_CONNECTIONS,max_sescmd_history=10,disable_sescmd_history=false +``` + +Can also be written in the following form. + +``` +[RW Split Router] +type=service +router=readwritesplit +servers=server1 +user=maxuser +passwd=maxpwd +slave_selection_criteria=LEAST_ROUTER_CONNECTIONS +max_sescmd_history=10 +disable_sescmd_history=false +``` + ### Configuration Files From 2.1.0 onwards MariaDB MaxScale supports hierarchical configuration From 8ef9781d6155208424e679d3bf9e6c0c8eefbae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Markus=20M=C3=A4kel=C3=A4?= Date: Wed, 8 Feb 2017 15:26:32 +0200 Subject: [PATCH 08/10] Add Upgrading to 2.1 document The document lists important changes that should be noted when MaxScale is upgraded from 2.0 to 2.1. --- .../Upgrading/Upgrading-To-MaxScale-2.1.md | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/Upgrading/Upgrading-To-MaxScale-2.1.md diff --git a/Documentation/Upgrading/Upgrading-To-MaxScale-2.1.md b/Documentation/Upgrading/Upgrading-To-MaxScale-2.1.md new file mode 100644 index 000000000..100c389cd --- /dev/null +++ b/Documentation/Upgrading/Upgrading-To-MaxScale-2.1.md @@ -0,0 +1,56 @@ +# Upgrading MariaDB MaxScale from 2.0 to 2.1 + +This document describes particular issues to take into account when upgrading +MariaDB MaxScale from version 2.0 to 2.1. + +For more information about MariaDB MaxScale 2.1, please refer to the +[ChangeLog](../Changelog.md). + +For a complete list of changes in MaxScale 2.1.0, refer to the +[MaxScale 2.1.0 Release Notes](../Release-Notes/MaxScale-2.1.0-Release-Notes.md). + +## Installation + +Before starting the upgrade, we **strongly** recommend you back up your current +configuration file. + +## MaxScale Log Files + +The name of the log file was changed from _maxscaleN.log_ to _maxscale.log_. The +default location for the log file is _/var/log/maxscale/maxscale.log_. + +Rotating the log files will cause MaxScale to reopen the file instead of +renaming them. This makes the MaxScale logging facility _logrotate_ compatible. + +## ReadWriteSplit + +The `disable_sescmd_history` option is now enabled by default. This means that +slaves will not be recovered mid-session even if a replacement slave is +available. To enable the legacy behavior, add the `disable_sescmd_history=true` +parameter to the service definition. + +## Persistent Connections + +The MySQL session state is reset in MaxScale 2.1 for persistent +connections. This means that any modifications to the session state (default +database, user variable etc.) will not survive if the connection is put into the +connection pool. For most users, this is the expected behavior. + +## User Data Cache + +The location of the MySQL user data cache was moved from +`/var/cache/maxscale/` to `/var/cache/maxscale//`. + +## Galeramon Monitoring Algorithm + +Galeramon will assign the master status *only* to the node which has a +_wsrep_local_index_ value of 0. This will guarantee consistent writes with +multiple MaxScales but it also causes slower changes of the master node. + +To enable the legacy behavior, add `root_node_as_master=false` to the Galera +monitor configuration. + +## MaxAdmin Editing Mode + +The default editing mode was changed from _vim_ to _emacs_ mode. To start +maxadmin in the legacy mode, use the `-i` option. From d76be99cc56ea74b89f25ab38c152bfa53f76a92 Mon Sep 17 00:00:00 2001 From: Johan Wikman Date: Wed, 8 Feb 2017 15:46:53 +0200 Subject: [PATCH 09/10] Improve error message A parameter may be wrong because the parameter name is wrong or the value of the parameter is wrong. That needs to be reflected in the error message. --- server/core/config.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/core/config.c b/server/core/config.c index 88cab4c17..99953c2d8 100644 --- a/server/core/config.c +++ b/server/core/config.c @@ -1931,8 +1931,9 @@ check_config_objects(CONFIG_CONTEXT *context) if (mod == NULL || !config_param_is_valid(mod->parameters, params->name, params->value, context)) { - MXS_ERROR("Unexpected parameter '%s' or parameter value for object '%s' of type '%s'.", - params->name, obj->object, type); + MXS_ERROR("Unexpected parameter '%s' for object '%s' of type '%s', " + "or '%s' is an invalid value for parameter '%s'.", + params->name, obj->object, type, params->value, params->name); rval = false; } else if (is_path_parameter(mod->parameters, params->name)) From fdcc9333c8fd0d4ab3f3faca5d6af375bc957ef3 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Wed, 8 Feb 2017 14:48:55 +0100 Subject: [PATCH 10/10] MaxRows configuration example MaxRows configuration example --- Documentation/Filters/Maxrows.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/Filters/Maxrows.md b/Documentation/Filters/Maxrows.md index f36269564..5941affc9 100644 --- a/Documentation/Filters/Maxrows.md +++ b/Documentation/Filters/Maxrows.md @@ -70,3 +70,16 @@ Default is `0`. To log everything, give `debug` a value of `3`. ``` debug=2 ``` + +## Example Configuration + +Here is an example of filter configuration where the max number of returned +rows is 10000 and max allowed resultset size is 256KB + +``` +[MaxRows] +type=filter +module=maxrows +max_resultset_rows=10000 +max_resultset_size=256000 +```