Clean up instance and session lifecycle functions

The createInstace, newSession, closeSession and freeSession functions were
cleaned up and reorganized to be a bit clearer for the reader. Removed
unnecessary comments and replaced them with ones that explain what's
happening in the code.

Removed unused linked lists from both sessions and instances and replaced
them with better alternatives. This should improve performance since new
session don't have to acquire the instance level lock to put themselves
into the session list.
This commit is contained in:
Markus Makela
2016-11-04 09:11:32 +02:00
parent 88dca05dc7
commit ea0dcea5d6
2 changed files with 156 additions and 271 deletions

View File

@ -43,7 +43,7 @@ MODULE_INFO info =
* by the entry point functions. Some of these are used by functions in other * by the entry point functions. Some of these are used by functions in other
* modules of the read write split router, others are used only within this * modules of the read write split router, others are used only within this
* module. * module.
* *
* @verbatim * @verbatim
* Revision History * Revision History
* *
@ -107,13 +107,6 @@ static ROUTER_OBJECT MyObject =
getCapabilities getCapabilities
}; };
/*
* A couple of static variables that are used throughout the router
*/
static SPINLOCK instlock;
static ROUTER_INSTANCE *instances;
/* /*
* Declaration of functions that are used only within this module, and are * Declaration of functions that are used only within this module, and are
* not part of the API. * not part of the API.
@ -129,9 +122,9 @@ static void handle_error_reply_client(SESSION *ses, ROUTER_CLIENT_SES *rses,
static bool handle_error_new_connection(ROUTER_INSTANCE *inst, static bool handle_error_new_connection(ROUTER_INSTANCE *inst,
ROUTER_CLIENT_SES **rses, ROUTER_CLIENT_SES **rses,
DCB *backend_dcb, GWBUF *errmsg); DCB *backend_dcb, GWBUF *errmsg);
static bool have_enough_servers(ROUTER_CLIENT_SES **p_rses, const int min_nsrv, static bool have_enough_servers(ROUTER_CLIENT_SES *rses, const int min_nsrv,
int router_nsrv, ROUTER_INSTANCE *router); int router_nsrv, ROUTER_INSTANCE *router);
static bool create_backends(ROUTER_CLIENT_SES *rses, backend_ref_t** dest, int* n_backend);
/** /**
* Implementation of the mandatory version entry point * Implementation of the mandatory version entry point
* *
@ -149,8 +142,6 @@ char *version()
void ModuleInit() void ModuleInit()
{ {
MXS_NOTICE("Initializing statement-based read/write split router module."); MXS_NOTICE("Initializing statement-based read/write split router module.");
spinlock_init(&instlock);
instances = NULL;
} }
/** /**
@ -172,7 +163,7 @@ ROUTER_OBJECT *GetModuleObject()
/** /**
* @brief Create an instance of the read/write router (API). * @brief Create an instance of the read/write router (API).
* *
* Create an instance of read/write statement router within the MaxScale. One * Create an instance of read/write statement router within the MaxScale. One
* instance of the router is required for each service that is defined in the * instance of the router is required for each service that is defined in the
* configuration as using this router. One instance of the router will handle * configuration as using this router. One instance of the router will handle
@ -261,15 +252,6 @@ static ROUTER *createInstance(SERVICE *service, char **options)
{ {
refreshInstance(router, param); refreshInstance(router, param);
} }
/**
* We have completed the creation of the router data, so now
* insert this router into the linked list of routers
* that have been created with this module.
*/
spinlock_acquire(&instlock);
router->next = instances;
instances = router;
spinlock_release(&instlock);
return (ROUTER *)router; return (ROUTER *)router;
} }
@ -277,14 +259,14 @@ static ROUTER *createInstance(SERVICE *service, char **options)
/** /**
* @brief Associate a new session with this instance of the router (API). * @brief Associate a new session with this instance of the router (API).
* *
* The session is used to store all the data required by the router for a * The session is used to store all the data required by the router for a
* particular client connection. The instance of the router that relates to a * particular client connection. The instance of the router that relates to a
* particular service is passed as the first parameter. The second parameter is * particular service is passed as the first parameter. The second parameter is
* the session that has been created in response to the request from a client * the session that has been created in response to the request from a client
* for a connection. The passed session contains generic information; this * for a connection. The passed session contains generic information; this
* function creates the session structure that holds router specific data. * function creates the session structure that holds router specific data.
* There is often a one to one relationship between sessions and router * There is often a one to one relationship between sessions and router
* sessions, although it is possible to create configurations where a * sessions, although it is possible to create configurations where a
* connection is handled by multiple routers, one after another. * connection is handled by multiple routers, one after another.
* *
* @param instance The router instance data * @param instance The router instance data
@ -293,17 +275,8 @@ static ROUTER *createInstance(SERVICE *service, char **options)
*/ */
static void *newSession(ROUTER *router_inst, SESSION *session) static void *newSession(ROUTER *router_inst, SESSION *session)
{ {
backend_ref_t *backend_ref; /*< array of backend references (DCB,BACKEND,cursor) */
backend_ref_t *master_ref = NULL; /*< pointer to selected master */
ROUTER_CLIENT_SES *client_rses = NULL;
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)router_inst; ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)router_inst;
bool succp; ROUTER_CLIENT_SES *client_rses = (ROUTER_CLIENT_SES *)MXS_CALLOC(1, sizeof(ROUTER_CLIENT_SES));
int router_nservers = 0; /*< # of servers in total */
int max_nslaves; /*< max # of slaves used in this session */
int max_slave_rlag; /*< max allowed replication lag for any slave */
const int min_nservers = 1; /*< hard-coded for now */
client_rses = (ROUTER_CLIENT_SES *)MXS_CALLOC(1, sizeof(ROUTER_CLIENT_SES));
if (client_rses == NULL) if (client_rses == NULL)
{ {
@ -316,117 +289,50 @@ static void *newSession(ROUTER *router_inst, SESSION *session)
client_rses->router = router; client_rses->router = router;
client_rses->client_dcb = session->client_dcb; client_rses->client_dcb = session->client_dcb;
/**
* If service config has been changed, reload config from service to
* router instance first.
*/
spinlock_acquire(&router->lock);
if (router->service->svc_config_version > router->rwsplit_version)
{
/** re-read all parameters to rwsplit config structure */
refreshInstance(router, NULL); /*< scan through all parameters */
/** increment rwsplit router's config version number */
router->rwsplit_version = router->service->svc_config_version;
/** Read options */
rwsplit_process_router_options(router, router->service->routerOptions);
}
/** Copy config struct from router instance */
memcpy(&client_rses->rses_config, &router->rwsplit_config, sizeof(rwsplit_config_t));
spinlock_release(&router->lock);
/**
* Set defaults to session variables.
*/
client_rses->rses_autocommit_enabled = true; client_rses->rses_autocommit_enabled = true;
client_rses->rses_transaction_active = false; client_rses->rses_transaction_active = false;
client_rses->have_tmp_tables = false; client_rses->have_tmp_tables = false;
client_rses->forced_node = NULL; client_rses->forced_node = NULL;
spinlock_init(&client_rses->rses_lock);
memcpy(&client_rses->rses_config, &router->rwsplit_config, sizeof(client_rses->rses_config));
router_nservers = router->service->n_dbref; int router_nservers = router->service->n_dbref;
const int min_nservers = 1; /*< hard-coded for now */
if (!have_enough_servers(&client_rses, min_nservers, router_nservers, router)) if (!have_enough_servers(client_rses, min_nservers, router_nservers, router))
{ {
MXS_FREE(client_rses); MXS_FREE(client_rses);
return NULL; return NULL;
} }
/** /**
* Create backend reference objects for this session. * Create backend reference objects for this session.
*/ */
backend_ref = (backend_ref_t *)MXS_CALLOC(1, router_nservers * sizeof(backend_ref_t)); backend_ref_t *backend_ref;
if (backend_ref == NULL) if (!create_backends(client_rses, &backend_ref, &router_nservers))
{ {
/** log this */
MXS_FREE(client_rses); MXS_FREE(client_rses);
return NULL; return NULL;
} }
/**
* Initialize backend references with BACKEND ptr.
* Initialize session command cursors for each backend reference.
*/
int i = 0;
for (SERVER_REF *sref = router->service->dbref; sref && i < router_nservers; sref = sref->next)
{
if (sref->active)
{
#if defined(SS_DEBUG)
backend_ref[i].bref_chk_top = CHK_NUM_BACKEND_REF;
backend_ref[i].bref_chk_tail = CHK_NUM_BACKEND_REF;
backend_ref[i].bref_sescmd_cur.scmd_cur_chk_top = CHK_NUM_SESCMD_CUR;
backend_ref[i].bref_sescmd_cur.scmd_cur_chk_tail = CHK_NUM_SESCMD_CUR;
#endif
backend_ref[i].bref_state = 0;
backend_ref[i].ref = sref;
/** store pointers to sescmd list to both cursors */
backend_ref[i].bref_sescmd_cur.scmd_cur_rses = client_rses;
backend_ref[i].bref_sescmd_cur.scmd_cur_active = false;
backend_ref[i].bref_sescmd_cur.scmd_cur_ptr_property =
&client_rses->rses_properties[RSES_PROP_TYPE_SESCMD];
backend_ref[i].bref_sescmd_cur.scmd_cur_cmd = NULL;
i++;
}
}
if (i < router_nservers) int max_nslaves = rses_get_max_slavecount(client_rses, router_nservers);
{ int max_slave_rlag = rses_get_max_replication_lag(client_rses);
/** The service reported more servers than we took into use */
router_nservers = i;
}
max_nslaves = rses_get_max_slavecount(client_rses, router_nservers);
max_slave_rlag = rses_get_max_replication_lag(client_rses);
spinlock_init(&client_rses->rses_lock);
client_rses->rses_backend_ref = backend_ref; client_rses->rses_backend_ref = backend_ref;
client_rses->rses_nbackends = router_nservers; /*< # of backend servers */
/** backend_ref_t *master_ref = NULL; /*< pointer to selected master */
* Find a backend servers to connect to. if (!select_connect_backend_servers(&master_ref, backend_ref, router_nservers,
* This command requires that rsession's lock is held. max_nslaves, max_slave_rlag,
*/ client_rses->rses_config.rw_slave_select_criteria,
session, router))
succp = rses_begin_locked_router_action(client_rses);
if (!succp)
{
MXS_FREE(client_rses->rses_backend_ref);
MXS_FREE(client_rses);
return NULL;
}
succp = select_connect_backend_servers(&master_ref, backend_ref, router_nservers,
max_nslaves, max_slave_rlag,
client_rses->rses_config.rw_slave_select_criteria,
session, router);
rses_end_locked_router_action(client_rses);
/**
* Master and at least <min_nslaves> slaves must be found if the router is
* in the strict mode. If sessions without master are allowed, only
* <min_nslaves> slaves must be found.
*/
if (!succp)
{ {
/**
* Master and at least <min_nslaves> slaves must be found if the router is
* in the strict mode. If sessions without master are allowed, only
* <min_nslaves> slaves must be found.
*/
MXS_FREE(client_rses->rses_backend_ref); MXS_FREE(client_rses->rses_backend_ref);
MXS_FREE(client_rses); MXS_FREE(client_rses);
return NULL; return NULL;
@ -434,7 +340,6 @@ static void *newSession(ROUTER *router_inst, SESSION *session)
/** Copy backend pointers to router session. */ /** Copy backend pointers to router session. */
client_rses->rses_master_ref = master_ref; client_rses->rses_master_ref = master_ref;
client_rses->rses_nbackends = router_nservers; /*< # of backend servers */
if (client_rses->rses_config.rw_max_slave_conn_percent) if (client_rses->rses_config.rw_max_slave_conn_percent)
{ {
@ -446,23 +351,15 @@ static void *newSession(ROUTER *router_inst, SESSION *session)
router->stats.n_sessions += 1; router->stats.n_sessions += 1;
/**
* Add this session to end of the list of active sessions in router.
*/
spinlock_acquire(&router->lock);
client_rses->next = router->connections;
router->connections = client_rses;
spinlock_release(&router->lock);
return (void *)client_rses; return (void *)client_rses;
} }
/** /**
* @brief Close a router session (API). * @brief Close a router session (API).
* *
* Close a session with the router, this is the mechanism by which a router * Close a session with the router, this is the mechanism by which a router
* may cleanup data structure etc. The instance of the router that relates to * may cleanup data structure etc. The instance of the router that relates to
* the relevant service is passed, along with the router session that is to * the relevant service is passed, along with the router session that is to
* be closed. Typically the function is used in conjunction with freeSession * be closed. Typically the function is used in conjunction with freeSession
* which will release the resources used by a router session (see below). * which will release the resources used by a router session (see below).
* *
@ -471,58 +368,38 @@ static void *newSession(ROUTER *router_inst, SESSION *session)
*/ */
static void closeSession(ROUTER *instance, void *router_session) static void closeSession(ROUTER *instance, void *router_session)
{ {
ROUTER_CLIENT_SES *router_cli_ses; ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
backend_ref_t *backend_ref;
MXS_DEBUG("%lu [RWSplit:closeSession]", pthread_self());
/**
* router session can be NULL if newSession failed and it is discarding
* its connections and DCB's.
*/
if (router_session == NULL)
{
return;
}
router_cli_ses = (ROUTER_CLIENT_SES *)router_session;
CHK_CLIENT_RSES(router_cli_ses); CHK_CLIENT_RSES(router_cli_ses);
backend_ref = router_cli_ses->rses_backend_ref; if (!router_cli_ses->rses_closed && rses_begin_locked_router_action(router_cli_ses))
/**
* Lock router client session for secure read and update.
*/
if (!router_cli_ses->rses_closed &&
rses_begin_locked_router_action(router_cli_ses))
{ {
int i;
/** /**
* This sets router closed. Nobody is allowed to use router * Mark router session as closed. @c rses_closed is checked at the start
* without checking this first. * of every API function to quickly stop the processing of closed sessions.
*/ */
router_cli_ses->rses_closed = true; router_cli_ses->rses_closed = true;
for (i = 0; i < router_cli_ses->rses_nbackends; i++) for (int i = 0; i < router_cli_ses->rses_nbackends; i++)
{ {
backend_ref_t *bref = &backend_ref[i]; backend_ref_t *bref = &router_cli_ses->rses_backend_ref[i];
DCB *dcb = bref->bref_dcb;
/** Close those which had been connected */
if (BREF_IS_IN_USE(bref)) if (BREF_IS_IN_USE(bref))
{ {
/** This backend is in use and it needs to be closed */
DCB *dcb = bref->bref_dcb;
CHK_DCB(dcb); CHK_DCB(dcb);
ss_dassert(dcb->session->state == SESSION_STATE_STOPPING); ss_dassert(dcb->session->state == SESSION_STATE_STOPPING);
/** Clean operation counter in bref and in SERVER */
if (BREF_IS_WAITING_RESULT(bref)) if (BREF_IS_WAITING_RESULT(bref))
{ {
/** This backend was executing a query when the session was closed */
bref_clear_state(bref, BREF_WAITING_RESULT); bref_clear_state(bref, BREF_WAITING_RESULT);
} }
bref_clear_state(bref, BREF_IN_USE); bref_clear_state(bref, BREF_IN_USE);
bref_set_state(bref, BREF_CLOSED); bref_set_state(bref, BREF_CLOSED);
/**
* closes protocol and dcb
*/
dcb_close(dcb); dcb_close(dcb);
/** decrease server current connection counters */
/** Decrease server reference connection count */
atomic_add(&bref->ref->connections, -1); atomic_add(&bref->ref->connections, -1);
} }
else else
@ -539,15 +416,15 @@ static void closeSession(ROUTER *instance, void *router_session)
} }
} }
} }
/** Unlock */
rses_end_locked_router_action(router_cli_ses); rses_end_locked_router_action(router_cli_ses);
} }
} }
/** /**
* @brief Free a router session (API). * @brief Free a router session (API).
* *
* When a router session has been closed, freeSession can be called to free * When a router session has been closed, freeSession can be called to free
* allocated resources. * allocated resources.
* *
* @param router_instance The router instance the session belongs to * @param router_instance The router instance the session belongs to
@ -556,40 +433,13 @@ static void closeSession(ROUTER *instance, void *router_session)
*/ */
static void freeSession(ROUTER *router_instance, void *router_client_session) static void freeSession(ROUTER *router_instance, void *router_client_session)
{ {
ROUTER_CLIENT_SES *router_cli_ses; ROUTER_CLIENT_SES *router_cli_ses = (ROUTER_CLIENT_SES *)router_client_session;
ROUTER_INSTANCE *router;
int i;
router_cli_ses = (ROUTER_CLIENT_SES *)router_client_session;
router = (ROUTER_INSTANCE *)router_instance;
spinlock_acquire(&router->lock);
if (router->connections == router_cli_ses)
{
router->connections = router_cli_ses->next;
}
else
{
ROUTER_CLIENT_SES *ptr = router->connections;
while (ptr && ptr->next != router_cli_ses)
{
ptr = ptr->next;
}
if (ptr)
{
ptr->next = router_cli_ses->next;
}
}
spinlock_release(&router->lock);
/** /**
* For each property type, walk through the list, finalize properties * For each property type, walk through the list, finalize properties
* and free the allocated memory. * and free the allocated memory.
*/ */
for (i = RSES_PROP_TYPE_FIRST; i < RSES_PROP_TYPE_COUNT; i++) for (int i = RSES_PROP_TYPE_FIRST; i < RSES_PROP_TYPE_COUNT; i++)
{ {
rses_property_t *p = router_cli_ses->rses_properties[i]; rses_property_t *p = router_cli_ses->rses_properties[i];
rses_property_t *q = p; rses_property_t *q = p;
@ -601,11 +451,7 @@ static void freeSession(ROUTER *router_instance, void *router_client_session)
p = q; p = q;
} }
} }
/*
* We are no longer in the linked list, free
* all the memory and other resources associated
* to the client session.
*/
MXS_FREE(router_cli_ses->rses_backend_ref); MXS_FREE(router_cli_ses->rses_backend_ref);
MXS_FREE(router_cli_ses); MXS_FREE(router_cli_ses);
return; return;
@ -663,20 +509,8 @@ static int routeQuery(ROUTER *instance, void *router_session, GWBUF *querybuf)
*/ */
static void diagnostics(ROUTER *instance, DCB *dcb) static void diagnostics(ROUTER *instance, DCB *dcb)
{ {
ROUTER_CLIENT_SES *router_cli_ses;
ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance; ROUTER_INSTANCE *router = (ROUTER_INSTANCE *)instance;
int i = 0;
char *weightby; char *weightby;
spinlock_acquire(&router->lock);
router_cli_ses = router->connections;
while (router_cli_ses)
{
i++;
router_cli_ses = router_cli_ses->next;
}
spinlock_release(&router->lock);
double master_pct = 0.0, slave_pct = 0.0, all_pct = 0.0; double master_pct = 0.0, slave_pct = 0.0, all_pct = 0.0;
if (router->stats.n_queries > 0) if (router->stats.n_queries > 0)
@ -688,7 +522,8 @@ static void diagnostics(ROUTER *instance, DCB *dcb)
dcb_printf(dcb, "\tNumber of router sessions: %d\n", dcb_printf(dcb, "\tNumber of router sessions: %d\n",
router->stats.n_sessions); router->stats.n_sessions);
dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n", i); dcb_printf(dcb, "\tCurrent no. of router sessions: %d\n",
router->service->stats.n_current);
dcb_printf(dcb, "\tNumber of queries forwarded: %d\n", dcb_printf(dcb, "\tNumber of queries forwarded: %d\n",
router->stats.n_queries); router->stats.n_queries);
dcb_printf(dcb, "\tNumber of queries forwarded to master: %d (%.2f%%)\n", dcb_printf(dcb, "\tNumber of queries forwarded to master: %d (%.2f%%)\n",
@ -709,9 +544,9 @@ static void diagnostics(ROUTER *instance, DCB *dcb)
for (SERVER_REF *ref = router->service->dbref; ref; ref = ref->next) for (SERVER_REF *ref = router->service->dbref; ref; ref = ref->next)
{ {
dcb_printf(dcb, "\t\t%-20s %3.1f%% %-6d %-6d %d\n", dcb_printf(dcb, "\t\t%-20s %3.1f%% %-6d %-6d %d\n",
ref->server->unique_name, (float)ref->weight / 10, ref->server->unique_name, (float)ref->weight / 10,
ref->server->stats.n_current, ref->connections, ref->server->stats.n_current, ref->connections,
ref->server->stats.n_current_ops); ref->server->stats.n_current_ops);
} }
} }
} }
@ -796,7 +631,7 @@ static void clientReply(ROUTER *instance, void *router_session, GWBUF *writebuf,
if (sescmd_cursor_is_active(scur)) if (sescmd_cursor_is_active(scur))
{ {
check_session_command_reply(writebuf, scur, bref); check_session_command_reply(writebuf, scur, bref);
if (GWBUF_IS_TYPE_SESCMD_RESPONSE(writebuf)) if (GWBUF_IS_TYPE_SESCMD_RESPONSE(writebuf))
{ {
/** /**
@ -876,7 +711,7 @@ static void clientReply(ROUTER *instance, void *router_session, GWBUF *writebuf,
CHK_GWBUF(bref->bref_pending_cmd); CHK_GWBUF(bref->bref_pending_cmd);
if ((ret = bref->bref_dcb->func.write(bref->bref_dcb, if ((ret = bref->bref_dcb->func.write(bref->bref_dcb,
gwbuf_clone(bref->bref_pending_cmd))) == 1) gwbuf_clone(bref->bref_pending_cmd))) == 1)
{ {
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance; ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
atomic_add(&inst->stats.n_queries, 1); atomic_add(&inst->stats.n_queries, 1);
@ -992,12 +827,12 @@ void rses_end_locked_router_action(ROUTER_CLIENT_SES *rses)
/* /*
* @brief Clear one or more bits in the backend reference state * @brief Clear one or more bits in the backend reference state
* *
* The router session holds details of the backend servers that are * The router session holds details of the backend servers that are
* involved in the routing for this particular service. Each backend * involved in the routing for this particular service. Each backend
* server has a state bit string, and this function (along with * server has a state bit string, and this function (along with
* bref_set_state) is used to manage the state. * bref_set_state) is used to manage the state.
* *
* @param bref The backend reference to be modified * @param bref The backend reference to be modified
* @param state A bit string where the 1 bits indicate bits that should * @param state A bit string where the 1 bits indicate bits that should
* be turned off in the bref state. * be turned off in the bref state.
@ -1041,12 +876,12 @@ void bref_clear_state(backend_ref_t *bref, bref_state_t state)
/* /*
* @brief Set one or more bits in the backend reference state * @brief Set one or more bits in the backend reference state
* *
* The router session holds details of the backend servers that are * The router session holds details of the backend servers that are
* involved in the routing for this particular service. Each backend * involved in the routing for this particular service. Each backend
* server has a state bit string, and this function (along with * server has a state bit string, and this function (along with
* bref_clear_state) is used to manage the state. * bref_clear_state) is used to manage the state.
* *
* @param bref The backend reference to be modified * @param bref The backend reference to be modified
* @param state A bit string where the 1 bits indicate bits that should * @param state A bit string where the 1 bits indicate bits that should
* be turned on in the bref state. * be turned on in the bref state.
@ -1088,9 +923,9 @@ void bref_set_state(backend_ref_t *bref, bref_state_t state)
/** /**
* @brief Free resources belonging to a property * @brief Free resources belonging to a property
* *
* Property is freed at the end of router client session. * Property is freed at the end of router client session.
* *
* @param prop The property whose resources are to be released * @param prop The property whose resources are to be released
*/ */
void rses_property_done(rses_property_t *prop) void rses_property_done(rses_property_t *prop)
@ -1124,16 +959,16 @@ void rses_property_done(rses_property_t *prop)
/** /**
* @brief Get count of backend servers that are slaves. * @brief Get count of backend servers that are slaves.
* *
* Find out the number of read backend servers. * Find out the number of read backend servers.
* Depending on the configuration value type, either copy direct count * Depending on the configuration value type, either copy direct count
* of slave connections or calculate the count from percentage value. * of slave connections or calculate the count from percentage value.
* *
* @param rses Router client session * @param rses Router client session
* @param router_nservers The number of backend servers in total * @param router_nservers The number of backend servers in total
*/ */
int rses_get_max_slavecount(ROUTER_CLIENT_SES *rses, int rses_get_max_slavecount(ROUTER_CLIENT_SES *rses,
int router_nservers) int router_nservers)
{ {
int conf_max_nslaves; int conf_max_nslaves;
int max_nslaves; int max_nslaves;
@ -1155,7 +990,7 @@ int rses_get_max_slavecount(ROUTER_CLIENT_SES *rses,
/* /*
* @brief Get the maximum replication lag for this router * @brief Get the maximum replication lag for this router
* *
* @param rses Router client session * @param rses Router client session
* @return Replication lag from configuration or very large number * @return Replication lag from configuration or very large number
*/ */
@ -1180,10 +1015,10 @@ int rses_get_max_replication_lag(ROUTER_CLIENT_SES *rses)
/** /**
* @brief Find a back end reference that matches the given DCB * @brief Find a back end reference that matches the given DCB
* *
* Finds out if there is a backend reference pointing at the DCB given as * Finds out if there is a backend reference pointing at the DCB given as
* parameter. * parameter.
* *
* @param rses router client session * @param rses router client session
* @param dcb DCB * @param dcb DCB
* *
@ -1217,14 +1052,14 @@ backend_ref_t *get_bref_from_dcb(ROUTER_CLIENT_SES *rses, DCB *dcb)
/** /**
* @brief Call hang up function * @brief Call hang up function
* *
* Calls hang-up function for DCB if it is not both running and in * Calls hang-up function for DCB if it is not both running and in
* master/slave/joined/ndb role. Called by DCB's callback routine. * master/slave/joined/ndb role. Called by DCB's callback routine.
* *
* @param dcb DCB relating to a backend server * @param dcb DCB relating to a backend server
* @param reason The reason for the state change * @param reason The reason for the state change
* @param data Data is a backend reference structure belonging to this router * @param data Data is a backend reference structure belonging to this router
* *
* @return 1 for success, 0 for failure * @return 1 for success, 0 for failure
*/ */
int router_handle_state_switch(DCB *dcb, DCB_REASON reason, void *data) int router_handle_state_switch(DCB *dcb, DCB_REASON reason, void *data)
@ -1324,10 +1159,10 @@ static bool rwsplit_process_router_options(ROUTER_INSTANCE *router,
if (c == UNDEFINED_CRITERIA) if (c == UNDEFINED_CRITERIA)
{ {
MXS_ERROR("Unknown slave selection criteria \"%s\". " MXS_ERROR("Unknown slave selection criteria \"%s\". "
"Allowed values are LEAST_GLOBAL_CONNECTIONS, " "Allowed values are LEAST_GLOBAL_CONNECTIONS, "
"LEAST_ROUTER_CONNECTIONS, LEAST_BEHIND_MASTER," "LEAST_ROUTER_CONNECTIONS, LEAST_BEHIND_MASTER,"
"and LEAST_CURRENT_OPERATIONS.", "and LEAST_CURRENT_OPERATIONS.",
STRCRITERIA(router->rwsplit_config.rw_slave_select_criteria)); STRCRITERIA(router->rwsplit_config.rw_slave_select_criteria));
success = false; success = false;
} }
else else
@ -1385,9 +1220,9 @@ static bool rwsplit_process_router_options(ROUTER_INSTANCE *router,
/** /**
* @brief Router error handling routine (API) * @brief Router error handling routine (API)
* *
* Error Handler routine to resolve _backend_ failures. If it succeeds then * Error Handler routine to resolve _backend_ failures. If it succeeds then
* there are enough operative backends available and connected. Otherwise it * there are enough operative backends available and connected. Otherwise it
* fails, and session is terminated. * fails, and session is terminated.
* *
* @param instance The router instance * @param instance The router instance
@ -1450,8 +1285,8 @@ static void handleError(ROUTER *instance, void *router_session,
if (!rses_begin_locked_router_action(rses)) if (!rses_begin_locked_router_action(rses))
{ {
close_dcb = false; /* With the assumption that if the router session is closed, close_dcb = false; /* With the assumption that if the router session is closed,
* then so is the dcb. * then so is the dcb.
*/ */
*succp = false; *succp = false;
break; break;
} }
@ -1548,8 +1383,8 @@ static void handleError(ROUTER *instance, void *router_session,
if (close_dcb) if (close_dcb)
{ {
dcb_close(problem_dcb); dcb_close(problem_dcb);
} }
} }
/** /**
@ -1697,7 +1532,7 @@ static bool handle_error_new_connection(ROUTER_INSTANCE *inst,
*/ */
if (inst->rwsplit_config.rw_disable_sescmd_hist) if (inst->rwsplit_config.rw_disable_sescmd_hist)
{ {
succp = have_enough_servers(&myrses, 1, myrses->rses_nbackends, inst) ? true : false; succp = have_enough_servers(myrses, 1, myrses->rses_nbackends, inst) ? true : false;
} }
else else
{ {
@ -1723,16 +1558,16 @@ return_succp:
* *
* @return bool - whether enough, side effect is error logging * @return bool - whether enough, side effect is error logging
*/ */
static bool have_enough_servers(ROUTER_CLIENT_SES **p_rses, const int min_nsrv, static bool have_enough_servers(ROUTER_CLIENT_SES *rses, const int min_nsrv,
int router_nsrv, ROUTER_INSTANCE *router) int router_nsrv, ROUTER_INSTANCE *router)
{ {
bool succp; bool succp;
/** With too few servers session is not created */ /** With too few servers session is not created */
if (router_nsrv < min_nsrv || if (router_nsrv < min_nsrv ||
MXS_MAX((*p_rses)->rses_config.rw_max_slave_conn_count, MXS_MAX((rses)->rses_config.rw_max_slave_conn_count,
(router_nsrv * (*p_rses)->rses_config.rw_max_slave_conn_percent) / (router_nsrv * (rses)->rses_config.rw_max_slave_conn_percent) /
100) < min_nsrv) 100) < min_nsrv)
{ {
if (router_nsrv < min_nsrv) if (router_nsrv < min_nsrv)
{ {
@ -1743,16 +1578,16 @@ static bool have_enough_servers(ROUTER_CLIENT_SES **p_rses, const int min_nsrv,
} }
else else
{ {
int pct = (*p_rses)->rses_config.rw_max_slave_conn_percent / 100; int pct = (rses)->rses_config.rw_max_slave_conn_percent / 100;
int nservers = router_nsrv * pct; int nservers = router_nsrv * pct;
if ((*p_rses)->rses_config.rw_max_slave_conn_count < min_nsrv) if ((rses)->rses_config.rw_max_slave_conn_count < min_nsrv)
{ {
MXS_ERROR("Unable to start %s service. There are " MXS_ERROR("Unable to start %s service. There are "
"too few backend servers configured in " "too few backend servers configured in "
"MaxScale.cnf. Found %d when %d is required.", "MaxScale.cnf. Found %d when %d is required.",
router->service->name, router->service->name,
(*p_rses)->rses_config.rw_max_slave_conn_count, min_nsrv); (rses)->rses_config.rw_max_slave_conn_count, min_nsrv);
} }
if (nservers < min_nsrv) if (nservers < min_nsrv)
{ {
@ -1762,11 +1597,9 @@ static bool have_enough_servers(ROUTER_CLIENT_SES **p_rses, const int min_nsrv,
"MaxScale.cnf. Found %d%% when at least %.0f%% " "MaxScale.cnf. Found %d%% when at least %.0f%% "
"would be required.", "would be required.",
router->service->name, router->service->name,
(*p_rses)->rses_config.rw_max_slave_conn_percent, dbgpct); (rses)->rses_config.rw_max_slave_conn_percent, dbgpct);
} }
} }
MXS_FREE(*p_rses);
*p_rses = NULL;
succp = false; succp = false;
} }
else else
@ -1780,7 +1613,7 @@ static bool have_enough_servers(ROUTER_CLIENT_SES **p_rses, const int min_nsrv,
* @brief Refresh the instance by the given parameter value. * @brief Refresh the instance by the given parameter value.
* *
* Used by createInstance and newSession * Used by createInstance and newSession
* *
* @param router Router instance * @param router Router instance
* @param singleparam Parameter fo be reloaded * @param singleparam Parameter fo be reloaded
* *
@ -1884,11 +1717,11 @@ static void refreshInstance(ROUTER_INSTANCE *router,
/* /*
* @brief Release resources when createInstance fails to complete * @brief Release resources when createInstance fails to complete
* *
* Internal to createInstance * Internal to createInstance
* *
* @param router Router instance * @param router Router instance
* *
*/ */
static void free_rwsplit_instance(ROUTER_INSTANCE *router) static void free_rwsplit_instance(ROUTER_INSTANCE *router)
{ {
@ -1898,3 +1731,57 @@ static void free_rwsplit_instance(ROUTER_INSTANCE *router)
} }
} }
/**
* @brief Create backend server references
*
* This creates a new set of backend references for the client session. Currently
* this is only used on startup but it could be used to dynamically change the
* set of used servers.
*
* @param rses Client router session
* @param dest Destination where the array of backens is stored
* @param n_backend Number of items in the array
* @return True on success, false on error
*/
static bool create_backends(ROUTER_CLIENT_SES *rses, backend_ref_t** dest, int* n_backend)
{
backend_ref_t *backend_ref = (backend_ref_t *)MXS_CALLOC(1, *n_backend * sizeof(backend_ref_t));
if (backend_ref == NULL)
{
return false;
}
int i = 0;
for (SERVER_REF *sref = rses->router->service->dbref; sref && i < *n_backend; sref = sref->next)
{
if (sref->active)
{
#if defined(SS_DEBUG)
backend_ref[i].bref_chk_top = CHK_NUM_BACKEND_REF;
backend_ref[i].bref_chk_tail = CHK_NUM_BACKEND_REF;
backend_ref[i].bref_sescmd_cur.scmd_cur_chk_top = CHK_NUM_SESCMD_CUR;
backend_ref[i].bref_sescmd_cur.scmd_cur_chk_tail = CHK_NUM_SESCMD_CUR;
#endif
backend_ref[i].bref_state = 0;
backend_ref[i].ref = sref;
/** store pointers to sescmd list to both cursors */
backend_ref[i].bref_sescmd_cur.scmd_cur_rses = rses;
backend_ref[i].bref_sescmd_cur.scmd_cur_active = false;
backend_ref[i].bref_sescmd_cur.scmd_cur_ptr_property =
&rses->rses_properties[RSES_PROP_TYPE_SESCMD];
backend_ref[i].bref_sescmd_cur.scmd_cur_cmd = NULL;
i++;
}
}
if (i < *n_backend)
{
MXS_INFO("The service reported %d servers but only took %d into use.", *n_backend, i);
*n_backend = i;
}
*dest = backend_ref;
return true;
}

View File

@ -304,12 +304,10 @@ typedef struct
typedef struct router_instance typedef struct router_instance
{ {
SERVICE* service; /*< Pointer to service */ SERVICE* service; /*< Pointer to service */
ROUTER_CLIENT_SES* connections; /*< List of client connections */
SPINLOCK lock; /*< Lock for the instance data */ SPINLOCK lock; /*< Lock for the instance data */
rwsplit_config_t rwsplit_config; /*< expanded config info from SERVICE */ rwsplit_config_t rwsplit_config; /*< expanded config info from SERVICE */
int rwsplit_version; /*< version number for router's config */ int rwsplit_version; /*< version number for router's config */
ROUTER_STATS stats; /*< Statistics for this router */ ROUTER_STATS stats; /*< Statistics for this router */
struct router_instance* next; /*< Next router on the list */
bool available_slaves; /*< The router has some slaves avialable */ bool available_slaves; /*< The router has some slaves avialable */
} ROUTER_INSTANCE; } ROUTER_INSTANCE;