Merge pull request #58665 from ClickHouse/jemalloc-system-commands

Add SYSTEM commands and Keeper 4LW for jemalloc
This commit is contained in:
Antonio Andelic 2024-01-12 10:08:13 +01:00 committed by GitHub
commit 654fee820e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 272 additions and 21 deletions

View File

@ -95,6 +95,7 @@ if (BUILD_STANDALONE_KEEPER)
${CMAKE_CURRENT_SOURCE_DIR}/../../src/Common/CurrentThread.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../src/Common/NamedCollections/NamedCollections.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../src/Common/NamedCollections/NamedCollectionConfiguration.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../src/Common/Jemalloc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../src/Common/ZooKeeper/IKeeper.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../../src/Common/ZooKeeper/TestKeeper.cpp

View File

@ -200,6 +200,7 @@ enum class AccessType
M(SYSTEM_UNFREEZE, "SYSTEM UNFREEZE", GLOBAL, SYSTEM) \
M(SYSTEM_FAILPOINT, "SYSTEM ENABLE FAILPOINT, SYSTEM DISABLE FAILPOINT", GLOBAL, SYSTEM) \
M(SYSTEM_LISTEN, "SYSTEM START LISTEN, SYSTEM STOP LISTEN", GLOBAL, SYSTEM) \
M(SYSTEM_JEMALLOC, "SYSTEM JEMALLOC PURGE, SYSTEM JEMALLOC ENABLE PROFILE, SYSTEM JEMALLOC DISABLE PROFILE, SYSTEM JEMALLOC FLUSH PROFILE", GLOBAL, SYSTEM) \
M(SYSTEM, "", GROUP, ALL) /* allows to execute SYSTEM {SHUTDOWN|RELOAD CONFIG|...} */ \
\
M(dictGet, "dictHas, dictGetHierarchy, dictIsIn", DICTIONARY, ALL) /* allows to execute functions dictGet(), dictHas(), dictGetHierarchy(), dictIsIn() */\

88
src/Common/Jemalloc.cpp Normal file
View File

@ -0,0 +1,88 @@
#include <Common/Jemalloc.h>
#if USE_JEMALLOC
#include <Common/Stopwatch.h>
#include <Common/logger_useful.h>
#include <jemalloc/jemalloc.h>
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
namespace ProfileEvents
{
extern const Event MemoryAllocatorPurge;
extern const Event MemoryAllocatorPurgeTimeMicroseconds;
}
namespace DB
{
namespace ErrorCodes
{
extern const int BAD_ARGUMENTS;
}
void purgeJemallocArenas()
{
LOG_TRACE(&Poco::Logger::get("SystemJemalloc"), "Purging unused memory");
Stopwatch watch;
mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", nullptr, nullptr, nullptr, 0);
ProfileEvents::increment(ProfileEvents::MemoryAllocatorPurge);
ProfileEvents::increment(ProfileEvents::MemoryAllocatorPurgeTimeMicroseconds, watch.elapsedMicroseconds());
}
void checkJemallocProfilingEnabled()
{
bool active = true;
size_t active_size = sizeof(active);
mallctl("opt.prof", &active, &active_size, nullptr, 0);
if (!active)
throw Exception(
ErrorCodes::BAD_ARGUMENTS,
"ClickHouse was started without enabling profiling for jemalloc. To use jemalloc's profiler, following env variable should be "
"set: MALLOC_CONF=background_thread:true,prof:true");
}
void setJemallocProfileActive(bool value)
{
checkJemallocProfilingEnabled();
bool active = true;
size_t active_size = sizeof(active);
mallctl("prof.active", &active, &active_size, nullptr, 0);
if (active == value)
{
LOG_TRACE(&Poco::Logger::get("SystemJemalloc"), "Profiling is already {}", active ? "enabled" : "disabled");
return;
}
mallctl("prof.active", nullptr, nullptr, &value, sizeof(bool));
LOG_TRACE(&Poco::Logger::get("SystemJemalloc"), "Profiling is {}", value ? "enabled" : "disabled");
}
std::string flushJemallocProfile(const std::string & file_prefix)
{
checkJemallocProfilingEnabled();
char * prefix_buffer;
size_t prefix_size = sizeof(prefix_buffer);
int n = mallctl("opt.prof_prefix", &prefix_buffer, &prefix_size, nullptr, 0);
if (!n && std::string_view(prefix_buffer) != "jeprof")
{
LOG_TRACE(&Poco::Logger::get("SystemJemalloc"), "Flushing memory profile with prefix {}", prefix_buffer);
mallctl("prof.dump", nullptr, nullptr, nullptr, 0);
return prefix_buffer;
}
static std::atomic<size_t> profile_counter{0};
std::string profile_dump_path = fmt::format("{}.{}.{}.heap", file_prefix, getpid(), profile_counter.fetch_add(1));
const auto * profile_dump_path_str = profile_dump_path.c_str();
LOG_TRACE(&Poco::Logger::get("SystemJemalloc"), "Flushing memory profile to {}", profile_dump_path_str);
mallctl("prof.dump", nullptr, nullptr, &profile_dump_path_str, sizeof(profile_dump_path_str));
return profile_dump_path;
}
}
#endif

