Fix to MXS-430: https://mariadb.atlassian.net/browse/MXS-430
Added caching of the hashtables used to map databases to servers.
This commit is contained in:
@ -56,6 +56,26 @@ typedef enum showdb_response
|
|||||||
SHOWDB_DUPLICATE_DATABASES,
|
SHOWDB_DUPLICATE_DATABASES,
|
||||||
SHOWDB_FATAL_ERROR
|
SHOWDB_FATAL_ERROR
|
||||||
} showdb_response_t;
|
} showdb_response_t;
|
||||||
|
|
||||||
|
enum shard_map_state
|
||||||
|
{
|
||||||
|
SHMAP_UNINIT, /*< No databases have been added to this shard map */
|
||||||
|
SHMAP_READY, /*< All available databases have been added */
|
||||||
|
SHMAP_STALE /*< The shard map has old data or has not been updated recently */
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A map of the shards tied to a single user.
|
||||||
|
*/
|
||||||
|
typedef struct shard_map
|
||||||
|
{
|
||||||
|
HASHTABLE *hash; /*< A hashtable of database names and the servers which
|
||||||
|
* have these databases. */
|
||||||
|
SPINLOCK lock;
|
||||||
|
time_t last_updated;
|
||||||
|
enum shard_map_state state; /*< State of the shard map */
|
||||||
|
}shard_map_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The state of the backend server reference
|
* The state of the backend server reference
|
||||||
*/
|
*/
|
||||||
@ -274,6 +294,8 @@ typedef struct {
|
|||||||
double ses_longest; /*< Longest session */
|
double ses_longest; /*< Longest session */
|
||||||
double ses_shortest; /*< Shortest session */
|
double ses_shortest; /*< Shortest session */
|
||||||
double ses_average; /*< Average session length */
|
double ses_average; /*< Average session length */
|
||||||
|
int shmap_cache_hit; /*< Shard map was found from the cache */
|
||||||
|
int shmap_cache_miss;/*< No shard map found from the cache */
|
||||||
} ROUTER_STATS;
|
} ROUTER_STATS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -299,7 +321,7 @@ struct router_client_session {
|
|||||||
bool rses_transaction_active; /*< Is a transaction active */
|
bool rses_transaction_active; /*< Is a transaction active */
|
||||||
struct router_instance *router; /*< The router instance */
|
struct router_instance *router; /*< The router instance */
|
||||||
struct router_client_session* next; /*< List of router sessions */
|
struct router_client_session* next; /*< List of router sessions */
|
||||||
HASHTABLE* dbhash; /*< Database hash containing names of the databases mapped to the servers that contain them */
|
shard_map_t* shardmap; /*< Database hash containing names of the databases mapped to the servers that contain them */
|
||||||
char connect_db[MYSQL_DATABASE_MAXLEN+1]; /*< Database the user was trying to connect to */
|
char connect_db[MYSQL_DATABASE_MAXLEN+1]; /*< Database the user was trying to connect to */
|
||||||
init_mask_t init; /*< Initialization state bitmask */
|
init_mask_t init; /*< Initialization state bitmask */
|
||||||
GWBUF* queue; /*< Query that was received before the session was ready */
|
GWBUF* queue; /*< Query that was received before the session was ready */
|
||||||
@ -318,6 +340,7 @@ struct router_client_session {
|
|||||||
* The per instance data for the router.
|
* The per instance data for the router.
|
||||||
*/
|
*/
|
||||||
typedef struct router_instance {
|
typedef struct router_instance {
|
||||||
|
HASHTABLE* shard_maps; /*< Shard maps hashed by user name */
|
||||||
SERVICE* service; /*< Pointer to service */
|
SERVICE* service; /*< Pointer to service */
|
||||||
ROUTER_CLIENT_SES* connections; /*< List of client connections */
|
ROUTER_CLIENT_SES* connections; /*< List of client connections */
|
||||||
SPINLOCK lock; /*< Lock for the instance data */
|
SPINLOCK lock; /*< Lock for the instance data */
|
||||||
|
@ -41,6 +41,9 @@
|
|||||||
/** Size of the hashtable used to store ignored databases */
|
/** Size of the hashtable used to store ignored databases */
|
||||||
#define SCHEMAROUTER_HASHSIZE 100
|
#define SCHEMAROUTER_HASHSIZE 100
|
||||||
|
|
||||||
|
/** Hashtable size for the per user shard maps */
|
||||||
|
#define SCHEMAROUTER_USERHASH_SIZE 10
|
||||||
|
|
||||||
MODULE_INFO info = {
|
MODULE_INFO info = {
|
||||||
MODULE_API_ROUTER,
|
MODULE_API_ROUTER,
|
||||||
MODULE_BETA_RELEASE,
|
MODULE_BETA_RELEASE,
|
||||||
@ -223,6 +226,7 @@ int inspect_backend_mapping_states(ROUTER_CLIENT_SES *router_cli_ses,
|
|||||||
GWBUF** wbuf);
|
GWBUF** wbuf);
|
||||||
bool handle_default_db(ROUTER_CLIENT_SES *router_cli_ses);
|
bool handle_default_db(ROUTER_CLIENT_SES *router_cli_ses);
|
||||||
void route_queued_query(ROUTER_CLIENT_SES *router_cli_ses);
|
void route_queued_query(ROUTER_CLIENT_SES *router_cli_ses);
|
||||||
|
void synchronize_shard_map(ROUTER_CLIENT_SES *client);
|
||||||
static int hashkeyfun(void* key)
|
static int hashkeyfun(void* key)
|
||||||
{
|
{
|
||||||
if(key == NULL){
|
if(key == NULL){
|
||||||
@ -247,7 +251,39 @@ static int hashcmpfun(
|
|||||||
return strcmp(i1,i2);
|
return strcmp(i1,i2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* keyfreefun(void* data)
|
||||||
|
{
|
||||||
|
free(data);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a shard map and initialize it.
|
||||||
|
* @return Pointer to new shard_map_t or NULL if memory allocation failed
|
||||||
|
*/
|
||||||
|
shard_map_t* create_shard_map()
|
||||||
|
{
|
||||||
|
shard_map_t *rval;
|
||||||
|
|
||||||
|
if ((rval = (shard_map_t*) malloc(sizeof(shard_map_t))))
|
||||||
|
{
|
||||||
|
if ((rval->hash = hashtable_alloc(SCHEMAROUTER_HASHSIZE, hashkeyfun, hashcmpfun)))
|
||||||
|
{
|
||||||
|
HASHMEMORYFN kcopy = (HASHMEMORYFN)strdup;
|
||||||
|
HASHMEMORYFN kfree = (HASHMEMORYFN)keyfreefun;
|
||||||
|
hashtable_memory_fns(rval->hash, kcopy, NULL, kfree, NULL);
|
||||||
|
spinlock_init(&rval->lock);
|
||||||
|
rval->last_updated = 0;
|
||||||
|
rval->state = SHMAP_UNINIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
free(rval);
|
||||||
|
rval = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a length encoded string into a C string.
|
* Convert a length encoded string into a C string.
|
||||||
@ -363,6 +399,7 @@ showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t*
|
|||||||
ptr += gw_mysql_get_byte3(ptr) + 4;
|
ptr += gw_mysql_get_byte3(ptr) + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spinlock_acquire(&rses->shardmap->lock);
|
||||||
while (ptr < (unsigned char*) buf->end && !PTR_IS_EOF(ptr))
|
while (ptr < (unsigned char*) buf->end && !PTR_IS_EOF(ptr))
|
||||||
{
|
{
|
||||||
int payloadlen = gw_mysql_get_byte3(ptr);
|
int payloadlen = gw_mysql_get_byte3(ptr);
|
||||||
@ -371,7 +408,7 @@ showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t*
|
|||||||
|
|
||||||
if (data)
|
if (data)
|
||||||
{
|
{
|
||||||
if (hashtable_add(rses->dbhash, data, target))
|
if (hashtable_add(rses->shardmap->hash, data, target))
|
||||||
{
|
{
|
||||||
skygw_log_write(LOGFILE_TRACE, "schemarouter: <%s, %s>", target, data);
|
skygw_log_write(LOGFILE_TRACE, "schemarouter: <%s, %s>", target, data);
|
||||||
}
|
}
|
||||||
@ -385,7 +422,7 @@ showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t*
|
|||||||
{
|
{
|
||||||
duplicate_found = true;
|
duplicate_found = true;
|
||||||
skygw_log_write(LE, "Error: Database '%s' found on servers '%s' and '%s' for user %s@%s.",
|
skygw_log_write(LE, "Error: Database '%s' found on servers '%s' and '%s' for user %s@%s.",
|
||||||
data, target, hashtable_fetch(rses->dbhash, data),
|
data, target, hashtable_fetch(rses->shardmap->hash, data),
|
||||||
rses->rses_client_dcb->user,
|
rses->rses_client_dcb->user,
|
||||||
rses->rses_client_dcb->remote);
|
rses->rses_client_dcb->remote);
|
||||||
}
|
}
|
||||||
@ -394,6 +431,7 @@ showdb_response_t parse_showdb_response(ROUTER_CLIENT_SES* rses, backend_ref_t*
|
|||||||
}
|
}
|
||||||
ptr += packetlen;
|
ptr += packetlen;
|
||||||
}
|
}
|
||||||
|
spinlock_release(&rses->shardmap->lock);
|
||||||
|
|
||||||
if (ptr < (unsigned char*) buf->end && PTR_IS_EOF(ptr) &&
|
if (ptr < (unsigned char*) buf->end && PTR_IS_EOF(ptr) &&
|
||||||
bref->n_mapping_eof == 1)
|
bref->n_mapping_eof == 1)
|
||||||
@ -478,8 +516,8 @@ int gen_databaselist(ROUTER_INSTANCE* inst, ROUTER_CLIENT_SES* session)
|
|||||||
* @param buffer Query to inspect
|
* @param buffer Query to inspect
|
||||||
* @return Name of the backend or NULL if the query contains no known databases.
|
* @return Name of the backend or NULL if the query contains no known databases.
|
||||||
*/
|
*/
|
||||||
char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF* buffer,skygw_query_type_t qtype){
|
char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client, GWBUF* buffer,skygw_query_type_t qtype)
|
||||||
HASHTABLE* ht = client->dbhash;
|
{
|
||||||
int sz = 0,i,j;
|
int sz = 0,i,j;
|
||||||
char** dbnms = NULL;
|
char** dbnms = NULL;
|
||||||
char* rval = NULL,*query, *tmp = NULL;
|
char* rval = NULL,*query, *tmp = NULL;
|
||||||
@ -491,6 +529,9 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client,
|
|||||||
|
|
||||||
dbnms = skygw_get_database_names(buffer,&sz);
|
dbnms = skygw_get_database_names(buffer,&sz);
|
||||||
|
|
||||||
|
spinlock_acquire(&client->shardmap->lock);
|
||||||
|
HASHTABLE* ht = client->shardmap->hash;
|
||||||
|
|
||||||
if(sz > 0){
|
if(sz > 0){
|
||||||
for(i = 0; i < sz; i++){
|
for(i = 0; i < sz; i++){
|
||||||
char* name;
|
char* name;
|
||||||
@ -553,6 +594,7 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client,
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
spinlock_release(&client->shardmap->lock);
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,6 +627,7 @@ char* get_shard_target_name(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spinlock_release(&client->shardmap->lock);
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -765,6 +808,17 @@ createInstance(SERVICE *service, char **options)
|
|||||||
(HASHMEMORYFN)free,
|
(HASHMEMORYFN)free,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
|
if ((router->shard_maps = hashtable_alloc(SCHEMAROUTER_USERHASH_SIZE, hashkeyfun, hashcmpfun)) == NULL)
|
||||||
|
{
|
||||||
|
skygw_log_write(LE, "Error: Memory allocation failed when allocating schemarouter database ignore list.");
|
||||||
|
hashtable_free(router->ignored_dbs);
|
||||||
|
free(router);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hashtable_memory_fns(router->shard_maps,(HASHMEMORYFN)strdup,
|
||||||
|
NULL, (HASHMEMORYFN)keyfreefun, NULL);
|
||||||
|
|
||||||
/** Add default system databases to ignore */
|
/** Add default system databases to ignore */
|
||||||
hashtable_add(router->ignored_dbs,"mysql","");
|
hashtable_add(router->ignored_dbs,"mysql","");
|
||||||
hashtable_add(router->ignored_dbs,"information_schema","");
|
hashtable_add(router->ignored_dbs,"information_schema","");
|
||||||
@ -1007,7 +1061,7 @@ static void* newSession(
|
|||||||
|
|
||||||
memset(db,0,MYSQL_DATABASE_MAXLEN+1);
|
memset(db,0,MYSQL_DATABASE_MAXLEN+1);
|
||||||
|
|
||||||
spinlock_acquire(&protocol->protocol_lock);
|
spinlock_acquire(&session->ses_lock);
|
||||||
|
|
||||||
/* To enable connecting directly to a sharded database we first need
|
/* To enable connecting directly to a sharded database we first need
|
||||||
* to disable it for the client DCB's protocol so that we can connect to them*/
|
* to disable it for the client DCB's protocol so that we can connect to them*/
|
||||||
@ -1028,7 +1082,7 @@ static void* newSession(
|
|||||||
LOGIF(LT,(skygw_log_write(LT,"schemarouter: Client'%s' connecting with empty database.",data->user)));
|
LOGIF(LT,(skygw_log_write(LT,"schemarouter: Client'%s' connecting with empty database.",data->user)));
|
||||||
}
|
}
|
||||||
|
|
||||||
spinlock_release(&protocol->protocol_lock);
|
spinlock_release(&session->ses_lock);
|
||||||
|
|
||||||
client_rses = (ROUTER_CLIENT_SES *)calloc(1, sizeof(ROUTER_CLIENT_SES));
|
client_rses = (ROUTER_CLIENT_SES *)calloc(1, sizeof(ROUTER_CLIENT_SES));
|
||||||
|
|
||||||
@ -1046,6 +1100,43 @@ static void* newSession(
|
|||||||
client_rses->rses_mysql_session = (MYSQL_session*)session->data;
|
client_rses->rses_mysql_session = (MYSQL_session*)session->data;
|
||||||
client_rses->rses_client_dcb = (DCB*)session->client;
|
client_rses->rses_client_dcb = (DCB*)session->client;
|
||||||
|
|
||||||
|
spinlock_acquire(&router->lock);
|
||||||
|
|
||||||
|
shard_map_t *map = hashtable_fetch(router->shard_maps, session->client->user);
|
||||||
|
enum shard_map_state state;
|
||||||
|
|
||||||
|
if (map)
|
||||||
|
{
|
||||||
|
spinlock_acquire(&map->lock);
|
||||||
|
double tdiff = difftime(time(NULL), map->last_updated);
|
||||||
|
if (tdiff > router->schemarouter_config.refresh_min_interval)
|
||||||
|
{
|
||||||
|
map->state = SHMAP_STALE;
|
||||||
|
}
|
||||||
|
state = map->state;
|
||||||
|
spinlock_release(&map->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_release(&router->lock);
|
||||||
|
|
||||||
|
if (map == NULL || state != SHMAP_READY)
|
||||||
|
{
|
||||||
|
if ((map = create_shard_map()) == NULL)
|
||||||
|
{
|
||||||
|
skygw_log_write(LE, "Error: Failed to allocate enough memory to create"
|
||||||
|
"new shard mapping. Session will be closed.");
|
||||||
|
free(client_rses);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
client_rses->init = INIT_UNINT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client_rses->init = INIT_READY;
|
||||||
|
atomic_add(&router->stats.shmap_cache_hit, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
client_rses->shardmap = map;
|
||||||
client_rses->dcb_reply = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
client_rses->dcb_reply = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||||
client_rses->dcb_reply->func.read = internalReply;
|
client_rses->dcb_reply->func.read = internalReply;
|
||||||
client_rses->dcb_reply->state = DCB_STATE_POLLING;
|
client_rses->dcb_reply->state = DCB_STATE_POLLING;
|
||||||
@ -1057,7 +1148,7 @@ static void* newSession(
|
|||||||
client_rses->dcb_route->state = DCB_STATE_POLLING;
|
client_rses->dcb_route->state = DCB_STATE_POLLING;
|
||||||
client_rses->dcb_route->session = session;
|
client_rses->dcb_route->session = session;
|
||||||
client_rses->rses_config.last_refresh = time(NULL);
|
client_rses->rses_config.last_refresh = time(NULL);
|
||||||
client_rses->init = INIT_UNINT;
|
|
||||||
if(using_db)
|
if(using_db)
|
||||||
client_rses->init |= INIT_USE_DB;
|
client_rses->init |= INIT_USE_DB;
|
||||||
/**
|
/**
|
||||||
@ -1132,12 +1223,6 @@ static void* newSession(
|
|||||||
session,
|
session,
|
||||||
router);
|
router);
|
||||||
|
|
||||||
client_rses->dbhash = hashtable_alloc(SCHEMAROUTER_HASHSIZE, hashkeyfun, hashcmpfun);
|
|
||||||
hashtable_memory_fns(client_rses->dbhash,(HASHMEMORYFN)strdup,
|
|
||||||
(HASHMEMORYFN)strdup,
|
|
||||||
(HASHMEMORYFN)free,
|
|
||||||
(HASHMEMORYFN)free);
|
|
||||||
|
|
||||||
rses_end_locked_router_action(client_rses);
|
rses_end_locked_router_action(client_rses);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1366,7 +1451,6 @@ static void freeSession(
|
|||||||
* all the memory and other resources associated
|
* all the memory and other resources associated
|
||||||
* to the client session.
|
* to the client session.
|
||||||
*/
|
*/
|
||||||
hashtable_free(router_cli_ses->dbhash);
|
|
||||||
free(router_cli_ses->rses_backend_ref);
|
free(router_cli_ses->rses_backend_ref);
|
||||||
free(router_cli_ses);
|
free(router_cli_ses);
|
||||||
return;
|
return;
|
||||||
@ -1744,8 +1828,6 @@ GWBUF*
|
|||||||
gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client)
|
gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client)
|
||||||
{
|
{
|
||||||
GWBUF* rval = NULL;
|
GWBUF* rval = NULL;
|
||||||
HASHTABLE* ht = client->dbhash;
|
|
||||||
HASHITERATOR* iter = hashtable_iterator(ht);
|
|
||||||
backend_ref_t *bref = client->rses_backend_ref;
|
backend_ref_t *bref = client->rses_backend_ref;
|
||||||
BACKEND** backends = router->servers;
|
BACKEND** backends = router->servers;
|
||||||
unsigned int coldef_len = 0;
|
unsigned int coldef_len = 0;
|
||||||
@ -1848,21 +1930,25 @@ gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client)
|
|||||||
if((dbs = malloc(sizeof(char*) * bufsz)) == NULL)
|
if((dbs = malloc(sizeof(char*) * bufsz)) == NULL)
|
||||||
{
|
{
|
||||||
gwbuf_free(rval);
|
gwbuf_free(rval);
|
||||||
hashtable_iterator_free(iter);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spinlock_acquire(&client->shardmap->lock);
|
||||||
|
|
||||||
|
if(client->shardmap->state == SHMAP_READY)
|
||||||
|
{
|
||||||
|
HASHTABLE* ht = client->shardmap->hash;
|
||||||
|
HASHITERATOR* iter = hashtable_iterator(ht);
|
||||||
|
|
||||||
while((value = (char*) hashtable_next(iter)))
|
while((value = (char*) hashtable_next(iter)))
|
||||||
{
|
{
|
||||||
char* bend = hashtable_fetch(ht, value);
|
char* bend = hashtable_fetch(ht, value);
|
||||||
|
|
||||||
for(i = 0; backends[i]; i++)
|
for(i = 0; backends[i]; i++)
|
||||||
{
|
{
|
||||||
if(strcmp(bref[i].bref_backend->backend_server->unique_name, bend) == 0 &&
|
if(strcmp(bref[i].bref_backend->backend_server->unique_name, bend) == 0 &&
|
||||||
BREF_IS_IN_USE(&bref[i]) && !BREF_IS_CLOSED(&bref[i]))
|
BREF_IS_IN_USE(&bref[i]) && !BREF_IS_CLOSED(&bref[i]))
|
||||||
{
|
{
|
||||||
ndbs++;
|
ndbs++;
|
||||||
|
|
||||||
if(ndbs >= bufsz)
|
if(ndbs >= bufsz)
|
||||||
{
|
{
|
||||||
bufsz += bufsz / 2;
|
bufsz += bufsz / 2;
|
||||||
@ -1871,17 +1957,24 @@ gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client)
|
|||||||
{
|
{
|
||||||
gwbuf_free(rval);
|
gwbuf_free(rval);
|
||||||
hashtable_iterator_free(iter);
|
hashtable_iterator_free(iter);
|
||||||
for(i=0;i<ndbs-1;i++)free(dbs[i]);
|
for (i = 0; i < ndbs - 1; i++)
|
||||||
|
{
|
||||||
|
free(dbs[i]);
|
||||||
|
}
|
||||||
free(dbs);
|
free(dbs);
|
||||||
|
spinlock_release(&client->shardmap->lock);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
dbs = tmp;
|
dbs = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
dbs[j++] = strdup(value);
|
dbs[j++] = strdup(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hashtable_iterator_free(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
spinlock_release(&client->shardmap->lock);
|
||||||
|
|
||||||
qsort(&dbs[0],(size_t)ndbs,sizeof(char*),cmpfn);
|
qsort(&dbs[0],(size_t)ndbs,sizeof(char*),cmpfn);
|
||||||
|
|
||||||
@ -1912,9 +2005,6 @@ gen_show_dbs_response(ROUTER_INSTANCE* router, ROUTER_CLIENT_SES* client)
|
|||||||
GWBUF* last_packet = gwbuf_alloc(sizeof(eof));
|
GWBUF* last_packet = gwbuf_alloc(sizeof(eof));
|
||||||
memcpy(last_packet->start, eof, sizeof(eof));
|
memcpy(last_packet->start, eof, sizeof(eof));
|
||||||
rval = gwbuf_append(rval, last_packet);
|
rval = gwbuf_append(rval, last_packet);
|
||||||
|
|
||||||
rval = gwbuf_make_contiguous(rval);
|
|
||||||
hashtable_iterator_free(iter);
|
|
||||||
free(dbs);
|
free(dbs);
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
@ -1987,9 +2077,15 @@ static int routeQuery(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(router_cli_ses->init & INIT_MAPPING)
|
/**
|
||||||
|
* If the databases are still being mapped or if the client connected
|
||||||
|
* with a default database but no database mapping was performed we need
|
||||||
|
* to store the query. Once the databases have been mapped and/or the
|
||||||
|
* default database is taken into use we can send the query forward.
|
||||||
|
*/
|
||||||
|
if(router_cli_ses->init & (INIT_MAPPING|INIT_USE_DB))
|
||||||
{
|
{
|
||||||
|
int init_rval = 1;
|
||||||
char* querystr = modutil_get_SQL(querybuf);
|
char* querystr = modutil_get_SQL(querybuf);
|
||||||
skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"schemarouter: Storing query for session %p: %s",
|
skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"schemarouter: Storing query for session %p: %s",
|
||||||
router_cli_ses->rses_client_dcb->session,
|
router_cli_ses->rses_client_dcb->session,
|
||||||
@ -2012,8 +2108,20 @@ static int routeQuery(
|
|||||||
ptr->next = querybuf;
|
ptr->next = querybuf;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(router_cli_ses->init == (INIT_READY|INIT_USE_DB))
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* This state is possible if a client connects with a default database
|
||||||
|
* and the shard map was found from the router cache
|
||||||
|
*/
|
||||||
|
if (!handle_default_db(router_cli_ses))
|
||||||
|
{
|
||||||
|
init_rval = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
rses_end_locked_router_action(router_cli_ses);
|
rses_end_locked_router_action(router_cli_ses);
|
||||||
return 1;
|
return init_rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -2139,9 +2247,12 @@ static int routeQuery(
|
|||||||
if (packet_type == MYSQL_COM_INIT_DB ||
|
if (packet_type == MYSQL_COM_INIT_DB ||
|
||||||
op == QUERY_OP_CHANGE_DB)
|
op == QUERY_OP_CHANGE_DB)
|
||||||
{
|
{
|
||||||
if (!(change_successful = change_current_db(router_cli_ses->rses_mysql_session,
|
spinlock_acquire(&router_cli_ses->shardmap->lock);
|
||||||
router_cli_ses->dbhash,
|
change_successful = change_current_db(router_cli_ses->rses_mysql_session,
|
||||||
querybuf)))
|
router_cli_ses->shardmap->hash,
|
||||||
|
querybuf);
|
||||||
|
spinlock_release(&router_cli_ses->shardmap->lock);
|
||||||
|
if (!change_successful)
|
||||||
{
|
{
|
||||||
time_t now = time(NULL);
|
time_t now = time(NULL);
|
||||||
if(router_cli_ses->rses_config.refresh_databases &&
|
if(router_cli_ses->rses_config.refresh_databases &&
|
||||||
@ -2149,22 +2260,21 @@ static int routeQuery(
|
|||||||
router_cli_ses->rses_config.refresh_min_interval)
|
router_cli_ses->rses_config.refresh_min_interval)
|
||||||
{
|
{
|
||||||
rses_begin_locked_router_action(router_cli_ses);
|
rses_begin_locked_router_action(router_cli_ses);
|
||||||
|
|
||||||
router_cli_ses->rses_config.last_refresh = now;
|
router_cli_ses->rses_config.last_refresh = now;
|
||||||
router_cli_ses->queue = querybuf;
|
router_cli_ses->queue = querybuf;
|
||||||
hashtable_free(router_cli_ses->dbhash);
|
int rc_refresh = 1;
|
||||||
if((router_cli_ses->dbhash = hashtable_alloc(SCHEMAROUTER_HASHSIZE, hashkeyfun, hashcmpfun)) == NULL)
|
|
||||||
|
if((router_cli_ses->shardmap = create_shard_map()))
|
||||||
{
|
{
|
||||||
skygw_log_write(LE,"Error: Hashtable allocation failed.");
|
|
||||||
rses_end_locked_router_action(router_cli_ses);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
hashtable_memory_fns(router_cli_ses->dbhash,(HASHMEMORYFN)strdup,
|
|
||||||
(HASHMEMORYFN)strdup,
|
|
||||||
(HASHMEMORYFN)free,
|
|
||||||
(HASHMEMORYFN)free);
|
|
||||||
gen_databaselist(inst,router_cli_ses);
|
gen_databaselist(inst,router_cli_ses);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rc_refresh = 0;
|
||||||
|
}
|
||||||
rses_end_locked_router_action(router_cli_ses);
|
rses_end_locked_router_action(router_cli_ses);
|
||||||
return 1;
|
return rc_refresh;
|
||||||
}
|
}
|
||||||
extract_database(querybuf,db);
|
extract_database(querybuf,db);
|
||||||
snprintf(errbuf,25+MYSQL_DATABASE_MAXLEN,"Unknown database: %s",db);
|
snprintf(errbuf,25+MYSQL_DATABASE_MAXLEN,"Unknown database: %s",db);
|
||||||
@ -2207,7 +2317,10 @@ static int routeQuery(
|
|||||||
op == QUERY_OP_CHANGE_DB)
|
op == QUERY_OP_CHANGE_DB)
|
||||||
{
|
{
|
||||||
route_target = TARGET_UNDEFINED;
|
route_target = TARGET_UNDEFINED;
|
||||||
tname = hashtable_fetch(router_cli_ses->dbhash,router_cli_ses->rses_mysql_session->db);
|
|
||||||
|
spinlock_acquire(&router_cli_ses->shardmap->lock);
|
||||||
|
tname = hashtable_fetch(router_cli_ses->shardmap->hash,router_cli_ses->rses_mysql_session->db);
|
||||||
|
spinlock_release(&router_cli_ses->shardmap->lock);
|
||||||
|
|
||||||
if(tname)
|
if(tname)
|
||||||
{
|
{
|
||||||
@ -2556,6 +2669,8 @@ diagnostic(ROUTER *instance, DCB *dcb)
|
|||||||
dcb_printf(dcb,"Shortest session: %.2lf seconds\n",router->stats.ses_shortest);
|
dcb_printf(dcb,"Shortest session: %.2lf seconds\n",router->stats.ses_shortest);
|
||||||
dcb_printf(dcb,"Average session length: %.2lf seconds\n",router->stats.ses_average);
|
dcb_printf(dcb,"Average session length: %.2lf seconds\n",router->stats.ses_average);
|
||||||
}
|
}
|
||||||
|
dcb_printf(dcb,"Shard map cache hits: %d\n",router->stats.shmap_cache_hit);
|
||||||
|
dcb_printf(dcb,"Shard map cache misses: %d\n",router->stats.shmap_cache_miss);
|
||||||
dcb_printf(dcb,"\n");
|
dcb_printf(dcb,"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2633,6 +2748,21 @@ static void clientReply(ROUTER* instance,
|
|||||||
|
|
||||||
if (rc == 1)
|
if (rc == 1)
|
||||||
{
|
{
|
||||||
|
spinlock_acquire(&router_cli_ses->shardmap->lock);
|
||||||
|
|
||||||
|
router_cli_ses->shardmap->state = SHMAP_READY;
|
||||||
|
router_cli_ses->shardmap->last_updated = time(NULL);
|
||||||
|
spinlock_release(&router_cli_ses->shardmap->lock);
|
||||||
|
|
||||||
|
rses_end_locked_router_action(router_cli_ses);
|
||||||
|
|
||||||
|
synchronize_shard_map(router_cli_ses);
|
||||||
|
|
||||||
|
if (!rses_begin_locked_router_action(router_cli_ses))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the session is reconnecting with a database name
|
* Check if the session is reconnecting with a database name
|
||||||
* that is not in the hashtable. If the database is not found
|
* that is not in the hashtable. If the database is not found
|
||||||
@ -2653,6 +2783,7 @@ static void clientReply(ROUTER* instance,
|
|||||||
|
|
||||||
if (router_cli_ses->queue)
|
if (router_cli_ses->queue)
|
||||||
{
|
{
|
||||||
|
ss_dassert(router_cli_ses->init == INIT_READY);
|
||||||
route_queued_query(router_cli_ses);
|
route_queued_query(router_cli_ses);
|
||||||
}
|
}
|
||||||
skygw_log_write_flush(LOGFILE_DEBUG,
|
skygw_log_write_flush(LOGFILE_DEBUG,
|
||||||
@ -2669,11 +2800,6 @@ static void clientReply(ROUTER* instance,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (router_cli_ses->queue)
|
|
||||||
{
|
|
||||||
route_queued_query(router_cli_ses);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (router_cli_ses->init & INIT_USE_DB)
|
if (router_cli_ses->init & INIT_USE_DB)
|
||||||
{
|
{
|
||||||
skygw_log_write(LOGFILE_DEBUG, "schemarouter: Reply to USE '%s' received for session %p",
|
skygw_log_write(LOGFILE_DEBUG, "schemarouter: Reply to USE '%s' received for session %p",
|
||||||
@ -2682,6 +2808,12 @@ static void clientReply(ROUTER* instance,
|
|||||||
router_cli_ses->init &= ~INIT_USE_DB;
|
router_cli_ses->init &= ~INIT_USE_DB;
|
||||||
strcpy(router_cli_ses->rses_mysql_session->db, router_cli_ses->connect_db);
|
strcpy(router_cli_ses->rses_mysql_session->db, router_cli_ses->connect_db);
|
||||||
ss_dassert(router_cli_ses->init == INIT_READY);
|
ss_dassert(router_cli_ses->init == INIT_READY);
|
||||||
|
|
||||||
|
if (router_cli_ses->queue)
|
||||||
|
{
|
||||||
|
route_queued_query(router_cli_ses);
|
||||||
|
}
|
||||||
|
|
||||||
rses_end_locked_router_action(router_cli_ses);
|
rses_end_locked_router_action(router_cli_ses);
|
||||||
if (writebuf)
|
if (writebuf)
|
||||||
{
|
{
|
||||||
@ -2690,6 +2822,14 @@ static void clientReply(ROUTER* instance,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (router_cli_ses->queue)
|
||||||
|
{
|
||||||
|
ss_dassert(router_cli_ses->init == INIT_READY);
|
||||||
|
route_queued_query(router_cli_ses);
|
||||||
|
rses_end_locked_router_action(router_cli_ses);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CHK_BACKEND_REF(bref);
|
CHK_BACKEND_REF(bref);
|
||||||
scur = &bref->bref_sescmd_cur;
|
scur = &bref->bref_sescmd_cur;
|
||||||
/**
|
/**
|
||||||
@ -4447,7 +4587,7 @@ RESULT_ROW* shard_list_cb(struct resultset* rset, void* data)
|
|||||||
RESULT_ROW* rval = NULL;
|
RESULT_ROW* rval = NULL;
|
||||||
|
|
||||||
if((key = hashtable_next(sl->iter)) &&
|
if((key = hashtable_next(sl->iter)) &&
|
||||||
(value = hashtable_fetch(sl->rses->dbhash,key)))
|
(value = hashtable_fetch(sl->rses->shardmap->hash,key)))
|
||||||
{
|
{
|
||||||
if((rval = resultset_make_row(sl->rset)))
|
if((rval = resultset_make_row(sl->rset)))
|
||||||
{
|
{
|
||||||
@ -4465,7 +4605,12 @@ RESULT_ROW* shard_list_cb(struct resultset* rset, void* data)
|
|||||||
*/
|
*/
|
||||||
int process_show_shards(ROUTER_CLIENT_SES* rses)
|
int process_show_shards(ROUTER_CLIENT_SES* rses)
|
||||||
{
|
{
|
||||||
HASHITERATOR* iter = hashtable_iterator(rses->dbhash);
|
int rval = 0;
|
||||||
|
|
||||||
|
spinlock_acquire(&rses->shardmap->lock);
|
||||||
|
if(rses->shardmap->state == SHMAP_READY)
|
||||||
|
{
|
||||||
|
HASHITERATOR* iter = hashtable_iterator(rses->shardmap->hash);
|
||||||
struct shard_list sl;
|
struct shard_list sl;
|
||||||
|
|
||||||
sl.iter = iter;
|
sl.iter = iter;
|
||||||
@ -4473,15 +4618,19 @@ int process_show_shards(ROUTER_CLIENT_SES* rses)
|
|||||||
if ((sl.rset = resultset_create(shard_list_cb, &sl)) == NULL)
|
if ((sl.rset = resultset_create(shard_list_cb, &sl)) == NULL)
|
||||||
{
|
{
|
||||||
skygw_log_write(LE, "[%s] Error: Failed to create resultset.", __FUNCTION__);
|
skygw_log_write(LE, "[%s] Error: Failed to create resultset.", __FUNCTION__);
|
||||||
return -1;
|
rval = -1;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
resultset_add_column(sl.rset, "Database", MYSQL_DATABASE_MAXLEN, COL_TYPE_VARCHAR);
|
resultset_add_column(sl.rset, "Database", MYSQL_DATABASE_MAXLEN, COL_TYPE_VARCHAR);
|
||||||
resultset_add_column(sl.rset, "Server", MYSQL_DATABASE_MAXLEN, COL_TYPE_VARCHAR);
|
resultset_add_column(sl.rset, "Server", MYSQL_DATABASE_MAXLEN, COL_TYPE_VARCHAR);
|
||||||
resultset_stream_mysql(sl.rset, rses->rses_client_dcb);
|
resultset_stream_mysql(sl.rset, rses->rses_client_dcb);
|
||||||
resultset_free(sl.rset);
|
resultset_free(sl.rset);
|
||||||
hashtable_iterator_free(iter);
|
hashtable_iterator_free(iter);
|
||||||
return 0;
|
}
|
||||||
|
}
|
||||||
|
spinlock_release(&rses->shardmap->lock);
|
||||||
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -4514,41 +4663,26 @@ void write_error_to_client(DCB* dcb, int errnum, const char* mysqlstate, const c
|
|||||||
*/
|
*/
|
||||||
bool handle_default_db(ROUTER_CLIENT_SES *router_cli_ses)
|
bool handle_default_db(ROUTER_CLIENT_SES *router_cli_ses)
|
||||||
{
|
{
|
||||||
char* target;
|
bool rval = false;
|
||||||
|
char* target = NULL;
|
||||||
|
|
||||||
if ((target = hashtable_fetch(router_cli_ses->dbhash,
|
spinlock_acquire(&router_cli_ses->shardmap->lock);
|
||||||
router_cli_ses->connect_db)) == NULL)
|
if(router_cli_ses->shardmap->state == SHMAP_READY)
|
||||||
{
|
{
|
||||||
/** Unknown database, hang up on the client*/
|
target = hashtable_fetch(router_cli_ses->shardmap->hash, router_cli_ses->connect_db);
|
||||||
skygw_log_write_flush(LOGFILE_TRACE, "schemarouter: Connecting to a non-existent database '%s'",
|
|
||||||
router_cli_ses->connect_db);
|
|
||||||
char errmsg[128 + MYSQL_DATABASE_MAXLEN + 1];
|
|
||||||
sprintf(errmsg, "Unknown database '%s'", router_cli_ses->connect_db);
|
|
||||||
if (router_cli_ses->rses_config.debug)
|
|
||||||
{
|
|
||||||
sprintf(errmsg + strlen(errmsg), " ([%lu]: DB not found on connect)", router_cli_ses->rses_client_dcb->session->ses_id);
|
|
||||||
}
|
|
||||||
write_error_to_client(router_cli_ses->rses_client_dcb,
|
|
||||||
SCHEMA_ERR_DBNOTFOUND,
|
|
||||||
SCHEMA_ERRSTR_DBNOTFOUND,
|
|
||||||
errmsg);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
spinlock_release(&router_cli_ses->shardmap->lock);
|
||||||
|
|
||||||
|
if (target)
|
||||||
|
{
|
||||||
/* Send a COM_INIT_DB packet to the server with the right database
|
/* Send a COM_INIT_DB packet to the server with the right database
|
||||||
* and set it as the client's active database */
|
* and set it as the client's active database */
|
||||||
|
|
||||||
unsigned int qlen;
|
unsigned int qlen = strlen(router_cli_ses->connect_db);
|
||||||
GWBUF* buffer;
|
GWBUF* buffer = gwbuf_alloc(qlen + 5);
|
||||||
|
|
||||||
qlen = strlen(router_cli_ses->connect_db);
|
if (buffer)
|
||||||
buffer = gwbuf_alloc(qlen + 5);
|
|
||||||
if (buffer == NULL)
|
|
||||||
{
|
{
|
||||||
skygw_log_write_flush(LOGFILE_ERROR, "Error : Buffer allocation failed.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
gw_mysql_set_byte3((unsigned char*) buffer->start, qlen + 1);
|
gw_mysql_set_byte3((unsigned char*) buffer->start, qlen + 1);
|
||||||
gwbuf_set_type(buffer, GWBUF_TYPE_MYSQL);
|
gwbuf_set_type(buffer, GWBUF_TYPE_MYSQL);
|
||||||
*((unsigned char*) buffer->start + 3) = 0x0;
|
*((unsigned char*) buffer->start + 3) = 0x0;
|
||||||
@ -4563,13 +4697,37 @@ bool handle_default_db(ROUTER_CLIENT_SES *router_cli_ses)
|
|||||||
router_cli_ses->connect_db,
|
router_cli_ses->connect_db,
|
||||||
target,
|
target,
|
||||||
router_cli_ses->rses_client_dcb->session);
|
router_cli_ses->rses_client_dcb->session);
|
||||||
|
rval = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
skygw_log_write_flush(LOGFILE_TRACE, "schemarouter: Couldn't find target DCB for '%s'.", target);
|
skygw_log_write_flush(LOGFILE_TRACE, "schemarouter: Couldn't find target DCB for '%s'.", target);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
skygw_log_write_flush(LOGFILE_ERROR, "Error : Buffer allocation failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/** Unknown database, hang up on the client*/
|
||||||
|
skygw_log_write_flush(LOGFILE_TRACE, "schemarouter: Connecting to a non-existent database '%s'",
|
||||||
|
router_cli_ses->connect_db);
|
||||||
|
char errmsg[128 + MYSQL_DATABASE_MAXLEN + 1];
|
||||||
|
sprintf(errmsg, "Unknown database '%s'", router_cli_ses->connect_db);
|
||||||
|
if (router_cli_ses->rses_config.debug)
|
||||||
|
{
|
||||||
|
sprintf(errmsg + strlen(errmsg), " ([%lu]: DB not found on connect)",
|
||||||
|
router_cli_ses->rses_client_dcb->session->ses_id);
|
||||||
|
}
|
||||||
|
write_error_to_client(router_cli_ses->rses_client_dcb,
|
||||||
|
SCHEMA_ERR_DBNOTFOUND,
|
||||||
|
SCHEMA_ERRSTR_DBNOTFOUND,
|
||||||
|
errmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rval;
|
||||||
}
|
}
|
||||||
|
|
||||||
void route_queued_query(ROUTER_CLIENT_SES *router_cli_ses)
|
void route_queued_query(ROUTER_CLIENT_SES *router_cli_ses)
|
||||||
@ -4684,3 +4842,75 @@ int inspect_backend_mapping_states(ROUTER_CLIENT_SES *router_cli_ses,
|
|||||||
*wbuf = writebuf;
|
*wbuf = writebuf;
|
||||||
return mapped ? 1 : 0;
|
return mapped ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace a shard map with another one. This function copies the contents of
|
||||||
|
* the source shard map to the target and frees the source memory.
|
||||||
|
* @param target Target shard map to replace
|
||||||
|
* @param source Source shard map to use
|
||||||
|
*/
|
||||||
|
void replace_shard_map(shard_map_t **target, shard_map_t **source)
|
||||||
|
{
|
||||||
|
shard_map_t *tgt = *target;
|
||||||
|
shard_map_t *src = *source;
|
||||||
|
tgt->last_updated = src->last_updated;
|
||||||
|
tgt->state = src->state;
|
||||||
|
hashtable_free(tgt->hash);
|
||||||
|
tgt->hash = src->hash;
|
||||||
|
free(src);
|
||||||
|
*source = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronize the router client session shard map with the global shard map for
|
||||||
|
* this user.
|
||||||
|
*
|
||||||
|
* If the router doesn't have a shard map for this user then the current shard map
|
||||||
|
* of the client session is added to the router. If the shard map in the router is
|
||||||
|
* out of date, its contents are replaced with the contents of the current client
|
||||||
|
* session. If the router has a usable shard map, the current shard map of the client
|
||||||
|
* is discarded and the router's shard map is used.
|
||||||
|
* @param client Router session
|
||||||
|
*/
|
||||||
|
void synchronize_shard_map(ROUTER_CLIENT_SES *client)
|
||||||
|
{
|
||||||
|
spinlock_acquire(&client->router->lock);
|
||||||
|
|
||||||
|
client->router->stats.shmap_cache_miss++;
|
||||||
|
|
||||||
|
shard_map_t *map = hashtable_fetch(client->router->shard_maps,
|
||||||
|
client->rses_client_dcb->user);
|
||||||
|
if (map)
|
||||||
|
{
|
||||||
|
spinlock_acquire(&map->lock);
|
||||||
|
if (map->state == SHMAP_STALE)
|
||||||
|
{
|
||||||
|
replace_shard_map(&map, &client->shardmap);
|
||||||
|
}
|
||||||
|
else if (map->state != SHMAP_READY)
|
||||||
|
{
|
||||||
|
skygw_log_write(LE, "Warning: Shard map state is not ready but"
|
||||||
|
"it is in use. Replacing it with a newer one.");
|
||||||
|
replace_shard_map(&map, &client->shardmap);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Another thread has already updated the shard map for this user
|
||||||
|
*/
|
||||||
|
hashtable_free(client->shardmap->hash);
|
||||||
|
free(client->shardmap);
|
||||||
|
}
|
||||||
|
spinlock_release(&map->lock);
|
||||||
|
client->shardmap = map;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hashtable_add(client->router->shard_maps,
|
||||||
|
client->rses_client_dcb->user,
|
||||||
|
client->shardmap);
|
||||||
|
ss_dassert(hashtable_fetch(client->router->shard_maps,
|
||||||
|
client->rses_client_dcb->user) == client->shardmap);
|
||||||
|
}
|
||||||
|
spinlock_release(&client->router->lock);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user