Summary: address an existing limitation on compaction triggering mechanism that relies on events like flush/compaction/SetOptions. This is important for periodic compactions where files can become eligible without any of these events. The periodic task now runs every 12 hours and check CFs that enables `periodic_compaction_second` (TBD if we want to expand to all CFs) for eligible compactions. Some of the periodic tasks probably don't need to run immediately after Register(). I'm keeping the existing behavior for now for patch release and to makes tests happy. Pull Request resolved: https://github.com/facebook/rocksdb/pull/13736 Test Plan: - new unit test that fails before this change. - ran crash test for hours with the periodic task running every 5 seconds: `python3 ./tools/db_crashtest.py blackbox --test_batches_snapshot=0 --periodic_compaction_seconds=10` Reviewed By: pdillinger Differential Revision: D77460715 Pulled By: cbi42 fbshipit-source-id: 00f61502753185e76830c9ed44c5ccc4f4f16bfa
111 lines
3.6 KiB
C++
111 lines
3.6 KiB
C++
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
//
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
// (found in the LICENSE.Apache file in the root directory).
|
|
|
|
#pragma once
|
|
|
|
#include "util/timer.h"
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
class SystemClock;
|
|
|
|
using PeriodicTaskFunc = std::function<void()>;
|
|
|
|
constexpr uint64_t kInvalidPeriodSec = 0;
|
|
|
|
// List of task types
|
|
enum class PeriodicTaskType : uint8_t {
|
|
kDumpStats = 0,
|
|
kPersistStats,
|
|
kFlushInfoLog,
|
|
kRecordSeqnoTime,
|
|
kTriggerCompaction,
|
|
kMax,
|
|
};
|
|
|
|
// PeriodicTaskScheduler contains the periodic task scheduled from the DB
|
|
// instance. It's used to schedule/unschedule DumpStats(), PersistStats(),
|
|
// FlushInfoLog(), etc. Each type of the task can only have one instance,
|
|
// re-register the same task type would only update the repeat period.
|
|
//
|
|
// Internally, it uses a global single threaded timer object to run the periodic
|
|
// task functions. Timer thread will always be started since the info log
|
|
// flushing cannot be disabled.
|
|
class PeriodicTaskScheduler {
|
|
public:
|
|
explicit PeriodicTaskScheduler() = default;
|
|
|
|
PeriodicTaskScheduler(const PeriodicTaskScheduler&) = delete;
|
|
PeriodicTaskScheduler(PeriodicTaskScheduler&&) = delete;
|
|
PeriodicTaskScheduler& operator=(const PeriodicTaskScheduler&) = delete;
|
|
PeriodicTaskScheduler& operator=(PeriodicTaskScheduler&&) = delete;
|
|
|
|
// Register a task with its default repeat period. Thread safe call.
|
|
// @param run_immediately If true, the task will run soon after it's
|
|
// scheduled, instead of waiting for the repeat period.
|
|
Status Register(PeriodicTaskType task_type, const PeriodicTaskFunc& fn,
|
|
bool run_immediately);
|
|
|
|
// Register a task with specified repeat period. 0 is an invalid argument
|
|
// (kInvalidPeriodSec). To stop the task, please use Unregister().
|
|
// Thread safe call.
|
|
Status Register(PeriodicTaskType task_type, const PeriodicTaskFunc& fn,
|
|
uint64_t repeat_period_seconds, bool run_immediately);
|
|
|
|
// Unregister the task. Thread safe call.
|
|
Status Unregister(PeriodicTaskType task_type);
|
|
|
|
#ifndef NDEBUG
|
|
// Override the timer for the unittest
|
|
void TEST_OverrideTimer(SystemClock* clock);
|
|
|
|
// Call Timer TEST_WaitForRun() which wait until Timer starting waiting.
|
|
void TEST_WaitForRun(const std::function<void()>& callback) const {
|
|
if (timer_ != nullptr) {
|
|
timer_->TEST_WaitForRun(callback);
|
|
}
|
|
}
|
|
|
|
// Get global valid task number in the Timer
|
|
size_t TEST_GetValidTaskNum() const {
|
|
if (timer_ != nullptr) {
|
|
return timer_->TEST_GetPendingTaskNum();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// If it has the specified task type registered
|
|
bool TEST_HasTask(PeriodicTaskType task_type) const {
|
|
auto it = tasks_map_.find(task_type);
|
|
return it != tasks_map_.end();
|
|
}
|
|
#endif // NDEBUG
|
|
|
|
private:
|
|
// default global Timer instance
|
|
static Timer* Default();
|
|
|
|
// Internal structure to store task information
|
|
struct TaskInfo {
|
|
TaskInfo(std::string _name, uint64_t _repeat_every_sec)
|
|
: name(std::move(_name)), repeat_every_sec(_repeat_every_sec) {}
|
|
std::string name;
|
|
uint64_t repeat_every_sec;
|
|
};
|
|
|
|
// Internal tasks map
|
|
std::map<PeriodicTaskType, TaskInfo> tasks_map_;
|
|
|
|
// Global timer pointer, which doesn't support synchronous add/cancel tasks
|
|
// so having a global `timer_mutex` for add/cancel task.
|
|
Timer* timer_ = Default();
|
|
|
|
// Global task id, protected by the global `timer_mutex`
|
|
inline static uint64_t id_;
|
|
|
|
static constexpr uint64_t kMicrosInSecond = 1000U * 1000U;
|
|
};
|
|
|
|
} // namespace ROCKSDB_NAMESPACE
|