22
src/Common/Jemalloc.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include "config.h"
#if USE_JEMALLOC
#include <string>
namespace DB
{
void purgeJemallocArenas();
void checkJemallocProfilingEnabled();
void setJemallocProfileActive(bool value);
std::string flushJemallocProfile(const std::string & file_prefix);
}
#endif

View File

@ -1,10 +1,11 @@
#include <Coordination/CoordinationSettings.h>
#include <Common/logger_useful.h>
#include <filesystem>
#include <Coordination/Defines.h>
#include <IO/WriteHelpers.h>
#include <IO/WriteIntText.h>
#include "config.h"
namespace DB
{
namespace ErrorCodes
@ -36,7 +37,11 @@ void CoordinationSettings::loadFromConfig(const String & config_elem, const Poco
}
const String KeeperConfigurationAndSettings::DEFAULT_FOUR_LETTER_WORD_CMD = "conf,cons,crst,envi,ruok,srst,srvr,stat,wchs,dirs,mntr,isro,rcvr,apiv,csnp,lgif,rqld,rclc,clrs,ftfl,ydld";
const String KeeperConfigurationAndSettings::DEFAULT_FOUR_LETTER_WORD_CMD =
#if USE_JEMALLOC
"jmst,jmfp,jmep,jmdp,"
#endif
"conf,cons,crst,envi,ruok,srst,srvr,stat,wchs,dirs,mntr,isro,rcvr,apiv,csnp,lgif,rqld,rclc,clrs,ftfl,ydld";
KeeperConfigurationAndSettings::KeeperConfigurationAndSettings()
: server_id(NOT_EXIST)

View File

@ -18,6 +18,11 @@
#include <unistd.h>
#include <bit>
#if USE_JEMALLOC
#include <Common/Jemalloc.h>
#include <jemalloc/jemalloc.h>
#endif
namespace
{
@ -175,6 +180,20 @@ void FourLetterCommandFactory::registerCommands(KeeperDispatcher & keeper_dispat
FourLetterCommandPtr yield_leadership_command = std::make_shared<YieldLeadershipCommand>(keeper_dispatcher);
factory.registerCommand(yield_leadership_command);
#if USE_JEMALLOC
FourLetterCommandPtr jemalloc_dump_stats = std::make_shared<JemallocDumpStats>(keeper_dispatcher);
factory.registerCommand(jemalloc_dump_stats);
FourLetterCommandPtr jemalloc_flush_profile = std::make_shared<JemallocFlushProfile>(keeper_dispatcher);
factory.registerCommand(jemalloc_flush_profile);
FourLetterCommandPtr jemalloc_enable_profile = std::make_shared<JemallocEnableProfile>(keeper_dispatcher);
factory.registerCommand(jemalloc_enable_profile);
FourLetterCommandPtr jemalloc_disable_profile = std::make_shared<JemallocDisableProfile>(keeper_dispatcher);
factory.registerCommand(jemalloc_disable_profile);
#endif
factory.initializeAllowList(keeper_dispatcher);
factory.setInitialize(true);
}
@ -588,4 +607,37 @@ String YieldLeadershipCommand::run()
return "Sent yield leadership request to leader.";
}
#if USE_JEMALLOC
void printToString(void * output, const char * data)
{
std::string * output_data = reinterpret_cast<std::string *>(output);
*output_data += std::string(data);
}
String JemallocDumpStats::run()
{
std::string output;
malloc_stats_print(printToString, &output, nullptr);
return output;
}
String JemallocFlushProfile::run()
{
return flushJemallocProfile("/tmp/jemalloc_keeper");
}
String JemallocEnableProfile::run()
{
setJemallocProfileActive(true);
return "ok";
}
String JemallocDisableProfile::run()
{
setJemallocProfileActive(false);
return "ok";
}
#endif
}

