#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace CurrentMetrics { extern const Metric Query; } namespace DB { /** List of currently executing queries. * Also implements limit on their number. */ /** Information of process list element. * To output in SHOW PROCESSLIST query. Does not contain any complex objects, that do something on copy or destructor. */ struct ProcessInfo { String query; double elapsed_seconds; size_t rows; size_t bytes; size_t total_rows; Int64 memory_usage; ClientInfo client_info; }; /// Query and information about its execution. struct ProcessListElement { String query; ClientInfo client_info; Stopwatch watch; Progress progress; MemoryTracker memory_tracker; QueryPriorities::Handle priority_handle; CurrentMetrics::Increment num_queries {CurrentMetrics::Query}; bool is_cancelled = false; /// Temporary tables could be registered here. Modify under mutex. Tables temporary_tables; ProcessListElement( const String & query_, const ClientInfo & client_info_, size_t max_memory_usage, double memory_tracker_fault_probability, QueryPriorities::Handle && priority_handle_) : query(query_), client_info(client_info_), memory_tracker(max_memory_usage), priority_handle(std::move(priority_handle_)) { memory_tracker.setDescription("(for query)"); current_memory_tracker = &memory_tracker; if (memory_tracker_fault_probability) memory_tracker.setFaultProbability(memory_tracker_fault_probability); } ~ProcessListElement() { current_memory_tracker = nullptr; } bool update(const Progress & value) { progress.incrementPiecewiseAtomically(value); if (priority_handle) priority_handle->waitIfNeed(std::chrono::seconds(1)); /// NOTE Could make timeout customizable. return !is_cancelled; } ProcessInfo getInfo() const { ProcessInfo res; res.query = query; res.client_info = client_info; res.elapsed_seconds = watch.elapsedSeconds(); res.rows = progress.rows; res.bytes = progress.bytes; res.total_rows = progress.total_rows; res.memory_usage = memory_tracker.get(); return res; } }; /// Data about queries for one user. struct ProcessListForUser { /// Query_id -> ProcessListElement * using QueryToElement = std::unordered_map; QueryToElement queries; /// Limit and counter for memory of all simultaneously running queries of single user. MemoryTracker user_memory_tracker; }; class ProcessList; /// Keeps iterator to process list and removes element in destructor. class ProcessListEntry { private: using Container = std::list; ProcessList & parent; Container::iterator it; public: ProcessListEntry(ProcessList & parent_, Container::iterator it_) : parent(parent_), it(it_) {} ~ProcessListEntry(); ProcessListElement * operator->() { return &*it; } const ProcessListElement * operator->() const { return &*it; } ProcessListElement & get() { return *it; } const ProcessListElement & get() const { return *it; } }; class ProcessList { friend class ProcessListEntry; public: using Element = ProcessListElement; using Entry = ProcessListEntry; /// list, for iterators not to invalidate. NOTE: could replace with cyclic buffer, but not worth. using Container = std::list; using Info = std::vector; /// User -> queries using UserToQueries = std::unordered_map; private: mutable std::mutex mutex; mutable Poco::Condition have_space; /// Number of currently running queries has become less than maximum. Container cont; size_t cur_size; /// In C++03 or C++11 and old ABI, std::list::size is not O(1). size_t max_size; /// 0 means no limit. Otherwise, when limit exceeded, an exception is thrown. UserToQueries user_to_queries; QueryPriorities priorities; /// Limit and counter for memory of all simultaneously running queries. MemoryTracker total_memory_tracker; public: ProcessList(size_t max_size_ = 0) : cur_size(0), max_size(max_size_) {} using EntryPtr = std::shared_ptr; /** Register running query. Returns refcounted object, that will remove element from list in destructor. * If too much running queries - wait for not more than specified (see settings) amount of time. * If timeout is passed - throw an exception. */ EntryPtr insert(const String & query_, const ClientInfo & client_info, const Settings & settings); /// Number of currently executing queries. size_t size() const { return cur_size; } /// Get current state of process list. Info getInfo() const { std::lock_guard lock(mutex); Info res; res.reserve(cur_size); for (const auto & elem : cont) res.emplace_back(elem.getInfo()); return res; } void setMaxSize(size_t max_size_) { std::lock_guard lock(mutex); max_size = max_size_; } /// Register temporary table. Then it is accessible by query_id and name. void addTemporaryTable(ProcessListElement & elem, const String & table_name, StoragePtr storage); /// Find temporary table by query_id and name. NOTE: doesn't work fine if there are many queries with same query_id. StoragePtr tryGetTemporaryTable(const String & query_id, const String & table_name) const; }; }