// ----------------------------------------------------------------------
// File: SmartSpaceStats.cc
// Author: Steven Murray - CERN
// ----------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2011 CERN/Switzerland *
* *
* 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, either version 3 of the License, or *
* (at your option) any later version. *
* *
* 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, see .*
************************************************************************/
#include "common/Logging.hh"
#include "common/ShellCmd.hh"
#include "common/StringUtils.hh"
#include "mgm/tgc/SmartSpaceStats.hh"
#include "mgm/CtaUtils.hh"
#include
EOSTGCNAMESPACE_BEGIN
//------------------------------------------------------------------------------
//! Constructor
//------------------------------------------------------------------------------
SmartSpaceStats::SmartSpaceStats(const std::string& spaceName, ITapeGcMgm& mgm,
CachedValue& config):
m_singleAsyncFreeBytesScript(mgm),
m_spaceName(spaceName),
m_mgm(mgm),
m_queryMgmTimestamp(0),
m_freedBytesHistogram(TGC_FREED_BYTES_HISTOGRAM_NB_BINS,
TGC_DEFAULT_FREED_BYTES_HISTOGRAM_BIN_WIDTH_SECS, m_clock),
m_config(config)
{
}
//------------------------------------------------------------------------------
// Return statistics about the EOS space being managed
//------------------------------------------------------------------------------
SmartSpaceStats::SpaceStatsAndAvailBytesSrc
SmartSpaceStats::get()
{
const std::time_t now = time(nullptr);
const auto spaceConfig = m_config.get();
std::lock_guard lock(m_mutex);
const std::time_t secsSinceLastQuery = now - m_queryMgmTimestamp;
if (secsSinceLastQuery >= spaceConfig.queryPeriodSecs) {
m_mgmStats = SpaceStatsAndAvailBytesSrc();
try {
m_mgmStats.stats = m_mgm.getSpaceStats(m_spaceName);
if (spaceConfig.freeBytesScript.empty()) {
m_mgmStats.availBytesSrc = Src::INTERNAL_BECAUSE_SCRIPT_PATH_EMPTY;
} else {
// Try to overwrite m_mgmStats.availBytes if tgc.freebytesscript is set
if (!spaceConfig.freeBytesScript.empty()) {
try {
std::ostringstream cmd;
cmd << spaceConfig.freeBytesScript << " " << m_spaceName;
const auto asyncResult =
m_singleAsyncFreeBytesScript.getUint64FromShellCmdStdOut(cmd.str());
switch (asyncResult.getState()) {
case AsyncResult::State::PENDING_AND_NO_PREVIOUS_VALUE:
// Don't overwrite m_mgmStats.availBytes
m_mgmStats.availBytesSrc =
Src::INTERNAL_BECAUSE_SCRIPT_PENDING_AND_NO_PREVIOUS_VALUE;
break;
case AsyncResult::State::PENDING_AND_PREVIOUS_VALUE:
if (asyncResult.getPreviousValue()) {
// Use the previous value for now
m_mgmStats.stats.availBytes = asyncResult.getPreviousValue().value();
m_mgmStats.availBytesSrc = Src::SCRIPT_PREVIOUS_VALUE_BECAUSE_SCRIPT_PENDING;
} else {
throw std::runtime_error(
"State of AsyncResult is PENDING_AND_PREVIOUS_VALUE but it does not contain a previous value");
}
case AsyncResult::State::VALUE:
if (asyncResult.getValue()) {
m_mgmStats.stats.availBytes = asyncResult.getValue().value();
m_mgmStats.availBytesSrc = Src::SCRIPT_VALUE_BECAUSE_SCRIPT_JUST_FINISHED;
}
break;
case AsyncResult::State::ERROR:
if (asyncResult.getError()) {
std::stringstream msg;
msg << "Execution of script failed with an error: " <<
asyncResult.getError().value();
throw std::runtime_error(msg.str());
} else {
throw std::runtime_error("State of AsyncResult is EXCEPTION but it does not contain an exception");
}
default:
throw std::runtime_error("Unknown AsyncResult::State");
}
} catch (std::exception& ex) {
m_mgmStats.availBytesSrc = Src::INTERNAL_BECAUSE_SCRIPT_ERROR;
eos_static_err(
"msg=\"Failed to get and parse output of tgc.freebytesscript. Falling back to internal filesystem stats\""
" space=\"%s\" tgc.freebytesscript=\"%s\" error=\"%s\"", m_spaceName.c_str(),
spaceConfig.freeBytesScript.c_str(), ex.what());
}
}
}
} catch (std::exception& se) {
eos_static_err("spaceName=\"%s\" msg=\"Failed to get space stats\" error=\"%s\"",
m_spaceName.c_str(), se.what());
} catch (...) {
eos_static_err("spaceName=\"%s\" msg=\"Failed to get space stats\" error=\"Caught unknown exception\"",
m_spaceName.c_str());
}
m_queryMgmTimestamp = now;
}
if (0 == spaceConfig.queryPeriodSecs ||
TGC_MAX_QRY_PERIOD_SECS < ((std::uint64_t)spaceConfig.queryPeriodSecs)) {
std::ostringstream msg;
msg << "spaceName=\"" << m_spaceName << "\" msg=\"Ignoring new value of " <<
TGC_NAME_QRY_PERIOD_SECS <<
" : Value must be > 0 and <= " << TGC_MAX_QRY_PERIOD_SECS << ": Value=" <<
spaceConfig.queryPeriodSecs << "\"";
eos_static_err(msg.str().c_str());
} else {
const std::uint32_t oldBinWidthSecs = m_freedBytesHistogram.getBinWidthSecs();
const std::uint32_t newBinWidthSecs =
CtaUtils::divideAndRoundUp(spaceConfig.queryPeriodSecs,
m_freedBytesHistogram.getNbBins());
if (0 == newBinWidthSecs) {
std::ostringstream msg;
msg << "spaceName=\"" << m_spaceName <<
"\" msg=\"The newBinWidthSecs value of 0 will be ignored."
" Value must be greater than 0.\"";
eos_static_err(msg.str().c_str());
} else if (newBinWidthSecs != oldBinWidthSecs) {
m_freedBytesHistogram.setBinWidthSecs(newBinWidthSecs);
std::ostringstream msg;
msg << "spaceName=\"" << m_spaceName <<
"\" msg=\"Changed bin width of freed bytes histogram:"
" oldValue=" << oldBinWidthSecs << " newValue=" << newBinWidthSecs << "\"";
eos_static_info(msg.str().c_str());
}
}
// Space statistics from the MGM are not timestamped and therefore may
// themselves be out of data by as much as spaceConfig.queryPeriodSecs
//
// Add the count of bytes the garbage collector has freed in the last
// spaceConfig.queryPeriodSecs even if this may cause a temporary double
// count
uint64_t nbBytesFreed = 0;
try {
nbBytesFreed = m_freedBytesHistogram.getNbBytesFreedInLastNbSecs(
spaceConfig.queryPeriodSecs);
} catch (FreedBytesHistogram::TooFarBackInTime& ex) {
nbBytesFreed = m_freedBytesHistogram.getTotalBytesFreed();
std::ostringstream msg;
msg << "msg=\"" << ex.what() << "\"";
eos_static_err(msg.str().c_str());
}
m_mgmStats.stats.availBytes += nbBytesFreed;
return m_mgmStats;
}
//----------------------------------------------------------------------------
// Return timestamp at which the last query was made
//----------------------------------------------------------------------------
std::time_t
SmartSpaceStats::getQueryTimestamp()
{
std::lock_guard lock(m_mutex);
return m_queryMgmTimestamp;
}
//------------------------------------------------------------------------------
// Notify this object that a disk replica has been queued for deletion
//------------------------------------------------------------------------------
void
SmartSpaceStats::diskReplicaQueuedForDeletion(const size_t fileSizeBytes)
{
std::lock_guard lock(m_mutex);
m_freedBytesHistogram.bytesFreed(fileSizeBytes);
}
EOSTGCNAMESPACE_END