mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-25 09:02:00 +00:00
Add settings for soft limit raising.
This commit is contained in:
parent
1934706ca9
commit
008faaa760
@ -9,6 +9,7 @@
|
|||||||
#include <common/logger_useful.h>
|
#include <common/logger_useful.h>
|
||||||
#include <common/singleton.h>
|
#include <common/singleton.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
|
|
||||||
@ -84,7 +85,8 @@ void MemoryTracker::alloc(Int64 size)
|
|||||||
if (metric != CurrentMetrics::end())
|
if (metric != CurrentMetrics::end())
|
||||||
CurrentMetrics::add(metric, size);
|
CurrentMetrics::add(metric, size);
|
||||||
|
|
||||||
Int64 current_limit = limit.load(std::memory_order_relaxed);
|
Int64 current_hard_limit = hard_limit.load(std::memory_order_relaxed);
|
||||||
|
Int64 current_soft_limit = soft_limit.load(std::memory_order_relaxed);
|
||||||
|
|
||||||
/// Using non-thread-safe random number generator. Joint distribution in different threads would not be uniform.
|
/// Using non-thread-safe random number generator. Joint distribution in different threads would not be uniform.
|
||||||
/// In this case, it doesn't matter.
|
/// In this case, it doesn't matter.
|
||||||
@ -101,12 +103,19 @@ void MemoryTracker::alloc(Int64 size)
|
|||||||
message << " " << description;
|
message << " " << description;
|
||||||
message << ": fault injected. Would use " << formatReadableSizeWithBinarySuffix(will_be)
|
message << ": fault injected. Would use " << formatReadableSizeWithBinarySuffix(will_be)
|
||||||
<< " (attempt to allocate chunk of " << size << " bytes)"
|
<< " (attempt to allocate chunk of " << size << " bytes)"
|
||||||
<< ", maximum: " << formatReadableSizeWithBinarySuffix(current_limit);
|
<< ", maximum: " << formatReadableSizeWithBinarySuffix(current_hard_limit);
|
||||||
|
|
||||||
throw DB::Exception(message.str(), DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED);
|
throw DB::Exception(message.str(), DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(current_limit && will_be > current_limit))
|
if (unlikely(current_soft_limit && will_be > current_soft_limit))
|
||||||
|
{
|
||||||
|
auto no_track = blocker.cancel();
|
||||||
|
Singleton<DB::TraceCollector>()->collect(size);
|
||||||
|
setOrRaiseSoftLimit(current_soft_limit + Int64(ceil((will_be - current_soft_limit) / soft_limit_step)) * soft_limit_step);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(current_hard_limit && will_be > current_hard_limit))
|
||||||
{
|
{
|
||||||
free(size);
|
free(size);
|
||||||
|
|
||||||
@ -119,7 +128,7 @@ void MemoryTracker::alloc(Int64 size)
|
|||||||
message << " " << description;
|
message << " " << description;
|
||||||
message << " exceeded: would use " << formatReadableSizeWithBinarySuffix(will_be)
|
message << " exceeded: would use " << formatReadableSizeWithBinarySuffix(will_be)
|
||||||
<< " (attempt to allocate chunk of " << size << " bytes)"
|
<< " (attempt to allocate chunk of " << size << " bytes)"
|
||||||
<< ", maximum: " << formatReadableSizeWithBinarySuffix(current_limit);
|
<< ", maximum: " << formatReadableSizeWithBinarySuffix(current_hard_limit);
|
||||||
|
|
||||||
throw DB::Exception(message.str(), DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED);
|
throw DB::Exception(message.str(), DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED);
|
||||||
}
|
}
|
||||||
@ -177,7 +186,8 @@ void MemoryTracker::resetCounters()
|
|||||||
{
|
{
|
||||||
amount.store(0, std::memory_order_relaxed);
|
amount.store(0, std::memory_order_relaxed);
|
||||||
peak.store(0, std::memory_order_relaxed);
|
peak.store(0, std::memory_order_relaxed);
|
||||||
limit.store(0, std::memory_order_relaxed);
|
hard_limit.store(0, std::memory_order_relaxed);
|
||||||
|
soft_limit.store(0, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -190,11 +200,20 @@ void MemoryTracker::reset()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void MemoryTracker::setOrRaiseLimit(Int64 value)
|
void MemoryTracker::setOrRaiseHardLimit(Int64 value)
|
||||||
{
|
{
|
||||||
/// This is just atomic set to maximum.
|
/// This is just atomic set to maximum.
|
||||||
Int64 old_value = limit.load(std::memory_order_relaxed);
|
Int64 old_value = hard_limit.load(std::memory_order_relaxed);
|
||||||
while (old_value < value && !limit.compare_exchange_weak(old_value, value))
|
while (old_value < value && !hard_limit.compare_exchange_weak(old_value, value))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MemoryTracker::setOrRaiseSoftLimit(Int64 value)
|
||||||
|
{
|
||||||
|
/// This is just atomic set to maximum.
|
||||||
|
Int64 old_value = soft_limit.load(std::memory_order_relaxed);
|
||||||
|
while (old_value < value && !soft_limit.compare_exchange_weak(old_value, value))
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,9 +233,6 @@ namespace CurrentMemoryTracker
|
|||||||
Int64 tmp = untracked;
|
Int64 tmp = untracked;
|
||||||
untracked = 0;
|
untracked = 0;
|
||||||
memory_tracker->alloc(tmp);
|
memory_tracker->alloc(tmp);
|
||||||
|
|
||||||
auto no_track = memory_tracker->blocker.cancel();
|
|
||||||
Singleton<DB::TraceCollector>()->collect(tmp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,10 @@ class MemoryTracker
|
|||||||
{
|
{
|
||||||
std::atomic<Int64> amount {0};
|
std::atomic<Int64> amount {0};
|
||||||
std::atomic<Int64> peak {0};
|
std::atomic<Int64> peak {0};
|
||||||
std::atomic<Int64> limit {0};
|
std::atomic<Int64> hard_limit {0};
|
||||||
|
std::atomic<Int64> soft_limit {0};
|
||||||
|
|
||||||
|
Int64 soft_limit_step = 0;
|
||||||
|
|
||||||
/// To test exception safety of calling code, memory tracker throws an exception on each memory allocation with specified probability.
|
/// To test exception safety of calling code, memory tracker throws an exception on each memory allocation with specified probability.
|
||||||
double fault_probability = 0;
|
double fault_probability = 0;
|
||||||
@ -32,7 +35,6 @@ class MemoryTracker
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
MemoryTracker(VariableContext level_ = VariableContext::Thread) : level(level_) {}
|
MemoryTracker(VariableContext level_ = VariableContext::Thread) : level(level_) {}
|
||||||
MemoryTracker(Int64 limit_, VariableContext level_ = VariableContext::Thread) : limit(limit_), level(level_) {}
|
|
||||||
MemoryTracker(MemoryTracker * parent_, VariableContext level_ = VariableContext::Thread) : parent(parent_), level(level_) {}
|
MemoryTracker(MemoryTracker * parent_, VariableContext level_ = VariableContext::Thread) : parent(parent_), level(level_) {}
|
||||||
|
|
||||||
~MemoryTracker();
|
~MemoryTracker();
|
||||||
@ -66,21 +68,22 @@ public:
|
|||||||
return peak.load(std::memory_order_relaxed);
|
return peak.load(std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setLimit(Int64 limit_)
|
|
||||||
{
|
|
||||||
limit.store(limit_, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Set limit if it was not set.
|
/** Set limit if it was not set.
|
||||||
* Otherwise, set limit to new value, if new value is greater than previous limit.
|
* Otherwise, set limit to new value, if new value is greater than previous limit.
|
||||||
*/
|
*/
|
||||||
void setOrRaiseLimit(Int64 value);
|
void setOrRaiseHardLimit(Int64 value);
|
||||||
|
void setOrRaiseSoftLimit(Int64 value);
|
||||||
|
|
||||||
void setFaultProbability(double value)
|
void setFaultProbability(double value)
|
||||||
{
|
{
|
||||||
fault_probability = value;
|
fault_probability = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSoftLimitStep(Int64 value)
|
||||||
|
{
|
||||||
|
soft_limit_step = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// next should be changed only once: from nullptr to some value.
|
/// next should be changed only once: from nullptr to some value.
|
||||||
/// NOTE: It is not true in MergeListElement
|
/// NOTE: It is not true in MergeListElement
|
||||||
void setParent(MemoryTracker * elem)
|
void setParent(MemoryTracker * elem)
|
||||||
|
@ -212,10 +212,7 @@ void TraceCollector::run()
|
|||||||
Int64 size;
|
Int64 size;
|
||||||
readPODBinary(size, in);
|
readPODBinary(size, in);
|
||||||
|
|
||||||
UInt64 pointer;
|
TraceLogElement element{std::time(nullptr), trace_type, thread_number, query_id, trace, size};
|
||||||
readPODBinary(pointer, in);
|
|
||||||
|
|
||||||
TraceLogElement element{std::time(nullptr), trace_type, thread_number, query_id, trace, size, pointer};
|
|
||||||
trace_log->add(element);
|
trace_log->add(element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,6 +325,7 @@ struct Settings : public SettingsCollection<Settings>
|
|||||||
M(SettingUInt64, max_memory_usage, 0, "Maximum memory usage for processing of single query. Zero means unlimited.", 0) \
|
M(SettingUInt64, max_memory_usage, 0, "Maximum memory usage for processing of single query. Zero means unlimited.", 0) \
|
||||||
M(SettingUInt64, max_memory_usage_for_user, 0, "Maximum memory usage for processing all concurrently running queries for the user. Zero means unlimited.", 0) \
|
M(SettingUInt64, max_memory_usage_for_user, 0, "Maximum memory usage for processing all concurrently running queries for the user. Zero means unlimited.", 0) \
|
||||||
M(SettingUInt64, max_memory_usage_for_all_queries, 0, "Maximum memory usage for processing all concurrently running queries on the server. Zero means unlimited.", 0) \
|
M(SettingUInt64, max_memory_usage_for_all_queries, 0, "Maximum memory usage for processing all concurrently running queries on the server. Zero means unlimited.", 0) \
|
||||||
|
M(SettingUInt64, total_memory_profiler_step, 0, "Every number of bytes the memory profiler will dump the allocating stacktrace", 0) \
|
||||||
\
|
\
|
||||||
M(SettingUInt64, max_network_bandwidth, 0, "The maximum speed of data exchange over the network in bytes per second for a query. Zero means unlimited.", 0) \
|
M(SettingUInt64, max_network_bandwidth, 0, "The maximum speed of data exchange over the network in bytes per second for a query. Zero means unlimited.", 0) \
|
||||||
M(SettingUInt64, max_network_bytes, 0, "The maximum number of bytes (compressed) to receive or transmit over the network for execution of the query.", 0) \
|
M(SettingUInt64, max_network_bytes, 0, "The maximum number of bytes (compressed) to receive or transmit over the network for execution of the query.", 0) \
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#include "Common/quoteString.h"
|
||||||
#include <Common/typeid_cast.h>
|
#include <Common/typeid_cast.h>
|
||||||
#include <Common/PODArray.h>
|
#include <Common/PODArray.h>
|
||||||
|
|
||||||
@ -286,7 +287,7 @@ void ActionsMatcher::visit(const ASTIdentifier & identifier, const ASTPtr & ast,
|
|||||||
found = true;
|
found = true;
|
||||||
|
|
||||||
if (found)
|
if (found)
|
||||||
throw Exception("Column " + column_name.get(ast) + " is not under aggregate function and not in GROUP BY.",
|
throw Exception("Column " + backQuote(column_name.get(ast)) + " is not under aggregate function and not in GROUP BY",
|
||||||
ErrorCodes::NOT_AN_AGGREGATE);
|
ErrorCodes::NOT_AN_AGGREGATE);
|
||||||
|
|
||||||
/// Special check for WITH statement alias. Add alias action to be able to use this alias.
|
/// Special check for WITH statement alias. Add alias action to be able to use this alias.
|
||||||
|
@ -181,12 +181,14 @@ ProcessList::EntryPtr ProcessList::insert(const String & query_, const IAST * as
|
|||||||
/// You should specify this value in configuration for default profile,
|
/// You should specify this value in configuration for default profile,
|
||||||
/// not for specific users, sessions or queries,
|
/// not for specific users, sessions or queries,
|
||||||
/// because this setting is effectively global.
|
/// because this setting is effectively global.
|
||||||
total_memory_tracker.setOrRaiseLimit(settings.max_memory_usage_for_all_queries);
|
total_memory_tracker.setOrRaiseHardLimit(settings.max_memory_usage_for_all_queries);
|
||||||
|
total_memory_tracker.setOrRaiseSoftLimit(settings.total_memory_profiler_step);
|
||||||
|
total_memory_tracker.setSoftLimitStep(settings.total_memory_profiler_step);
|
||||||
total_memory_tracker.setDescription("(total)");
|
total_memory_tracker.setDescription("(total)");
|
||||||
|
|
||||||
/// Track memory usage for all simultaneously running queries from single user.
|
/// Track memory usage for all simultaneously running queries from single user.
|
||||||
user_process_list.user_memory_tracker.setParent(&total_memory_tracker);
|
user_process_list.user_memory_tracker.setParent(&total_memory_tracker);
|
||||||
user_process_list.user_memory_tracker.setOrRaiseLimit(settings.max_memory_usage_for_user);
|
user_process_list.user_memory_tracker.setOrRaiseHardLimit(settings.max_memory_usage_for_user);
|
||||||
user_process_list.user_memory_tracker.setDescription("(for user)");
|
user_process_list.user_memory_tracker.setDescription("(for user)");
|
||||||
|
|
||||||
/// Actualize thread group info
|
/// Actualize thread group info
|
||||||
@ -198,7 +200,7 @@ ProcessList::EntryPtr ProcessList::insert(const String & query_, const IAST * as
|
|||||||
thread_group->query = process_it->query;
|
thread_group->query = process_it->query;
|
||||||
|
|
||||||
/// Set query-level memory trackers
|
/// Set query-level memory trackers
|
||||||
thread_group->memory_tracker.setOrRaiseLimit(process_it->max_memory_usage);
|
thread_group->memory_tracker.setOrRaiseHardLimit(process_it->max_memory_usage);
|
||||||
thread_group->memory_tracker.setDescription("(for query)");
|
thread_group->memory_tracker.setDescription("(for query)");
|
||||||
if (process_it->memory_tracker_fault_probability)
|
if (process_it->memory_tracker_fault_probability)
|
||||||
thread_group->memory_tracker.setFaultProbability(process_it->memory_tracker_fault_probability);
|
thread_group->memory_tracker.setFaultProbability(process_it->memory_tracker_fault_probability);
|
||||||
|
@ -29,7 +29,6 @@ Block TraceLogElement::createBlock()
|
|||||||
{std::make_shared<DataTypeString>(), "query_id"},
|
{std::make_shared<DataTypeString>(), "query_id"},
|
||||||
{std::make_shared<DataTypeArray>(std::make_shared<DataTypeUInt64>()), "trace"},
|
{std::make_shared<DataTypeArray>(std::make_shared<DataTypeUInt64>()), "trace"},
|
||||||
{std::make_shared<DataTypeInt64>(), "size"},
|
{std::make_shared<DataTypeInt64>(), "size"},
|
||||||
{std::make_shared<DataTypeUInt64>(), "pointer"},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +46,6 @@ void TraceLogElement::appendToBlock(Block & block) const
|
|||||||
columns[i++]->insertData(query_id.data(), query_id.size());
|
columns[i++]->insertData(query_id.data(), query_id.size());
|
||||||
columns[i++]->insert(trace);
|
columns[i++]->insert(trace);
|
||||||
columns[i++]->insert(size);
|
columns[i++]->insert(size);
|
||||||
columns[i++]->insert(pointer);
|
|
||||||
|
|
||||||
block.setColumns(std::move(columns));
|
block.setColumns(std::move(columns));
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,7 @@ struct TraceLogElement
|
|||||||
String query_id;
|
String query_id;
|
||||||
Array trace;
|
Array trace;
|
||||||
|
|
||||||
/// for |TraceType::MEMORY|
|
Int64 size; /// Allocation size in bytes for |TraceType::MEMORY|
|
||||||
Int64 size; /// Allocation size in bytes. In case of deallocation should match the allocation size.
|
|
||||||
UInt64 pointer; /// Address of allocated region - to track the deallocations.
|
|
||||||
|
|
||||||
static std::string name() { return "TraceLog"; }
|
static std::string name() { return "TraceLog"; }
|
||||||
static Block createBlock();
|
static Block createBlock();
|
||||||
|
Loading…
Reference in New Issue
Block a user