ClickHouse/dbms/include/DB/Storages/MergeTree/DiskSpaceMonitor.h

142 lines
4.1 KiB
C++
Raw Normal View History

2014-03-13 12:48:07 +00:00
#pragma once
#include <Poco/Mutex.h>
#include <sys/statvfs.h>
#include <boost/noncopyable.hpp>
2015-09-29 19:19:54 +00:00
#include <common/logger_useful.h>
2015-10-05 01:35:28 +00:00
#include <DB/Common/Exception.h>
2014-05-14 17:51:37 +00:00
#include <DB/IO/WriteHelpers.h>
2015-06-11 00:35:36 +00:00
#include <DB/Common/formatReadable.h>
#include <DB/Common/CurrentMetrics.h>
2014-03-13 12:48:07 +00:00
namespace DB
{
namespace ErrorCodes
{
extern const int CANNOT_STATVFS;
extern const int NOT_ENOUGH_SPACE;
}
2014-03-13 12:48:07 +00:00
/** Узнает количество свободного места в файловой системе.
* Можно "резервировать" место, чтобы разные операции могли согласованно планировать использование диска.
* Резервирования не разделяются по файловым системам.
* Вместо этого при запросе свободного места считается, что все резервирования сделаны в той же файловой системе.
*/
class DiskSpaceMonitor
{
public:
class Reservation : private boost::noncopyable
{
friend class DiskSpaceMonitor;
public:
~Reservation()
{
try
{
2014-08-04 11:41:59 +00:00
Poco::ScopedLock<Poco::FastMutex> lock(DiskSpaceMonitor::mutex);
2014-03-13 12:48:07 +00:00
if (DiskSpaceMonitor::reserved_bytes < size)
{
DiskSpaceMonitor::reserved_bytes = 0;
2014-08-04 11:41:59 +00:00
LOG_ERROR(&Logger::get("DiskSpaceMonitor"), "Unbalanced reservations size; it's a bug");
2014-03-13 12:48:07 +00:00
}
else
{
DiskSpaceMonitor::reserved_bytes -= size;
}
2014-08-04 11:41:59 +00:00
if (DiskSpaceMonitor::reservation_count == 0)
{
LOG_ERROR(&Logger::get("DiskSpaceMonitor"), "Unbalanced reservation count; it's a bug");
}
else
{
--DiskSpaceMonitor::reservation_count;
}
2014-03-13 12:48:07 +00:00
}
catch (...)
{
tryLogCurrentException("~DiskSpaceMonitor");
}
}
2014-08-04 11:41:59 +00:00
/// Изменить количество зарезервированного места. При увеличении не делается проверка, что места достаточно.
void update(size_t new_size)
{
Poco::ScopedLock<Poco::FastMutex> lock(DiskSpaceMonitor::mutex);
DiskSpaceMonitor::reserved_bytes -= size;
size = new_size;
DiskSpaceMonitor::reserved_bytes += size;
}
size_t getSize() const
{
return size;
}
2014-03-13 12:48:07 +00:00
private:
Reservation(size_t size_)
: size(size_), metric_increment(CurrentMetrics::DiskSpaceReservedForMerge, size)
2014-03-13 12:48:07 +00:00
{
2014-08-04 11:41:59 +00:00
Poco::ScopedLock<Poco::FastMutex> lock(DiskSpaceMonitor::mutex);
2014-03-13 12:48:07 +00:00
DiskSpaceMonitor::reserved_bytes += size;
2014-08-04 11:41:59 +00:00
++DiskSpaceMonitor::reservation_count;
2014-03-13 12:48:07 +00:00
}
2014-03-13 12:48:07 +00:00
size_t size;
CurrentMetrics::Increment metric_increment;
2014-03-13 12:48:07 +00:00
};
typedef Poco::SharedPtr<Reservation> ReservationPtr;
static size_t getUnreservedFreeSpace(const std::string & path)
{
struct statvfs fs;
if (statvfs(path.c_str(), &fs) != 0)
throwFromErrno("Could not calculate available disk space (statvfs)", ErrorCodes::CANNOT_STATVFS);
size_t res = fs.f_bfree * fs.f_bsize;
2014-05-14 17:51:37 +00:00
/// Зарезервируем дополнительно 30 МБ. Когда я тестировал, statvfs показывал на несколько мегабайт больше свободного места, чем df.
res -= std::min(res, 30 * (1ul << 20));
2014-08-04 11:41:59 +00:00
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
2014-03-13 12:48:07 +00:00
if (reserved_bytes > res)
res = 0;
else
res -= reserved_bytes;
return res;
}
2014-08-04 11:41:59 +00:00
static size_t getReservedSpace()
{
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
return reserved_bytes;
}
static size_t getReservationCount()
{
Poco::ScopedLock<Poco::FastMutex> lock(mutex);
return reservation_count;
}
2014-05-14 17:51:37 +00:00
/// Если места (приблизительно) недостаточно, бросает исключение.
2014-03-13 12:48:07 +00:00
static ReservationPtr reserve(const std::string & path, size_t size)
{
2014-05-14 17:51:37 +00:00
size_t free_bytes = getUnreservedFreeSpace(path);
if (free_bytes < size)
2015-06-11 00:35:36 +00:00
throw Exception("Not enough free disk space to reserve: " + formatReadableSizeWithBinarySuffix(free_bytes) + " available, "
+ formatReadableSizeWithBinarySuffix(size) + " requested", ErrorCodes::NOT_ENOUGH_SPACE);
2014-03-13 12:48:07 +00:00
return new Reservation(size);
}
private:
static size_t reserved_bytes;
2014-08-04 11:41:59 +00:00
static size_t reservation_count;
static Poco::FastMutex mutex;
2014-03-13 12:48:07 +00:00
};
}