Merge branch '2.1' into develop

This commit is contained in:
Johan Wikman 2017-02-09 15:07:13 +02:00
commit 54d4a562ce
31 changed files with 517 additions and 229 deletions

View File

@ -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)

View File

@ -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
@ -68,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.

View File

@ -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"
}
]
}
```

View File

@ -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

View File

@ -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.
@ -302,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"
}
}
]
}
```

View File

@ -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.
@ -68,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
```

View File

@ -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
@ -271,6 +303,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).

View File

@ -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/<Service>` to `/var/cache/maxscale/<Service>/<Listener>`.
## 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.

View File

@ -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}}
/**

View File

@ -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 */

View File

@ -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 */

View File

@ -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))

View File

@ -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]);

View File

@ -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}

View File

@ -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

View File

@ -31,7 +31,6 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <maxscale/spinlock.h>
#include <maxscale/random_jkiss.h>
/* 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++)

View File

@ -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;
}

View File

@ -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)

View File

@ -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)

View File

@ -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 <string>
#include <maxscale/utils.h>
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;
}

View File

@ -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 <maxscale/cppdefs.hh>
#include <maxscale/filter.hh>
#include "nullfiltersession.hh"
class NullFilter : public maxscale::Filter<NullFilter, NullFilterSession>
{
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&);
};

View File

@ -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);
}

View File

@ -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 <maxscale/cppdefs.hh>
#include <maxscale/filter.hh>
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;
};

View File

@ -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);

View File

@ -637,9 +637,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
@ -736,9 +734,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;
}
@ -1173,22 +1169,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)
{
@ -1225,9 +1206,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:
@ -1277,22 +1256,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)
{
@ -1330,9 +1294,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:
@ -1371,7 +1333,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.
@ -1381,19 +1343,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;
@ -1471,9 +1423,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);
}
}
@ -1564,8 +1514,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);
@ -1595,7 +1543,6 @@ static int gw_change_user(DCB *backend,
client_sha1, sizeof(client_sha1));
strcpy(current_session->db, current_database);
spinlock_release(&in_session->ses_lock);
if (auth_ret != 0)
{
@ -1603,7 +1550,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 = dcb->authfunc.reauthenticate(dcb, username,
@ -1613,7 +1559,6 @@ static int gw_change_user(DCB *backend,
client_sha1, sizeof(client_sha1));
strcpy(current_session->db, current_database);
spinlock_release(&in_session->ses_lock);
}
}

View File

@ -1361,7 +1361,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)
{
@ -1375,14 +1374,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;
}

View File

@ -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,

View File

@ -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);

View File

@ -82,7 +82,6 @@
#include <maxscale/atomic.h>
#include <maxscale/spinlock.h>
#include <maxscale/dcb.h>
#include <maxscale/spinlock.h>
#include <maxscale/modinfo.h>
#include <maxscale/log_manager.h>
#include <maxscale/protocol/mysql.h>
@ -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)
{

View File

@ -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)
{

View File

@ -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