mirror of https://github.com/facebook/rocksdb.git
Add structures for exposing thread events and operations.
Summary: Add structures for exposing events and operations. Event describes high-level action about a thread such as doing compaciton or doing flush, while an operation describes lower-level action of a thread such as reading / writing a SST table, waiting for mutex. Events and operations are designed to be independent. One thread would typically involve in one event and one operation. Code instrument will be in a separate diff. Test Plan: Add unit-tests in thread_list_test make dbg -j32 ./thread_list_test export ROCKSDB_TESTS=ThreadList ./db_test Reviewers: ljin, igor, sdong Reviewed By: sdong Subscribers: rven, jonahcohen, dhruba, leveldb Differential Revision: https://reviews.facebook.net/D29781
This commit is contained in:
parent
a801c1fb09
commit
bf287b76e0
|
@ -9435,10 +9435,10 @@ TEST(DBTest, GetThreadList) {
|
|||
env_->SleepForMicroseconds(100000);
|
||||
s = env_->GetThreadList(&thread_list);
|
||||
ASSERT_OK(s);
|
||||
unsigned int thread_type_counts[ThreadStatus::ThreadType::TOTAL];
|
||||
unsigned int thread_type_counts[ThreadStatus::NUM_THREAD_TYPES];
|
||||
memset(thread_type_counts, 0, sizeof(thread_type_counts));
|
||||
for (auto thread : thread_list) {
|
||||
ASSERT_LT(thread.thread_type, ThreadStatus::ThreadType::TOTAL);
|
||||
ASSERT_LT(thread.thread_type, ThreadStatus::NUM_THREAD_TYPES);
|
||||
thread_type_counts[thread.thread_type]++;
|
||||
}
|
||||
// Verify the total number of threades
|
||||
|
@ -9447,11 +9447,11 @@ TEST(DBTest, GetThreadList) {
|
|||
kHighPriCounts[test] + kLowPriCounts[test]);
|
||||
// Verify the number of high-priority threads
|
||||
ASSERT_EQ(
|
||||
thread_type_counts[ThreadStatus::ThreadType::ROCKSDB_HIGH_PRIORITY],
|
||||
thread_type_counts[ThreadStatus::HIGH_PRIORITY],
|
||||
kHighPriCounts[test]);
|
||||
// Verify the number of low-priority threads
|
||||
ASSERT_EQ(
|
||||
thread_type_counts[ThreadStatus::ThreadType::ROCKSDB_LOW_PRIORITY],
|
||||
thread_type_counts[ThreadStatus::LOW_PRIORITY],
|
||||
kLowPriCounts[test]);
|
||||
}
|
||||
if (i == 0) {
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
// This file defines the structures for exposing run-time status of any
|
||||
// rocksdb-related thread. Such run-time status can be obtained via
|
||||
// GetThreadList() API.
|
||||
//
|
||||
// Note that all thread-status features are still under-development, and
|
||||
// thus APIs and class definitions might subject to change at this point.
|
||||
// Will remove this comment once the APIs have been finalized.
|
||||
|
||||
#pragma once
|
||||
|
||||
|
@ -22,29 +30,48 @@ namespace rocksdb {
|
|||
// The status of active threads can be fetched using
|
||||
// rocksdb::GetThreadList().
|
||||
struct ThreadStatus {
|
||||
enum ThreadType {
|
||||
ROCKSDB_HIGH_PRIORITY = 0x0,
|
||||
ROCKSDB_LOW_PRIORITY = 0x1,
|
||||
USER_THREAD = 0x2,
|
||||
TOTAL = 0x3
|
||||
// The type of a thread.
|
||||
enum ThreadType : int {
|
||||
HIGH_PRIORITY = 0, // RocksDB BG thread in high-pri thread pool
|
||||
LOW_PRIORITY, // RocksDB BG thread in low-pri thread pool
|
||||
USER, // User thread (Non-RocksDB BG thread)
|
||||
NUM_THREAD_TYPES
|
||||
};
|
||||
|
||||
// The type used to refer to a thread operation.
|
||||
// A thread operation describes high-level action of a thread.
|
||||
// Examples include compaction and flush.
|
||||
enum OperationType : int {
|
||||
OP_UNKNOWN = 0,
|
||||
OP_COMPACTION,
|
||||
OP_FLUSH,
|
||||
NUM_OP_TYPES
|
||||
};
|
||||
|
||||
// The type used to refer to a thread state.
|
||||
// A state describes lower-level action of a thread
|
||||
// such as reading / writing a file or waiting for a mutex.
|
||||
enum StateType : int {
|
||||
STATE_UNKNOWN = 0,
|
||||
NUM_STATE_TYPES
|
||||
};
|
||||
|
||||
#if ROCKSDB_USING_THREAD_STATUS
|
||||
ThreadStatus(const uint64_t _id,
|
||||
const ThreadType _thread_type,
|
||||
const std::string& _db_name,
|
||||
const std::string& _cf_name,
|
||||
const std::string& _event) :
|
||||
const OperationType _operation_type,
|
||||
const StateType _state_type) :
|
||||
thread_id(_id), thread_type(_thread_type),
|
||||
db_name(_db_name),
|
||||
cf_name(_cf_name),
|
||||
event(_event) {}
|
||||
operation_type(_operation_type), state_type(_state_type) {}
|
||||
|
||||
// An unique ID for the thread.
|
||||
const uint64_t thread_id;
|
||||
|
||||
// The type of the thread, it could be ROCKSDB_HIGH_PRIORITY,
|
||||
// ROCKSDB_LOW_PRIORITY, and USER_THREAD
|
||||
// The type of the thread, it could be HIGH_PRIORITY,
|
||||
// LOW_PRIORITY, and USER
|
||||
const ThreadType thread_type;
|
||||
|
||||
// The name of the DB instance where the thread is currently
|
||||
|
@ -57,11 +84,11 @@ struct ThreadStatus {
|
|||
// in any column family.
|
||||
const std::string cf_name;
|
||||
|
||||
// The event that the current thread is involved.
|
||||
// It would be set to empty string if the information about event
|
||||
// is not currently available.
|
||||
const std::string event;
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
// The operation (high-level action) that the current thread is involved.
|
||||
const OperationType operation_type;
|
||||
|
||||
// The state (lower-level action) that the current thread is involved.
|
||||
const StateType state_type;
|
||||
};
|
||||
|
||||
} // namespace rocksdb
|
||||
|
|
|
@ -1694,8 +1694,8 @@ class PosixEnv : public Env {
|
|||
// for thread-status
|
||||
ThreadStatusUtil::SetThreadType(tp->env_,
|
||||
(tp->GetThreadPriority() == Env::Priority::HIGH ?
|
||||
ThreadStatus::ThreadType::ROCKSDB_HIGH_PRIORITY :
|
||||
ThreadStatus::ThreadType::ROCKSDB_LOW_PRIORITY));
|
||||
ThreadStatus::HIGH_PRIORITY :
|
||||
ThreadStatus::LOW_PRIORITY));
|
||||
#endif
|
||||
delete meta;
|
||||
tp->BGThread(thread_id);
|
||||
|
|
|
@ -14,46 +14,65 @@
|
|||
|
||||
namespace rocksdb {
|
||||
|
||||
class SleepingBackgroundTask {
|
||||
class SimulatedBackgroundTask {
|
||||
public:
|
||||
SleepingBackgroundTask(const void* db_key, const std::string& db_name,
|
||||
const void* cf_key, const std::string& cf_name)
|
||||
SimulatedBackgroundTask(
|
||||
const void* db_key, const std::string& db_name,
|
||||
const void* cf_key, const std::string& cf_name,
|
||||
const ThreadStatus::OperationType operation_type =
|
||||
ThreadStatus::OP_UNKNOWN,
|
||||
const ThreadStatus::StateType state_type =
|
||||
ThreadStatus::STATE_UNKNOWN)
|
||||
: db_key_(db_key), db_name_(db_name),
|
||||
cf_key_(cf_key), cf_name_(cf_name),
|
||||
should_sleep_(true), sleeping_count_(0) {
|
||||
operation_type_(operation_type), state_type_(state_type),
|
||||
should_run_(true), running_count_(0) {
|
||||
Env::Default()->GetThreadStatusUpdater()->NewColumnFamilyInfo(
|
||||
db_key_, db_name_, cf_key_, cf_name_);
|
||||
}
|
||||
|
||||
~SleepingBackgroundTask() {
|
||||
~SimulatedBackgroundTask() {
|
||||
Env::Default()->GetThreadStatusUpdater()->EraseDatabaseInfo(db_key_);
|
||||
}
|
||||
|
||||
void DoSleep() {
|
||||
Env::Default()->GetThreadStatusUpdater()->SetColumnFamilyInfoKey(cf_key_);
|
||||
void Run() {
|
||||
std::unique_lock<std::mutex> l(mutex_);
|
||||
sleeping_count_++;
|
||||
while (should_sleep_) {
|
||||
running_count_++;
|
||||
Env::Default()->GetThreadStatusUpdater()->SetColumnFamilyInfoKey(cf_key_);
|
||||
Env::Default()->GetThreadStatusUpdater()->SetThreadOperation(
|
||||
operation_type_);
|
||||
Env::Default()->GetThreadStatusUpdater()->SetThreadState(state_type_);
|
||||
while (should_run_) {
|
||||
bg_cv_.wait(l);
|
||||
}
|
||||
sleeping_count_--;
|
||||
bg_cv_.notify_all();
|
||||
Env::Default()->GetThreadStatusUpdater()->ClearThreadState();
|
||||
Env::Default()->GetThreadStatusUpdater()->ClearThreadOperation();
|
||||
Env::Default()->GetThreadStatusUpdater()->SetColumnFamilyInfoKey(0);
|
||||
}
|
||||
void WakeUp() {
|
||||
std::unique_lock<std::mutex> l(mutex_);
|
||||
should_sleep_ = false;
|
||||
running_count_--;
|
||||
bg_cv_.notify_all();
|
||||
}
|
||||
|
||||
void FinishAllTasks() {
|
||||
std::unique_lock<std::mutex> l(mutex_);
|
||||
should_run_ = false;
|
||||
bg_cv_.notify_all();
|
||||
}
|
||||
|
||||
void WaitUntilScheduled(int job_count, Env* env) {
|
||||
while (running_count_ < job_count) {
|
||||
env->SleepForMicroseconds(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void WaitUntilDone() {
|
||||
std::unique_lock<std::mutex> l(mutex_);
|
||||
while (sleeping_count_ > 0) {
|
||||
while (running_count_ > 0) {
|
||||
bg_cv_.wait(l);
|
||||
}
|
||||
}
|
||||
|
||||
static void DoSleepTask(void* arg) {
|
||||
reinterpret_cast<SleepingBackgroundTask*>(arg)->DoSleep();
|
||||
static void DoSimulatedTask(void* arg) {
|
||||
reinterpret_cast<SimulatedBackgroundTask*>(arg)->Run();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -61,10 +80,12 @@ class SleepingBackgroundTask {
|
|||
const std::string db_name_;
|
||||
const void* cf_key_;
|
||||
const std::string cf_name_;
|
||||
const ThreadStatus::OperationType operation_type_;
|
||||
const ThreadStatus::StateType state_type_;
|
||||
std::mutex mutex_;
|
||||
std::condition_variable bg_cv_;
|
||||
bool should_sleep_;
|
||||
std::atomic<int> sleeping_count_;
|
||||
bool should_run_;
|
||||
std::atomic<int> running_count_;
|
||||
};
|
||||
|
||||
class ThreadListTest {
|
||||
|
@ -73,72 +94,232 @@ class ThreadListTest {
|
|||
}
|
||||
};
|
||||
|
||||
TEST(ThreadListTest, EventTables) {
|
||||
// verify the global tables for operations and states are properly indexed.
|
||||
for (int type = 0; type != ThreadStatus::NUM_OP_TYPES; ++type) {
|
||||
ASSERT_EQ(global_operation_table[type].type, type);
|
||||
}
|
||||
|
||||
for (int type = 0; type != ThreadStatus::NUM_STATE_TYPES; ++type) {
|
||||
ASSERT_EQ(global_state_table[type].type, type);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ThreadListTest, SimpleColumnFamilyInfoTest) {
|
||||
Env* env = Env::Default();
|
||||
const int kHighPriorityThreads = 3;
|
||||
const int kLowPriorityThreads = 5;
|
||||
const int kSleepingHighPriThreads = kHighPriorityThreads - 1;
|
||||
const int kSleepingLowPriThreads = kLowPriorityThreads / 3;
|
||||
const int kSimulatedHighPriThreads = kHighPriorityThreads - 1;
|
||||
const int kSimulatedLowPriThreads = kLowPriorityThreads / 3;
|
||||
env->SetBackgroundThreads(kHighPriorityThreads, Env::HIGH);
|
||||
env->SetBackgroundThreads(kLowPriorityThreads, Env::LOW);
|
||||
|
||||
SleepingBackgroundTask sleeping_task(
|
||||
reinterpret_cast<void*>(1234), "sleeping",
|
||||
SimulatedBackgroundTask running_task(
|
||||
reinterpret_cast<void*>(1234), "running",
|
||||
reinterpret_cast<void*>(5678), "pikachu");
|
||||
|
||||
for (int test = 0; test < kSleepingHighPriThreads; ++test) {
|
||||
env->Schedule(&SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_task, Env::Priority::HIGH);
|
||||
for (int test = 0; test < kSimulatedHighPriThreads; ++test) {
|
||||
env->Schedule(&SimulatedBackgroundTask::DoSimulatedTask,
|
||||
&running_task, Env::Priority::HIGH);
|
||||
}
|
||||
for (int test = 0; test < kSleepingLowPriThreads; ++test) {
|
||||
env->Schedule(&SleepingBackgroundTask::DoSleepTask,
|
||||
&sleeping_task, Env::Priority::LOW);
|
||||
for (int test = 0; test < kSimulatedLowPriThreads; ++test) {
|
||||
env->Schedule(&SimulatedBackgroundTask::DoSimulatedTask,
|
||||
&running_task, Env::Priority::LOW);
|
||||
}
|
||||
|
||||
// make sure everything is scheduled.
|
||||
env->SleepForMicroseconds(10000);
|
||||
running_task.WaitUntilScheduled(
|
||||
kSimulatedHighPriThreads + kSimulatedLowPriThreads, env);
|
||||
|
||||
std::vector<ThreadStatus> thread_list;
|
||||
|
||||
// Verify the number of sleeping threads in each pool.
|
||||
// Verify the number of running threads in each pool.
|
||||
env->GetThreadList(&thread_list);
|
||||
int sleeping_count[ThreadStatus::ThreadType::TOTAL] = {0};
|
||||
int running_count[ThreadStatus::NUM_THREAD_TYPES] = {0};
|
||||
for (auto thread_status : thread_list) {
|
||||
if (thread_status.cf_name == "pikachu" &&
|
||||
thread_status.db_name == "sleeping") {
|
||||
sleeping_count[thread_status.thread_type]++;
|
||||
thread_status.db_name == "running") {
|
||||
running_count[thread_status.thread_type]++;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(
|
||||
sleeping_count[ThreadStatus::ThreadType::ROCKSDB_HIGH_PRIORITY],
|
||||
kSleepingHighPriThreads);
|
||||
running_count[ThreadStatus::HIGH_PRIORITY],
|
||||
kSimulatedHighPriThreads);
|
||||
ASSERT_EQ(
|
||||
sleeping_count[ThreadStatus::ThreadType::ROCKSDB_LOW_PRIORITY],
|
||||
kSleepingLowPriThreads);
|
||||
running_count[ThreadStatus::LOW_PRIORITY],
|
||||
kSimulatedLowPriThreads);
|
||||
ASSERT_EQ(
|
||||
sleeping_count[ThreadStatus::ThreadType::USER_THREAD], 0);
|
||||
running_count[ThreadStatus::USER], 0);
|
||||
|
||||
sleeping_task.WakeUp();
|
||||
sleeping_task.WaitUntilDone();
|
||||
running_task.FinishAllTasks();
|
||||
running_task.WaitUntilDone();
|
||||
|
||||
// Verify none of the threads are sleeping
|
||||
// Verify none of the threads are running
|
||||
env->GetThreadList(&thread_list);
|
||||
for (int i = 0; i < ThreadStatus::ThreadType::TOTAL; ++i) {
|
||||
sleeping_count[i] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ThreadStatus::NUM_THREAD_TYPES; ++i) {
|
||||
running_count[i] = 0;
|
||||
}
|
||||
for (auto thread_status : thread_list) {
|
||||
if (thread_status.cf_name == "pikachu" &&
|
||||
thread_status.db_name == "sleeping") {
|
||||
sleeping_count[thread_status.thread_type]++;
|
||||
thread_status.db_name == "running") {
|
||||
running_count[thread_status.thread_type]++;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(
|
||||
sleeping_count[ThreadStatus::ThreadType::ROCKSDB_HIGH_PRIORITY], 0);
|
||||
running_count[ThreadStatus::HIGH_PRIORITY], 0);
|
||||
ASSERT_EQ(
|
||||
sleeping_count[ThreadStatus::ThreadType::ROCKSDB_LOW_PRIORITY], 0);
|
||||
running_count[ThreadStatus::LOW_PRIORITY], 0);
|
||||
ASSERT_EQ(
|
||||
sleeping_count[ThreadStatus::ThreadType::USER_THREAD], 0);
|
||||
running_count[ThreadStatus::USER], 0);
|
||||
}
|
||||
|
||||
namespace {
|
||||
void UpdateStatusCounts(
|
||||
const std::vector<ThreadStatus>& thread_list,
|
||||
int operation_counts[], int state_counts[]) {
|
||||
for (auto thread_status : thread_list) {
|
||||
operation_counts[thread_status.operation_type]++;
|
||||
state_counts[thread_status.state_type]++;
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyAndResetCounts(
|
||||
const int correct_counts[], int collected_counts[], int size) {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
ASSERT_EQ(collected_counts[i], correct_counts[i]);
|
||||
collected_counts[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateCount(
|
||||
int operation_counts[], int from_event, int to_event, int amount) {
|
||||
operation_counts[from_event] -= amount;
|
||||
operation_counts[to_event] += amount;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
TEST(ThreadListTest, SimpleEventTest) {
|
||||
Env* env = Env::Default();
|
||||
|
||||
// simulated tasks
|
||||
const int kFlushWriteTasks = 3;
|
||||
SimulatedBackgroundTask flush_write_task(
|
||||
reinterpret_cast<void*>(1234), "running",
|
||||
reinterpret_cast<void*>(5678), "pikachu",
|
||||
ThreadStatus::OP_FLUSH);
|
||||
|
||||
const int kCompactionWriteTasks = 4;
|
||||
SimulatedBackgroundTask compaction_write_task(
|
||||
reinterpret_cast<void*>(1234), "running",
|
||||
reinterpret_cast<void*>(5678), "pikachu",
|
||||
ThreadStatus::OP_COMPACTION);
|
||||
|
||||
const int kCompactionReadTasks = 5;
|
||||
SimulatedBackgroundTask compaction_read_task(
|
||||
reinterpret_cast<void*>(1234), "running",
|
||||
reinterpret_cast<void*>(5678), "pikachu",
|
||||
ThreadStatus::OP_COMPACTION);
|
||||
|
||||
const int kCompactionWaitTasks = 6;
|
||||
SimulatedBackgroundTask compaction_wait_task(
|
||||
reinterpret_cast<void*>(1234), "running",
|
||||
reinterpret_cast<void*>(5678), "pikachu",
|
||||
ThreadStatus::OP_COMPACTION);
|
||||
|
||||
// setup right answers
|
||||
int correct_operation_counts[ThreadStatus::NUM_OP_TYPES] = {0};
|
||||
correct_operation_counts[ThreadStatus::OP_FLUSH] =
|
||||
kFlushWriteTasks;
|
||||
correct_operation_counts[ThreadStatus::OP_COMPACTION] =
|
||||
kCompactionWriteTasks + kCompactionReadTasks + kCompactionWaitTasks;
|
||||
|
||||
env->SetBackgroundThreads(
|
||||
correct_operation_counts[ThreadStatus::OP_FLUSH], Env::HIGH);
|
||||
env->SetBackgroundThreads(
|
||||
correct_operation_counts[ThreadStatus::OP_COMPACTION], Env::LOW);
|
||||
|
||||
// schedule the simulated tasks
|
||||
for (int t = 0; t < kFlushWriteTasks; ++t) {
|
||||
env->Schedule(&SimulatedBackgroundTask::DoSimulatedTask,
|
||||
&flush_write_task, Env::Priority::HIGH);
|
||||
}
|
||||
flush_write_task.WaitUntilScheduled(kFlushWriteTasks, env);
|
||||
|
||||
for (int t = 0; t < kCompactionWriteTasks; ++t) {
|
||||
env->Schedule(&SimulatedBackgroundTask::DoSimulatedTask,
|
||||
&compaction_write_task, Env::Priority::LOW);
|
||||
}
|
||||
compaction_write_task.WaitUntilScheduled(kCompactionWriteTasks, env);
|
||||
|
||||
for (int t = 0; t < kCompactionReadTasks; ++t) {
|
||||
env->Schedule(&SimulatedBackgroundTask::DoSimulatedTask,
|
||||
&compaction_read_task, Env::Priority::LOW);
|
||||
}
|
||||
compaction_read_task.WaitUntilScheduled(kCompactionReadTasks, env);
|
||||
|
||||
for (int t = 0; t < kCompactionWaitTasks; ++t) {
|
||||
env->Schedule(&SimulatedBackgroundTask::DoSimulatedTask,
|
||||
&compaction_wait_task, Env::Priority::LOW);
|
||||
}
|
||||
compaction_wait_task.WaitUntilScheduled(kCompactionWaitTasks, env);
|
||||
|
||||
// verify the thread-status
|
||||
int operation_counts[ThreadStatus::NUM_OP_TYPES] = {0};
|
||||
int state_counts[ThreadStatus::NUM_STATE_TYPES] = {0};
|
||||
|
||||
std::vector<ThreadStatus> thread_list;
|
||||
env->GetThreadList(&thread_list);
|
||||
UpdateStatusCounts(thread_list, operation_counts, state_counts);
|
||||
VerifyAndResetCounts(correct_operation_counts, operation_counts,
|
||||
ThreadStatus::NUM_OP_TYPES);
|
||||
|
||||
// terminate compaction-wait tasks and see if the thread-status
|
||||
// reflects this update
|
||||
compaction_wait_task.FinishAllTasks();
|
||||
compaction_wait_task.WaitUntilDone();
|
||||
UpdateCount(correct_operation_counts, ThreadStatus::OP_COMPACTION,
|
||||
ThreadStatus::OP_UNKNOWN, kCompactionWaitTasks);
|
||||
|
||||
env->GetThreadList(&thread_list);
|
||||
UpdateStatusCounts(thread_list, operation_counts, state_counts);
|
||||
VerifyAndResetCounts(correct_operation_counts, operation_counts,
|
||||
ThreadStatus::NUM_OP_TYPES);
|
||||
|
||||
// terminate flush-write tasks and see if the thread-status
|
||||
// reflects this update
|
||||
flush_write_task.FinishAllTasks();
|
||||
flush_write_task.WaitUntilDone();
|
||||
UpdateCount(correct_operation_counts, ThreadStatus::OP_FLUSH,
|
||||
ThreadStatus::OP_UNKNOWN, kFlushWriteTasks);
|
||||
|
||||
env->GetThreadList(&thread_list);
|
||||
UpdateStatusCounts(thread_list, operation_counts, state_counts);
|
||||
VerifyAndResetCounts(correct_operation_counts, operation_counts,
|
||||
ThreadStatus::NUM_OP_TYPES);
|
||||
|
||||
// terminate compaction-write tasks and see if the thread-status
|
||||
// reflects this update
|
||||
compaction_write_task.FinishAllTasks();
|
||||
compaction_write_task.WaitUntilDone();
|
||||
UpdateCount(correct_operation_counts, ThreadStatus::OP_COMPACTION,
|
||||
ThreadStatus::OP_UNKNOWN, kCompactionWriteTasks);
|
||||
|
||||
env->GetThreadList(&thread_list);
|
||||
UpdateStatusCounts(thread_list, operation_counts, state_counts);
|
||||
VerifyAndResetCounts(correct_operation_counts, operation_counts,
|
||||
ThreadStatus::NUM_OP_TYPES);
|
||||
|
||||
// terminate compaction-write tasks and see if the thread-status
|
||||
// reflects this update
|
||||
compaction_read_task.FinishAllTasks();
|
||||
compaction_read_task.WaitUntilDone();
|
||||
UpdateCount(correct_operation_counts, ThreadStatus::OP_COMPACTION,
|
||||
ThreadStatus::OP_UNKNOWN, kCompactionReadTasks);
|
||||
|
||||
env->GetThreadList(&thread_list);
|
||||
UpdateStatusCounts(thread_list, operation_counts, state_counts);
|
||||
VerifyAndResetCounts(correct_operation_counts, operation_counts,
|
||||
ThreadStatus::NUM_OP_TYPES);
|
||||
}
|
||||
|
||||
} // namespace rocksdb
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
// Copyright (c) 2013, Facebook, Inc. All rights reserved.
|
||||
// This source code is licensed under the BSD-style license found in the
|
||||
// LICENSE file in the root directory of this source tree. An additional grant
|
||||
// of patent rights can be found in the PATENTS file in the same directory.
|
||||
//
|
||||
// This file defines the structures for thread operation and state.
|
||||
// Thread operations are used to describe high level action of a
|
||||
// thread such as doing compaction or flush, while thread state
|
||||
// are used to describe lower-level action such as reading /
|
||||
// writing a file or waiting for a mutex. Operations and states
|
||||
// are designed to be independent. Typically, a thread usually involves
|
||||
// in one operation and one state at any specific point in time.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "include/rocksdb/thread_status.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
#if ROCKSDB_USING_THREAD_STATUS
|
||||
|
||||
// The structure that describes a major thread operation.
|
||||
struct OperationInfo {
|
||||
const ThreadStatus::OperationType type;
|
||||
const std::string name;
|
||||
};
|
||||
|
||||
// The global operation table.
|
||||
//
|
||||
// When updating a status of a thread, the pointer of the OperationInfo
|
||||
// of the current ThreadStatusData will be pointing to one of the
|
||||
// rows in this global table.
|
||||
//
|
||||
// Note that it's not designed to be constant as in the future we
|
||||
// might consider adding global count to the OperationInfo.
|
||||
static OperationInfo global_operation_table[] = {
|
||||
{ThreadStatus::OP_UNKNOWN, ""},
|
||||
{ThreadStatus::OP_COMPACTION, "Compaction"},
|
||||
{ThreadStatus::OP_FLUSH, "Flush"}
|
||||
};
|
||||
|
||||
// The structure that describes a state.
|
||||
struct StateInfo {
|
||||
const ThreadStatus::StateType type;
|
||||
const std::string name;
|
||||
};
|
||||
|
||||
// The global state table.
|
||||
//
|
||||
// When updating a status of a thread, the pointer of the StateInfo
|
||||
// of the current ThreadStatusData will be pointing to one of the
|
||||
// rows in this global table.
|
||||
static StateInfo global_state_table[] = {
|
||||
{ThreadStatus::STATE_UNKNOWN, ""},
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
struct OperationInfo {
|
||||
};
|
||||
|
||||
struct StateInfo {
|
||||
};
|
||||
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
} // namespace rocksdb
|
|
@ -34,10 +34,28 @@ void ThreadStatusUpdater::SetColumnFamilyInfoKey(
|
|||
data->cf_key.store(cf_key, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ThreadStatusUpdater::SetEventInfoPtr(
|
||||
const ThreadEventInfo* event_info) {
|
||||
void ThreadStatusUpdater::SetThreadOperation(
|
||||
const ThreadStatus::OperationType type) {
|
||||
auto* data = InitAndGet();
|
||||
data->event_info.store(event_info, std::memory_order_relaxed);
|
||||
data->operation_type.store(type, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ThreadStatusUpdater::ClearThreadOperation() {
|
||||
auto* data = InitAndGet();
|
||||
data->operation_type.store(
|
||||
ThreadStatus::OP_UNKNOWN, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ThreadStatusUpdater::SetThreadState(
|
||||
const ThreadStatus::StateType type) {
|
||||
auto* data = InitAndGet();
|
||||
data->state_type.store(type, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void ThreadStatusUpdater::ClearThreadState() {
|
||||
auto* data = InitAndGet();
|
||||
data->state_type.store(
|
||||
ThreadStatus::STATE_UNKNOWN, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
Status ThreadStatusUpdater::GetThreadList(
|
||||
|
@ -50,30 +68,35 @@ Status ThreadStatusUpdater::GetThreadList(
|
|||
assert(thread_data);
|
||||
auto thread_type = thread_data->thread_type.load(
|
||||
std::memory_order_relaxed);
|
||||
// Since any change to cf_info_map requires thread_list_mutex,
|
||||
// which is currently held by GetThreadList(), here we can safely
|
||||
// use "memory_order_relaxed" to load the cf_key.
|
||||
auto cf_key = thread_data->cf_key.load(
|
||||
std::memory_order_relaxed);
|
||||
auto iter = cf_info_map_.find(cf_key);
|
||||
assert(cf_key == 0 || iter != cf_info_map_.end());
|
||||
auto* cf_info = iter != cf_info_map_.end() ?
|
||||
iter->second.get() : nullptr;
|
||||
auto* event_info = thread_data->event_info.load(
|
||||
std::memory_order_relaxed);
|
||||
const std::string* db_name = nullptr;
|
||||
const std::string* cf_name = nullptr;
|
||||
const std::string* event_name = nullptr;
|
||||
ThreadStatus::OperationType op_type = ThreadStatus::OP_UNKNOWN;
|
||||
ThreadStatus::StateType state_type = ThreadStatus::STATE_UNKNOWN;
|
||||
if (cf_info != nullptr) {
|
||||
db_name = &cf_info->db_name;
|
||||
cf_name = &cf_info->cf_name;
|
||||
op_type = thread_data->operation_type.load(
|
||||
std::memory_order_relaxed);
|
||||
// display lower-level info only when higher-level info is available.
|
||||
if (event_info != nullptr) {
|
||||
event_name = &event_info->event_name;
|
||||
if (op_type != ThreadStatus::OP_UNKNOWN) {
|
||||
state_type = thread_data->state_type.load(
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
thread_list->emplace_back(
|
||||
thread_data->thread_id, thread_type,
|
||||
db_name ? *db_name : "",
|
||||
cf_name ? *cf_name : "",
|
||||
event_name ? *event_name : "");
|
||||
op_type, state_type);
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
|
@ -93,6 +116,8 @@ ThreadStatusData* ThreadStatusUpdater::InitAndGet() {
|
|||
void ThreadStatusUpdater::NewColumnFamilyInfo(
|
||||
const void* db_key, const std::string& db_name,
|
||||
const void* cf_key, const std::string& cf_name) {
|
||||
// Acquiring same lock as GetThreadList() to guarantee
|
||||
// a consistent view of global column family table (cf_info_map).
|
||||
std::lock_guard<std::mutex> lck(thread_list_mutex_);
|
||||
|
||||
cf_info_map_[cf_key].reset(
|
||||
|
@ -101,6 +126,8 @@ void ThreadStatusUpdater::NewColumnFamilyInfo(
|
|||
}
|
||||
|
||||
void ThreadStatusUpdater::EraseColumnFamilyInfo(const void* cf_key) {
|
||||
// Acquiring same lock as GetThreadList() to guarantee
|
||||
// a consistent view of global column family table (cf_info_map).
|
||||
std::lock_guard<std::mutex> lck(thread_list_mutex_);
|
||||
auto cf_pair = cf_info_map_.find(cf_key);
|
||||
assert(cf_pair != cf_info_map_.end());
|
||||
|
@ -122,6 +149,8 @@ void ThreadStatusUpdater::EraseColumnFamilyInfo(const void* cf_key) {
|
|||
}
|
||||
|
||||
void ThreadStatusUpdater::EraseDatabaseInfo(const void* db_key) {
|
||||
// Acquiring same lock as GetThreadList() to guarantee
|
||||
// a consistent view of global column family table (cf_info_map).
|
||||
std::lock_guard<std::mutex> lck(thread_list_mutex_);
|
||||
auto db_pair = db_key_map_.find(db_key);
|
||||
if (UNLIKELY(db_pair == db_key_map_.end())) {
|
||||
|
@ -154,8 +183,18 @@ void ThreadStatusUpdater::SetColumnFamilyInfoKey(
|
|||
const void* cf_key) {
|
||||
}
|
||||
|
||||
void ThreadStatusUpdater::SetEventInfoPtr(
|
||||
const ThreadEventInfo* event_info) {
|
||||
void ThreadStatusUpdater::SetThreadOperation(
|
||||
const ThreadStatus::OperationType type) {
|
||||
}
|
||||
|
||||
void ThreadStatusUpdater::ClearThreadOperation() {
|
||||
}
|
||||
|
||||
void ThreadStatusUpdater::SetThreadState(
|
||||
const ThreadStatus::StateType type) {
|
||||
}
|
||||
|
||||
void ThreadStatusUpdater::ClearThreadState() {
|
||||
}
|
||||
|
||||
Status ThreadStatusUpdater::GetThreadList(
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
// should be ignored.
|
||||
//
|
||||
// The high to low level information would be:
|
||||
// thread_id > thread_type > db > cf > event > event_count > event_details
|
||||
// thread_id > thread_type > db > cf > operation > state
|
||||
//
|
||||
// This means user might not always get full information, but whenever
|
||||
// returned by the GetThreadList() is guaranteed to be consistent.
|
||||
|
@ -37,6 +37,7 @@
|
|||
#include "rocksdb/status.h"
|
||||
#include "rocksdb/thread_status.h"
|
||||
#include "port/port_posix.h"
|
||||
#include "util/thread_operation.h"
|
||||
|
||||
namespace rocksdb {
|
||||
|
||||
|
@ -57,27 +58,21 @@ struct ConstantColumnFamilyInfo {
|
|||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
};
|
||||
|
||||
// The structure that describes an event.
|
||||
struct ThreadEventInfo {
|
||||
#if ROCKSDB_USING_THREAD_STATUS
|
||||
public:
|
||||
const std::string event_name;
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
};
|
||||
|
||||
// the internal data-structure that is used to reflect the current
|
||||
// status of a thread using a set of atomic pointers.
|
||||
struct ThreadStatusData {
|
||||
#if ROCKSDB_USING_THREAD_STATUS
|
||||
explicit ThreadStatusData() : thread_id(0) {
|
||||
thread_type.store(ThreadStatus::ThreadType::USER_THREAD);
|
||||
thread_type.store(ThreadStatus::USER);
|
||||
cf_key.store(0);
|
||||
event_info.store(nullptr);
|
||||
operation_type.store(ThreadStatus::OP_UNKNOWN);
|
||||
state_type.store(ThreadStatus::STATE_UNKNOWN);
|
||||
}
|
||||
uint64_t thread_id;
|
||||
std::atomic<ThreadStatus::ThreadType> thread_type;
|
||||
std::atomic<const void*> cf_key;
|
||||
std::atomic<const ThreadEventInfo*> event_info;
|
||||
std::atomic<ThreadStatus::OperationType> operation_type;
|
||||
std::atomic<ThreadStatus::StateType> state_type;
|
||||
#endif // ROCKSDB_USING_THREAD_STATUS
|
||||
};
|
||||
|
||||
|
@ -103,12 +98,20 @@ class ThreadStatusUpdater {
|
|||
void SetThreadType(ThreadStatus::ThreadType ttype);
|
||||
|
||||
// Update the column-family info of the current thread by setting
|
||||
// its thread-local pointer of ThreadEventInfo to the correct entry.
|
||||
// its thread-local pointer of ThreadStateInfo to the correct entry.
|
||||
void SetColumnFamilyInfoKey(const void* cf_key);
|
||||
|
||||
// Update the event info of the current thread by setting
|
||||
// its thread-local pointer of ThreadEventInfo to the correct entry.
|
||||
void SetEventInfoPtr(const ThreadEventInfo* event_info);
|
||||
// Update the thread operation of the current thread.
|
||||
void SetThreadOperation(const ThreadStatus::OperationType type);
|
||||
|
||||
// Clear thread operation of the current thread.
|
||||
void ClearThreadOperation();
|
||||
|
||||
// Update the thread state of the current thread.
|
||||
void SetThreadState(const ThreadStatus::StateType type);
|
||||
|
||||
// Clear the thread state of the current thread.
|
||||
void ClearThreadState();
|
||||
|
||||
// Obtain the status of all active registered threads.
|
||||
Status GetThreadList(
|
||||
|
|
Loading…
Reference in New Issue