-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Merge PR#5776 PR#5784 #5785
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merge PR#5776 PR#5784 #5785
Changes from all commits
ca202ce
587950d
1430f50
1fe7db3
afc94c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,8 +11,8 @@ | |
| // Explicitly instantiate the required template class and member functions | ||
| template MySQL_Session* Base_Thread::create_new_session_and_client_data_stream<MySQL_Thread, MySQL_Session*>(int); | ||
| template PgSQL_Session* Base_Thread::create_new_session_and_client_data_stream<PgSQL_Thread, PgSQL_Session*>(int); | ||
| template void Base_Thread::ProcessAllSessions_SortingSessions<MySQL_Session>(); | ||
| template void Base_Thread::ProcessAllSessions_SortingSessions<PgSQL_Session>(); | ||
| template void Base_Thread::ProcessAllSessions_Partition<MySQL_Session>(); | ||
| template void Base_Thread::ProcessAllSessions_Partition<PgSQL_Session>(); | ||
| template void Base_Thread::ProcessAllMyDS_AfterPoll<MySQL_Thread>(); | ||
| template void Base_Thread::ProcessAllMyDS_AfterPoll<PgSQL_Thread>(); | ||
| template void Base_Thread::ProcessAllMyDS_BeforePoll<MySQL_Thread>(); | ||
|
|
@@ -230,33 +230,97 @@ | |
| } | ||
| } | ||
|
|
||
| // this function was inline in MySQL_Thread::process_all_sessions() | ||
|
|
||
| /** | ||
| * @brief Sort all sessions based on maximum connection time. | ||
| * | ||
| * This function iterates through all MySQL sessions and sorts them based on their maximum connection time. | ||
| * Sessions with a valid maximum connection time are compared, and if one session has a greater maximum connection | ||
| * time than another, their positions in the session list are swapped. The sorting is performed in-place. | ||
| * | ||
| * @note This function assumes that MySQL sessions and their associated data structures have been initialized | ||
| * and are accessible within the MySQL Thread. | ||
| * @brief Partition all sessions into three blocks, then FIFO-bump | ||
| * the long-waiters within block B. | ||
| * | ||
| * Block layout produced in mysql_sessions->pdata: | ||
| * [0, running_end) block A - running a query against the backend | ||
| * (myconn != NULL, mct == 0, status != WAITING_CLIENT_DATA) | ||
| * [running_end, idle_begin) block B - acquiring/awaiting a backend | ||
| * (mct != 0) | ||
| * [idle_begin, len) block C - idle, or holds-conn-but-WAITING_CLIENT_DATA | ||
| * | ||
| * Block A drives the backend and may release its conn at end-of-query, giving | ||
| * block B sessions a fairness chance to acquire it. Sessions parked in | ||
| * WAITING_CLIENT_DATA (idle in a transaction after BEGIN) hold the conn but | ||
| * cannot release it until the client sends the next packet, so they live in C. | ||
| * | ||
| * Classification tests max_connect_time first: it must win over A even when | ||
| * myconn != NULL, to catch CHANGING_USER_SERVER on pooled connections and the | ||
| * post-error retry path where the old conn hasn't been destroyed yet. | ||
| * | ||
| * Loop 1 (partition + stats): single O(n) pass, in place. idx walks up, | ||
| * idle_begin walks down, they meet and terminate. While partitioning, we | ||
| * also accumulate sum_wait and b_count over block B, where | ||
| * wt = curtime - CurrentQuery.start_time (microseconds) | ||
| * | ||
| * Loop 2 (O(b_count)): promote sessions in B with | ||
| * wt > K_WAIT * avg(wt) | ||
| * to the front of the B band, preserving their relative pdata order. This | ||
| * ensures the longer-waiting sessions in B get a chance at the next released | ||
| * backend conn before the newer arrivals do. | ||
| * | ||
| */ | ||
| template<typename S> | ||
| void Base_Thread::ProcessAllSessions_SortingSessions() { | ||
| unsigned int a=0; | ||
| for (unsigned int n=0; n<mysql_sessions->len; n++) { | ||
| S *sess=(S *)mysql_sessions->index(n); | ||
| if (sess->mybe && sess->mybe->server_myds) { | ||
| if (sess->mybe->server_myds->max_connect_time) { | ||
| S *sess2=(S *)mysql_sessions->index(a); | ||
| 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) { | ||
| // do nothing | ||
| } else { | ||
| void *p=mysql_sessions->pdata[a]; | ||
| mysql_sessions->pdata[a]=mysql_sessions->pdata[n]; | ||
| mysql_sessions->pdata[n]=p; | ||
| a++; | ||
| void Base_Thread::ProcessAllSessions_Partition() { | ||
|
Check failure on line 267 in lib/Base_Thread.cpp
|
||
| unsigned long long sum_wait = 0; | ||
| size_t b_count = 0; | ||
| size_t running_end = 0; | ||
| size_t idle_begin = mysql_sessions->len; | ||
| size_t idx = 0; | ||
|
|
||
| while (idx < idle_begin) { | ||
| S* s = static_cast<S*>(mysql_sessions->index(idx)); | ||
|
|
||
| const bool has_be = (s->mybe && s->mybe->server_myds); | ||
| const bool is_B = has_be && (s->mybe->server_myds->max_connect_time != 0); | ||
| const bool is_A = !is_B && has_be && (s->mybe->server_myds->myconn != nullptr) && (s->status != WAITING_CLIENT_DATA); | ||
|
|
||
| if (is_A) { | ||
| if (idx != running_end) { | ||
| void* p = mysql_sessions->pdata[idx]; | ||
| mysql_sessions->pdata[idx] = mysql_sessions->pdata[running_end]; | ||
| mysql_sessions->pdata[running_end] = p; | ||
| } | ||
| ++running_end; | ||
| ++idx; | ||
| } else if (is_B) { | ||
| const unsigned long long st = s->CurrentQuery.start_time; | ||
| const unsigned long long wt = (st && curtime > st) ? (curtime - st) : 0; | ||
| sum_wait += wt; | ||
| ++b_count; | ||
| ++idx; | ||
| } else { | ||
| --idle_begin; | ||
| if (idx != idle_begin) { | ||
| void* p = mysql_sessions->pdata[idx]; | ||
| mysql_sessions->pdata[idx] = mysql_sessions->pdata[idle_begin]; | ||
| mysql_sessions->pdata[idle_begin] = p; | ||
| } | ||
| // do NOT advance idx - re-examine the swapped-in element test | ||
| } | ||
| } | ||
|
|
||
| // promote sessions with wait_time > K * avg(wait_time) | ||
| // to the front of B | ||
| constexpr double K_WAIT = 1.1; | ||
| if (b_count > 1) { | ||
| const unsigned long long avg_wait = sum_wait / b_count; | ||
| const unsigned long long threshold = static_cast<unsigned long long>(static_cast<double>(avg_wait) * K_WAIT); | ||
| size_t a = running_end; | ||
| for (size_t n = running_end; n < idle_begin; ++n) { | ||
| S * sn = static_cast<S*>(mysql_sessions->pdata[n]); | ||
| const unsigned long long st = sn->CurrentQuery.start_time; | ||
| const unsigned long long wt = (st && curtime > st) ? (curtime - st) : 0; | ||
| if (wt > threshold) { | ||
| if (a != n) { | ||
|
Check failure on line 318 in lib/Base_Thread.cpp
|
||
| void* p = mysql_sessions->pdata[a]; | ||
| mysql_sessions->pdata[a] = mysql_sessions->pdata[n]; | ||
| mysql_sessions->pdata[n] = p; | ||
| } | ||
| ++a; | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -347,10 +411,23 @@ | |
| template<typename T, typename DS> | ||
| void Base_Thread::tune_timeout_for_session_needs_pause(DS * myds) { | ||
| T* thr = static_cast<T*>(this); | ||
| if (thr->mypolls.poll_timeout==0 || (myds->sess->pause_until - curtime < thr->mypolls.poll_timeout) ) { | ||
| thr->mypolls.poll_timeout= myds->sess->pause_until - curtime; | ||
| proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Session=%p , poll_timeout=%u , pause_until=%llu , curtime=%llu\n", myds->sess, thr->mypolls.poll_timeout, myds->sess->pause_until, curtime); | ||
| } | ||
|
|
||
| if (myds->sess->pause_until > curtime) { | ||
| // Future pause: align poll_timeout to the pause expiration. | ||
| if (thr->mypolls.poll_timeout == 0 || (myds->sess->pause_until - curtime < thr->mypolls.poll_timeout)) { | ||
| thr->mypolls.poll_timeout = myds->sess->pause_until - curtime; | ||
| proxy_debug(PROXY_DEBUG_MYSQL_CONNECTION, 7, "Session=%p , poll_timeout=%u , pause_until=%llu , curtime=%llu\n", myds->sess, thr->mypolls.poll_timeout, | ||
| myds->sess->pause_until, curtime); | ||
| } | ||
| } | ||
| /* Do we need to immediately wake up poll() because of an already expired pause? | ||
| else { | ||
| // pause_until > 0 (caller checked) but <= curtime: pause has already expired. | ||
| // Wake poll() immediately rather than computing (pause_until - curtime) | ||
| if (thr->mypolls.poll_timeout == 0 || thr->mypolls.poll_timeout > 1) { | ||
| thr->mypolls.poll_timeout = 1; | ||
| } | ||
| }*/ | ||
|
Comment on lines
+423
to
+430
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This block of code is commented out. Leaving large blocks of commented-out code in the repository reduces maintainability. Furthermore, the logic within the comment appears to handle the case where a session's pause has already expired ( |
||
| } | ||
|
|
||
| template<typename T, typename DS> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a significant unit mismatch here.
myds->sess->pause_untilandcurtimeare typically in microseconds (as seen in the session handlers where delays are multiplied by 1000), whilepoll_timeoutfor thepoll()system call is in milliseconds. Assigning the microsecond difference directly topoll_timeoutwill result in a timeout that is 1000 times longer than intended (e.g., a 100ms pause becomes a 100-second wait). The comparison in theifstatement is also incorrect as it compares milliseconds with microseconds.