Merge lp:~akopytov/percona-server/tp-low-prio-queue-throttling-5.5 into lp:percona-server/5.5

Proposed by Alexey Kopytov
Status: Merged
Approved by: Laurynas Biveinis
Approved revision: no longer in the source branch.
Merged at revision: 603
Proposed branch: lp:~akopytov/percona-server/tp-low-prio-queue-throttling-5.5
Merge into: lp:percona-server/5.5
Diff against target: 229 lines (+93/-29)
1 file modified
Percona-Server/sql/threadpool_unix.cc (+93/-29)
To merge this branch: bzr merge lp:~akopytov/percona-server/tp-low-prio-queue-throttling-5.5
Reviewer Review Type Date Requested Status
Laurynas Biveinis (community) Approve
Review via email: mp+198720@code.launchpad.net

Description of the change

    Implementation of
    https://blueprints.launchpad.net/percona-server/+spec/tp-low-prio-queue-throttling-5.5

    Introduced a limit on ‘busy’ threads. A busy thread is either in the
    active (i.e. executing a statement) or waiting (i.e. between calls to
    thd_wait_begin() and thd_wait_end()) state. No events from the low
    priority queue are processed when that limit is reached in a thread
    group.

    Also made the code that creates new threads in thd_wait_begin() a
    compile-time option. It’s currently enabled, but benchmarks show that
    the code has essentially no effect. That code will likely be removed
    later.

http://jenkins.percona.com/view/PS%205.5/job/percona-server-5.5-param/906/

