mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-27 10:02:01 +00:00
131 lines
2.8 KiB
C++
131 lines
2.8 KiB
C++
#pragma once
|
|
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
#include <memory>
|
|
#include <chrono>
|
|
#include <DB/Common/CurrentMetrics.h>
|
|
|
|
|
|
namespace CurrentMetrics
|
|
{
|
|
extern const Metric QueryPreempted;
|
|
}
|
|
|
|
|
|
namespace DB
|
|
{
|
|
|
|
/** Implements query priorities in very primitive way.
|
|
* Allows to freeze query execution if at least one query of higher priority is executed.
|
|
*
|
|
* Priority value is integer, smaller means higher priority.
|
|
*
|
|
* Priority 0 is special - queries with that priority is always executed,
|
|
* not depends on other queries and not affect other queries.
|
|
* Thus 0 means - don't use priorities.
|
|
*
|
|
* NOTE Possibilities for improvement:
|
|
* - implement limit on maximum number of running queries with same priority.
|
|
*/
|
|
class QueryPriorities
|
|
{
|
|
public:
|
|
using Priority = int;
|
|
|
|
private:
|
|
friend struct Handle;
|
|
|
|
using Count = int;
|
|
|
|
/// Number of currently running queries for each priority.
|
|
using Container = std::map<Priority, Count>;
|
|
|
|
std::mutex mutex;
|
|
std::condition_variable condvar;
|
|
Container container;
|
|
|
|
|
|
/** If there are higher priority queries - sleep until they are finish or timeout happens.
|
|
* Returns true, if higher priority queries has finished at return of function, false, if timout exceeded.
|
|
*/
|
|
template <typename Duration>
|
|
bool waitIfNeed(Priority priority, Duration timeout)
|
|
{
|
|
if (0 == priority)
|
|
return true;
|
|
|
|
std::unique_lock<std::mutex> lock(mutex);
|
|
|
|
while (true)
|
|
{
|
|
/// Если ли хотя бы один более приоритетный запрос?
|
|
bool found = false;
|
|
for (const auto & value : container)
|
|
{
|
|
if (value.first >= priority)
|
|
break;
|
|
|
|
if (value.second > 0)
|
|
{
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
return true;
|
|
|
|
CurrentMetrics::Increment metric_increment{CurrentMetrics::QueryPreempted};
|
|
if (std::cv_status::timeout == condvar.wait_for(lock, timeout))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public:
|
|
struct HandleImpl
|
|
{
|
|
private:
|
|
QueryPriorities & parent;
|
|
QueryPriorities::Container::value_type & value;
|
|
|
|
public:
|
|
HandleImpl(QueryPriorities & parent_, QueryPriorities::Container::value_type & value_)
|
|
: parent(parent_), value(value_) {}
|
|
|
|
~HandleImpl()
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(parent.mutex);
|
|
--value.second;
|
|
}
|
|
parent.condvar.notify_all();
|
|
}
|
|
|
|
template <typename Duration>
|
|
bool waitIfNeed(Duration timeout)
|
|
{
|
|
return parent.waitIfNeed(value.first, timeout);
|
|
}
|
|
};
|
|
|
|
using Handle = std::shared_ptr<HandleImpl>;
|
|
|
|
/** Register query with specified priority.
|
|
* Returns an object that remove record in destructor.
|
|
*/
|
|
Handle insert(Priority priority)
|
|
{
|
|
if (0 == priority)
|
|
return {};
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
auto it = container.emplace(priority, 0).first;
|
|
++it->second;
|
|
return std::make_shared<HandleImpl>(*this, *it);
|
|
}
|
|
};
|
|
|
|
}
|