1111// Explicitly instantiate the required template class and member functions
1212template MySQL_Session* Base_Thread::create_new_session_and_client_data_stream<MySQL_Thread, MySQL_Session*>(int );
1313template PgSQL_Session* Base_Thread::create_new_session_and_client_data_stream<PgSQL_Thread, PgSQL_Session*>(int );
14- template void Base_Thread::ProcessAllSessions_SortingSessions <MySQL_Session>();
15- template void Base_Thread::ProcessAllSessions_SortingSessions <PgSQL_Session>();
14+ template void Base_Thread::ProcessAllSessions_Partition <MySQL_Session>();
15+ template void Base_Thread::ProcessAllSessions_Partition <PgSQL_Session>();
1616template void Base_Thread::ProcessAllMyDS_AfterPoll<MySQL_Thread>();
1717template void Base_Thread::ProcessAllMyDS_AfterPoll<PgSQL_Thread>();
1818template void Base_Thread::ProcessAllMyDS_BeforePoll<MySQL_Thread>();
@@ -34,6 +34,29 @@ Base_Thread::Base_Thread() :
3434Base_Thread::~Base_Thread () {
3535};
3636
37+ bool Base_Thread::update_partition_gate () {
38+ // 64-bit so the multiplications below cannot overflow unsigned int.
39+ const uint64_t attempts = partition_pool_attempts;
40+ const uint64_t nulls = partition_pool_nulls;
41+ partition_pool_attempts = 0 ;
42+ partition_pool_nulls = 0 ;
43+
44+ // Low-volume ticks carry no signal; leave gate and streak unchanged.
45+ if (attempts < PARTITION_GATE_MIN_ATTEMPTS ) {
46+ return partition_active;
47+ }
48+
49+ const bool stressed = (nulls * PARTITION_GATE_NULL_RATIO_DEN
50+ >= attempts * PARTITION_GATE_NULL_RATIO_NUM );
51+ if (stressed == partition_active) {
52+ partition_streak = 0 ;
53+ } else if (++partition_streak >= PARTITION_GATE_STREAK ) {
54+ partition_active = stressed;
55+ partition_streak = 0 ;
56+ }
57+ return partition_active;
58+ }
59+
3760template <typename T, typename S>
3861void Base_Thread::register_session (T thr, S _sess, bool up_start) {
3962 if (mysql_sessions==NULL ) {
@@ -230,34 +253,64 @@ void Base_Thread::check_for_invalid_fd(unsigned int n) {
230253 }
231254}
232255
233- // this function was inline in MySQL_Thread::process_all_sessions()
256+
234257/* *
235- * @brief Sort all sessions based on maximum connection time.
236- *
237- * This function iterates through all MySQL sessions and sorts them based on their maximum connection time.
238- * Sessions with a valid maximum connection time are compared, and if one session has a greater maximum connection
239- * time than another, their positions in the session list are swapped. The sorting is performed in-place.
240- *
241- * @note This function assumes that MySQL sessions and their associated data structures have been initialized
242- * and are accessible within the MySQL Thread.
258+ * @brief Partition all sessions into three blocks by backend state.
259+ *
260+ * Block layout produced in mysql_sessions->pdata:
261+ * [0, running_end) block A - running a query against the backend
262+ * (myconn != NULL, mct == 0, status != WAITING_CLIENT_DATA)
263+ * [running_end, idle_begin) block B - acquiring/awaiting a backend
264+ * (mct != 0)
265+ * [idle_begin, len) block C - idle, or holds-conn-but-WAITING_CLIENT_DATA
266+ *
267+ * Block A drives the backend and may release its conn at end-of-query, giving
268+ * block B sessions a fairness chance to acquire it. Sessions parked in
269+ * WAITING_CLIENT_DATA (idle in a transaction after BEGIN) hold the conn but
270+ * cannot release it until the client sends the next packet, so they live in C.
271+ *
272+ * Classification tests max_connect_time first: it must win over A even when
273+ * myconn != NULL, to catch CHANGING_USER_SERVER on pooled connections and the
274+ * post-error retry path where the old conn hasn't been destroyed yet.
275+ *
276+ * Single O(n) pass, in place. idx walks up, idle_begin walks down, they meet
277+ * and terminate. A previous Lomuto-style sort of the B band by max_connect_time
278+ * was removed: measurement showed it hurt throughput by ~12% at 500 clients /
279+ * 50-conn pool under SSL, without a corresponding tail-latency benefit. If
280+ * reintroduced, it should be gated on an explicit starvation-age signal rather
281+ * than run unconditionally on every iteration.
243282 */
244283template <typename S>
245- void Base_Thread::ProcessAllSessions_SortingSessions () {
246- unsigned int a=0 ;
247- for (unsigned int n=0 ; n<mysql_sessions->len ; n++) {
248- S *sess=(S *)mysql_sessions->index (n);
249- if (sess->mybe && sess->mybe ->server_myds ) {
250- if (sess->mybe ->server_myds ->max_connect_time ) {
251- S *sess2=(S *)mysql_sessions->index (a);
252- if (sess2->mybe && sess2->mybe ->server_myds && sess2->mybe ->server_myds ->max_connect_time && sess2->mybe ->server_myds ->max_connect_time <= sess->mybe ->server_myds ->max_connect_time ) {
253- // do nothing
254- } else {
255- void *p=mysql_sessions->pdata [a];
256- mysql_sessions->pdata [a]=mysql_sessions->pdata [n];
257- mysql_sessions->pdata [n]=p;
258- a++;
259- }
284+ void Base_Thread::ProcessAllSessions_Partition () {
285+ size_t running_end = 0 ;
286+ size_t idle_begin = mysql_sessions->len ;
287+ size_t idx = 0 ;
288+
289+ while (idx < idle_begin) {
290+ S* s = static_cast <S*>(mysql_sessions->index (idx));
291+
292+ const bool has_be = (s->mybe && s->mybe ->server_myds );
293+ const bool is_B = has_be && (s->mybe ->server_myds ->max_connect_time != 0 );
294+ const bool is_A = !is_B && has_be && (s->mybe ->server_myds ->myconn != nullptr ) && (s->status != WAITING_CLIENT_DATA );
295+
296+ if (is_A) {
297+ if (idx != running_end) {
298+ void * p = mysql_sessions->pdata [idx];
299+ mysql_sessions->pdata [idx] = mysql_sessions->pdata [running_end];
300+ mysql_sessions->pdata [running_end] = p;
301+ }
302+ ++running_end;
303+ ++idx;
304+ } else if (is_B) {
305+ ++idx;
306+ } else {
307+ --idle_begin;
308+ if (idx != idle_begin) {
309+ void * p = mysql_sessions->pdata [idx];
310+ mysql_sessions->pdata [idx] = mysql_sessions->pdata [idle_begin];
311+ mysql_sessions->pdata [idle_begin] = p;
260312 }
313+ // do NOT advance idx - re-examine the swapped-in element test
261314 }
262315 }
263316}
0 commit comments