mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-27 01:51:59 +00:00
dbms: Quota: development [#CONV-8459].
This commit is contained in:
parent
181651991d
commit
c10d3a4bb9
@ -7,6 +7,7 @@
|
|||||||
#include <DB/Core/Names.h>
|
#include <DB/Core/Names.h>
|
||||||
|
|
||||||
#include <DB/Interpreters/Limits.h>
|
#include <DB/Interpreters/Limits.h>
|
||||||
|
#include <DB/Interpreters/Quota.h>
|
||||||
|
|
||||||
#include <DB/DataStreams/IBlockInputStream.h>
|
#include <DB/DataStreams/IBlockInputStream.h>
|
||||||
|
|
||||||
@ -68,7 +69,8 @@ private:
|
|||||||
class IProfilingBlockInputStream : public IBlockInputStream
|
class IProfilingBlockInputStream : public IBlockInputStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IProfilingBlockInputStream(StoragePtr owned_storage_ = StoragePtr()) : IBlockInputStream(owned_storage_), is_cancelled(false) {}
|
IProfilingBlockInputStream(StoragePtr owned_storage_ = StoragePtr())
|
||||||
|
: IBlockInputStream(owned_storage_), is_cancelled(false), quota(NULL), quota_mode(QUOTA_READ) {}
|
||||||
|
|
||||||
Block read();
|
Block read();
|
||||||
|
|
||||||
@ -130,6 +132,21 @@ public:
|
|||||||
limits = limits_;
|
limits = limits_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Какая квота используется - на объём исходных данных или на объём результата.
|
||||||
|
enum QuotaMode
|
||||||
|
{
|
||||||
|
QUOTA_READ,
|
||||||
|
QUOTA_RESULT,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Установить квоту.
|
||||||
|
void setQuota(QuotaForIntervals & quota_, QuotaMode quota_mode_)
|
||||||
|
{
|
||||||
|
quota = "a_;
|
||||||
|
quota_mode = quota_mode_;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
BlockStreamProfileInfo info;
|
BlockStreamProfileInfo info;
|
||||||
volatile bool is_cancelled;
|
volatile bool is_cancelled;
|
||||||
@ -137,6 +154,9 @@ protected:
|
|||||||
|
|
||||||
LocalLimits limits;
|
LocalLimits limits;
|
||||||
|
|
||||||
|
QuotaForIntervals * quota; /// Если NULL - квота не используется.
|
||||||
|
QuotaMode quota_mode;
|
||||||
|
|
||||||
/// Наследники должны реализовать эту функцию.
|
/// Наследники должны реализовать эту функцию.
|
||||||
virtual Block readImpl() = 0;
|
virtual Block readImpl() = 0;
|
||||||
};
|
};
|
||||||
|
@ -75,12 +75,13 @@ struct QuotaForInterval
|
|||||||
void checkExceeded(time_t current_time, const String & quota_name);
|
void checkExceeded(time_t current_time, const String & quota_name);
|
||||||
|
|
||||||
/// Проверить соответствующее значение. Если превышено - кинуть исключение. Иначе - увеличить его.
|
/// Проверить соответствующее значение. Если превышено - кинуть исключение. Иначе - увеличить его.
|
||||||
void checkAndAddResultRows(time_t current_time, const String & quota_name, size_t amount);
|
void checkAndAddResultRowsBytes(time_t current_time, const String & quota_name, size_t rows, size_t bytes);
|
||||||
void checkAndAddResultBytes(time_t current_time, const String & quota_name, size_t amount);
|
void checkAndAddReadRowsBytes(time_t current_time, const String & quota_name, size_t rows, size_t bytes);
|
||||||
void checkAndAddReadRows(time_t current_time, const String & quota_name, size_t amount);
|
|
||||||
void checkAndAddReadBytes(time_t current_time, const String & quota_name, size_t amount);
|
|
||||||
void checkAndAddExecutionTime(time_t current_time, const String & quota_name, Poco::Timespan amount);
|
void checkAndAddExecutionTime(time_t current_time, const String & quota_name, Poco::Timespan amount);
|
||||||
|
|
||||||
|
/// Получить текст, описывающий, какая часть квоты израсходована.
|
||||||
|
String toString() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Сбросить счётчик использованных ресурсов, если соответствующий интервал, за который считается квота, прошёл.
|
/// Сбросить счётчик использованных ресурсов, если соответствующий интервал, за который считается квота, прошёл.
|
||||||
void updateTime(time_t current_time);
|
void updateTime(time_t current_time);
|
||||||
@ -103,6 +104,12 @@ private:
|
|||||||
public:
|
public:
|
||||||
QuotaForIntervals(Quota * parent_) : parent(parent_) {}
|
QuotaForIntervals(Quota * parent_) : parent(parent_) {}
|
||||||
|
|
||||||
|
/// Есть ли хотя бы один интервал, за который считается квота?
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
return cont.empty();
|
||||||
|
}
|
||||||
|
|
||||||
void initFromConfig(const String & config_elem);
|
void initFromConfig(const String & config_elem);
|
||||||
|
|
||||||
void addQuery(time_t current_time);
|
void addQuery(time_t current_time);
|
||||||
@ -110,11 +117,12 @@ public:
|
|||||||
|
|
||||||
void checkExceeded(time_t current_time);
|
void checkExceeded(time_t current_time);
|
||||||
|
|
||||||
void checkAndAddResultRows(time_t current_time, size_t amount);
|
void checkAndAddResultRowsBytes(time_t current_time, size_t rows, size_t bytes);
|
||||||
void checkAndAddResultBytes(time_t current_time, size_t amount);
|
void checkAndAddReadRowsBytes(time_t current_time, size_t rows, size_t bytes);
|
||||||
void checkAndAddReadRows(time_t current_time, size_t amount);
|
|
||||||
void checkAndAddReadBytes(time_t current_time, size_t amount);
|
|
||||||
void checkAndAddExecutionTime(time_t current_time, Poco::Timespan amount);
|
void checkAndAddExecutionTime(time_t current_time, Poco::Timespan amount);
|
||||||
|
|
||||||
|
/// Получить текст, описывающий, какая часть квоты израсходована.
|
||||||
|
String toString() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -302,7 +302,7 @@ BlockInputStreamPtr InterpreterSelectQuery::execute()
|
|||||||
|
|
||||||
executeUnion(streams);
|
executeUnion(streams);
|
||||||
|
|
||||||
/// Ограничения на результат, а также колбек для прогресса.
|
/// Ограничения на результат, квота на результат, а также колбек для прогресса.
|
||||||
if (IProfilingBlockInputStream * stream = dynamic_cast<IProfilingBlockInputStream *>(&*streams[0]))
|
if (IProfilingBlockInputStream * stream = dynamic_cast<IProfilingBlockInputStream *>(&*streams[0]))
|
||||||
{
|
{
|
||||||
IProfilingBlockInputStream::LocalLimits limits;
|
IProfilingBlockInputStream::LocalLimits limits;
|
||||||
@ -311,6 +311,7 @@ BlockInputStreamPtr InterpreterSelectQuery::execute()
|
|||||||
limits.read_overflow_mode = settings.limits.result_overflow_mode;
|
limits.read_overflow_mode = settings.limits.result_overflow_mode;
|
||||||
|
|
||||||
stream->setLimits(limits);
|
stream->setLimits(limits);
|
||||||
|
stream->setQuota(context.getQuota(), IProfilingBlockInputStream::QUOTA_RESULT);
|
||||||
|
|
||||||
stream->setProgressCallback(context.getProgressCallback());
|
stream->setProgressCallback(context.getProgressCallback());
|
||||||
}
|
}
|
||||||
@ -405,7 +406,7 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu
|
|||||||
if (streams.size() > settings.max_threads)
|
if (streams.size() > settings.max_threads)
|
||||||
streams = narrowBlockInputStreams(streams, settings.max_threads);
|
streams = narrowBlockInputStreams(streams, settings.max_threads);
|
||||||
|
|
||||||
/** Установка ограничений на чтение данных.
|
/** Установка ограничений и квоты на чтение данных.
|
||||||
* Они устанавливаются на самые "глубокие" чтения.
|
* Они устанавливаются на самые "глубокие" чтения.
|
||||||
* То есть, не должны устанавливаться для чтений из удалённых серверов и подзапросов.
|
* То есть, не должны устанавливаться для чтений из удалённых серверов и подзапросов.
|
||||||
*/
|
*/
|
||||||
@ -420,9 +421,16 @@ QueryProcessingStage::Enum InterpreterSelectQuery::executeFetchColumns(BlockInpu
|
|||||||
limits.min_execution_speed = settings.limits.min_execution_speed;
|
limits.min_execution_speed = settings.limits.min_execution_speed;
|
||||||
limits.timeout_before_checking_execution_speed = settings.limits.timeout_before_checking_execution_speed;
|
limits.timeout_before_checking_execution_speed = settings.limits.timeout_before_checking_execution_speed;
|
||||||
|
|
||||||
|
QuotaForIntervals & quota = context.getQuota();
|
||||||
|
|
||||||
for (BlockInputStreams::iterator it = streams.begin(); it != streams.end(); ++it)
|
for (BlockInputStreams::iterator it = streams.begin(); it != streams.end(); ++it)
|
||||||
|
{
|
||||||
if (IProfilingBlockInputStream * stream = dynamic_cast<IProfilingBlockInputStream *>(&**it))
|
if (IProfilingBlockInputStream * stream = dynamic_cast<IProfilingBlockInputStream *>(&**it))
|
||||||
|
{
|
||||||
stream->setLimits(limits);
|
stream->setLimits(limits);
|
||||||
|
stream->setQuota(quota, IProfilingBlockInputStream::QUOTA_READ);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return from_stage;
|
return from_stage;
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include <Yandex/logger_useful.h>
|
||||||
|
|
||||||
#include <DB/Common/SipHash.h>
|
#include <DB/Common/SipHash.h>
|
||||||
#include <DB/Interpreters/Quota.h>
|
#include <DB/Interpreters/Quota.h>
|
||||||
|
|
||||||
@ -36,10 +40,23 @@ void QuotaForInterval::checkExceeded(time_t current_time, const String & quota_n
|
|||||||
check(max.read_rows, used.read_rows, current_time, quota_name, "Total rows read");
|
check(max.read_rows, used.read_rows, current_time, quota_name, "Total rows read");
|
||||||
check(max.read_bytes, used.read_bytes, current_time, quota_name, "Total bytes read");
|
check(max.read_bytes, used.read_bytes, current_time, quota_name, "Total bytes read");
|
||||||
check(max.execution_time.totalSeconds(), used.execution_time.totalSeconds(), current_time, quota_name, "Total execution time");
|
check(max.execution_time.totalSeconds(), used.execution_time.totalSeconds(), current_time, quota_name, "Total execution time");
|
||||||
|
}
|
||||||
|
|
||||||
std::cerr << "Current values for interval " << mysqlxx::DateTime(rounded_time) << " - " << mysqlxx::DateTime(rounded_time + duration) << ":\n"
|
String QuotaForInterval::toString() const
|
||||||
<< "queries: " << used.queries << "\n"
|
{
|
||||||
<< "errors: " << used.errors << "\n";
|
std::stringstream res;
|
||||||
|
|
||||||
|
res << std::fixed << std::setprecision(3)
|
||||||
|
<< "Interval: " << mysqlxx::DateTime(rounded_time) << " - " << mysqlxx::DateTime(rounded_time + duration) << "."
|
||||||
|
<< "Queries: " << used.queries << "."
|
||||||
|
<< "Errors: " << used.errors << "."
|
||||||
|
<< "Result rows: " << used.result_rows << "."
|
||||||
|
<< "Result bytes: " << used.result_bytes << "."
|
||||||
|
<< "Read rows: " << used.read_rows << "."
|
||||||
|
<< "Read bytes: " << used.read_bytes << "."
|
||||||
|
<< "Execution time: " << used.execution_time.totalMilliseconds() / 1000.0 << " seconds.";
|
||||||
|
|
||||||
|
return res.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuotaForInterval::addQuery(time_t current_time, const String & quota_name)
|
void QuotaForInterval::addQuery(time_t current_time, const String & quota_name)
|
||||||
@ -52,28 +69,18 @@ void QuotaForInterval::addError(time_t current_time, const String & quota_name)
|
|||||||
++used.errors;
|
++used.errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuotaForInterval::checkAndAddResultRows(time_t current_time, const String & quota_name, size_t amount)
|
void QuotaForInterval::checkAndAddResultRowsBytes(time_t current_time, const String & quota_name, size_t rows, size_t bytes)
|
||||||
{
|
{
|
||||||
checkExceeded(current_time, quota_name);
|
checkExceeded(current_time, quota_name);
|
||||||
used.result_rows += amount;
|
used.result_rows += rows;
|
||||||
|
used.result_bytes += bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuotaForInterval::checkAndAddResultBytes(time_t current_time, const String & quota_name, size_t amount)
|
void QuotaForInterval::checkAndAddReadRowsBytes(time_t current_time, const String & quota_name, size_t rows, size_t bytes)
|
||||||
{
|
{
|
||||||
checkExceeded(current_time, quota_name);
|
checkExceeded(current_time, quota_name);
|
||||||
used.result_bytes += amount;
|
used.read_rows += rows;
|
||||||
}
|
used.read_bytes += bytes;
|
||||||
|
|
||||||
void QuotaForInterval::checkAndAddReadRows(time_t current_time, const String & quota_name, size_t amount)
|
|
||||||
{
|
|
||||||
checkExceeded(current_time, quota_name);
|
|
||||||
used.read_rows += amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuotaForInterval::checkAndAddReadBytes(time_t current_time, const String & quota_name, size_t amount)
|
|
||||||
{
|
|
||||||
checkExceeded(current_time, quota_name);
|
|
||||||
used.read_bytes += amount;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuotaForInterval::checkAndAddExecutionTime(time_t current_time, const String & quota_name, Poco::Timespan amount)
|
void QuotaForInterval::checkAndAddExecutionTime(time_t current_time, const String & quota_name, Poco::Timespan amount)
|
||||||
@ -158,32 +165,18 @@ void QuotaForIntervals::addError(time_t current_time)
|
|||||||
it->second.addError(current_time, parent->name);
|
it->second.addError(current_time, parent->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuotaForIntervals::checkAndAddResultRows(time_t current_time, size_t amount)
|
void QuotaForIntervals::checkAndAddResultRowsBytes(time_t current_time, size_t rows, size_t bytes)
|
||||||
{
|
{
|
||||||
Poco::ScopedLock<Poco::FastMutex> lock(parent->mutex);
|
Poco::ScopedLock<Poco::FastMutex> lock(parent->mutex);
|
||||||
for (Container::reverse_iterator it = cont.rbegin(); it != cont.rend(); ++it)
|
for (Container::reverse_iterator it = cont.rbegin(); it != cont.rend(); ++it)
|
||||||
it->second.checkAndAddResultRows(current_time, parent->name, amount);
|
it->second.checkAndAddResultRowsBytes(current_time, parent->name, rows, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuotaForIntervals::checkAndAddResultBytes(time_t current_time, size_t amount)
|
void QuotaForIntervals::checkAndAddReadRowsBytes(time_t current_time, size_t rows, size_t bytes)
|
||||||
{
|
{
|
||||||
Poco::ScopedLock<Poco::FastMutex> lock(parent->mutex);
|
Poco::ScopedLock<Poco::FastMutex> lock(parent->mutex);
|
||||||
for (Container::reverse_iterator it = cont.rbegin(); it != cont.rend(); ++it)
|
for (Container::reverse_iterator it = cont.rbegin(); it != cont.rend(); ++it)
|
||||||
it->second.checkAndAddResultBytes(current_time, parent->name, amount);
|
it->second.checkAndAddReadRowsBytes(current_time, parent->name, rows, bytes);
|
||||||
}
|
|
||||||
|
|
||||||
void QuotaForIntervals::checkAndAddReadRows(time_t current_time, size_t amount)
|
|
||||||
{
|
|
||||||
Poco::ScopedLock<Poco::FastMutex> lock(parent->mutex);
|
|
||||||
for (Container::reverse_iterator it = cont.rbegin(); it != cont.rend(); ++it)
|
|
||||||
it->second.checkAndAddReadRows(current_time, parent->name, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuotaForIntervals::checkAndAddReadBytes(time_t current_time, size_t amount)
|
|
||||||
{
|
|
||||||
Poco::ScopedLock<Poco::FastMutex> lock(parent->mutex);
|
|
||||||
for (Container::reverse_iterator it = cont.rbegin(); it != cont.rend(); ++it)
|
|
||||||
it->second.checkAndAddReadBytes(current_time, parent->name, amount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuotaForIntervals::checkAndAddExecutionTime(time_t current_time, Poco::Timespan amount)
|
void QuotaForIntervals::checkAndAddExecutionTime(time_t current_time, Poco::Timespan amount)
|
||||||
@ -193,6 +186,19 @@ void QuotaForIntervals::checkAndAddExecutionTime(time_t current_time, Poco::Time
|
|||||||
it->second.checkAndAddExecutionTime(current_time, parent->name, amount);
|
it->second.checkAndAddExecutionTime(current_time, parent->name, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String QuotaForIntervals::toString() const
|
||||||
|
{
|
||||||
|
std::stringstream res;
|
||||||
|
|
||||||
|
{
|
||||||
|
Poco::ScopedLock<Poco::FastMutex> lock(parent->mutex);
|
||||||
|
for (Container::reverse_iterator it = cont.rbegin(); it != cont.rend(); ++it)
|
||||||
|
res << std::endl << it->second.toString() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Quota::initFromConfig(const String & config_elem, const String & name_)
|
void Quota::initFromConfig(const String & config_elem, const String & name_)
|
||||||
{
|
{
|
||||||
|
@ -138,6 +138,10 @@ void HTTPHandler::processQuery(Poco::Net::HTTPServerRequest & request, Poco::Net
|
|||||||
<< static_cast<size_t>(rows / watch.elapsedSeconds()) << " rows/sec., " << bytes / 1048576.0 / watch.elapsedSeconds() << " MiB/sec.");
|
<< static_cast<size_t>(rows / watch.elapsedSeconds()) << " rows/sec., " << bytes / 1048576.0 / watch.elapsedSeconds() << " MiB/sec.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QuotaForIntervals & quota = context.getQuota();
|
||||||
|
if (!quota.empty())
|
||||||
|
LOG_INFO(log, "Quota:\n" << quota.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,6 +85,10 @@ namespace DB
|
|||||||
<< static_cast<size_t>(rows / watch.elapsedSeconds()) << " rows/sec., " << bytes / 1048576.0 / watch.elapsedSeconds() << " MiB/sec.");
|
<< static_cast<size_t>(rows / watch.elapsedSeconds()) << " rows/sec., " << bytes / 1048576.0 / watch.elapsedSeconds() << " MiB/sec.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QuotaForIntervals & quota = context.getQuota();
|
||||||
|
if (!quota.empty())
|
||||||
|
LOG_INFO(log, "Quota:\n" << quota.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -284,6 +284,10 @@ void TCPHandler::logProfileInfo(Stopwatch & watch, IBlockInputStream & in)
|
|||||||
<< "Read " << rows << " rows, " << bytes / 1048576.0 << " MiB in " << watch.elapsedSeconds() << " sec., "
|
<< "Read " << rows << " rows, " << bytes / 1048576.0 << " MiB in " << watch.elapsedSeconds() << " sec., "
|
||||||
<< static_cast<size_t>(rows / watch.elapsedSeconds()) << " rows/sec., " << bytes / 1048576.0 / watch.elapsedSeconds() << " MiB/sec.");
|
<< static_cast<size_t>(rows / watch.elapsedSeconds()) << " rows/sec., " << bytes / 1048576.0 / watch.elapsedSeconds() << " MiB/sec.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QuotaForIntervals & quota = query_context.getQuota();
|
||||||
|
if (!quota.empty())
|
||||||
|
LOG_INFO(log, "Quota:\n" << quota.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user