To post a comment you must log in.
Revision history for this message
Laurynas Biveinis (laurynas-biveinis) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Percona-Server/sql/threadpool_unix.cc'
--- Percona-Server/sql/threadpool_unix.cc 2013-12-02 12:17:20 +0000
+++ Percona-Server/sql/threadpool_unix.cc 2013-12-12 12:06:46 +0000
@@ -43,6 +43,10 @@
43/** Maximum number of native events a listener can read in one go */43/** Maximum number of native events a listener can read in one go */
44#define MAX_EVENTS 102444#define MAX_EVENTS 1024
4545
46/** Define if wait_begin() should create threads if necessary without waiting
47for stall detection to kick in */
48#define THREADPOOL_CREATE_THREADS_ON_WAIT
49
46/** Indicates that threadpool was initialized*/50/** Indicates that threadpool was initialized*/
47static bool threadpool_started= false; 51static bool threadpool_started= false;
4852
@@ -138,6 +142,7 @@
138 int thread_count;142 int thread_count;
139 int active_thread_count;143 int active_thread_count;
140 int connection_count;144 int connection_count;
145 int waiting_thread_count;
141 /* Stats for the deadlock detection timer routine.*/146 /* Stats for the deadlock detection timer routine.*/
142 int io_event_count;147 int io_event_count;
143 int queue_event_count;148 int queue_event_count;
@@ -396,6 +401,33 @@
396#error not ported yet to this OS401#error not ported yet to this OS
397#endif402#endif
398403
404namespace {
405
406/*
407 Prevent too many active threads executing at the same time, if the workload is
408 not CPU bound.
409*/
410
411inline bool too_many_active_threads(thread_group_t *thread_group)
412{
413 return (thread_group->active_thread_count
414 >= 1 + (int) threadpool_oversubscribe
415 && !thread_group->stalled);
416}
417
418/*
419 Limit the number of 'busy' threads by 1 + thread_pool_oversubscribe. A thread
420 is busy if it is in either the active state or the waiting state (i.e. between
421 thd_wait_begin() / thd_wait_end() calls).
422*/
423
424inline bool too_many_busy_threads(thread_group_t *thread_group)
425{
426 return (thread_group->active_thread_count + thread_group->waiting_thread_count
427 > 1 + (int) threadpool_oversubscribe);
428}
429
430} // namespace
399431
400/* Dequeue element from a workqueue */432/* Dequeue element from a workqueue */
401433
@@ -409,7 +441,12 @@
409 {441 {
410 thread_group->high_prio_queue.remove(c);442 thread_group->high_prio_queue.remove(c);
411 }443 }
412 else if ((c= thread_group->queue.front()))444 /*
445 Don't pick events from the low priority queue if there are too many
446 active + waiting threads.
447 */
448 else if (!too_many_busy_threads(thread_group) &&
449 (c= thread_group->queue.front()))
413 {450 {
414 thread_group->queue.remove(c);451 thread_group->queue.remove(c);
415 }452 }
@@ -530,7 +567,17 @@
530 return NULL;567 return NULL;
531}568}
532569
570/*
571 Check if both the high and low priority queues are empty.
533572
573 NOTE: we also consider the low priority queue empty in case it has events, but
574 they cannot be processed due to the too_many_busy_threads() limit.
575*/
576static bool queues_are_empty(thread_group_t *tg)
577{
578 return (tg->high_prio_queue.is_empty() &&
579 (tg->queue.is_empty() || too_many_busy_threads(tg)));
580}
534581
535void check_stall(thread_group_t *thread_group)582void check_stall(thread_group_t *thread_group)
536{583{
@@ -557,21 +604,21 @@
557 thread_group->io_event_count= 0;604 thread_group->io_event_count= 0;
558605
559 /* 606 /*
560 Check whether requests from the workqueue are being dequeued.607 Check whether requests from the workqueues are being dequeued.
561608
562 The stall detection and resolution works as follows:609 The stall detection and resolution works as follows:
563610
564 1. There is a counter thread_group->queue_event_count for the number of 611 1. There is a counter thread_group->queue_event_count for the number of
565 events removed from the queue. Timer resets the counter to 0 on each run.612 events removed from the queues. Timer resets the counter to 0 on each run.
566 2. Timer determines stall if this counter remains 0 since last check613 2. Timer determines stall if this counter remains 0 since last check
567 and the queue is not empty.614 and at least one of the high and low priority queues is not empty.
568 3. Once timer determined a stall it sets thread_group->stalled flag and615 3. Once timer determined a stall it sets thread_group->stalled flag and
569 wakes and idle worker (or creates a new one, subject to throttling).616 wakes and idle worker (or creates a new one, subject to throttling).
570 4. The stalled flag is reset, when an event is dequeued.617 4. The stalled flag is reset, when an event is dequeued.
571618
572 Q : Will this handling lead to an unbound growth of threads, if queue619 Q : Will this handling lead to an unbound growth of threads, if queues
573 stalls permanently?620 stall permanently?
574 A : No. If queue stalls permanently, it is an indication for many very long621 A : No. If queues stall permanently, it is an indication for many very long
575 simultaneous queries. The maximum number of simultanoues queries is 622 simultaneous queries. The maximum number of simultanoues queries is
576 max_connections, further we have threadpool_max_threads limit, upon which no623 max_connections, further we have threadpool_max_threads limit, upon which no
577 worker threads are created. So in case there is a flood of very long 624 worker threads are created. So in case there is a flood of very long
@@ -582,8 +629,7 @@
582 do wait and indicate that via thd_wait_begin/end callbacks, thread creation629 do wait and indicate that via thd_wait_begin/end callbacks, thread creation
583 will be faster.630 will be faster.
584 */631 */
585 if ((!thread_group->high_prio_queue.is_empty() ||632 if (!thread_group->queue_event_count && !queues_are_empty(thread_group))
586 !thread_group->queue.is_empty()) && !thread_group->queue_event_count)
587 {633 {
588 thread_group->stalled= true;634 thread_group->stalled= true;
589 wake_or_create_thread(thread_group);635 wake_or_create_thread(thread_group);
@@ -1027,19 +1073,6 @@
1027 DBUG_VOID_RETURN;1073 DBUG_VOID_RETURN;
1028}1074}
10291075
1030
1031/*
1032 Prevent too many threads executing at the same time,if the workload is
1033 not CPU bound.
1034*/
1035
1036static bool too_many_threads(thread_group_t *thread_group)
1037{
1038 return (thread_group->active_thread_count >= 1+(int)threadpool_oversubscribe
1039 && !thread_group->stalled);
1040}
1041
1042
1043/**1076/**
1044 Retrieve a connection with pending event.1077 Retrieve a connection with pending event.
1045 1078
@@ -1070,7 +1103,7 @@
10701103
1071 for(;;) 1104 for(;;)
1072 {1105 {
1073 bool oversubscribed = too_many_threads(thread_group); 1106 bool oversubscribed = too_many_active_threads(thread_group);
1074 if (thread_group->shutdown)1107 if (thread_group->shutdown)
1075 break;1108 break;
10761109
@@ -1109,7 +1142,35 @@
1109 {1142 {
1110 thread_group->io_event_count++;1143 thread_group->io_event_count++;
1111 connection = (connection_t *)native_event_get_userdata(&nev);1144 connection = (connection_t *)native_event_get_userdata(&nev);
1112 break;1145
1146 /*
1147 Since we are going to perform an out-of-order event processing for the
1148 connection, first check whether it is eligible for high priority
1149 processing. We can get here even if there are queued events, so it
1150 must either have a high priority ticket, or there must be not too many
1151 busy threads (as if it was coming from a low priority queue).
1152 */
1153 if (connection->tickets > 0 &&
1154 thd_is_transaction_active(connection->thd))
1155 connection->tickets--;
1156 else if (too_many_busy_threads(thread_group))
1157 {
1158 /*
1159 Not eligible for high priority processing. Restore tickets and put
1160 it into the low priority queue.
1161 */
1162
1163 connection->tickets=
1164 connection->thd->variables.threadpool_high_prio_tickets;
1165 thread_group->queue.push_back(connection);
1166 connection= NULL;
1167 }
1168
1169 if (connection)
1170 {
1171 thread_group->queue_event_count++;
1172 break;
1173 }
1113 }1174 }
1114 }1175 }
11151176
@@ -1167,13 +1228,14 @@
1167 DBUG_ENTER("wait_begin");1228 DBUG_ENTER("wait_begin");
1168 mysql_mutex_lock(&thread_group->mutex);1229 mysql_mutex_lock(&thread_group->mutex);
1169 thread_group->active_thread_count--;1230 thread_group->active_thread_count--;
1170 1231 thread_group->waiting_thread_count++;
1232
1171 DBUG_ASSERT(thread_group->active_thread_count >=0);1233 DBUG_ASSERT(thread_group->active_thread_count >=0);
1172 DBUG_ASSERT(thread_group->connection_count > 0);1234 DBUG_ASSERT(thread_group->connection_count > 0);
1173 1235
1236#ifdef THREADPOOL_CREATE_THREADS_ON_WAIT
1174 if ((thread_group->active_thread_count == 0) && 1237 if ((thread_group->active_thread_count == 0) &&
1175 (thread_group->high_prio_queue.is_empty() ||1238 (!queues_are_empty(thread_group) || !thread_group->listener))
1176 thread_group->queue.is_empty() || !thread_group->listener))
1177 {1239 {
1178 /* 1240 /*
1179 Group might stall while this thread waits, thus wake 1241 Group might stall while this thread waits, thus wake
@@ -1181,7 +1243,8 @@
1181 */1243 */
1182 wake_or_create_thread(thread_group);1244 wake_or_create_thread(thread_group);
1183 }1245 }
1184 1246#endif
1247
1185 mysql_mutex_unlock(&thread_group->mutex);1248 mysql_mutex_unlock(&thread_group->mutex);
1186 DBUG_VOID_RETURN;1249 DBUG_VOID_RETURN;
1187}1250}
@@ -1195,6 +1258,7 @@
1195 DBUG_ENTER("wait_end");1258 DBUG_ENTER("wait_end");
1196 mysql_mutex_lock(&thread_group->mutex);1259 mysql_mutex_lock(&thread_group->mutex);
1197 thread_group->active_thread_count++;1260 thread_group->active_thread_count++;
1261 thread_group->waiting_thread_count--;
1198 mysql_mutex_unlock(&thread_group->mutex);1262 mysql_mutex_unlock(&thread_group->mutex);
1199 DBUG_VOID_RETURN;1263 DBUG_VOID_RETURN;
1200}1264}

Subscribers

People subscribed via source and target branches