You've already forked mariadb-columnstore-engine
							
							
				mirror of
				https://github.com/mariadb-corporation/mariadb-columnstore-engine.git
				synced 2025-11-03 17:13:17 +03:00 
			
		
		
		
	Limit test containers by memory, fix cgroup path inside the containers by introducing new ugly setting name --------- Co-authored-by: drrtuy <roman.nozdrin@mariadb.com> Co-authored-by: Roman Nozdrin <rnozdrin@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
 |