Merge pull request #66587 from ClickHouse/backport/24.6/66237

Backport #66237 to 24.6: Fix detection of number of CPUs in containers
This commit is contained in:
robot-ch-test-poll 2024-07-16 15:17:25 +02:00 committed by GitHub
commit 293a9a0c6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 30 additions and 25 deletions

View File

@ -3,8 +3,9 @@
#include <base/defines.h> #include <base/defines.h>
#include <fstream> #include <fstream>
#include <sstream> #include <string>
namespace fs = std::filesystem;
bool cgroupsV2Enabled() bool cgroupsV2Enabled()
{ {
@ -13,11 +14,11 @@ bool cgroupsV2Enabled()
{ {
/// This file exists iff the host has cgroups v2 enabled. /// This file exists iff the host has cgroups v2 enabled.
auto controllers_file = default_cgroups_mount / "cgroup.controllers"; auto controllers_file = default_cgroups_mount / "cgroup.controllers";
if (!std::filesystem::exists(controllers_file)) if (!fs::exists(controllers_file))
return false; return false;
return true; return true;
} }
catch (const std::filesystem::filesystem_error &) /// all "underlying OS API errors", typically: permission denied catch (const fs::filesystem_error &) /// all "underlying OS API errors", typically: permission denied
{ {
return false; /// not logging the exception as most callers fall back to cgroups v1 return false; /// not logging the exception as most callers fall back to cgroups v1
} }
@ -33,8 +34,9 @@ bool cgroupsV2MemoryControllerEnabled()
/// According to https://docs.kernel.org/admin-guide/cgroup-v2.html, file "cgroup.controllers" defines which controllers are available /// According to https://docs.kernel.org/admin-guide/cgroup-v2.html, file "cgroup.controllers" defines which controllers are available
/// for the current + child cgroups. The set of available controllers can be restricted from level to level using file /// for the current + child cgroups. The set of available controllers can be restricted from level to level using file
/// "cgroups.subtree_control". It is therefore sufficient to check the bottom-most nested "cgroup.controllers" file. /// "cgroups.subtree_control". It is therefore sufficient to check the bottom-most nested "cgroup.controllers" file.
std::string cgroup = cgroupV2OfProcess(); fs::path cgroup_dir = cgroupV2PathOfProcess();
auto cgroup_dir = cgroup.empty() ? default_cgroups_mount : (default_cgroups_mount / cgroup); if (cgroup_dir.empty())
return false;
std::ifstream controllers_file(cgroup_dir / "cgroup.controllers"); std::ifstream controllers_file(cgroup_dir / "cgroup.controllers");
if (!controllers_file.is_open()) if (!controllers_file.is_open())
return false; return false;
@ -46,7 +48,7 @@ bool cgroupsV2MemoryControllerEnabled()
#endif #endif
} }
std::string cgroupV2OfProcess() fs::path cgroupV2PathOfProcess()
{ {
#if defined(OS_LINUX) #if defined(OS_LINUX)
chassert(cgroupsV2Enabled()); chassert(cgroupsV2Enabled());
@ -54,17 +56,18 @@ std::string cgroupV2OfProcess()
/// A simpler way to get the membership is: /// A simpler way to get the membership is:
std::ifstream cgroup_name_file("/proc/self/cgroup"); std::ifstream cgroup_name_file("/proc/self/cgroup");
if (!cgroup_name_file.is_open()) if (!cgroup_name_file.is_open())
return ""; return {};
/// With cgroups v2, there will be a *single* line with prefix "0::/" /// With cgroups v2, there will be a *single* line with prefix "0::/"
/// (see https://docs.kernel.org/admin-guide/cgroup-v2.html) /// (see https://docs.kernel.org/admin-guide/cgroup-v2.html)
std::string cgroup; std::string cgroup;
std::getline(cgroup_name_file, cgroup); std::getline(cgroup_name_file, cgroup);
static const std::string v2_prefix = "0::/"; static const std::string v2_prefix = "0::/";
if (!cgroup.starts_with(v2_prefix)) if (!cgroup.starts_with(v2_prefix))
return ""; return {};
cgroup = cgroup.substr(v2_prefix.length()); cgroup = cgroup.substr(v2_prefix.length());
return cgroup; /// Note: The 'root' cgroup can have an empty cgroup name, this is valid
return default_cgroups_mount / cgroup;
#else #else
return ""; return {};
#endif #endif
} }

