| 3052 | } |
| 3053 | |
| 3054 | [[noreturn]] void ImpalaServer::ExpireQueries() { |
| 3055 | while (true) { |
| 3056 | // The following block accomplishes four things: |
| 3057 | // |
| 3058 | // 1. Update the ordered list of queries by checking the 'idle_time' parameter in |
| 3059 | // client_request_state. We are able to avoid doing this for *every* query in flight |
| 3060 | // thanks to the observation that expiry times never move backwards, only |
| 3061 | // forwards. Therefore once we find a query that a) hasn't changed its idle time and |
| 3062 | // b) has not yet expired we can stop moving through the list. If the idle time has |
| 3063 | // changed, we need to re-insert the query in the right place in queries_by_timestamp_ |
| 3064 | // |
| 3065 | // 2. Remove any queries that would have expired but have already been closed for any |
| 3066 | // reason. |
| 3067 | // |
| 3068 | // 3. Compute the next time a query *might* expire, so that the sleep at the end of |
| 3069 | // this loop has an accurate duration to wait. If the list of queries is empty, the |
| 3070 | // default sleep duration is half the idle query timeout. |
| 3071 | // |
| 3072 | // 4. Cancel queries with CPU and scan bytes constraints if limit is exceeded |
| 3073 | int64_t now; |
| 3074 | { |
| 3075 | lock_guard<mutex> l(query_expiration_lock_); |
| 3076 | ExpirationQueue::iterator expiration_event = queries_by_timestamp_.begin(); |
| 3077 | now = UnixMillis(); |
| 3078 | while (expiration_event != queries_by_timestamp_.end()) { |
| 3079 | // 'queries_by_timestamp_' is stored in ascending order of deadline so we can |
| 3080 | // break out of the loop and sleep as soon as we see a deadline in the future. |
| 3081 | if (expiration_event->deadline > now) break; |
| 3082 | shared_ptr<QueryDriver> query_driver = GetQueryDriver(expiration_event->query_id); |
| 3083 | if (query_driver == nullptr) { |
| 3084 | // Query was deleted already from a previous expiration event |
| 3085 | expiration_event = queries_by_timestamp_.erase(expiration_event); |
| 3086 | continue; |
| 3087 | } |
| 3088 | ClientRequestState* crs = query_driver->GetActiveClientRequestState(); |
| 3089 | if (crs->is_expired() && expiration_event->kind != ExpirationKind::IDLE_TIMEOUT) { |
| 3090 | // Query was expired already from a previous expiration event. Keep idle |
| 3091 | // timeouts as they will additionally unregister the query. |
| 3092 | expiration_event = queries_by_timestamp_.erase(expiration_event); |
| 3093 | continue; |
| 3094 | } |
| 3095 | |
| 3096 | // Check for CPU and scanned bytes limits |
| 3097 | if (expiration_event->kind == ExpirationKind::RESOURCE_LIMIT) { |
| 3098 | Status resource_status = CheckResourceLimits(crs); |
| 3099 | if (resource_status.ok()) { |
| 3100 | queries_by_timestamp_.emplace( |
| 3101 | ExpirationEvent{now + EXPIRATION_CHECK_INTERVAL_MS, |
| 3102 | expiration_event->query_id, ExpirationKind::RESOURCE_LIMIT}); |
| 3103 | } else { |
| 3104 | ExpireQuery(crs, resource_status); |
| 3105 | } |
| 3106 | expiration_event = queries_by_timestamp_.erase(expiration_event); |
| 3107 | continue; |
| 3108 | } |
| 3109 | |
| 3110 | // If the query time limit expired, we must cancel the query. |
| 3111 | if (expiration_event->kind == ExpirationKind::EXEC_TIME_LIMIT) { |
nothing calls this directly
no test coverage detected