View File

@ -428,4 +428,55 @@ struct YieldLeadershipCommand : public IFourLetterCommand
~YieldLeadershipCommand() override = default;
};
#if USE_JEMALLOC
struct JemallocDumpStats : public IFourLetterCommand
{
explicit JemallocDumpStats(KeeperDispatcher & keeper_dispatcher_)
: IFourLetterCommand(keeper_dispatcher_)
{
}
String name() override { return "jmst"; }
String run() override;
~JemallocDumpStats() override = default;
};
struct JemallocFlushProfile : public IFourLetterCommand
{
explicit JemallocFlushProfile(KeeperDispatcher & keeper_dispatcher_)
: IFourLetterCommand(keeper_dispatcher_)
{
}
String name() override { return "jmfp"; }
String run() override;
~JemallocFlushProfile() override = default;
};
struct JemallocEnableProfile : public IFourLetterCommand
{
explicit JemallocEnableProfile(KeeperDispatcher & keeper_dispatcher_)
: IFourLetterCommand(keeper_dispatcher_)
{
}
String name() override { return "jmep"; }
String run() override;
~JemallocEnableProfile() override = default;
};
struct JemallocDisableProfile : public IFourLetterCommand
{
explicit JemallocDisableProfile(KeeperDispatcher & keeper_dispatcher_)
: IFourLetterCommand(keeper_dispatcher_)
{
}
String name() override { return "jmdp"; }
String run() override;
~JemallocDisableProfile() override = default;
};
#endif
}

View File

@ -15,16 +15,10 @@
#include <atomic>
#include <future>
#include <chrono>
#include <filesystem>
#include <iterator>
#include <limits>
#if USE_JEMALLOC
# include <jemalloc/jemalloc.h>
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#include <Common/Jemalloc.h>
#endif
namespace CurrentMetrics
@ -33,12 +27,6 @@ namespace CurrentMetrics
extern const Metric KeeperOutstandingRequets;
}
namespace ProfileEvents
{
extern const Event MemoryAllocatorPurge;
extern const Event MemoryAllocatorPurgeTimeMicroseconds;
}
using namespace std::chrono_literals;
namespace DB
@ -986,11 +974,7 @@ Keeper4LWInfo KeeperDispatcher::getKeeper4LWInfo() const
void KeeperDispatcher::cleanResources()
{
#if USE_JEMALLOC
LOG_TRACE(&Poco::Logger::get("KeeperDispatcher"), "Purging unused memory");
Stopwatch watch;
mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", nullptr, nullptr, nullptr, 0);
ProfileEvents::increment(ProfileEvents::MemoryAllocatorPurge);
ProfileEvents::increment(ProfileEvents::MemoryAllocatorPurgeTimeMicroseconds, watch.elapsedMicroseconds());
purgeJemallocArenas();
#endif
}

