Fixed not an issue, shown by TSan; minor modifications [#CLICKHOUSE-2].

This commit is contained in:
Alexey Milovidov 2017-09-10 01:28:38 +03:00
parent 0c41b87647
commit fe717a4d6e
2 changed files with 58 additions and 26 deletions

View File

@ -37,10 +37,11 @@ template void QuotaValues<size_t>::initFromConfig(const String & config_elem, Po
template void QuotaValues<std::atomic<size_t>>::initFromConfig(const String & config_elem, Poco::Util::AbstractConfiguration & config);
void QuotaForInterval::initFromConfig(const String & config_elem, time_t duration_, time_t offset_, Poco::Util::AbstractConfiguration & config)
void QuotaForInterval::initFromConfig(const String & config_elem, time_t duration_, bool randomize_, time_t offset_, Poco::Util::AbstractConfiguration & config)
{
rounded_time = 0;
rounded_time.store(0, std::memory_order_relaxed);
duration = duration_;
randomize = randomize_;
offset = offset_;
max.initFromConfig(config_elem, config);
}
@ -61,8 +62,10 @@ String QuotaForInterval::toString() const
{
std::stringstream res;
auto loaded_rounded_time = rounded_time.load(std::memory_order_relaxed);
res << std::fixed << std::setprecision(3)
<< "Interval: " << LocalDateTime(rounded_time) << " - " << LocalDateTime(rounded_time + duration) << ".\n"
<< "Interval: " << LocalDateTime(loaded_rounded_time) << " - " << LocalDateTime(loaded_rounded_time + duration) << ".\n"
<< "Queries: " << used.queries << ".\n"
<< "Errors: " << used.errors << ".\n"
<< "Result rows: " << used.result_rows << ".\n"
@ -107,10 +110,22 @@ void QuotaForInterval::checkAndAddExecutionTime(time_t current_time, const Strin
void QuotaForInterval::updateTime(time_t current_time)
{
if (current_time >= rounded_time + static_cast<int>(duration))
/** If current time is greater than end of interval,
* then clear accumulated quota values and switch to next interval [rounded_time, rounded_time + duration).
*/
auto loaded_rounded_time = rounded_time.load(std::memory_order_acquire);
while (true)
{
rounded_time = (current_time - offset) / duration * duration + offset;
used.clear();
if (current_time < loaded_rounded_time + static_cast<time_t>(duration))
break;
time_t new_rounded_time = (current_time - offset) / duration * duration + offset;
if (rounded_time.compare_exchange_strong(loaded_rounded_time, new_rounded_time))
{
used.clear();
break;
}
}
}
@ -136,7 +151,7 @@ void QuotaForInterval::check(
message << " has been exceeded. "
<< resource_name << ": " << used_amount << ", max: " << max_amount << ". "
<< "Interval will end at " << LocalDateTime(rounded_time + duration) << ". "
<< "Interval will end at " << LocalDateTime(rounded_time.load(std::memory_order_relaxed) + duration) << ". "
<< "Name of quota template: '" << quota_name << "'.";
throw Exception(message.str(), ErrorCodes::QUOTA_EXPIRED);
@ -158,14 +173,14 @@ void QuotaForIntervals::initFromConfig(const String & config_elem, Poco::Util::A
time_t duration = config.getInt(interval_config_elem + ".duration", 0);
time_t offset = 0;
if (!duration) /// Skip quaotas with zero duration
if (!duration) /// Skip quotas with zero duration
continue;
bool randomize = config.getBool(interval_config_elem + ".randomize", false);
if (randomize)
offset = std::uniform_int_distribution<decltype(duration)>(0, duration - 1)(rng);
cont[duration].initFromConfig(interval_config_elem, duration, offset, config);
cont[duration].initFromConfig(interval_config_elem, duration, randomize, offset, config);
}
}
@ -182,7 +197,7 @@ void QuotaForIntervals::setMax(const QuotaForIntervals & quota)
for (auto & x : quota.cont)
{
if (!cont.count(x.first))
cont[x.first] = x.second;
cont.emplace(x.first, x.second);
else
cont[x.first].max = x.second.max;
}
@ -254,7 +269,7 @@ void Quota::loadFromConfig(const String & config_elem, const String & name_, Poc
QuotaForIntervals new_max(name, {});
new_max.initFromConfig(config_elem, config, rng);
if (!(new_max == max))
if (!new_max.hasEqualConfiguration(max))
{
max = new_max;
for (auto & quota : quota_for_keys)
@ -310,9 +325,9 @@ void Quotas::loadFromConfig(Poco::Util::AbstractConfiguration & config)
for (Poco::Util::AbstractConfiguration::Keys::const_iterator it = config_keys.begin(); it != config_keys.end(); ++it)
{
if (!cont[*it])
cont[*it] = std::make_unique<Quota>();
cont[*it]->loadFromConfig("quotas." + *it, *it, config, rng);
if (!cont.count(*it))
cont.try_emplace(*it);
cont[*it].loadFromConfig("quotas." + *it, *it, config, rng);
}
}
@ -322,7 +337,7 @@ QuotaForIntervalsPtr Quotas::get(const String & name, const String & quota_key,
if (cont.end() == it)
throw Exception("Unknown quota " + name, ErrorCodes::UNKNOWN_QUOTA);
return it->second->get(quota_key, user_name, ip);
return it->second.get(quota_key, user_name, ip);
}
}

View File

@ -99,16 +99,17 @@ inline auto QuotaValues<std::atomic<size_t>>::tuple() const
/// Time, rounded down to start of interval; limits for that interval and accumulated values.
struct QuotaForInterval
{
time_t rounded_time = 0;
std::atomic<time_t> rounded_time {0};
size_t duration = 0;
bool randomize = false;
time_t offset = 0; /// Offset of interval for randomization (to avoid DoS if intervals for many users end at one time).
QuotaValues<size_t> max;
QuotaValues<std::atomic<size_t>> used;
QuotaForInterval() {}
QuotaForInterval() = default;
QuotaForInterval(time_t duration_) : duration(duration_) {}
void initFromConfig(const String & config_elem, time_t duration_, time_t offset_, Poco::Util::AbstractConfiguration & config);
void initFromConfig(const String & config_elem, time_t duration_, bool randomize_, time_t offset_, Poco::Util::AbstractConfiguration & config);
/// Increase current value.
void addQuery() noexcept;
@ -125,14 +126,30 @@ struct QuotaForInterval
/// Get a text, describing what quota is exceeded.
String toString() const;
/// Only compare configuration, not accumulated (used) values or random offsets.
bool operator== (const QuotaForInterval & rhs) const
{
return
rounded_time == rhs.rounded_time &&
duration == rhs.duration &&
max == rhs.max &&
used == rhs.used;
return randomize == rhs.randomize
&& duration == rhs.duration
&& max == rhs.max;
}
QuotaForInterval & operator= (const QuotaForInterval & rhs)
{
rounded_time.store(rhs.rounded_time.load(std::memory_order_relaxed));
duration = rhs.duration;
randomize = rhs.randomize;
offset = rhs.offset;
max = rhs.max;
used = rhs.used;
return *this;
}
QuotaForInterval(const QuotaForInterval & rhs)
{
*this = rhs;
}
private:
/// Reset counters of used resources, if interval for quota is expired.
void updateTime(time_t current_time);
@ -192,7 +209,7 @@ public:
/// Get text, describing what part of quota has been exceeded.
String toString() const;
bool operator== (const QuotaForIntervals & rhs) const
bool hasEqualConfiguration(const QuotaForIntervals & rhs) const
{
return cont == rhs.cont && quota_name == rhs.quota_name;
}
@ -233,13 +250,13 @@ class Quotas
{
private:
/// Name of quota -> quota.
using Container = std::unordered_map<String, std::unique_ptr<Quota>>;
using Container = std::unordered_map<String, Quota>;
Container cont;
public:
void loadFromConfig(Poco::Util::AbstractConfiguration & config);
QuotaForIntervalsPtr get(const String & name, const String & quota_key,
const String & user_name, const Poco::Net::IPAddress & ip);
const String & user_name, const Poco::Net::IPAddress & ip);
};
}