Merge branch '2.2' into develop

This commit is contained in:
Johan Wikman
2018-03-27 10:17:15 +03:00
7 changed files with 231 additions and 1 deletions

View File

@ -726,6 +726,35 @@ possible to explicitly cause the users of a service to be reloaded.
users_refresh_time=120
```
#### `retain_last_statements`
How many statements MaxScale should store for each session. This is for
debugging purposes, as in case of problems it is often of value to be able
to find out exactly what statements were sent before a particular
problem turned up. See also `dump_last_statements` using which the actual
dumping of the statements is enabled.
```
retain_last_statements=20
```
Default is `0`.
#### `dump_last_statements`
With this configuration item it is specified in what circumstances MaxScale
should dump the last statements that a client sent. The allowed values are
`never, `on_error` and `on_close`. With `never` the statements are never
logged, with `on_error` they are logged if the client closes the connection
improperly, and with `on_close` they are always logged when a client session
is closed.
```
dump_last_statements=on_error
```
Default is `never`.
Note that you need to specify with `retain_last_statements` how many statements
MaxScale should retain for each session. Unless it has been set to another value
than `0`, this configuration setting will not have an effect.
### Service
A service represents the database service that MariaDB MaxScale offers to the

View File

@ -21,6 +21,11 @@ is considered a match if the column name matches. Please consult the
## New Features
New configuration parameters `retain_last_statements` and
`dump_last_statements` that can be of help when debugging problems. Please
see the [configuration guide](../Getting-Started/Configuration-Guide.md)
for details.
## Bug fixes
## Known Issues and Limitations

View File

@ -99,6 +99,7 @@ extern const char CN_AUTH_READ_TIMEOUT[];
extern const char CN_AUTH_WRITE_TIMEOUT[];
extern const char CN_AUTO[];
extern const char CN_CONNECTION_TIMEOUT[];
extern const char CN_DUMP_LAST_STATEMENTS[];
extern const char CN_DATA[];
extern const char CN_DEFAULT[];
extern const char CN_DESCRIPTION[];
@ -142,6 +143,7 @@ extern const char CN_QUERY_RETRY_TIMEOUT[];
extern const char CN_RELATIONSHIPS[];
extern const char CN_LINKS[];
extern const char CN_REQUIRED[];
extern const char CN_RETAIN_LAST_STATEMENTS[];
extern const char CN_RETRY_ON_FAILURE[];
extern const char CN_ROUTER[];
extern const char CN_ROUTER_DIAGNOSTICS[];

View File

@ -30,6 +30,11 @@
#ifdef __cplusplus
#include <tr1/unordered_map>
#include <string>
#include <deque>
#include <vector>
typedef std::deque<std::vector<uint8_t> > SessionStmtQueue;
#else
typedef void SessionStmtQueue;
#endif
MXS_BEGIN_DECLS
@ -80,6 +85,13 @@ typedef enum
SESSION_TRX_READ_WRITE_ENDING = (SESSION_TRX_ENDING_BIT | SESSION_TRX_READ_WRITE),
} mxs_session_trx_state_t;
typedef enum
{
SESSION_DUMP_STATEMENTS_NEVER,
SESSION_DUMP_STATEMENTS_ON_CLOSE,
SESSION_DUMP_STATEMENTS_ON_ERROR,
} session_dump_statements_t;
/**
* The session statistics structure
*/
@ -200,7 +212,8 @@ typedef struct session
MXS_UPSTREAM up; /*< Upward component to receive buffer. */
GWBUF* buffer; /*< Buffer to deliver to up. */
} response; /*< Shortcircuited response */
skygw_chk_t ses_chk_tail;
SessionStmtQueue* last_statements; /*< The N last statements by the client */
skygw_chk_t ses_chk_tail;
} MXS_SESSION;
/**
@ -623,4 +636,41 @@ char* session_set_variable_value(MXS_SESSION* session,
const char* value_begin,
const char* value_end);
/**
* @brief Specify how many statements each session should retain for
* debugging purposes.
*
* @param n The number of statements.
*/
void session_set_retain_last_statements(uint32_t n);
/**
* @brief Retain provided statement, if configured to do so.
*
* @param session The session.
* @param buffer Buffer assumed to contain a full statement.
*/
void session_retain_statement(MXS_SESSION* session, GWBUF* buffer);
/**
* @brief Dump the last statements, if statements have been retained.
*
* @param session The session.
*/
void session_dump_statements(MXS_SESSION* pSession);
/**
* @brief Specify whether statements should be dumped or not.
*
* @param value Whether and when to dump statements.
*/
void session_set_dump_statements(session_dump_statements_t value);
/**
* @brief Returns in what contexts statements should be dumped.
*
* @return Whether and when to dump statements.
*/
session_dump_statements_t session_get_dump_statements();
MXS_END_DECLS

