|
58 | 58 | waits_a.index_name,
|
59 | 59 | waits_a.object_type,
|
60 | 60 | waits_a.source
|
| 61 | + {blocking_columns} |
61 | 62 | FROM
|
62 | 63 | performance_schema.threads AS thread_a
|
63 | 64 | LEFT JOIN performance_schema.events_waits_current AS waits_a ON waits_a.thread_id = thread_a.thread_id
|
64 | 65 | LEFT JOIN performance_schema.events_statements_current AS statement ON statement.thread_id = thread_a.thread_id
|
| 66 | + {blocking_joins} |
65 | 67 | WHERE
|
66 |
| - thread_a.processlist_state IS NOT NULL |
67 |
| - AND thread_a.processlist_command != 'Sleep' |
68 |
| - AND thread_a.processlist_id != CONNECTION_ID() |
69 |
| - AND thread_a.PROCESSLIST_COMMAND != 'Daemon' |
70 |
| - AND (waits_a.EVENT_NAME != 'idle' OR waits_a.EVENT_NAME IS NULL) |
71 |
| - AND (waits_a.operation != 'idle' OR waits_a.operation IS NULL) |
72 |
| - -- events_waits_current can have multiple rows per thread, thus we use EVENT_ID to identify the row we want to use. |
73 |
| - -- Additionally, we want the row with the highest EVENT_ID which reflects the most recent and current wait. |
74 |
| - AND ( |
75 |
| - waits_a.event_id = ( |
76 |
| - SELECT |
77 |
| - MAX(waits_b.EVENT_ID) |
78 |
| - FROM performance_schema.events_waits_current AS waits_b |
79 |
| - Where waits_b.thread_id = thread_a.thread_id |
80 |
| - ) OR waits_a.event_id is NULL) |
81 |
| - -- We ignore rows without SQL text because there will be rows for background operations that do not have |
82 |
| - -- SQL text associated with it. |
83 |
| - AND COALESCE(statement.sql_text, thread_a.PROCESSLIST_info) != ''; |
| 68 | + ( |
| 69 | + thread_a.processlist_state IS NOT NULL |
| 70 | + AND thread_a.processlist_id != CONNECTION_ID() |
| 71 | + AND thread_a.PROCESSLIST_COMMAND != 'Daemon' |
| 72 | + AND thread_a.processlist_command != 'Sleep' |
| 73 | + AND (waits_a.EVENT_NAME != 'idle' OR waits_a.EVENT_NAME IS NULL) |
| 74 | + AND (waits_a.operation != 'idle' OR waits_a.operation IS NULL) |
| 75 | + -- events_waits_current can have multiple rows per thread, thus we use EVENT_ID to identify the row |
| 76 | + -- we want to use. Additionally, we want the row with the highest EVENT_ID which reflects the most recent wait. |
| 77 | + AND ( |
| 78 | + waits_a.event_id = ( |
| 79 | + SELECT |
| 80 | + MAX(waits_b.EVENT_ID) |
| 81 | + FROM performance_schema.events_waits_current AS waits_b |
| 82 | + Where waits_b.thread_id = thread_a.thread_id |
| 83 | + ) OR waits_a.event_id is NULL) |
| 84 | + -- We ignore rows without SQL text because there will be rows for background operations that do not have |
| 85 | + -- SQL text associated with it. |
| 86 | + AND COALESCE(statement.sql_text, thread_a.PROCESSLIST_info) != '' |
| 87 | + ) |
| 88 | + {idle_blockers_subquery}; |
| 89 | +""" |
| 90 | + |
| 91 | +BLOCKING_COLUMNS = """\ |
| 92 | + ,blocking_thread.thread_id AS blocking_thread_id, |
| 93 | + blocking_thread.processlist_id AS blocking_processlist_id |
| 94 | +""" |
| 95 | + |
| 96 | +BLOCKING_JOINS = """\ |
| 97 | + LEFT JOIN performance_schema.data_lock_waits AS lock_waits ON thread_a.thread_id = lock_waits.requesting_thread_id |
| 98 | + LEFT JOIN performance_schema.threads AS blocking_thread ON lock_waits.blocking_thread_id = blocking_thread.thread_id |
| 99 | +""" |
| 100 | + |
| 101 | +IDLE_BLOCKERS_SUBQUERY = """\ |
| 102 | + OR |
| 103 | + -- Include idle sessions that are blocking others |
| 104 | + thread_a.thread_id IN ( |
| 105 | + SELECT blocking_thread_id |
| 106 | + FROM performance_schema.data_lock_waits |
| 107 | + ) |
84 | 108 | """
|
85 | 109 |
|
86 | 110 |
|
@@ -127,6 +151,7 @@ def __init__(self, check, config, connection_args):
|
127 | 151 | self._db = None
|
128 | 152 | self._db_version = None
|
129 | 153 | self._obfuscator_options = to_native_string(json.dumps(self._config.obfuscator_options))
|
| 154 | + self._activity_query = None |
130 | 155 |
|
131 | 156 | def run_job(self):
|
132 | 157 | # type: () -> None
|
@@ -180,11 +205,36 @@ def _collect_activity(self):
|
180 | 205 | tags=tags + self._check._get_debug_tags(),
|
181 | 206 | )
|
182 | 207 |
|
| 208 | + def _should_collect_blocking_queries(self): |
| 209 | + # type: () -> bool |
| 210 | + blocking_queries_configured = self._config.activity_config.get("collect_blocking_queries", False) |
| 211 | + return ( |
| 212 | + blocking_queries_configured and self._db_version == MySQLVersion.VERSION_80 and not self._check.is_mariadb |
| 213 | + ) |
| 214 | + |
| 215 | + def _get_activity_query(self): |
| 216 | + # type: () -> str |
| 217 | + if self._activity_query: |
| 218 | + return self._activity_query |
| 219 | + blocking_columns = "" |
| 220 | + blocking_joins = "" |
| 221 | + idle_blockers_subquery = "" |
| 222 | + if self._should_collect_blocking_queries(): |
| 223 | + blocking_columns = BLOCKING_COLUMNS |
| 224 | + blocking_joins = BLOCKING_JOINS |
| 225 | + idle_blockers_subquery = IDLE_BLOCKERS_SUBQUERY |
| 226 | + return ACTIVITY_QUERY.format( |
| 227 | + blocking_columns=blocking_columns, |
| 228 | + blocking_joins=blocking_joins, |
| 229 | + idle_blockers_subquery=idle_blockers_subquery, |
| 230 | + ) |
| 231 | + |
183 | 232 | @tracked_method(agent_check_getter=agent_check_getter, track_result_length=True)
|
184 | 233 | def _get_activity(self, cursor):
|
185 | 234 | # type: (pymysql.cursor) -> List[Dict[str]]
|
186 |
| - self._log.debug("Running activity query [%s]", ACTIVITY_QUERY) |
187 |
| - cursor.execute(ACTIVITY_QUERY) |
| 235 | + query = self._get_activity_query() |
| 236 | + self._log.debug("Running activity query [%s]", query) |
| 237 | + cursor.execute(query) |
188 | 238 | return cursor.fetchall()
|
189 | 239 |
|
190 | 240 | def _normalize_rows(self, rows):
|
|
0 commit comments