View File

@ -74,6 +74,10 @@
#include <IO/S3/Client.h>
#endif
#if USE_JEMALLOC
#include <Common/Jemalloc.h>
#endif
#include "config.h"
namespace CurrentMetrics
@ -98,7 +102,6 @@ namespace ErrorCodes
extern const int SUPPORT_IS_DISABLED;
}
namespace ActionLocks
{
extern const StorageActionBlockType PartsMerge;
@ -728,6 +731,33 @@ BlockIO InterpreterSystemQuery::execute()
resetCoverage();
break;
}
#if USE_JEMALLOC
case Type::JEMALLOC_PURGE:
{
getContext()->checkAccess(AccessType::SYSTEM_JEMALLOC);
purgeJemallocArenas();
break;
}
case Type::JEMALLOC_ENABLE_PROFILE:
{
getContext()->checkAccess(AccessType::SYSTEM_JEMALLOC);
setJemallocProfileActive(true);
break;
}
case Type::JEMALLOC_DISABLE_PROFILE:
{
getContext()->checkAccess(AccessType::SYSTEM_JEMALLOC);
setJemallocProfileActive(false);
break;
}
case Type::JEMALLOC_FLUSH_PROFILE:
{
getContext()->checkAccess(AccessType::SYSTEM_JEMALLOC);
flushJemallocProfile("/tmp/jemalloc_clickhouse");
break;
}
#endif
default:
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Unknown type of SYSTEM query");
}
@ -1368,6 +1398,16 @@ AccessRightsElements InterpreterSystemQuery::getRequiredAccessForDDLOnCluster()
required_access.emplace_back(AccessType::SYSTEM_LISTEN);
break;
}
#if USE_JEMALLOC
case Type::JEMALLOC_PURGE:
case Type::JEMALLOC_ENABLE_PROFILE:
case Type::JEMALLOC_DISABLE_PROFILE:
case Type::JEMALLOC_FLUSH_PROFILE:
{
required_access.emplace_back(AccessType::SYSTEM_JEMALLOC);
break;
}
#endif
case Type::STOP_THREAD_FUZZER:
case Type::START_THREAD_FUZZER:
case Type::ENABLE_FAILPOINT:

View File

@ -46,6 +46,12 @@ public:
WAIT_LOADING_PARTS,
DROP_REPLICA,
DROP_DATABASE_REPLICA,
#if USE_JEMALLOC
JEMALLOC_PURGE,
JEMALLOC_ENABLE_PROFILE,
JEMALLOC_DISABLE_PROFILE,
JEMALLOC_FLUSH_PROFILE,
#endif
SYNC_REPLICA,
SYNC_DATABASE_REPLICA,
SYNC_TRANSACTION_LOG,

View File

@ -150,6 +150,7 @@ SYSTEM THREAD FUZZER ['SYSTEM START THREAD FUZZER','SYSTEM STOP THREAD FUZZER','
SYSTEM UNFREEZE ['SYSTEM UNFREEZE'] GLOBAL SYSTEM
SYSTEM FAILPOINT ['SYSTEM ENABLE FAILPOINT','SYSTEM DISABLE FAILPOINT'] GLOBAL SYSTEM
SYSTEM LISTEN ['SYSTEM START LISTEN','SYSTEM STOP LISTEN'] GLOBAL SYSTEM
SYSTEM JEMALLOC ['SYSTEM JEMALLOC PURGE','SYSTEM JEMALLOC ENABLE PROFILE','SYSTEM JEMALLOC DISABLE PROFILE','SYSTEM JEMALLOC FLUSH PROFILE'] GLOBAL SYSTEM
SYSTEM [] \N ALL
dictGet ['dictHas','dictGetHierarchy','dictIsIn'] DICTIONARY ALL
displaySecretsInShowAndSelect [] GLOBAL ALL