MCPcopy Create free account
hub / github.com/ClickHouse/ClickHouse / finishQuery

Method finishQuery

src/Interpreters/QueryMetricLog.cpp:180–253  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

178}
179
180void QueryMetricLog::finishQuery(const String & query_id, TimePoint finish_time, QueryStatusInfoPtr query_info)
181{
182 /// Test-only failpoint placed before `queries_mutex` so a concurrent periodic
183 /// `collectMetric` can overtake `finishQuery` and advance `info.last_collect_time`
184 /// past `finish_time`. The gate on `query_info` and the dedicated query-id prefix
185 /// ensures unrelated traffic is unaffected even when the failpoint is enabled.
186 /// The `query_info` gate filters `nullptr` finish paths. If a non-null phantom
187 /// finish-call still reaches this failpoint, `it == queries.end()` after resume
188 /// makes this function return immediately.
189 if (query_info && FailPointInjection::hasAnyFailPointBeenRegistered()
190 && query_id.starts_with(query_metric_log_final_row_failpoint_query_id_prefix))
191 FailPointInjection::pauseFailPoint(FailPoints::query_metric_log_pause_before_finish);
192
193 UniqueLock global_lock(queries_mutex);
194 auto it = queries.find(query_id);
195
196 /// finishQuery may be called from logExceptionBeforeStart when the query has not even started
197 /// yet, so its corresponding startQuery is never called.
198 if (it == queries.end())
199 return;
200
201 auto & query_status = it->second;
202
203 /// Get a refcounted reference to the mutex to ensure it's not destroyed until the query is
204 /// removed from queries. Otherwise, query_lock would attempt to unlock a non-existing mutex.
205 auto mutex = query_status.mutex;
206 UniqueLock query_lock(query_status.getMutex());
207
208 /// finishQuery may be called twice for the same query_id if a new query with the same query_id
209 /// is attempted to run in case replace_running_query=0 (the default). Make sure we only execute
210 /// the finishQuery once.
211 auto thread_id = CurrentThread::get().thread_id;
212 if (thread_id != query_status.thread_id || query_status.finished)
213 {
214 LOG_TEST(logger, "Query {} finished from a different thread_id than the one it started it: "
215 "original was {}, this one is {}. Ignoring this finishQuery",
216 query_id, query_status.thread_id, thread_id);
217 return;
218 }
219
220 setQueryFinished(query_status);
221 global_lock.unlock();
222
223 if (query_info)
224 {
225 auto elem = query_status.createLogMetricElement(query_id, *query_info, finish_time, /* is_final = */ true);
226 if (elem)
227 add(std::move(elem.value()));
228 }
229
230 /// The task has an `exec_mutex` locked while being executed. This same mutex is locked when
231 /// deactivating the task, which happens automatically on its destructor. Thus, we cannot
232 /// deactivate/destroy the task while it's running. Now, the task locks `queries_mutex` to
233 /// prevent concurrent edition of the `queries`. In short, the mutex order is: `exec_mutex` ->
234 /// `queries_mutex` -> `query_status.mutex`. So, to prevent a deadlock we need to make sure that
235 /// we always lock them in that order.
236 {
237 /// Take ownership of the task so that we can destroy it in this scope after unlocking `queries_mutex`.

Callers 1

logQueryMetricLogFinishFunction · 0.45

Calls 9

getFunction · 0.50
addFunction · 0.50
findMethod · 0.45
endMethod · 0.45
unlockMethod · 0.45
valueMethod · 0.45
lockMethod · 0.45
eraseMethod · 0.45

Tested by

no test coverage detected