Merge branch '2.2' into develop
This commit is contained in:
@ -726,6 +726,35 @@ possible to explicitly cause the users of a service to be reloaded.
|
|||||||
users_refresh_time=120
|
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
|
### Service
|
||||||
|
|
||||||
A service represents the database service that MariaDB MaxScale offers to the
|
A service represents the database service that MariaDB MaxScale offers to the
|
||||||
|
@ -21,6 +21,11 @@ is considered a match if the column name matches. Please consult the
|
|||||||
|
|
||||||
## New Features
|
## 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
|
## Bug fixes
|
||||||
|
|
||||||
## Known Issues and Limitations
|
## Known Issues and Limitations
|
||||||
|
@ -99,6 +99,7 @@ extern const char CN_AUTH_READ_TIMEOUT[];
|
|||||||
extern const char CN_AUTH_WRITE_TIMEOUT[];
|
extern const char CN_AUTH_WRITE_TIMEOUT[];
|
||||||
extern const char CN_AUTO[];
|
extern const char CN_AUTO[];
|
||||||
extern const char CN_CONNECTION_TIMEOUT[];
|
extern const char CN_CONNECTION_TIMEOUT[];
|
||||||
|
extern const char CN_DUMP_LAST_STATEMENTS[];
|
||||||
extern const char CN_DATA[];
|
extern const char CN_DATA[];
|
||||||
extern const char CN_DEFAULT[];
|
extern const char CN_DEFAULT[];
|
||||||
extern const char CN_DESCRIPTION[];
|
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_RELATIONSHIPS[];
|
||||||
extern const char CN_LINKS[];
|
extern const char CN_LINKS[];
|
||||||
extern const char CN_REQUIRED[];
|
extern const char CN_REQUIRED[];
|
||||||
|
extern const char CN_RETAIN_LAST_STATEMENTS[];
|
||||||
extern const char CN_RETRY_ON_FAILURE[];
|
extern const char CN_RETRY_ON_FAILURE[];
|
||||||
extern const char CN_ROUTER[];
|
extern const char CN_ROUTER[];
|
||||||
extern const char CN_ROUTER_DIAGNOSTICS[];
|
extern const char CN_ROUTER_DIAGNOSTICS[];
|
||||||
|
@ -30,6 +30,11 @@
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
#include <tr1/unordered_map>
|
#include <tr1/unordered_map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <deque>
|
||||||
|
#include <vector>
|
||||||
|
typedef std::deque<std::vector<uint8_t> > SessionStmtQueue;
|
||||||
|
#else
|
||||||
|
typedef void SessionStmtQueue;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MXS_BEGIN_DECLS
|
MXS_BEGIN_DECLS
|
||||||
@ -80,6 +85,13 @@ typedef enum
|
|||||||
SESSION_TRX_READ_WRITE_ENDING = (SESSION_TRX_ENDING_BIT | SESSION_TRX_READ_WRITE),
|
SESSION_TRX_READ_WRITE_ENDING = (SESSION_TRX_ENDING_BIT | SESSION_TRX_READ_WRITE),
|
||||||
} mxs_session_trx_state_t;
|
} 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
|
* The session statistics structure
|
||||||
*/
|
*/
|
||||||
@ -200,7 +212,8 @@ typedef struct session
|
|||||||
MXS_UPSTREAM up; /*< Upward component to receive buffer. */
|
MXS_UPSTREAM up; /*< Upward component to receive buffer. */
|
||||||
GWBUF* buffer; /*< Buffer to deliver to up. */
|
GWBUF* buffer; /*< Buffer to deliver to up. */
|
||||||
} response; /*< Shortcircuited response */
|
} 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;
|
} MXS_SESSION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -623,4 +636,41 @@ char* session_set_variable_value(MXS_SESSION* session,
|
|||||||
const char* value_begin,
|
const char* value_begin,
|
||||||
const char* value_end);
|
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
|
MXS_END_DECLS
|
||||||
|
@ -80,6 +80,7 @@ const char CN_CONNECTION_TIMEOUT[] = "connection_timeout";
|
|||||||
const char CN_DATA[] = "data";
|
const char CN_DATA[] = "data";
|
||||||
const char CN_DEFAULT[] = "default";
|
const char CN_DEFAULT[] = "default";
|
||||||
const char CN_DESCRIPTION[] = "description";
|
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_ENABLE_ROOT_USER[] = "enable_root_user";
|
||||||
const char CN_FILTERS[] = "filters";
|
const char CN_FILTERS[] = "filters";
|
||||||
const char CN_FILTER[] = "filter";
|
const char CN_FILTER[] = "filter";
|
||||||
@ -121,6 +122,7 @@ const char CN_RELATIONSHIPS[] = "relationships";
|
|||||||
const char CN_LINKS[] = "links";
|
const char CN_LINKS[] = "links";
|
||||||
const char CN_LOCAL_ADDRESS[] = "local_address";
|
const char CN_LOCAL_ADDRESS[] = "local_address";
|
||||||
const char CN_REQUIRED[] = "required";
|
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_RETRY_ON_FAILURE[] = "retry_on_failure";
|
||||||
const char CN_ROUTER[] = "router";
|
const char CN_ROUTER[] = "router";
|
||||||
const char CN_ROUTER_DIAGNOSTICS[] = "router_diagnostics";
|
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;
|
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
|
else
|
||||||
{
|
{
|
||||||
for (i = 0; lognames[i].name; i++)
|
for (i = 0; lognames[i].name; i++)
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include <maxscale/spinlock.h>
|
#include <maxscale/spinlock.h>
|
||||||
#include <maxscale/utils.h>
|
#include <maxscale/utils.h>
|
||||||
#include <maxscale/json_api.h>
|
#include <maxscale/json_api.h>
|
||||||
|
#include <maxscale/protocol/mysql.h>
|
||||||
|
|
||||||
#include "internal/dcb.h"
|
#include "internal/dcb.h"
|
||||||
#include "internal/session.h"
|
#include "internal/session.h"
|
||||||
@ -52,6 +53,9 @@ using std::stringstream;
|
|||||||
*/
|
*/
|
||||||
static uint64_t next_session_id = 1;
|
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 struct session session_dummy_struct;
|
||||||
|
|
||||||
static void session_initialize(void *session);
|
static void session_initialize(void *session);
|
||||||
@ -88,6 +92,7 @@ session_initialize(MXS_SESSION *session)
|
|||||||
|
|
||||||
session->ses_chk_top = CHK_NUM_SESSION;
|
session->ses_chk_top = CHK_NUM_SESSION;
|
||||||
session->state = SESSION_STATE_ALLOC;
|
session->state = SESSION_STATE_ALLOC;
|
||||||
|
session->last_statements = new SessionStmtQueue;
|
||||||
session->ses_chk_tail = CHK_NUM_SESSION;
|
session->ses_chk_tail = CHK_NUM_SESSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,8 +393,14 @@ static void session_free(MXS_SESSION *session)
|
|||||||
static void
|
static void
|
||||||
session_final_free(MXS_SESSION *session)
|
session_final_free(MXS_SESSION *session)
|
||||||
{
|
{
|
||||||
|
if (dump_statements == SESSION_DUMP_STATEMENTS_ON_CLOSE)
|
||||||
|
{
|
||||||
|
session_dump_statements(session);
|
||||||
|
}
|
||||||
|
|
||||||
gwbuf_free(session->stmt.buffer);
|
gwbuf_free(session->stmt.buffer);
|
||||||
delete session->variables;
|
delete session->variables;
|
||||||
|
delete session->last_statements;
|
||||||
MXS_FREE(session);
|
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.up.error);
|
||||||
ss_dassert(!session->response.buffer);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1030,6 +1030,8 @@ gw_read_normal_data(DCB *dcb, GWBUF *read_buffer, int nbytes_read)
|
|||||||
// is thread and not session specific.
|
// is thread and not session specific.
|
||||||
qc_set_sql_mode(static_cast<qc_sql_mode_t>(session->client_protocol_data));
|
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 */
|
/** Update the current protocol command being executed */
|
||||||
else if (!process_client_commands(dcb, nbytes_read, &read_buffer))
|
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);
|
CHK_SESSION(session);
|
||||||
if (session->state != SESSION_STATE_DUMMY && !session_valid_for_pool(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
|
// The client did not send a COM_QUIT packet
|
||||||
modutil_send_mysql_err_packet(dcb, 0, 0, 1927, "08S01", "Connection killed by MaxScale");
|
modutil_send_mysql_err_packet(dcb, 0, 0, 1927, "08S01", "Connection killed by MaxScale");
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user