2015-02-10 20:50:01 +00:00
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <memory>
|
2015-10-05 00:44:40 +00:00
|
|
|
|
#include <DB/Common/Stopwatch.h>
|
2015-10-05 01:35:28 +00:00
|
|
|
|
#include <DB/Common/Exception.h>
|
2015-09-25 12:52:47 +00:00
|
|
|
|
#include <DB/IO/WriteHelpers.h>
|
2015-02-10 20:50:01 +00:00
|
|
|
|
|
|
|
|
|
|
2015-09-25 12:52:47 +00:00
|
|
|
|
namespace DB
|
|
|
|
|
{
|
|
|
|
|
|
2015-02-10 20:50:01 +00:00
|
|
|
|
/** Позволяет ограничить скорость чего либо (в штуках в секунду) с помощью sleep.
|
|
|
|
|
* Особенности работы:
|
2015-02-11 00:07:00 +00:00
|
|
|
|
* - считается только средняя скорость, от момента первого вызова функции add;
|
2015-02-10 20:50:01 +00:00
|
|
|
|
* если были периоды с низкой скоростью, то в течение промежутка времени после них, скорость будет выше;
|
2015-09-25 12:52:47 +00:00
|
|
|
|
*
|
|
|
|
|
* Также позволяет задать ограничение на максимальное количество в штуках. При превышении кидается исключение.
|
2015-02-10 20:50:01 +00:00
|
|
|
|
*/
|
|
|
|
|
class Throttler
|
|
|
|
|
{
|
|
|
|
|
public:
|
2015-09-25 12:52:47 +00:00
|
|
|
|
Throttler(size_t max_speed_, size_t limit_, const char * limit_exceeded_exception_message_)
|
|
|
|
|
: max_speed(max_speed_), limit(limit_), limit_exceeded_exception_message(limit_exceeded_exception_message_) {}
|
2015-02-10 20:50:01 +00:00
|
|
|
|
|
|
|
|
|
void add(size_t amount)
|
|
|
|
|
{
|
|
|
|
|
size_t new_count;
|
2015-09-25 12:52:47 +00:00
|
|
|
|
UInt64 elapsed_ns = 0;
|
2015-02-10 20:50:01 +00:00
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
|
|
2015-09-25 12:52:47 +00:00
|
|
|
|
if (max_speed)
|
2015-02-11 00:07:00 +00:00
|
|
|
|
{
|
2015-09-25 12:52:47 +00:00
|
|
|
|
if (0 == count)
|
|
|
|
|
{
|
|
|
|
|
watch.start();
|
|
|
|
|
elapsed_ns = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
elapsed_ns = watch.elapsed();
|
2015-02-11 00:07:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-02-10 20:50:01 +00:00
|
|
|
|
count += amount;
|
|
|
|
|
new_count = count;
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-25 12:52:47 +00:00
|
|
|
|
if (limit && new_count > limit)
|
|
|
|
|
throw Exception(limit_exceeded_exception_message + std::string(" Maximum: ") + toString(limit), ErrorCodes::LIMIT_EXCEEDED);
|
2015-02-10 20:50:01 +00:00
|
|
|
|
|
2015-09-25 12:52:47 +00:00
|
|
|
|
if (max_speed)
|
2015-02-10 20:50:01 +00:00
|
|
|
|
{
|
2015-09-25 12:52:47 +00:00
|
|
|
|
/// Сколько должно было бы пройти времени, если бы скорость была равна max_speed.
|
|
|
|
|
UInt64 desired_ns = new_count * 1000000000 / max_speed;
|
|
|
|
|
|
|
|
|
|
if (desired_ns > elapsed_ns)
|
|
|
|
|
{
|
|
|
|
|
UInt64 sleep_ns = desired_ns - elapsed_ns;
|
|
|
|
|
timespec sleep_ts;
|
|
|
|
|
sleep_ts.tv_sec = sleep_ns / 1000000000;
|
|
|
|
|
sleep_ts.tv_nsec = sleep_ns % 1000000000;
|
|
|
|
|
nanosleep(&sleep_ts, nullptr); /// NOTE Завершается раньше в случае сигнала. Это считается нормальным.
|
|
|
|
|
}
|
2015-02-10 20:50:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2015-09-25 12:52:47 +00:00
|
|
|
|
size_t max_speed = 0;
|
2015-02-10 20:50:01 +00:00
|
|
|
|
size_t count = 0;
|
2015-09-25 12:52:47 +00:00
|
|
|
|
size_t limit = 0; /// 0 - не ограничено.
|
|
|
|
|
const char * limit_exceeded_exception_message = nullptr;
|
2015-02-11 00:38:07 +00:00
|
|
|
|
Stopwatch watch {CLOCK_MONOTONIC_COARSE};
|
2015-02-10 20:50:01 +00:00
|
|
|
|
std::mutex mutex;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef std::shared_ptr<Throttler> ThrottlerPtr;
|
2015-09-25 12:52:47 +00:00
|
|
|
|
|
|
|
|
|
}
|