// ----------------------------------------------------------------------
// File: OpenFileTracker.cc
// Author: Georgios Bitzes - CERN
// ----------------------------------------------------------------------
/************************************************************************
* EOS - the CERN Disk Storage System *
* Copyright (C) 2019 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 "fst/utils/OpenFileTracker.hh"
#include "common/Assert.hh"
#include "common/Logging.hh"
EOSFSTNAMESPACE_BEGIN
//------------------------------------------------------------------------------
// Constructor
//------------------------------------------------------------------------------
OpenFileTracker::OpenFileTracker()
{
mMutex.SetBlocking(true);
}
//------------------------------------------------------------------------------
// Mark that the given file ID, on the given filesystem ID, was just opened
//------------------------------------------------------------------------------
void OpenFileTracker::up(eos::common::FileSystem::fsid_t fsid, uint64_t fid)
{
eos::common::RWMutexWriteLock wr_lock(mMutex);
mContents[fsid][fid]++;
if (mContents[fsid][fid] > 1) {
mMultiOpen[fsid][fid] = true;
}
}
//------------------------------------------------------------------------------
//! Wait for an excl open of a file and count up
//------------------------------------------------------------------------------
void OpenFileTracker::waitExclOpen(eos::common::FileSystem::fsid_t fsid,
uint64_t fid)
{
do {
bool busy = false;
{
eos::common::RWMutexWriteLock wr_lock(mMutex);
busy = (mContents[fsid].find(fid) != mContents[fsid].end());
if (!busy) {
mContents[fsid][fid]++;
break;
}
}
// this is not starvation free, but considering the use-case it won't happen
std::this_thread::sleep_for(std::chrono::milliseconds(25));
} while (1);
}
//------------------------------------------------------------------------------
// Mark that the given file ID, on the given filesystem ID, was just closed
//
// Prints warning in the logs if the value was about to go negative - it will
// never go negative.
//------------------------------------------------------------------------------
void OpenFileTracker::down(eos::common::FileSystem::fsid_t fsid, uint64_t fid)
{
eos::common::RWMutexWriteLock wr_lock(mMutex);
auto fsit = mContents.find(fsid);
if (fsit == mContents.end()) {
// Can happen if OpenFileTracker is misused
eos_static_crit("Could not find fsid=%lu when calling OpenFileTracker::down "
"for fxid=%08llx", fsid, fid);
return;
}
auto fidit = fsit->second.find(fid);
if (fidit == fsit->second.end()) {
// Can happen if OpenFileTracker is misused
eos_static_crit("Could not find fxid=%08llx when calling OpenFileTracker::down "
"for fsid=%lu", fid, fsid);
return;
}
if (fidit->second == 1) {
// Last use, remove from map
fsit->second.erase(fidit);
// Last use, remove from map
mMultiOpen[fsid].erase(fid);
// Also remove fs from top-level map?
if (fsit->second.empty()) {
mContents.erase(fsit);
}
// Also remove fs from top-level map?
if (mMultiOpen[fsid].empty()) {
mMultiOpen.erase(fsid);
}
return;
}
if (fidit->second < 1) {
eos_static_crit("Should never happen - encountered bogus value in "
"OpenFileTracker::down for fsid=%lu, fid=%08llx - dropping",
fsid, fid);
fsit->second.erase(fidit);
mMultiOpen.erase(fid);
return;
}
// Simply decrement
fidit->second--;
}
//------------------------------------------------------------------------------
// Checks if the given file ID, on the given filesystem ID, is currently open
//------------------------------------------------------------------------------
bool OpenFileTracker::isOpen(eos::common::FileSystem::fsid_t fsid,
uint64_t fid) const
{
return getUseCount(fsid, fid) > 0;
}
//------------------------------------------------------------------------------
// Checks if the given file ID, on the given filesystem ID, had multiple opens
//------------------------------------------------------------------------------
bool OpenFileTracker::hadMultiOpen(eos::common::FileSystem::fsid_t fsid,
uint64_t fid) const
{
eos::common::RWMutexReadLock rd_lock(mMutex);
auto fsit = mMultiOpen.find(fsid);
if (fsit == mMultiOpen.end()) {
return 0;
}
auto fidit = fsit->second.find(fid);
if (fidit == fsit->second.end()) {
return 0;
}
return fidit->second;
}
//------------------------------------------------------------------------------
// Checks if the given file ID, on the given filesystem ID, is currently open
//------------------------------------------------------------------------------
int32_t OpenFileTracker::getUseCount(eos::common::FileSystem::fsid_t fsid,
uint64_t fid) const
{
eos::common::RWMutexReadLock rd_lock(mMutex);
auto fsit = mContents.find(fsid);
if (fsit == mContents.end()) {
return 0;
}
auto fidit = fsit->second.find(fid);
if (fidit == fsit->second.end()) {
return 0;
}
return fidit->second;
}
//------------------------------------------------------------------------------
// Checks if there's _any_ operation currently in progress
//------------------------------------------------------------------------------
bool OpenFileTracker::isAnyOpen() const
{
eos::common::RWMutexReadLock rd_lock(mMutex);
return ! mContents.empty();
}
//------------------------------------------------------------------------------
// Get open file IDs of a filesystem, sorted by usecount
//------------------------------------------------------------------------------
std::map> OpenFileTracker::getSortedByUsecount(
eos::common::FileSystem::fsid_t fsid) const
{
std::map> contentsSortedByUsecount;
eos::common::RWMutexReadLock rd_lock(mMutex);
auto fsit = mContents.find(fsid);
if (fsit == mContents.end()) {
// Filesystem has no open files
return {};
}
for (auto it = fsit->second.begin(); it != fsit->second.end(); it++) {
contentsSortedByUsecount[it->second].insert(it->first);
}
return contentsSortedByUsecount;
}
//------------------------------------------------------------------------------
// Get number of distinct open files by filesystem
//------------------------------------------------------------------------------
int32_t OpenFileTracker::getOpenOnFilesystem(eos::common::FileSystem::fsid_t
fsid) const
{
eos::common::RWMutexReadLock rd_lock(mMutex);
auto fsit = mContents.find(fsid);
if (fsit == mContents.end()) {
return 0;
}
return fsit->second.size();
}
//------------------------------------------------------------------------------
// Get round-robin scheduling mutex per fsid/app
//------------------------------------------------------------------------------
std::mutex*
OpenFileTracker::scheduleRR(eos::common::FileSystem::fsid_t fsid,
const std::string app)
{
eos::common::RWMutexReadLock rd_lock(mMutex);
return &mApp[fsid][app];
}
//------------------------------------------------------------------------------
// Get top hot files on current filesystem
//------------------------------------------------------------------------------
std::vector OpenFileTracker::getHotFiles(
eos::common::FileSystem::fsid_t fsid, size_t maxEntries) const
{
auto sorted = getSortedByUsecount(fsid);
std::vector results;
for (auto it = sorted.rbegin(); it != sorted.rend(); it++) {
for (auto it2 = it->second.begin(); it2 != it->second.end(); it2++) {
if (results.size() >= maxEntries) {
goto done;
}
results.emplace_back(fsid, *it2, it->first);
}
}
done:
return results;
}
EOSFSTNAMESPACE_END