View File

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <filesystem> #include <filesystem>
#include <string>
#if defined(OS_LINUX) #if defined(OS_LINUX)
/// I think it is possible to mount the cgroups hierarchy somewhere else (e.g. when in containers). /// I think it is possible to mount the cgroups hierarchy somewhere else (e.g. when in containers).
@ -16,7 +15,7 @@ bool cgroupsV2Enabled();
/// Assumes that cgroupsV2Enabled() is enabled. /// Assumes that cgroupsV2Enabled() is enabled.
bool cgroupsV2MemoryControllerEnabled(); bool cgroupsV2MemoryControllerEnabled();
/// Which cgroup does the process belong to? /// Detects which cgroup v2 the process belongs to and returns the filesystem path to the cgroup.
/// Returns an empty string if the cgroup cannot be determined. /// Returns an empty path the cgroup cannot be determined.
/// Assumes that cgroupsV2Enabled() is enabled. /// Assumes that cgroupsV2Enabled() is enabled.
std::string cgroupV2OfProcess(); std::filesystem::path cgroupV2PathOfProcess();

View File

@ -23,8 +23,9 @@ std::optional<uint64_t> getCgroupsV2MemoryLimit()
if (!cgroupsV2MemoryControllerEnabled()) if (!cgroupsV2MemoryControllerEnabled())
return {}; return {};
std::string cgroup = cgroupV2OfProcess(); std::filesystem::path current_cgroup = cgroupV2PathOfProcess();
auto current_cgroup = cgroup.empty() ? default_cgroups_mount : (default_cgroups_mount / cgroup); if (current_cgroup.empty())
return {};
/// Open the bottom-most nested memory limit setting file. If there is no such file at the current /// Open the bottom-most nested memory limit setting file. If there is no such file at the current
/// level, try again at the parent level as memory settings are inherited. /// level, try again at the parent level as memory settings are inherited.

View File

@ -22,6 +22,7 @@
#define STRINGIFY(x) STRINGIFY_HELPER(x) #define STRINGIFY(x) STRINGIFY_HELPER(x)
#endif #endif
namespace fs = std::filesystem;
namespace DB namespace DB
{ {
@ -125,15 +126,16 @@ std::optional<std::string> getCgroupsV2FileName()
if (!cgroupsV2MemoryControllerEnabled()) if (!cgroupsV2MemoryControllerEnabled())
return {}; return {};
String cgroup = cgroupV2OfProcess(); fs::path current_cgroup = cgroupV2PathOfProcess();
auto current_cgroup = cgroup.empty() ? default_cgroups_mount : (default_cgroups_mount / cgroup); if (current_cgroup.empty())
return {};
/// Return the bottom-most nested current memory file. If there is no such file at the current /// Return the bottom-most nested current memory file. If there is no such file at the current
/// level, try again at the parent level as memory settings are inherited. /// level, try again at the parent level as memory settings are inherited.
while (current_cgroup != default_cgroups_mount.parent_path()) while (current_cgroup != default_cgroups_mount.parent_path())
{ {
auto path = current_cgroup / "memory.current"; auto path = current_cgroup / "memory.current";
if (std::filesystem::exists(path)) if (fs::exists(path))
return {path}; return {path};
current_cgroup = current_cgroup.parent_path(); current_cgroup = current_cgroup.parent_path();
} }
@ -143,7 +145,7 @@ std::optional<std::string> getCgroupsV2FileName()
std::optional<std::string> getCgroupsV1FileName() std::optional<std::string> getCgroupsV1FileName()
{ {
auto path = default_cgroups_mount / "memory/memory.stat"; auto path = default_cgroups_mount / "memory/memory.stat";
if (!std::filesystem::exists(path)) if (!fs::exists(path))
return {}; return {};
return {path}; return {path};
} }

View File

@ -37,12 +37,12 @@ uint32_t getCGroupLimitedCPUCores(unsigned default_cpu_count)
/// cgroupsv2 /// cgroupsv2
if (cgroupsV2Enabled()) if (cgroupsV2Enabled())
{ {
/// First, we identify the cgroup the process belongs /// First, we identify the path of the cgroup the process belongs
std::string cgroup = cgroupV2OfProcess(); std::filesystem::path cgroup_path = cgroupV2PathOfProcess();
if (cgroup.empty()) if (cgroup_path.empty())
return default_cpu_count; return default_cpu_count;
auto current_cgroup = cgroup.empty() ? default_cgroups_mount : (default_cgroups_mount / cgroup); auto current_cgroup = cgroup_path;
// Looking for cpu.max in directories from the current cgroup to the top level // Looking for cpu.max in directories from the current cgroup to the top level
// It does not stop on the first time since the child could have a greater value than parent // It does not stop on the first time since the child could have a greater value than parent
@ -62,7 +62,7 @@ uint32_t getCGroupLimitedCPUCores(unsigned default_cpu_count)
} }
current_cgroup = current_cgroup.parent_path(); current_cgroup = current_cgroup.parent_path();
} }
current_cgroup = default_cgroups_mount / cgroup; current_cgroup = cgroup_path;
// Looking for cpuset.cpus.effective in directories from the current cgroup to the top level // Looking for cpuset.cpus.effective in directories from the current cgroup to the top level
while (current_cgroup != default_cgroups_mount.parent_path()) while (current_cgroup != default_cgroups_mount.parent_path())
{ {