View File

@ -80,6 +80,7 @@ const char CN_CONNECTION_TIMEOUT[] = "connection_timeout";
const char CN_DATA[] = "data";
const char CN_DEFAULT[] = "default";
const char CN_DESCRIPTION[] = "description";
const char CN_DUMP_LAST_STATEMENTS[] = "dump_last_statements";
const char CN_ENABLE_ROOT_USER[] = "enable_root_user";
const char CN_FILTERS[] = "filters";
const char CN_FILTER[] = "filter";
@ -121,6 +122,7 @@ const char CN_RELATIONSHIPS[] = "relationships";
const char CN_LINKS[] = "links";
const char CN_LOCAL_ADDRESS[] = "local_address";
const char CN_REQUIRED[] = "required";
const char CN_RETAIN_LAST_STATEMENTS[] = "retain_last_statements";
const char CN_RETRY_ON_FAILURE[] = "retry_on_failure";
const char CN_ROUTER[] = "router";
const char CN_ROUTER_DIAGNOSTICS[] = "router_diagnostics";
@ -1782,6 +1784,39 @@ handle_global_item(const char *name, const char *value)
gateway.users_refresh_time = USERS_REFRESH_TIME_DEFAULT;
}
}
else if (strcmp(name, CN_RETAIN_LAST_STATEMENTS) == 0)
{
char* endptr;
int intval = strtol(value, &endptr, 0);
if (*endptr == '\0' && intval >= 0)
{
session_set_retain_last_statements(intval);
}
else
{
MXS_ERROR("Invalid value for '%s': %s", CN_RETAIN_LAST_STATEMENTS, value);
}
}
else if (strcmp(name, CN_DUMP_LAST_STATEMENTS) == 0)
{
if (strcmp(value, "on_close") == 0)
{
session_set_dump_statements(SESSION_DUMP_STATEMENTS_ON_CLOSE);
}
else if (strcmp(value, "on_error") == 0)
{
session_set_dump_statements(SESSION_DUMP_STATEMENTS_ON_ERROR);
}
else if (strcmp(value, "never") == 0)
{
session_set_dump_statements(SESSION_DUMP_STATEMENTS_NEVER);
}
else
{
MXS_ERROR("%s can have the values 'never', 'on_close' or 'on_error'.",
CN_DUMP_LAST_STATEMENTS);
}
}
else
{
for (i = 0; lognames[i].name; i++)

View File

@ -37,6 +37,7 @@
#include <maxscale/spinlock.h>
#include <maxscale/utils.h>
#include <maxscale/json_api.h>
#include <maxscale/protocol/mysql.h>
#include "internal/dcb.h"
#include "internal/session.h"
@ -52,6 +53,9 @@ using std::stringstream;
*/
static uint64_t next_session_id = 1;
static uint32_t retain_last_statements = 0;
static session_dump_statements_t dump_statements = SESSION_DUMP_STATEMENTS_NEVER;
static struct session session_dummy_struct;
static void session_initialize(void *session);
@ -88,6 +92,7 @@ session_initialize(MXS_SESSION *session)
session->ses_chk_top = CHK_NUM_SESSION;
session->state = SESSION_STATE_ALLOC;
session->last_statements = new SessionStmtQueue;
session->ses_chk_tail = CHK_NUM_SESSION;
}
@ -388,8 +393,14 @@ static void session_free(MXS_SESSION *session)
static void
session_final_free(MXS_SESSION *session)
{
if (dump_statements == SESSION_DUMP_STATEMENTS_ON_CLOSE)
{
session_dump_statements(session);
}
gwbuf_free(session->stmt.buffer);
delete session->variables;
delete session->last_statements;
MXS_FREE(session);
}
@ -1276,3 +1287,94 @@ static void session_deliver_response(MXS_SESSION* session)
ss_dassert(!session->response.up.error);
ss_dassert(!session->response.buffer);
}
void session_set_retain_last_statements(uint32_t n)
{
retain_last_statements = n;
}
void session_set_dump_statements(session_dump_statements_t value)
{
dump_statements = value;
}
session_dump_statements_t session_get_dump_statements()
{
return dump_statements;
}
void session_retain_statement(MXS_SESSION* pSession, GWBUF* pBuffer)
{
if (retain_last_statements)
{
size_t len = gwbuf_length(pBuffer);
if (len > MYSQL_HEADER_LEN)
{
uint8_t header[MYSQL_HEADER_LEN + 1];
uint8_t* pHeader = NULL;
if (GWBUF_LENGTH(pBuffer) > MYSQL_HEADER_LEN)
{
pHeader = GWBUF_DATA(pBuffer);
}
else
{
gwbuf_copy_data(pBuffer, 0, MYSQL_HEADER_LEN + 1, header);
pHeader = header;
}
if (MYSQL_GET_COMMAND(pHeader) == MXS_COM_QUERY)
{
ss_dassert(pSession->last_statements->size() <= retain_last_statements);
if (pSession->last_statements->size() == retain_last_statements)
{
pSession->last_statements->pop_back();
}
std::vector<uint8_t> stmt(len - MYSQL_HEADER_LEN - 1);
gwbuf_copy_data(pBuffer, MYSQL_HEADER_LEN + 1, len - (MYSQL_HEADER_LEN + 1), &stmt.front());
pSession->last_statements->push_front(stmt);
}
}
}
}
void session_dump_statements(MXS_SESSION* pSession)
{
if (retain_last_statements)
{
int n = pSession->last_statements->size();
uint64_t id = session_get_current_id();
if ((id != 0) && (id != pSession->ses_id))
{
MXS_WARNING("Current session is %" PRIu64 ", yet statements are dumped for %" PRIu64 ". "
"The session id in the subsequent dumped statements is the wrong one.",
id, pSession->ses_id);
}
for (auto i = pSession->last_statements->rbegin(); i != pSession->last_statements->rend(); ++i)
{
int len = i->size();
const char* pStmt = (char*) &i->front();
if (id != 0)
{
MXS_NOTICE("Stmt %d: %.*s", n, len, pStmt);
}
else
{
// We are in a context where we do not have a current session, so we need to
// log the session id ourselves.
MXS_NOTICE("(%" PRIu64 ") Stmt %d: %.*s", pSession->ses_id, n, len, pStmt);
}
--n;
}
}
}

View File

@ -1030,6 +1030,8 @@ gw_read_normal_data(DCB *dcb, GWBUF *read_buffer, int nbytes_read)
// is thread and not session specific.
qc_set_sql_mode(static_cast<qc_sql_mode_t>(session->client_protocol_data));
}
session_retain_statement(session, read_buffer);
}
/** Update the current protocol command being executed */
else if (!process_client_commands(dcb, nbytes_read, &read_buffer))
@ -1464,6 +1466,11 @@ static int gw_client_hangup_event(DCB *dcb)
CHK_SESSION(session);
if (session->state != SESSION_STATE_DUMMY && !session_valid_for_pool(session))
{
if (session_get_dump_statements() == SESSION_DUMP_STATEMENTS_ON_ERROR)
{
session_dump_statements(session);
}
// The client did not send a COM_QUIT packet
modutil_send_mysql_err_packet(dcb, 0, 0, 1927, "08S01", "Connection killed by MaxScale");
}