mirror of
https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
synced 2025-04-18 21:44:02 +03:00
Limit test containers by memory, fix cgroup path inside the containers by introducing new ugly setting name --------- Co-authored-by: Roman Nozdrin <rnozdrin@mariadb.com> Co-authored-by: Leonid Fedorov <leonid.fedorov@mariadb.com>
392 lines
9.5 KiB
C++
392 lines
9.5 KiB
C++
/* Copyright (C) 2014 InfiniDB, Inc.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; version 2 of
|
|
the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
MA 02110-1301, USA. */
|
|
|
|
#include "cgroupconfigurator.h"
|
|
#include "configcpp.h"
|
|
#include "logger.h"
|
|
#include <charconv>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <limits>
|
|
#include <sys/sysinfo.h>
|
|
|
|
using namespace boost;
|
|
using namespace std;
|
|
|
|
// minor space-savers
|
|
#define RETURN_NO_GROUP(err) \
|
|
do \
|
|
{ \
|
|
if (!printedWarning) \
|
|
{ \
|
|
printedWarning = true; \
|
|
ostringstream os; \
|
|
os << "CGroup warning! The group " << cGroupName << " does not exist."; \
|
|
cerr << os.str() << endl; \
|
|
log(logging::LOG_TYPE_WARNING, os.str()); \
|
|
} \
|
|
return err; \
|
|
} while (0)
|
|
|
|
#define RETURN_READ_ERROR(err) \
|
|
do \
|
|
{ \
|
|
if (!printedWarning) \
|
|
{ \
|
|
printedWarning = true; \
|
|
ostringstream os; \
|
|
os << "CGroup warning! Could not read the file " << filename << "."; \
|
|
cerr << os.str() << endl; \
|
|
log(logging::LOG_TYPE_WARNING, os.str()); \
|
|
} \
|
|
return err; \
|
|
} while (0)
|
|
|
|
namespace
|
|
{
|
|
void log(logging::LOG_TYPE whichLogFile, const string& msg)
|
|
{
|
|
logging::Logger logger(12); // 12 = configcpp
|
|
logger.logMessage(whichLogFile, msg, logging::LoggingID(12));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace utils
|
|
{
|
|
CGroupConfigurator::CGroupConfigurator()
|
|
{
|
|
config = config::Config::makeConfig();
|
|
|
|
cGroupName = config->getConfig("SystemConfig", "CGroup");
|
|
|
|
if (cGroupName.empty())
|
|
cGroupDefined = false;
|
|
else
|
|
cGroupDefined = true;
|
|
|
|
if (cGroupName == "just_no_group_use_local")
|
|
cGroupName = std::string{};
|
|
|
|
ifstream v2Check("/sys/fs/cgroup/cgroup.controllers");
|
|
cGroupVersion_ = (v2Check) ? v2 : v1;
|
|
}
|
|
|
|
CGroupConfigurator::~CGroupConfigurator()
|
|
{
|
|
}
|
|
|
|
uint32_t CGroupConfigurator::getNumCoresFromCGroup()
|
|
{
|
|
ostringstream filenameOs;
|
|
if (cGroupVersion_ == v1)
|
|
{
|
|
filenameOs << "/sys/fs/cgroup/cpuset/" << cGroupName << "/cpuset.cpus";
|
|
}
|
|
else
|
|
{
|
|
filenameOs << "/sys/fs/cgroup/" << cGroupName << "/cpuset.cpus";
|
|
}
|
|
string filename = filenameOs.str();
|
|
|
|
ifstream in(filename.c_str());
|
|
string cpusString;
|
|
uint32_t cpus = 0;
|
|
|
|
if (!in)
|
|
RETURN_NO_GROUP(0);
|
|
|
|
try
|
|
{
|
|
// Need to parse & count how many CPUs we have access to
|
|
in >> cpusString;
|
|
}
|
|
catch (...)
|
|
{
|
|
RETURN_READ_ERROR(0);
|
|
}
|
|
|
|
// the file has comma-deliminted CPU ranges like "0-7,9,11-12".
|
|
size_t first = 0, last;
|
|
bool lastRange = false;
|
|
|
|
while (!lastRange)
|
|
{
|
|
size_t dash;
|
|
string oneRange;
|
|
|
|
last = cpusString.find(',', first);
|
|
|
|
if (last == string::npos)
|
|
{
|
|
lastRange = true;
|
|
oneRange = cpusString.substr(first);
|
|
}
|
|
else
|
|
oneRange = cpusString.substr(first, last - first - 1);
|
|
|
|
if ((dash = oneRange.find('-')) == string::npos) // single-cpu range
|
|
cpus++;
|
|
else
|
|
{
|
|
const char* data = oneRange.c_str();
|
|
uint32_t firstCPU = strtol(data, NULL, 10);
|
|
uint32_t lastCPU = strtol(&data[dash + 1], NULL, 10);
|
|
cpus += lastCPU - firstCPU + 1;
|
|
}
|
|
|
|
first = last + 1;
|
|
}
|
|
|
|
return cpus;
|
|
}
|
|
|
|
uint32_t CGroupConfigurator::getNumCoresFromProc()
|
|
{
|
|
uint32_t nc = sysconf(_SC_NPROCESSORS_ONLN);
|
|
|
|
return nc;
|
|
}
|
|
|
|
uint32_t CGroupConfigurator::getNumCores()
|
|
{
|
|
/*
|
|
Detect if MCS is in a C-Group
|
|
- get the group ID
|
|
If not, get the number of cores from /proc
|
|
*/
|
|
uint32_t ret;
|
|
|
|
if (!cGroupDefined)
|
|
ret = getNumCoresFromProc();
|
|
else
|
|
{
|
|
ret = getNumCoresFromCGroup();
|
|
|
|
if (ret == 0)
|
|
ret = getNumCoresFromProc();
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
uint64_t CGroupConfigurator::getTotalMemory()
|
|
{
|
|
uint64_t ret;
|
|
|
|
if (totalMemory != 0)
|
|
return totalMemory;
|
|
|
|
cout << __func__ << " cGroupDefined (from getConfig) " << cGroupDefined << endl;
|
|
if (!cGroupDefined)
|
|
ret = getTotalMemoryFromProc();
|
|
else
|
|
{
|
|
cout << __func__ << " cGroupVersion " << ((cGroupVersion_ == v2) ? std::string("v2") : std::string("v1")) << endl;
|
|
ret = getTotalMemoryFromCGroup();
|
|
|
|
if (ret == 0 || ret == std::numeric_limits<uint64_t>::max())
|
|
ret = getTotalMemoryFromProc();
|
|
}
|
|
|
|
cout << __func__ << " Total mem available (bytes) " << ret << " (GIB) " << ret/GIB << endl;
|
|
totalMemory = ret;
|
|
return totalMemory;
|
|
}
|
|
|
|
uint64_t CGroupConfigurator::getTotalMemoryFromProc()
|
|
{
|
|
size_t memTot;
|
|
cout << __func__ << " reading /proc/meminfo " << endl;
|
|
ifstream in("/proc/meminfo");
|
|
string x;
|
|
|
|
in >> x;
|
|
in >> memTot;
|
|
|
|
// memTot is now in KB, convert to bytes
|
|
memTot *= 1024;
|
|
|
|
return memTot;
|
|
}
|
|
|
|
uint64_t CGroupConfigurator::getTotalMemoryFromCGroup()
|
|
{
|
|
std::string memLimitStr;
|
|
uint64_t memLimit = std::numeric_limits<uint64_t>::max();
|
|
ostringstream os;
|
|
|
|
if (cGroupVersion_ == v1)
|
|
{
|
|
os << "/sys/fs/cgroup/memory/" << cGroupName << "/memory.limit_in_bytes";
|
|
}
|
|
else
|
|
{
|
|
os << "/sys/fs/cgroup/" << cGroupName << "/memory.max";
|
|
}
|
|
|
|
string filename = os.str();
|
|
cout << __func__ <<" reading " << filename << endl;
|
|
|
|
ifstream in(filename.c_str());
|
|
|
|
if (!in)
|
|
RETURN_NO_GROUP(0);
|
|
|
|
try
|
|
{
|
|
in >> memLimitStr;
|
|
}
|
|
catch (...)
|
|
{
|
|
RETURN_READ_ERROR(0);
|
|
}
|
|
cout << __func__<< " read into memLimitStr " << memLimitStr << endl;
|
|
|
|
if (cGroupVersion_ == v2 && memLimitStr == "max")
|
|
{
|
|
return std::numeric_limits<uint64_t>::max();
|
|
}
|
|
|
|
auto [ch, ec] = std::from_chars(memLimitStr.c_str(), memLimitStr.c_str() + memLimitStr.size(), memLimit);
|
|
if (ec != std::errc())
|
|
{
|
|
return std::numeric_limits<uint64_t>::max();
|
|
}
|
|
|
|
if (cGroupVersion_ == v1)
|
|
{
|
|
return std::min(getTotalMemoryFromProc(), memLimit);
|
|
}
|
|
return memLimit;
|
|
}
|
|
|
|
uint64_t CGroupConfigurator::getFreeMemory()
|
|
{
|
|
uint64_t ret;
|
|
if (!cGroupDefined)
|
|
{
|
|
ret = getFreeMemoryFromProc();
|
|
if (logCounter++ % logMemoryPeriod == 0)
|
|
cout <<__func__<< " : returned from getFreeMemoryFromProc " << ret << " (GIB) " << ret/GIB << endl;
|
|
|
|
}
|
|
else
|
|
{
|
|
uint64_t usage = getMemUsageFromCGroup();
|
|
if (logCounter++ % 1000 == 0)
|
|
cout << __func__<< " : returned from getMemUsageFromCGroup : usage " << usage << " (GIB) " << usage/GIB << endl;
|
|
|
|
if (usage == 0)
|
|
ret = getFreeMemoryFromProc();
|
|
else
|
|
ret = getTotalMemory() - usage;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
uint64_t CGroupConfigurator::getMemUsageFromCGroup()
|
|
{
|
|
char oneline[80];
|
|
if (memUsageFilename.empty())
|
|
{
|
|
ostringstream filename;
|
|
if (cGroupVersion_ == v1)
|
|
{
|
|
memStatePrefix = "rss ";
|
|
filename << "/sys/fs/cgroup/memory/" << cGroupName << "/memory.stat";
|
|
}
|
|
else
|
|
{
|
|
memStatePrefix = "anon ";
|
|
filename << "/sys/fs/cgroup/" << cGroupName << "/memory.stat";
|
|
}
|
|
memUsageFilename = filename.str();
|
|
}
|
|
|
|
ifstream in(memUsageFilename.c_str());
|
|
string& filename = memUsageFilename;
|
|
|
|
if (!in)
|
|
RETURN_NO_GROUP(0);
|
|
|
|
try
|
|
{
|
|
while (in)
|
|
{
|
|
in.getline(oneline, 80);
|
|
|
|
if (strncmp(oneline, memStatePrefix.c_str(), memStatePrefix.size() - 1) == 0)
|
|
{
|
|
return atoll(&oneline[memStatePrefix.size()]);
|
|
}
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
RETURN_READ_ERROR(0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint64_t CGroupConfigurator::getFreeMemoryFromProc()
|
|
{
|
|
uint64_t memFree = 0;
|
|
uint64_t buffers = 0;
|
|
uint64_t cached = 0;
|
|
uint64_t memTotal = 0;
|
|
uint64_t memAvailable = 0;
|
|
|
|
ifstream in("/proc/meminfo");
|
|
string x;
|
|
|
|
in >> x; // MemTotal:
|
|
in >> memTotal;
|
|
in >> x; // kB
|
|
|
|
in >> x; // MemFree:
|
|
in >> memFree;
|
|
in >> x; // kB
|
|
|
|
// check if available or buffers is passed
|
|
in >> x;
|
|
|
|
if (x == "MemAvailable:")
|
|
{
|
|
in >> memAvailable; // MemAvailable
|
|
}
|
|
else
|
|
{
|
|
// centos 6 and older OSs
|
|
in >> buffers;
|
|
in >> x; // kB
|
|
|
|
in >> x; // Cached:
|
|
in >> cached;
|
|
|
|
memAvailable = memFree + buffers + cached;
|
|
}
|
|
|
|
// amount available for application
|
|
memAvailable *= 1024;
|
|
return memAvailable;
|
|
}
|
|
|
|
} // namespace utils
|