summaryrefslogtreecommitdiff
path: root/sql/sql_connect.cc
diff options
context:
space:
mode:
authorPraveenkumar Hulakund <praveenkumar.hulakund@oracle.com>2012-05-28 11:14:43 +0530
committerPraveenkumar Hulakund <praveenkumar.hulakund@oracle.com>2012-05-28 11:14:43 +0530
commitb2c3acc987193775d392e763c8b9eef3da7c65a8 (patch)
tree8354a7926a55bf3417008739a31a74dc31ba35a0 /sql/sql_connect.cc
parentb2888adb701c1cfd1db137937d50f571fe8366d4 (diff)
downloadmariadb-git-b2c3acc987193775d392e763c8b9eef3da7c65a8.tar.gz
Bug#14003080:65104: MAX_USER_CONNECTIONS WITH PROCESSLIST EMPTY
Analysis: ------------- If server is started with limit of MAX_CONNECTIONS and MAX_USER_CONNECTIONS then only MAX_USER_CONNECTIONS of any particular users can be connected to server and total MAX_CONNECTIONS of client can be connected to server. Server maintains a counter for total CONNECTIONS and total CONNECTIONS from particular user. Here, MAX_CONNECTIONS of connections are created to server. Out of this MAX_CONNECTIONS, connections from particular user (say USER1) are also created. The connections from USER1 is lesser than MAX_USER_CONNECTIONS. After that there was one more connection request from USER1. Since USER1 can still create connections as he havent reached MAX_USER_CONNECTIONS, server increments counter of CONNECTIONS per user. As server already has MAX_CONNECTIONS of connections, next check to total CONNECTION count fails. In this case control is returned WITHOUT decrementing the CONNECTIONS per user. So the counter per user CONNECTIONS goes on incrementing for each attempt until current connections are closed. And because of this counter per CONNECTIONS reached MAX_USER_CONNECTIONS. So, next connections form USER1 user always returns with MAX_USER_CONNECTION limit error, even when total connection to sever are less than MAX_CONNECTIONS. Fix: ------------- This issue is occurred because of not handling counters properly in the server. Changed the code to handle per user connection counters properly.
Diffstat (limited to 'sql/sql_connect.cc')
-rw-r--r--sql/sql_connect.cc134
1 files changed, 80 insertions, 54 deletions
diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc
index 366dec733c4..cdb0f5de049 100644
--- a/sql/sql_connect.cc
+++ b/sql/sql_connect.cc
@@ -105,8 +105,8 @@ int get_or_create_user_conn(THD *thd, const char *user,
goto end;
}
}
- thd->user_connect=uc;
- uc->connections++;
+ thd->set_user_connect(uc);
+ thd->increment_user_connections_counter();
end:
mysql_mutex_unlock(&LOCK_user_conn);
return return_val;
@@ -131,7 +131,7 @@ end:
1 error
*/
-int check_for_max_user_connections(THD *thd, USER_CONN *uc)
+int check_for_max_user_connections(THD *thd, const USER_CONN *uc)
{
int error=0;
DBUG_ENTER("check_for_max_user_connections");
@@ -145,7 +145,7 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
error=1;
goto end;
}
- time_out_user_resource_limits(thd, uc);
+ thd->time_out_user_resource_limits();
if (uc->user_resources.user_conn &&
uc->user_resources.user_conn < uc->connections)
{
@@ -164,18 +164,18 @@ int check_for_max_user_connections(THD *thd, USER_CONN *uc)
error=1;
goto end;
}
- uc->conn_per_hour++;
+ thd->increment_con_per_hour_counter();
end:
if (error)
{
- uc->connections--; // no need for decrease_user_connections() here
+ thd->decrement_user_connections_counter();
/*
The thread may returned back to the pool and assigned to a user
that doesn't have a limit. Ensure the user is not using resources
of someone else.
*/
- thd->user_connect= NULL;
+ thd->set_user_connect(NULL);
}
mysql_mutex_unlock(&LOCK_user_conn);
DBUG_RETURN(error);
@@ -214,38 +214,37 @@ void decrease_user_connections(USER_CONN *uc)
DBUG_VOID_RETURN;
}
-
/*
- Reset per-hour user resource limits when it has been more than
- an hour since they were last checked
-
- SYNOPSIS:
- time_out_user_resource_limits()
- thd Thread handler
- uc User connection details
-
- NOTE:
- This assumes that the LOCK_user_conn mutex has been acquired, so it is
- safe to test and modify members of the USER_CONN structure.
-*/
-
-void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
+ Decrements user connections count from the USER_CONN held by THD
+ And removes USER_CONN from the hash if no body else is using it.
+
+ SYNOPSIS
+ release_user_connection()
+ THD Thread context object.
+ */
+void release_user_connection(THD *thd)
{
- ulonglong check_time= thd->start_utime;
- DBUG_ENTER("time_out_user_resource_limits");
+ const USER_CONN *uc= thd->get_user_connect();
+ DBUG_ENTER("release_user_connection");
- /* If more than a hour since last check, reset resource checking */
- if (check_time - uc->reset_utime >= LL(3600000000))
+ if (uc)
{
- uc->questions=1;
- uc->updates=0;
- uc->conn_per_hour=0;
- uc->reset_utime= check_time;
+ mysql_mutex_lock(&LOCK_user_conn);
+ DBUG_ASSERT(uc->connections > 0);
+ thd->decrement_user_connections_counter();
+ if (!uc->connections && !mqh_used)
+ {
+ /* Last connection for user; Delete it */
+ (void) my_hash_delete(&hash_user_connections,(uchar*) uc);
+ }
+ mysql_mutex_unlock(&LOCK_user_conn);
+ thd->set_user_connect(NULL);
}
DBUG_VOID_RETURN;
}
+
/*
Check if maximum queries per hour limit has been reached
returns 0 if OK.
@@ -254,40 +253,70 @@ void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
bool check_mqh(THD *thd, uint check_command)
{
bool error= 0;
- USER_CONN *uc=thd->user_connect;
+ const USER_CONN *uc=thd->get_user_connect();
DBUG_ENTER("check_mqh");
DBUG_ASSERT(uc != 0);
mysql_mutex_lock(&LOCK_user_conn);
- time_out_user_resource_limits(thd, uc);
+ thd->time_out_user_resource_limits();
/* Check that we have not done too many questions / hour */
- if (uc->user_resources.questions &&
- uc->questions++ >= uc->user_resources.questions)
+ if (uc->user_resources.questions)
{
- my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_questions",
- (long) uc->user_resources.questions);
- error=1;
- goto end;
+ thd->increment_questions_counter();
+ if ((uc->questions - 1) >= uc->user_resources.questions)
+ {
+ my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_questions",
+ (long) uc->user_resources.questions);
+ error=1;
+ goto end;
+ }
}
if (check_command < (uint) SQLCOM_END)
{
/* Check that we have not done too many updates / hour */
if (uc->user_resources.updates &&
- (sql_command_flags[check_command] & CF_CHANGES_DATA) &&
- uc->updates++ >= uc->user_resources.updates)
+ (sql_command_flags[check_command] & CF_CHANGES_DATA))
{
- my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates",
- (long) uc->user_resources.updates);
- error=1;
- goto end;
+ thd->increment_updates_counter();
+ if ((uc->updates - 1) >= uc->user_resources.updates)
+ {
+ my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates",
+ (long) uc->user_resources.updates);
+ error=1;
+ goto end;
+ }
}
}
end:
mysql_mutex_unlock(&LOCK_user_conn);
DBUG_RETURN(error);
}
+#else
+
+int check_for_max_user_connections(THD *thd, const USER_CONN *uc)
+{
+ return 0;
+}
+
+void decrease_user_connections(USER_CONN *uc)
+{
+ return;
+}
+
+void release_user_connection(THD *thd)
+{
+ const USER_CONN *uc= thd->get_user_connect();
+ DBUG_ENTER("release_user_connection");
+
+ if (uc)
+ {
+ thd->set_user_connect(NULL);
+ }
+
+ DBUG_VOID_RETURN;
+}
#endif /* NO_EMBEDDED_ACCESS_CHECKS */
@@ -609,16 +638,13 @@ void end_connection(THD *thd)
{
NET *net= &thd->net;
plugin_thdvar_cleanup(thd);
- if (thd->user_connect)
- {
- decrease_user_connections(thd->user_connect);
- /*
- The thread may returned back to the pool and assigned to a user
- that doesn't have a limit. Ensure the user is not using resources
- of someone else.
- */
- thd->user_connect= NULL;
- }
+
+ /*
+ The thread may returned back to the pool and assigned to a user
+ that doesn't have a limit. Ensure the user is not using resources
+ of someone else.
+ */
+ release_user_connection(thd);
if (thd->killed || (net->error && net->vio != 0))
{