Merge branch 'Z3' of https://github.com/skysql/MaxScale into Z3
Conflicts: gcov.diff server/core/buffer.c server/include/buffer.h server/modules/routing/readwritesplit/readwritesplit.c
This commit is contained in:
@ -88,19 +88,15 @@ install: $(MODULES)
|
||||
(cd readwritesplit; make DEST=$(DEST) install)
|
||||
|
||||
cleantests:
|
||||
$(MAKE) -C readwritesplit/test cleantests
|
||||
$(MAKE) -C test cleantests
|
||||
|
||||
|
||||
buildtests:
|
||||
$(MAKE) -C readwritesplit/test DEBUG=Y buildtests
|
||||
$(MAKE) -C test DEBUG=Y buildtests
|
||||
|
||||
|
||||
runtests:
|
||||
$(MAKE) -C test runtests
|
||||
$(MAKE) -C readwritesplit runtests
|
||||
|
||||
|
||||
testall:
|
||||
$(MAKE) -C test testall
|
||||
$(MAKE) -C readwritesplit testall
|
||||
|
||||
|
||||
include depend.mk
|
||||
|
@ -831,6 +831,7 @@ static struct {
|
||||
{ "master", SERVER_MASTER },
|
||||
{ "slave", SERVER_SLAVE },
|
||||
{ "synced", SERVER_JOINED },
|
||||
{ "ndb", SERVER_NDB },
|
||||
{ "maintenance", SERVER_MAINT },
|
||||
{ "maint", SERVER_MAINT },
|
||||
{ NULL, 0 }
|
||||
|
@ -311,6 +311,11 @@ char *weightby;
|
||||
inst->bitmask |= (SERVER_JOINED);
|
||||
inst->bitvalue |= SERVER_JOINED;
|
||||
}
|
||||
else if (!strcasecmp(options[i], "ndb"))
|
||||
{
|
||||
inst->bitmask |= (SERVER_NDB);
|
||||
inst->bitvalue |= SERVER_NDB;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
@ -318,7 +323,7 @@ char *weightby;
|
||||
"* Warning : Unsupported router "
|
||||
"option \'%s\' for readconnroute. "
|
||||
"Expected router options are "
|
||||
"[slave|master|synced]",
|
||||
"[slave|master|synced|ndb]",
|
||||
options[i])));
|
||||
}
|
||||
}
|
||||
|
@ -98,6 +98,12 @@ static int rses_get_max_slavecount(ROUTER_CLIENT_SES* rses, int router_nservers
|
||||
static int rses_get_max_replication_lag(ROUTER_CLIENT_SES* rses);
|
||||
static backend_ref_t* get_bref_from_dcb(ROUTER_CLIENT_SES* rses, DCB* dcb);
|
||||
|
||||
static route_target_t get_route_target (
|
||||
skygw_query_type_t qtype,
|
||||
bool trx_active,
|
||||
HINT* hint);
|
||||
|
||||
|
||||
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
|
||||
|
||||
#if defined(NOT_USED)
|
||||
@ -154,7 +160,9 @@ static bool select_connect_backend_servers(
|
||||
static bool get_dcb(
|
||||
DCB** dcb,
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
backend_type_t btype);
|
||||
backend_type_t btype,
|
||||
char* name,
|
||||
int max_rlag);
|
||||
|
||||
static void rwsplit_process_router_options(
|
||||
ROUTER_INSTANCE* router,
|
||||
@ -913,19 +921,29 @@ static void freeSession(
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a pointer to a suitable backend dcb.
|
||||
* Provide the router with a pointer to a suitable backend dcb.
|
||||
* Detect failures in server statuses and reselect backends if necessary.
|
||||
* If name is specified, server name becomes primary selection criteria.
|
||||
*
|
||||
* @param p_dcb Address of the pointer to the resulting DCB
|
||||
* @param rses Pointer to router client session
|
||||
* @param btype Backend type
|
||||
* @param name Name of the backend which is primarily searched. May be NULL.
|
||||
*
|
||||
* @return True if proper DCB was found, false otherwise.
|
||||
*/
|
||||
static bool get_dcb(
|
||||
DCB** p_dcb,
|
||||
ROUTER_CLIENT_SES* rses,
|
||||
backend_type_t btype)
|
||||
backend_type_t btype,
|
||||
char* name,
|
||||
int max_rlag)
|
||||
{
|
||||
backend_ref_t* backend_ref;
|
||||
int smallest_nconn = -1;
|
||||
int i;
|
||||
bool succp = false;
|
||||
BACKEND *master_host = NULL;
|
||||
BACKEND* master_host;
|
||||
|
||||
CHK_CLIENT_RSES(rses);
|
||||
ss_dassert(p_dcb != NULL && *(p_dcb) == NULL);
|
||||
@ -936,55 +954,95 @@ static bool get_dcb(
|
||||
}
|
||||
backend_ref = rses->rses_backend_ref;
|
||||
|
||||
/* get root master from availbal servers */
|
||||
/** get root master from available servers */
|
||||
master_host = get_root_master(backend_ref, rses->rses_nbackends);
|
||||
|
||||
if (btype == BE_SLAVE)
|
||||
{
|
||||
for (i=0; i<rses->rses_nbackends; i++)
|
||||
if (name != NULL) /*< Choose backend by name (hint) */
|
||||
{
|
||||
BACKEND* b = backend_ref[i].bref_backend;
|
||||
/* check slave bit, also for relay servers (Master & Servers) */
|
||||
if (BREF_IS_IN_USE((&backend_ref[i])) &&
|
||||
(SERVER_IS_SLAVE(b->backend_server) || SERVER_IS_RELAY_SERVER(b->backend_server)) &&
|
||||
(master_host != NULL && b->backend_server != master_host->backend_server) &&
|
||||
(smallest_nconn == -1 ||
|
||||
b->backend_conn_count < smallest_nconn))
|
||||
for (i=0; i<rses->rses_nbackends; i++)
|
||||
{
|
||||
*p_dcb = backend_ref[i].bref_dcb;
|
||||
smallest_nconn = b->backend_conn_count;
|
||||
succp = true;
|
||||
ss_dassert(backend_ref[i].bref_dcb->state != DCB_STATE_ZOMBIE);
|
||||
BACKEND* b = backend_ref[i].bref_backend;
|
||||
|
||||
/**
|
||||
* To become chosen:
|
||||
* backend must be in use, name must match,
|
||||
* root master node must be found,
|
||||
* backend's role must be either slave, relay
|
||||
* server, or master.
|
||||
*/
|
||||
if (BREF_IS_IN_USE((&backend_ref[i])) &&
|
||||
(strncasecmp(
|
||||
name,
|
||||
b->backend_server->unique_name,
|
||||
MIN(strlen(b->backend_server->unique_name), PATH_MAX)) == 0) &&
|
||||
master_host != NULL &&
|
||||
#if 0
|
||||
(max_rlag == MAX_RLAG_UNDEFINED ||
|
||||
(b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE &&
|
||||
b->backend_server->rlag <= max_rlag)) &&
|
||||
#endif
|
||||
(SERVER_IS_SLAVE(b->backend_server) ||
|
||||
SERVER_IS_RELAY_SERVER(b->backend_server) ||
|
||||
SERVER_IS_MASTER(b->backend_server)))
|
||||
{
|
||||
*p_dcb = backend_ref[i].bref_dcb;
|
||||
succp = true;
|
||||
ss_dassert(backend_ref[i].bref_dcb->state != DCB_STATE_ZOMBIE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!succp)
|
||||
if (!succp) /*< No hints or finding named backend failed */
|
||||
{
|
||||
backend_ref = rses->rses_master_ref;
|
||||
|
||||
if (BREF_IS_IN_USE(backend_ref))
|
||||
for (i=0; i<rses->rses_nbackends; i++)
|
||||
{
|
||||
*p_dcb = backend_ref->bref_dcb;
|
||||
succp = true;
|
||||
|
||||
ss_dassert(backend_ref->bref_dcb->state != DCB_STATE_ZOMBIE);
|
||||
|
||||
ss_dassert(
|
||||
(master_host && (backend_ref->bref_backend->backend_server == master_host->backend_server)) &&
|
||||
smallest_nconn == -1);
|
||||
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Warning : No slaves connected nor "
|
||||
"available. Choosing master %s:%d "
|
||||
"instead.",
|
||||
backend_ref->bref_backend->backend_server->name,
|
||||
backend_ref->bref_backend->backend_server->port)));
|
||||
BACKEND* b = backend_ref[i].bref_backend;
|
||||
/**
|
||||
* To become chosen:
|
||||
* backend must be in use,
|
||||
* root master node must be found,
|
||||
* backend is not allowed to be the master,
|
||||
* backend's role can be either slave or relay
|
||||
* server and it must have least connections
|
||||
* at the moment.
|
||||
*/
|
||||
if (BREF_IS_IN_USE((&backend_ref[i])) &&
|
||||
master_host != NULL &&
|
||||
b->backend_server != master_host->backend_server &&
|
||||
(max_rlag == MAX_RLAG_UNDEFINED ||
|
||||
(b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE &&
|
||||
b->backend_server->rlag <= max_rlag)) &&
|
||||
(SERVER_IS_SLAVE(b->backend_server) ||
|
||||
SERVER_IS_RELAY_SERVER(b->backend_server)) &&
|
||||
(smallest_nconn == -1 ||
|
||||
b->backend_conn_count < smallest_nconn))
|
||||
{
|
||||
*p_dcb = backend_ref[i].bref_dcb;
|
||||
smallest_nconn = b->backend_conn_count;
|
||||
succp = true;
|
||||
ss_dassert(backend_ref[i].bref_dcb->state != DCB_STATE_ZOMBIE);
|
||||
}
|
||||
}
|
||||
}
|
||||
ss_dassert(succp);
|
||||
}
|
||||
|
||||
if (!succp) /*< No valid slave was found, search master next */
|
||||
{
|
||||
btype = BE_MASTER;
|
||||
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Warning : No slaves connected nor "
|
||||
"available. Choosing master %s:%d "
|
||||
"instead.",
|
||||
backend_ref->bref_backend->backend_server->name,
|
||||
backend_ref->bref_backend->backend_server->port)));
|
||||
}
|
||||
}
|
||||
else if (btype == BE_MASTER)
|
||||
|
||||
if (btype == BE_MASTER)
|
||||
{
|
||||
for (i=0; i<rses->rses_nbackends; i++)
|
||||
{
|
||||
@ -999,10 +1057,107 @@ static bool get_dcb(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return_succp:
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine the query type, transaction state and routing hints. Find out the
|
||||
* target for query routing.
|
||||
*
|
||||
* @param qtype Type of query
|
||||
* @param trx_active Is transacation active or not
|
||||
* @param hint Pointer to list of hints attached to the query buffer
|
||||
*
|
||||
* @return bitfield including the routing target, or the target server name
|
||||
* if the query would otherwise be routed to slave.
|
||||
*/
|
||||
static route_target_t get_route_target (
|
||||
skygw_query_type_t qtype,
|
||||
bool trx_active,
|
||||
HINT* hint)
|
||||
{
|
||||
route_target_t target;
|
||||
|
||||
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) ||
|
||||
QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
||||
QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_NAMED_STMT))
|
||||
{
|
||||
/** hints don't affect on routing */
|
||||
target = TARGET_ALL;
|
||||
}
|
||||
else if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) && !trx_active)
|
||||
{
|
||||
target = TARGET_SLAVE;
|
||||
|
||||
/** process routing hints */
|
||||
while (hint != NULL)
|
||||
{
|
||||
if (hint->type == HINT_ROUTE_TO_MASTER)
|
||||
{
|
||||
target = TARGET_MASTER; /*< override */
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Hint: route to master.")));
|
||||
break;
|
||||
}
|
||||
else if (hint->type == HINT_ROUTE_TO_NAMED_SERVER)
|
||||
{
|
||||
target |= TARGET_NAMED_SERVER; /*< add */
|
||||
}
|
||||
else if (hint->type == HINT_ROUTE_TO_UPTODATE_SERVER)
|
||||
{
|
||||
/** not implemented */
|
||||
}
|
||||
else if (hint->type == HINT_ROUTE_TO_ALL)
|
||||
{
|
||||
/** not implemented */
|
||||
}
|
||||
else if (hint->type == HINT_PARAMETER)
|
||||
{
|
||||
if (strncasecmp(
|
||||
(char *)hint->data,
|
||||
"max_slave_replication_lag",
|
||||
strlen("max_slave_replication_lag")) == 0)
|
||||
{
|
||||
target |= TARGET_RLAG_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Error : Unknown hint parameter "
|
||||
"'%s' when 'max_slave_replication_lag' "
|
||||
"was expected.",
|
||||
(char *)hint->data)));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unknown hint parameter "
|
||||
"'%s' when 'max_slave_replication_lag' "
|
||||
"was expected.",
|
||||
(char *)hint->data)));
|
||||
}
|
||||
}
|
||||
else if (hint->type == HINT_ROUTE_TO_SLAVE)
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Hint: route to slave.")));
|
||||
}
|
||||
hint = hint->next;
|
||||
} /*< while (hint != NULL) */
|
||||
}
|
||||
else
|
||||
{
|
||||
/** hints don't affect on routing */
|
||||
target = TARGET_MASTER;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The main routing entry, this is called with every packet that is
|
||||
* received and has to be forwarded to the backend database.
|
||||
@ -1032,14 +1187,20 @@ static int routeQuery(
|
||||
GWBUF* querybuf)
|
||||
{
|
||||
skygw_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
||||
GWBUF* plainsqlbuf = NULL;
|
||||
char* querystr = NULL;
|
||||
char* startpos;
|
||||
mysql_server_cmd_t packet_type;
|
||||
uint8_t* packet;
|
||||
int ret = 0;
|
||||
DCB* master_dcb = NULL;
|
||||
DCB* slave_dcb = NULL;
|
||||
DCB* target_dcb = NULL;
|
||||
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
|
||||
ROUTER_CLIENT_SES* router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
|
||||
bool rses_is_closed = false;
|
||||
size_t len;
|
||||
MYSQL* mysql = NULL;
|
||||
route_target_t route_target;
|
||||
|
||||
CHK_CLIENT_RSES(router_cli_ses);
|
||||
|
||||
@ -1164,12 +1325,20 @@ static int routeQuery(
|
||||
router_cli_ses->rses_autocommit_enabled = true;
|
||||
router_cli_ses->rses_transaction_active = false;
|
||||
}
|
||||
/**
|
||||
* Session update is always routed in the same way.
|
||||
/**
|
||||
* Find out where to route the query. Result may not be clear; it is
|
||||
* possible to have a hint for routing to a named server which can
|
||||
* be either slave or master.
|
||||
* If query would otherwise be routed to slave then the hint determines
|
||||
* actual target server if it exists.
|
||||
*
|
||||
* route_target is a bitfield and may include multiple values.
|
||||
*/
|
||||
if (QUERY_IS_TYPE(qtype, QUERY_TYPE_SESSION_WRITE) ||
|
||||
QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
||||
QUERY_IS_TYPE(qtype, QUERY_TYPE_PREPARE_NAMED_STMT))
|
||||
route_target = get_route_target(qtype,
|
||||
router_cli_ses->rses_transaction_active,
|
||||
querybuf->hint);
|
||||
|
||||
if (TARGET_IS_ALL(route_target))
|
||||
{
|
||||
/**
|
||||
* It is not sure if the session command in question requires
|
||||
@ -1188,110 +1357,121 @@ static int routeQuery(
|
||||
}
|
||||
goto return_ret;
|
||||
}
|
||||
else if (QUERY_IS_TYPE(qtype, QUERY_TYPE_READ) &&
|
||||
!router_cli_ses->rses_transaction_active)
|
||||
/**
|
||||
* Handle routing to master and to slave
|
||||
*/
|
||||
else
|
||||
{
|
||||
bool succp;
|
||||
bool succp = true;
|
||||
HINT* hint;
|
||||
char* named_server = NULL;
|
||||
int rlag_max = MAX_RLAG_UNDEFINED;
|
||||
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"[%s]\tRead-only query, routing to Slave.",
|
||||
inst->service->name)));
|
||||
ss_dassert(QUERY_IS_TYPE(qtype, QUERY_TYPE_READ));
|
||||
if (router_cli_ses->rses_transaction_active) /*< all to master */
|
||||
{
|
||||
route_target = TARGET_MASTER; /*< override old value */
|
||||
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Transaction is active, routing to Master.")));
|
||||
}
|
||||
LOGIF(LT, (skygw_log_write(LOGFILE_TRACE, "%s", STRQTYPE(qtype))));
|
||||
|
||||
/** Lock router session */
|
||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||
{
|
||||
goto return_ret;
|
||||
}
|
||||
succp = get_dcb(&slave_dcb, router_cli_ses, BE_SLAVE);
|
||||
|
||||
if (TARGET_IS_SLAVE(route_target))
|
||||
{
|
||||
if (TARGET_IS_NAMED_SERVER(route_target) ||
|
||||
TARGET_IS_RLAG_MAX(route_target))
|
||||
{
|
||||
hint = querybuf->hint;
|
||||
|
||||
while (hint != NULL)
|
||||
{
|
||||
if (hint->type == HINT_ROUTE_TO_NAMED_SERVER)
|
||||
{
|
||||
named_server = hint->data;
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Hint: route to server "
|
||||
"'%s'",
|
||||
named_server)));
|
||||
|
||||
}
|
||||
else if (hint->type == HINT_PARAMETER &&
|
||||
(strncasecmp(
|
||||
(char *)hint->data,
|
||||
"max_slave_replication_lag",
|
||||
strlen("max_slave_replication_lag")) == 0))
|
||||
{
|
||||
int val = (int) strtol((char *)hint->value,
|
||||
(char **)NULL, 10);
|
||||
|
||||
if (val != 0 || errno == 0)
|
||||
{
|
||||
rlag_max = val;
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Hint: "
|
||||
"max_slave_replication_lag=%d",
|
||||
rlag_max)));
|
||||
}
|
||||
}
|
||||
hint = hint->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (rlag_max == MAX_RLAG_UNDEFINED) /*< no rlag max hint, use config */
|
||||
{
|
||||
rlag_max = rses_get_max_replication_lag(router_cli_ses);
|
||||
}
|
||||
|
||||
succp = get_dcb(&target_dcb,
|
||||
router_cli_ses,
|
||||
BE_SLAVE,
|
||||
named_server,
|
||||
rlag_max);
|
||||
}
|
||||
else if (TARGET_IS_MASTER(route_target))
|
||||
{
|
||||
if (master_dcb == NULL)
|
||||
{
|
||||
succp = get_dcb(&master_dcb,
|
||||
router_cli_ses,
|
||||
BE_MASTER,
|
||||
NULL,
|
||||
MAX_RLAG_UNDEFINED);
|
||||
}
|
||||
target_dcb = master_dcb;
|
||||
}
|
||||
|
||||
if (succp)
|
||||
if (succp) /*< Have DCB of the target backend */
|
||||
{
|
||||
if ((ret = slave_dcb->func.write(slave_dcb, gwbuf_clone(querybuf))) == 1)
|
||||
if ((ret = target_dcb->func.write(target_dcb, querybuf)) == 1)
|
||||
{
|
||||
backend_ref_t* bref;
|
||||
|
||||
atomic_add(&inst->stats.n_slave, 1);
|
||||
/**
|
||||
* Add one query response waiter to backend reference
|
||||
*/
|
||||
bref = get_bref_from_dcb(router_cli_ses, slave_dcb);
|
||||
* Add one query response waiter to backend reference
|
||||
*/
|
||||
bref = get_bref_from_dcb(router_cli_ses, target_dcb);
|
||||
bref_set_state(bref, BREF_QUERY_ACTIVE);
|
||||
bref_set_state(bref, BREF_WAITING_RESULT);
|
||||
}
|
||||
else
|
||||
{
|
||||
char* query_str = modutil_get_query(querybuf);
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Routing query \"%s\" failed.",
|
||||
(query_str == NULL ? "not available" : query_str))));
|
||||
free(query_str);
|
||||
querystr)));
|
||||
}
|
||||
}
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
|
||||
ss_dassert(succp);
|
||||
goto return_ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool succp = true;
|
||||
|
||||
if (LOG_IS_ENABLED(LOGFILE_TRACE))
|
||||
{
|
||||
if (router_cli_ses->rses_transaction_active) /*< all to master */
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Transaction is active, routing to Master.")));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Begin transaction, write or unspecified type, "
|
||||
"routing to Master.")));
|
||||
}
|
||||
}
|
||||
/** Lock router session */
|
||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||
{
|
||||
goto return_ret;
|
||||
}
|
||||
|
||||
if (master_dcb == NULL)
|
||||
{
|
||||
succp = get_dcb(&master_dcb, router_cli_ses, BE_MASTER);
|
||||
}
|
||||
|
||||
if (succp)
|
||||
{
|
||||
if ((ret = master_dcb->func.write(master_dcb, gwbuf_clone(querybuf))) == 1)
|
||||
{
|
||||
backend_ref_t* bref;
|
||||
|
||||
atomic_add(&inst->stats.n_master, 1);
|
||||
|
||||
/**
|
||||
* Add one write response waiter to backend reference
|
||||
*/
|
||||
bref = get_bref_from_dcb(router_cli_ses, master_dcb);
|
||||
bref_set_state(bref, BREF_QUERY_ACTIVE);
|
||||
bref_set_state(bref, BREF_WAITING_RESULT);
|
||||
}
|
||||
}
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
|
||||
ss_dassert(succp);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Routing to master failed.")));
|
||||
}
|
||||
}
|
||||
return_ret:
|
||||
#if defined(SS_DEBUG)
|
||||
@ -1315,7 +1495,7 @@ return_ret:
|
||||
}
|
||||
|
||||
|
||||
/** to be inline'd */
|
||||
|
||||
/**
|
||||
* @node Acquires lock to router client session if it is not closed.
|
||||
*
|
||||
@ -1520,7 +1700,6 @@ static void clientReply (
|
||||
if (LOG_IS_ENABLED(LOGFILE_ERROR) &&
|
||||
MYSQL_IS_ERROR_PACKET(((uint8_t *)GWBUF_DATA(writebuf))))
|
||||
{
|
||||
SESSION* ses = backend_dcb->session;
|
||||
uint8_t* buf =
|
||||
(uint8_t *)GWBUF_DATA((scur->scmd_cur_cmd->my_sescmd_buf));
|
||||
size_t len = MYSQL_GET_PACKET_LEN(buf);
|
||||
@ -1845,7 +2024,9 @@ static bool select_connect_backend_servers(
|
||||
#endif
|
||||
/* assert with master_host */
|
||||
ss_dassert(!master_connected ||
|
||||
(master_host && ((*p_master_ref)->bref_backend->backend_server == master_host->backend_server) && SERVER_MASTER));
|
||||
(master_host &&
|
||||
((*p_master_ref)->bref_backend->backend_server == master_host->backend_server) &&
|
||||
SERVER_MASTER));
|
||||
/**
|
||||
* Sort the pointer list to servers according to connection counts. As
|
||||
* a consequence those backends having least connections are in the
|
||||
@ -1923,8 +2104,8 @@ static bool select_connect_backend_servers(
|
||||
{
|
||||
/* check also for relay servers and don't take the master_host */
|
||||
if (slaves_found < max_nslaves &&
|
||||
(max_slave_rlag == -2 ||
|
||||
(b->backend_server->rlag != -1 && /*< information currently not available */
|
||||
(max_slave_rlag == MAX_RLAG_UNDEFINED ||
|
||||
(b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE &&
|
||||
b->backend_server->rlag <= max_slave_rlag)) &&
|
||||
(SERVER_IS_SLAVE(b->backend_server) || SERVER_IS_RELAY_SERVER(b->backend_server)) &&
|
||||
(master_host != NULL && (b->backend_server != master_host->backend_server)))
|
||||
@ -2002,7 +2183,7 @@ static bool select_connect_backend_servers(
|
||||
session,
|
||||
b->backend_server->protocol);
|
||||
|
||||
if (backend_ref[i].bref_dcb != NULL)
|
||||
if (backend_ref[i].bref_dcb != NULL)
|
||||
{
|
||||
master_connected = true;
|
||||
/**
|
||||
@ -2054,7 +2235,8 @@ static bool select_connect_backend_servers(
|
||||
}
|
||||
/* assert with master_host */
|
||||
ss_dassert(!master_connected ||
|
||||
(master_host && ((*p_master_ref)->bref_backend->backend_server == master_host->backend_server) && SERVER_MASTER));
|
||||
(master_host && ((*p_master_ref)->bref_backend->backend_server == master_host->backend_server) &&
|
||||
SERVER_MASTER));
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -2116,15 +2298,11 @@ static bool select_connect_backend_servers(
|
||||
BACKEND* b = backend_ref[i].bref_backend;
|
||||
|
||||
if (BREF_IS_IN_USE((&backend_ref[i])))
|
||||
{
|
||||
backend_type_t btype = BACKEND_TYPE(b);
|
||||
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Selected %s in \t%s:%d",
|
||||
(btype == BE_MASTER ? "master" :
|
||||
(btype == BE_SLAVE ? "slave" :
|
||||
"unknown node type")),
|
||||
STRSRVSTATUS(b->backend_server),
|
||||
b->backend_server->name,
|
||||
b->backend_server->port)));
|
||||
}
|
||||
@ -2943,7 +3121,6 @@ static bool route_session_write(
|
||||
/** Lock router session */
|
||||
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||
{
|
||||
rses_property_done(prop);
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
@ -3013,6 +3190,7 @@ return_succp:
|
||||
}
|
||||
|
||||
#if defined(NOT_USED)
|
||||
|
||||
static bool router_option_configured(
|
||||
ROUTER_INSTANCE* router,
|
||||
const char* optionstr,
|
||||
@ -3189,6 +3367,11 @@ static bool handle_error_reply_client(
|
||||
CHK_DCB(client_dcb);
|
||||
client_dcb->func.write(client_dcb, errmsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((errmsg=gwbuf_consume(errmsg, GWBUF_LENGTH(errmsg))) != NULL)
|
||||
;
|
||||
}
|
||||
succp = false; /** false because new servers aren's selected. */
|
||||
|
||||
return succp;
|
||||
@ -3243,6 +3426,11 @@ static bool handle_error_new_connection(
|
||||
client_dcb->func.write(client_dcb, errmsg);
|
||||
bref_clear_state(bref, BREF_WAITING_RESULT);
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((errmsg=gwbuf_consume(errmsg, GWBUF_LENGTH(errmsg))) != NULL)
|
||||
;
|
||||
}
|
||||
bref_clear_state(bref, BREF_IN_USE);
|
||||
bref_set_state(bref, BREF_CLOSED);
|
||||
/**
|
||||
|
@ -20,6 +20,7 @@ testall:
|
||||
-$(MAKE) cleantests
|
||||
-$(MAKE) DEBUG=Y buildtests
|
||||
-$(MAKE) runtests
|
||||
-$(MAKE) -C test_hints testall
|
||||
|
||||
buildtests:
|
||||
|
||||
@ -32,4 +33,4 @@ runtests:
|
||||
@echo "-------------------------------" >> $(TESTLOG)
|
||||
./rwsplit.sh $(TESTLOG) $(THOST) $(TPORT_RW) $(TMASTER_ID) $(TUSER) $(TPWD)
|
||||
@echo "" >> $(TESTLOG)
|
||||
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)
|
||||
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)
|
||||
|
@ -0,0 +1,53 @@
|
||||
# cleantests - clean local and subdirectories' tests
|
||||
# buildtests - build all local and subdirectories' tests
|
||||
# runtests - run all local tests
|
||||
# testall - clean, build and run local and subdirectories' tests
|
||||
|
||||
include ../../../../../../build_gateway.inc
|
||||
include $(ROOT_PATH)/makefile.inc
|
||||
include $(ROOT_PATH)/test.inc
|
||||
|
||||
ARGS=6
|
||||
|
||||
CC=cc
|
||||
TESTLOG := $(shell pwd)/testrwsplit_hints.log
|
||||
RET := -1
|
||||
|
||||
cleantests:
|
||||
- $(DEL) *.o
|
||||
- $(DEL) *~
|
||||
- $(DEL) *.sql
|
||||
- $(DEL) *.output
|
||||
- $(DEL) *.log
|
||||
|
||||
testall:
|
||||
-$(MAKE) cleantests
|
||||
-$(MAKE) DEBUG=Y buildtests
|
||||
-$(MAKE) runtests
|
||||
|
||||
buildtests:
|
||||
|
||||
|
||||
runtests:
|
||||
@echo "" >> $(TESTLOG)
|
||||
@echo "-------------------------------" >> $(TESTLOG)
|
||||
@echo $(shell date) >> $(TESTLOG)
|
||||
@echo "Test Read/Write split router - hint routing" >> $(TESTLOG)
|
||||
@echo "-------------------------------" >> $(TESTLOG)
|
||||
@echo "Running simple tests" >> $(TESTLOG)
|
||||
@echo "" >> $(TESTLOG)
|
||||
./rwsplit_hints.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) simple_tests
|
||||
@echo "" >> $(TESTLOG)
|
||||
@echo "Running syntax error tests" >> $(TESTLOG)
|
||||
@echo "" >> $(TESTLOG)
|
||||
./syntax_check.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) error_tests
|
||||
@echo "" >> $(TESTLOG)
|
||||
@echo "Running complex tests" >> $(TESTLOG)
|
||||
@echo "" >> $(TESTLOG)
|
||||
./rwsplit_hints.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) complex_tests
|
||||
@echo "" >> $(TESTLOG)
|
||||
@echo "Running stack tests" >> $(TESTLOG)
|
||||
@echo "" >> $(TESTLOG)
|
||||
./rwsplit_hints.sh $(TESTLOG) $(THOST) $(TPORT_RW_HINT) $(TMASTER_ID) $(TUSER) $(TPWD) stack_tests
|
||||
@echo "" >> $(TESTLOG)
|
||||
@cat $(TESTLOG) >> $(TEST_MAXSCALE_LOG)
|
@ -0,0 +1,48 @@
|
||||
select @@server_id; -- maxscale begin route to master:3000
|
||||
select @@server_id;:3000
|
||||
select @@server_id; -- maxscale route to server server3:3002
|
||||
select @@server_id;:3000
|
||||
select @@server_id; -- maxscale end:
|
||||
select @@server_id; -- maxscale named1 prepare route to master:
|
||||
select @@server_id; -- maxscale named1 begin:3000
|
||||
select @@server_id;:3000
|
||||
select @@server_id; -- maxscale route to server server3:3002
|
||||
select @@server_id;:3000
|
||||
select @@server_id; -- maxscale end:
|
||||
select @@server_id; -- maxscale shorthand1 begin route to server server2:3001
|
||||
select @@server_id;:3001
|
||||
select @@server_id; -- maxscale route to server server3:3002
|
||||
select @@server_id;:3001
|
||||
select @@server_id; -- maxscale end:
|
||||
select @@server_id; # maxscale begin route to master:3000
|
||||
select @@server_id;:3000
|
||||
select @@server_id; # maxscale route to server server3:3002
|
||||
select @@server_id;:3000
|
||||
select @@server_id; # maxscale end:
|
||||
select @@server_id; # maxscale named2 prepare route to master:
|
||||
select @@server_id; # maxscale named2 begin:3000
|
||||
select @@server_id;:3000
|
||||
select @@server_id; # maxscale route to server server3:3002
|
||||
select @@server_id;:3000
|
||||
select @@server_id; # maxscale end:
|
||||
select @@server_id; # maxscale shorthand2 begin route to server server2:3001
|
||||
select @@server_id;:3001
|
||||
select @@server_id; # maxscale route to server server3:3002
|
||||
select @@server_id;:3001
|
||||
select @@server_id; # maxscale end:
|
||||
select @@server_id/* maxscale begin route to master */;:3000
|
||||
select @@server_id;:3000
|
||||
select @@server_id/* maxscale route to server server3 */;:3002
|
||||
select @@server_id;:3000
|
||||
select @@server_id/* maxscale end */;:
|
||||
select @@server_id/* maxscale named3 prepare route to master */;:
|
||||
select @@server_id/* maxscale named3 begin */;:3000
|
||||
select @@server_id;:3000
|
||||
select @@server_id/* maxscale route to server server3 */;:3002
|
||||
select @@server_id;:3000
|
||||
select @@server_id/* maxscale end */;:
|
||||
select @@server_id/* maxscale shorthand3 begin route to server server2 */; :3001
|
||||
select @@server_id;:3001
|
||||
select @@server_id/* maxscale route to server server3 */;:3002
|
||||
select @@server_id;:3001
|
||||
select @@server_id/* maxscale end */;:
|
@ -0,0 +1,39 @@
|
||||
select @@server_id; -- maxscalemaxscale route to master:
|
||||
select @@server_id; -- master to route maxscale:
|
||||
select @@server_id; -- route to master:
|
||||
select @@server_id; -- maxscale to master:
|
||||
select @@server_id; -- maxscale route master:
|
||||
select @@server_id; -- maxscale route to:
|
||||
select @@server_id; -- maxscale begin master:
|
||||
select @@server_id; -- maxscale master route to master:
|
||||
select @@server_id; -- maxscale route to maxscale route to master:
|
||||
select @@server_id; -- maxscale maxscale route to master:
|
||||
select @@server_id; -- maxscale route to to server =):
|
||||
select @@server_id; -- maxscale route to maxscale server server1:
|
||||
select @@server_id; -- maxscale route to server1:
|
||||
select @@server_id; # maxscalemaxscale route to master:
|
||||
select @@server_id; # master to route maxscale:
|
||||
select @@server_id; # route to master:
|
||||
select @@server_id; # maxscale to master:
|
||||
select @@server_id; # maxscale route master:
|
||||
select @@server_id; # maxscale route to:
|
||||
select @@server_id; # maxscale begin master:
|
||||
select @@server_id; # maxscale master route to master:
|
||||
select @@server_id; # maxscale route to maxscale route to master:
|
||||
select @@server_id; # maxscale maxscale route to master:
|
||||
select @@server_id; # maxscale route to to server =):
|
||||
select @@server_id; # maxscale route to maxscale server server1:
|
||||
select @@server_id; # maxscale route to server1:
|
||||
select @@server_id; /* maxscalemaxscale route to master */;:
|
||||
select @@server_id; /* master to route maxscale */;:
|
||||
select @@server_id; /* route to master */;:
|
||||
select @@server_id; /* maxscale to master */;:
|
||||
select @@server_id; /* maxscale route master */;:
|
||||
select @@server_id; /* maxscale route to */;:
|
||||
select @@server_id; /* maxscale begin master */;:
|
||||
select @@server_id; /* maxscale master route to master */;:
|
||||
select @@server_id; /* maxscale route to maxscale route to master */;:
|
||||
select @@server_id; /* maxscale maxscale route to master */;:
|
||||
select @@server_id; /* maxscale route to to server =) */;:
|
||||
select @@server_id; /* maxscale route to maxscale server server1 */;:
|
||||
select @@server_id; /* maxscale route to server1 */;:
|
@ -0,0 +1,9 @@
|
||||
-- maxscale route to master:3000
|
||||
-- maxscale route to server server1:3000
|
||||
-- maxscale route to server server2:3001
|
||||
-- maxscale route to server server3:3002
|
||||
-- maxscale route to server server4:3003
|
||||
-- maxscale max_slave_replication_lag = 100:
|
||||
-- maxscale max_slave_replication_lag= 100:
|
||||
-- maxscale max_slave_replication_lag =100:
|
||||
-- maxscale max_slave_replication_lag=100:
|
65
server/modules/routing/readwritesplit/test/test_hints/rwsplit_hints.sh
Executable file
65
server/modules/routing/readwritesplit/test/test_hints/rwsplit_hints.sh
Executable file
@ -0,0 +1,65 @@
|
||||
#!/bin/bash
|
||||
NARGS=7
|
||||
TLOG=$1
|
||||
THOST=$2
|
||||
TPORT=$3
|
||||
TMASTER_ID=$4
|
||||
TUSER=$5
|
||||
TPWD=$6
|
||||
TESTINPUT=$7
|
||||
|
||||
if [ $# != $NARGS ] ;
|
||||
then
|
||||
echo""
|
||||
echo "Wrong number of arguments, gave "$#" but "$NARGS" is required"
|
||||
echo ""
|
||||
echo "Usage :"
|
||||
echo " rwsplit_hints.sh <log filename> <host> <port> <master id> <user> <password> <test file>"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
RUNCMD=mysql\ --host=$THOST\ -P$TPORT\ -u$TUSER\ -p$TPWD\ --unbuffered=true\ --disable-reconnect\ --silent\ --comment
|
||||
i=0
|
||||
|
||||
while read -r LINE
|
||||
do
|
||||
TINPUT[$i]=`echo "$LINE"|awk '{split($0,a,":");print a[1]}'`
|
||||
TRETVAL[$i]=`echo "$LINE"|awk '{split($0,a,":");print a[2]}'`
|
||||
echo "${TINPUT[i]}" >> $TESTINPUT.sql
|
||||
i=$((i+1))
|
||||
done < $TESTINPUT
|
||||
|
||||
`$RUNCMD < $TESTINPUT.sql > $TESTINPUT.output`
|
||||
|
||||
x=0
|
||||
crash=1
|
||||
all_passed=1
|
||||
|
||||
while read -r TOUTPUT
|
||||
do
|
||||
crash=0
|
||||
if [ "$TOUTPUT" != "${TRETVAL[x]}" -a "${TRETVAL[x]}" != "" ]
|
||||
then
|
||||
all_passed=0
|
||||
echo "$TESTINPUT:$((x + 1)): ${TINPUT[x]} FAILED, return value $TOUTPUT when ${TRETVAL[x]} was expected">>$TLOG;
|
||||
fi
|
||||
x=$((x+1))
|
||||
done < $TESTINPUT.output
|
||||
|
||||
if [ $crash -eq 1 ]
|
||||
then
|
||||
all_passed=0
|
||||
for ((v=0;v<$i;v++))
|
||||
do
|
||||
echo "${TINPUT[v]} FAILED, nothing was returned">>$TLOG;
|
||||
done
|
||||
fi
|
||||
|
||||
if [ $all_passed -eq 1 ]
|
||||
then
|
||||
echo "Test set: PASSED">>$TLOG;
|
||||
else
|
||||
echo "Test set: FAILED">>$TLOG;
|
||||
fi
|
@ -0,0 +1,18 @@
|
||||
select @@server_id; -- maxscale route to master:3000
|
||||
select @@server_id; -- maxscale route to slave:
|
||||
select @@server_id; -- maxscale route to server server1:3000
|
||||
select @@server_id; -- maxscale route to server server2:3001
|
||||
select @@server_id; -- maxscale route to server server3:3002
|
||||
select @@server_id; -- maxscale route to server server4:3003
|
||||
select @@server_id; # maxscale route to master:3000
|
||||
select @@server_id; # maxscale route to slave:
|
||||
select @@server_id; # maxscale route to server server1:3000
|
||||
select @@server_id; # maxscale route to server server2:3001
|
||||
select @@server_id; # maxscale route to server server3:3002
|
||||
select @@server_id; # maxscale route to server server4:3003
|
||||
select @@server_id/* maxscale route to master */;:3000
|
||||
select @@server_id/* maxscale route to slave */;:
|
||||
select @@server_id/* maxscale route to server server1 */;:3000
|
||||
select @@server_id/* maxscale route to server server2 */;:3001
|
||||
select @@server_id/* maxscale route to server server3 */;:3002
|
||||
select @@server_id/* maxscale route to server server4 */;:3003
|
@ -0,0 +1,50 @@
|
||||
select @@server_id; -- maxscale stack_named1 prepare route to server server1:
|
||||
select @@server_id; -- maxscale stack_named2 prepare route to server server2:
|
||||
select @@server_id; -- maxscale stack_named3 prepare route to server server3:
|
||||
select @@server_id; -- maxscale stack_named4 prepare route to server server4:
|
||||
select @@server_id; -- maxscale stack_named1 begin:3000
|
||||
select @@server_id;:3000
|
||||
select @@server_id; -- maxscale stack_named2 begin:3001
|
||||
select @@server_id;:3001
|
||||
select @@server_id; -- maxscale stack_named3 begin:3002
|
||||
select @@server_id;:3002
|
||||
select @@server_id; -- maxscale stack_named4 begin:3003
|
||||
select @@server_id;:3003
|
||||
select @@server_id; -- maxscale stack_shorthand1 begin route to server server1:3000
|
||||
select @@server_id;:3000
|
||||
select @@server_id; -- maxscale stack_shorthand2 begin route to server server2:3001
|
||||
select @@server_id;:3001
|
||||
select @@server_id; -- maxscale stack_shorthand3 begin route to server server3:3002
|
||||
select @@server_id;:3002
|
||||
select @@server_id; -- maxscale stack_shorthand4 begin route to server server4:3003
|
||||
select @@server_id;:3003
|
||||
select @@server_id; -- maxscale end:3002
|
||||
select @@server_id;:3002
|
||||
select @@server_id; -- maxscale end:3001
|
||||
select @@server_id;:3001
|
||||
select @@server_id; -- maxscale end:3000
|
||||
select @@server_id;:3000
|
||||
select @@server_id; -- maxscale end:3003
|
||||
select @@server_id;:3003
|
||||
select @@server_id; -- maxscale end:3002
|
||||
select @@server_id;:3002
|
||||
select @@server_id; -- maxscale end:3001
|
||||
select @@server_id;:3001
|
||||
select @@server_id; -- maxscale end:3000
|
||||
select @@server_id; -- maxscale end:
|
||||
select @@server_id; -- maxscale stack_shorthand1 begin:3000
|
||||
select @@server_id; -- maxscale stack_shorthand2 begin:3001
|
||||
select @@server_id; -- maxscale stack_shorthand3 begin:3002
|
||||
select @@server_id; -- maxscale stack_shorthand4 begin:3003
|
||||
select @@server_id; -- maxscale stack_named1 begin:3000
|
||||
select @@server_id; -- maxscale stack_named2 begin:3001
|
||||
select @@server_id; -- maxscale stack_named3 begin:3002
|
||||
select @@server_id; -- maxscale stack_named4 begin:3003
|
||||
select @@server_id; -- maxscale end:3002
|
||||
select @@server_id; -- maxscale end:3001
|
||||
select @@server_id; -- maxscale end:3000
|
||||
select @@server_id; -- maxscale end:3003
|
||||
select @@server_id; -- maxscale end:3002
|
||||
select @@server_id; -- maxscale end:3001
|
||||
select @@server_id; -- maxscale end:3000
|
||||
select @@server_id; -- maxscale end:
|
33
server/modules/routing/readwritesplit/test/test_hints/syntax_check.sh
Executable file
33
server/modules/routing/readwritesplit/test/test_hints/syntax_check.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#! /bin/bash
|
||||
NARGS=7
|
||||
TLOG=$1
|
||||
THOST=$2
|
||||
TPORT=$3
|
||||
TMASTER_ID=$4
|
||||
TUSER=$5
|
||||
TPWD=$6
|
||||
TESTINPUT=$7
|
||||
|
||||
if [ $# != $NARGS ] ;
|
||||
then
|
||||
echo""
|
||||
echo "Wrong number of arguments, gave "$#" but "$NARGS" is required"
|
||||
echo ""
|
||||
echo "Usage :"
|
||||
echo " syntax_check.sh <log filename> <host> <port> <master id> <user> <password> <test file>"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./rwsplit_hints.sh dummy.log $THOST $TPORT $TMASTER_ID $TUSER $TPWD $TESTINPUT
|
||||
|
||||
exp_count=`cat error_tests|wc -l`
|
||||
err_count=`tac ../../../../../test/log/skygw_err* | gawk '/enabled/{if(!bg){ bg = 1} else exit 0}{if(bg) print}'|grep -c 'Hint ignored'`
|
||||
|
||||
if [[ $err_count -ge $exp_count ]]
|
||||
then
|
||||
echo "Test set: PASSED">>$TLOG;
|
||||
else
|
||||
echo "Expected $exp_count ignored hints in the error log but found $err_count instead">>$TLOG
|
||||
echo "Test set: FAILED">>$TLOG;
|
||||
fi
|
616
server/modules/routing/webserver.c
Normal file
616
server/modules/routing/webserver.c
Normal file
@ -0,0 +1,616 @@
|
||||
/*
|
||||
* This file is distributed as part of MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright SkySQL Ab 2014
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <router.h>
|
||||
#include <modinfo.h>
|
||||
#include <server.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <monitor.h>
|
||||
#include <string.h>
|
||||
|
||||
/**
|
||||
* The instance structure for this router.
|
||||
*/
|
||||
typedef struct {
|
||||
SERVICE *service;
|
||||
} WEB_INSTANCE;
|
||||
|
||||
/**
|
||||
* The session structure for this router.
|
||||
*/
|
||||
typedef struct {
|
||||
SESSION *session;
|
||||
} WEB_SESSION;
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_ROUTER,
|
||||
MODULE_IN_DEVELOPMENT,
|
||||
ROUTER_VERSION,
|
||||
"A test router - not for use in real systems"
|
||||
};
|
||||
|
||||
static ROUTER *createInstance(SERVICE *service, char **options);
|
||||
static void *newSession(ROUTER *instance, SESSION *session);
|
||||
static void closeSession(ROUTER *instance, void *session);
|
||||
static void freeSession(ROUTER *instance, void *session);
|
||||
static int routeQuery(ROUTER *instance, void *session, GWBUF *queue);
|
||||
static void diagnostic(ROUTER *instance, DCB *dcb);
|
||||
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
|
||||
|
||||
|
||||
static ROUTER_OBJECT MyObject = {
|
||||
createInstance,
|
||||
newSession,
|
||||
closeSession,
|
||||
freeSession,
|
||||
routeQuery,
|
||||
diagnostic,
|
||||
NULL,
|
||||
NULL,
|
||||
getCapabilities
|
||||
};
|
||||
|
||||
|
||||
static void send_index(WEB_SESSION *);
|
||||
static void send_css(WEB_SESSION *);
|
||||
static void send_menu(WEB_SESSION *);
|
||||
static void send_blank(WEB_SESSION *);
|
||||
static void send_title(WEB_SESSION *);
|
||||
static void send_frame1(WEB_SESSION *);
|
||||
static void send_services(WEB_SESSION *);
|
||||
static void send_sessions(WEB_SESSION *);
|
||||
static void send_servers(WEB_SESSION *);
|
||||
static void send_monitors(WEB_SESSION *);
|
||||
static void respond_error(WEB_SESSION *, int, char *);
|
||||
|
||||
/**
|
||||
* A map of URL to function that implements the URL
|
||||
*/
|
||||
static struct {
|
||||
char *page; /* URL */
|
||||
void (*fcn)(WEB_SESSION *); /* Function to call */
|
||||
} pages[] = {
|
||||
{ "index.html", send_index },
|
||||
{ "services.html", send_services },
|
||||
{ "menu.html", send_menu },
|
||||
{ "sessions.html", send_sessions },
|
||||
{ "blank.html", send_blank },
|
||||
{ "title.html", send_title },
|
||||
{ "frame1.html", send_frame1 },
|
||||
{ "servers.html", send_servers },
|
||||
{ "monitors.html", send_monitors },
|
||||
{ "styles.css", send_css },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
ROUTER_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options The options for this query router
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static ROUTER *
|
||||
createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
WEB_INSTANCE *inst;
|
||||
|
||||
if ((inst = (WEB_INSTANCE *)malloc(sizeof(WEB_INSTANCE))) == NULL)
|
||||
return NULL;
|
||||
|
||||
inst->service = service;
|
||||
return (ROUTER *)inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the router.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void *
|
||||
newSession(ROUTER *instance, SESSION *session)
|
||||
{
|
||||
WEB_SESSION *wsession;
|
||||
|
||||
if ((wsession = (WEB_SESSION *)malloc(sizeof(WEB_SESSION))) == NULL)
|
||||
return NULL;
|
||||
|
||||
wsession->session = session;
|
||||
return wsession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the router, this is the mechanism
|
||||
* by which a router may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session being closed
|
||||
*/
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *session)
|
||||
{
|
||||
free(session);
|
||||
}
|
||||
|
||||
static void freeSession(
|
||||
ROUTER* router_instance,
|
||||
void* router_client_session)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static int
|
||||
routeQuery(ROUTER *instance, void *session, GWBUF *queue)
|
||||
{
|
||||
WEB_SESSION *wsession = (WEB_SESSION *)session;
|
||||
char *ptr;
|
||||
int i, found = 0;
|
||||
char *url;
|
||||
|
||||
if ((url = gwbuf_get_property(queue, "URL")) == NULL)
|
||||
{
|
||||
respond_error(wsession, 404, "No URL available");
|
||||
}
|
||||
|
||||
ptr = strrchr(url, '/');
|
||||
if (ptr)
|
||||
ptr++;
|
||||
else
|
||||
ptr = url;
|
||||
for (i = 0; pages[i].page; i++)
|
||||
{
|
||||
if (!strcmp(ptr, pages[i].page))
|
||||
{
|
||||
(pages[i].fcn)(wsession);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
respond_error(wsession, 404, "Unrecognised URL received");
|
||||
gwbuf_free(queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostics routine
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param dcb The DCB for diagnostic output
|
||||
*/
|
||||
static void
|
||||
diagnostic(ROUTER *instance, DCB *dcb)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the router capabilities bitmask
|
||||
*
|
||||
* @param inst The router instance
|
||||
* @param router_session The router session
|
||||
* @return Router capabilities bitmask
|
||||
*/
|
||||
static uint8_t
|
||||
getCapabilities(ROUTER *inst, void *router_session)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTML of the index page.
|
||||
*/
|
||||
static char *index_page =
|
||||
"<HTML><HEAD>"
|
||||
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
|
||||
"<TITLE>MaxScale</TITLE>"
|
||||
"</HEAD>"
|
||||
"<FRAMESET ROWS=\"60,*\">"
|
||||
"<FRAME SRC=\"title.html\">"
|
||||
"<FRAME SRC=\"frame1.html\">"
|
||||
"</FRAMESET>"
|
||||
"</HTML>";
|
||||
|
||||
/**
|
||||
* The HTML of the title page
|
||||
*/
|
||||
static char *title_page =
|
||||
"<HTML><HEAD>"
|
||||
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
|
||||
"<TITLE>MaxScale</TITLE>"
|
||||
"</HEAD><BODY>"
|
||||
"<H1>MaxScale - Status View</H1>"
|
||||
"</BODY></HTML>";
|
||||
|
||||
/**
|
||||
* HTML of the main frames, those below the title frame
|
||||
*/
|
||||
static char *frame1_page =
|
||||
"<HTML>"
|
||||
"<FRAMESET COLS=\"20%,80%\">"
|
||||
"<FRAME SRC=\"menu.html\">"
|
||||
"<FRAME SRC=\"blank.html\" NAME=\"darea\">"
|
||||
"</FRAMESET>"
|
||||
"</HTML>";
|
||||
|
||||
/**
|
||||
* The menu page HTML
|
||||
*/
|
||||
static char *menu_page =
|
||||
"<HTML><HEAD>"
|
||||
"<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">"
|
||||
"</HEAD><BODY>"
|
||||
"<H2>Options</H2><P>"
|
||||
"<UL><LI><A HREF=\"monitors.html\" target=\"darea\">Monitors</A>"
|
||||
"<LI><A HREF=\"services.html\" target=\"darea\">Services</A>"
|
||||
"<LI><A HREF=\"servers.html\" target=\"darea\">Servers</A>"
|
||||
"<LI><A HREF=\"sessions.html\" target=\"darea\">Sessions</A>"
|
||||
"</UL></BODY></HTML>";
|
||||
|
||||
/**
|
||||
* A blank page, contents of the display area when we first connect
|
||||
*/
|
||||
static char *blank_page = "<HTML><BODY> </BODY></HTML>";
|
||||
|
||||
/**
|
||||
* The CSS used for every "page"
|
||||
*/
|
||||
static char *css =
|
||||
"table, td, th { border: 1px solid blue; }\n"
|
||||
"th { background-color: blue; color: white; padding: 5px }\n"
|
||||
"td { padding: 5px; }\n"
|
||||
"table { border-collapse: collapse; }\n"
|
||||
"a:link { color: #0000FF; }\n"
|
||||
"a:visted { color: #0000FF; }\n"
|
||||
"a:hover { color: #FF0000; }\n"
|
||||
"a:active { color: #0000FF; }\n"
|
||||
"h1 { color: blue; font-family: serif }\n"
|
||||
"h2 { color: blue; font-family: serif }\n"
|
||||
"p { font-family: serif }\n"
|
||||
"li { font-family: serif }\n";
|
||||
|
||||
/**
|
||||
* Send the standard HTTP headers for an HTML file
|
||||
*/
|
||||
static void
|
||||
send_html_header(DCB *dcb)
|
||||
{
|
||||
char date[64] = "";
|
||||
const char *fmt = "%a, %d %b %Y %H:%M:%S GMT";
|
||||
|
||||
time_t httpd_current_time = time(NULL);
|
||||
|
||||
strftime(date, sizeof(date), fmt, localtime(&httpd_current_time));
|
||||
|
||||
dcb_printf(dcb, "HTTP/1.1 200 OK\r\nDate: %s\r\nServer: %s\r\nConnection: close\r\nContent-Type: text/html\r\n", date, "MaxScale");
|
||||
|
||||
dcb_printf(dcb, "\r\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a static HTML page
|
||||
*
|
||||
* @param dcb The DCB of the connection to the browser
|
||||
* @param html The HTML to send
|
||||
*/
|
||||
static void
|
||||
send_static_html(DCB *dcb, char *html)
|
||||
{
|
||||
dcb_printf(dcb, html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the index page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_index(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, index_page);
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the CSS
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_css(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, css);
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the title page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_title(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, title_page);
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the frame1 page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_frame1(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, frame1_page);
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the menu page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_menu(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, menu_page);
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a blank page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_blank(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
send_static_html(dcb, blank_page);
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a table row for a service. This is called using the service
|
||||
* iterator function
|
||||
*
|
||||
* @param service The service to display
|
||||
* @param dcb The DCB to print the HTML to
|
||||
*/
|
||||
static void
|
||||
service_row(SERVICE *service, DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD><TD>%d</TD><TD>%d</TD></TR>\n",
|
||||
service->name, service->routerModule,
|
||||
service->stats.n_current, service->stats.n_sessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the services page. This produces a table by means of the
|
||||
* serviceIterate call.
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_services(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
dcb_printf(dcb, "<HTML><HEAD>");
|
||||
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
|
||||
dcb_printf(dcb, "<BODY><H2>Services</H2><P>");
|
||||
dcb_printf(dcb, "<TABLE><TR><TH>Name</TH><TH>Router</TH><TH>");
|
||||
dcb_printf(dcb, "Current Sessions</TH><TH>Total Sessions</TH></TR>\n");
|
||||
serviceIterate(service_row, dcb);
|
||||
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a session row for a session. this is called using the session
|
||||
* iterator function
|
||||
*
|
||||
* @param session The session to display
|
||||
* @param dcb The DCB to send the HTML to
|
||||
*/
|
||||
static void
|
||||
session_row(SESSION *session, DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb, "<TR><TD>%-16p</TD><TD>%s</TD><TD>%s</TD><TD>%s</TD></TR>\n",
|
||||
session, ((session->client && session->client->remote)
|
||||
? session->client->remote : ""),
|
||||
(session->service && session->service->name
|
||||
? session->service->name : ""),
|
||||
session_state(session->state));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the sessions page. The produces a table of all the current sessions
|
||||
* display. It makes use of the sessionIterate call to call the function
|
||||
* session_row() with each session.
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_sessions(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
dcb_printf(dcb, "<HTML><HEAD>");
|
||||
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
|
||||
dcb_printf(dcb, "<BODY><H2>Sessions</H2><P>");
|
||||
dcb_printf(dcb, "<TABLE><TR><TH>Session</TH><TH>Client</TH><TH>");
|
||||
dcb_printf(dcb, "Service</TH><TH>State</TH></TR>\n");
|
||||
sessionIterate(session_row, dcb);
|
||||
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a table row for a particular server. This is called via the
|
||||
* serverIterate call in send_servers.
|
||||
*
|
||||
* @param server The server to print
|
||||
* @param dcb The DCB to send the HTML to
|
||||
*/
|
||||
static void
|
||||
server_row(SERVER *server, DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD><TD>%d</TD><TD>%s</TD><TD>%d</TD></TR>\n",
|
||||
server->unique_name, server->name, server->port,
|
||||
server_status(server), server->stats.n_current);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the servers page
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_servers(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
dcb_printf(dcb, "<HTML><HEAD>");
|
||||
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
|
||||
dcb_printf(dcb, "<BODY><H2>Servers</H2><P>");
|
||||
dcb_printf(dcb, "<TABLE><TR><TH>Server</TH><TH>Address</TH><TH>");
|
||||
dcb_printf(dcb, "Port</TH><TH>State</TH><TH>Connections</TH></TR>\n");
|
||||
serverIterate(server_row, dcb);
|
||||
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a table row for the monitors table
|
||||
*
|
||||
* @param monitor The monitor to print
|
||||
* @param dcb The DCB to print to
|
||||
*/
|
||||
static void
|
||||
monitor_row(MONITOR *monitor, DCB *dcb)
|
||||
{
|
||||
dcb_printf(dcb, "<TR><TD>%s</TD><TD>%s</TD></TR>\n",
|
||||
monitor->name, monitor->state & MONITOR_STATE_RUNNING
|
||||
? "Running" : "Stopped");
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the monitors page. This iterates on all the monitors and send
|
||||
* the rows via the monitor_monitor.
|
||||
*
|
||||
* @param session The router session
|
||||
*/
|
||||
static void
|
||||
send_monitors(WEB_SESSION *session)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
send_html_header(dcb);
|
||||
dcb_printf(dcb, "<HTML><HEAD>");
|
||||
dcb_printf(dcb, "<LINK REL=\"stylesheet\" type=\"text/css\" href=\"styles.css\">");
|
||||
dcb_printf(dcb, "<BODY><H2>Monitors</H2><P>");
|
||||
dcb_printf(dcb, "<TABLE><TR><TH>Monitor</TH><TH>State</TH></TR>\n");
|
||||
monitorIterate(monitor_row, dcb);
|
||||
dcb_printf(dcb, "</TABLE></BODY></HTML>\n");
|
||||
dcb_close(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond with an HTTP error
|
||||
*
|
||||
* @param session The router session
|
||||
* @param err The HTTP error code to send
|
||||
* @param msg The message to print
|
||||
*/
|
||||
static void
|
||||
respond_error(WEB_SESSION *session, int err, char *msg)
|
||||
{
|
||||
DCB *dcb = session->session->client;
|
||||
|
||||
dcb_printf(dcb, "HTTP/1.1 %d %s\n", err, msg);
|
||||
dcb_printf(dcb, "Content-Type: text/html\n");
|
||||
dcb_printf(dcb, "\n");
|
||||
dcb_printf(dcb, "<HTML><BODY>\n");
|
||||
dcb_printf(dcb, "MaxScale webserver plugin unable to satisfy request.\n");
|
||||
dcb_printf(dcb, "<P>Code: %d, %s\n", err, msg);
|
||||
dcb_printf(dcb, "</BODY></HTML>");
|
||||
dcb_close(dcb);
|
||||
}
|
Reference in New Issue
Block a user