Summary: Introducing WriteController, which is a source of truth about per-DB write delays. Let's define an DB epoch as a period where there are no flushes and compactions (i.e. new epoch is started when flush or compaction finishes). Each epoch can either: * proceed with all writes without delay * delay all writes by fixed time * stop all writes The three modes are recomputed at each epoch change (flush, compaction), rather than on every write (which is currently the case). When we have a lot of column families, our current pull behavior adds a big overhead, since we need to loop over every column family for every write. With new push model, overhead on Write code-path is minimal. This is just the start. Next step is to also take care of stalls introduced by slow memtable flushes. The final goal is to eliminate function MakeRoomForWrite(), which currently needs to be called for every column family by every write. Test Plan: make check for now. I'll add some unit tests later. Also, perf test. Reviewers: dhruba, yhchiang, MarkCallaghan, sdong, ljin Reviewed By: ljin Subscribers: leveldb Differential Revision: https://reviews.facebook.net/D22791main
parent
0af157f9bf
commit
a2bb7c3c33
@ -0,0 +1,37 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#include "db/write_controller.h" |
||||||
|
|
||||||
|
#include <cassert> |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
|
||||||
|
std::unique_ptr<WriteControllerToken> WriteController::GetStopToken() { |
||||||
|
++total_stopped_; |
||||||
|
return std::unique_ptr<WriteControllerToken>(new StopWriteToken(this)); |
||||||
|
} |
||||||
|
|
||||||
|
std::unique_ptr<WriteControllerToken> WriteController::GetDelayToken( |
||||||
|
uint64_t delay_us) { |
||||||
|
total_delay_us_ += delay_us; |
||||||
|
return std::unique_ptr<WriteControllerToken>( |
||||||
|
new DelayWriteToken(this, delay_us)); |
||||||
|
} |
||||||
|
|
||||||
|
bool WriteController::IsStopped() const { return total_stopped_ > 0; } |
||||||
|
uint64_t WriteController::GetDelay() const { return total_delay_us_; } |
||||||
|
|
||||||
|
StopWriteToken::~StopWriteToken() { |
||||||
|
assert(controller_->total_stopped_ >= 1); |
||||||
|
--controller_->total_stopped_; |
||||||
|
} |
||||||
|
|
||||||
|
DelayWriteToken::~DelayWriteToken() { |
||||||
|
assert(controller_->total_delay_us_ >= delay_us_); |
||||||
|
controller_->total_delay_us_ -= delay_us_; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace rocksdb
|
@ -0,0 +1,78 @@ |
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
#include <memory> |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
|
||||||
|
class WriteControllerToken; |
||||||
|
|
||||||
|
// WriteController is controlling write stalls in our write code-path. Write
|
||||||
|
// stalls happen when compaction can't keep up with write rate.
|
||||||
|
// All of the methods here (including WriteControllerToken's destructors) need
|
||||||
|
// to be called while holding DB mutex
|
||||||
|
class WriteController { |
||||||
|
public: |
||||||
|
WriteController() : total_stopped_(0), total_delay_us_(0) {} |
||||||
|
~WriteController() = default; |
||||||
|
|
||||||
|
// When an actor (column family) requests a stop token, all writes will be
|
||||||
|
// stopped until the stop token is released (deleted)
|
||||||
|
std::unique_ptr<WriteControllerToken> GetStopToken(); |
||||||
|
// When an actor (column family) requests a delay token, total delay for all
|
||||||
|
// writes will be increased by delay_us. The delay will last until delay token
|
||||||
|
// is released
|
||||||
|
std::unique_ptr<WriteControllerToken> GetDelayToken(uint64_t delay_us); |
||||||
|
|
||||||
|
// these two metods are querying the state of the WriteController
|
||||||
|
bool IsStopped() const; |
||||||
|
uint64_t GetDelay() const; |
||||||
|
|
||||||
|
private: |
||||||
|
friend class WriteControllerToken; |
||||||
|
friend class StopWriteToken; |
||||||
|
friend class DelayWriteToken; |
||||||
|
|
||||||
|
int total_stopped_; |
||||||
|
uint64_t total_delay_us_; |
||||||
|
}; |
||||||
|
|
||||||
|
class WriteControllerToken { |
||||||
|
public: |
||||||
|
explicit WriteControllerToken(WriteController* controller) |
||||||
|
: controller_(controller) {} |
||||||
|
virtual ~WriteControllerToken() = default; |
||||||
|
|
||||||
|
protected: |
||||||
|
WriteController* controller_; |
||||||
|
|
||||||
|
private: |
||||||
|
// no copying allowed
|
||||||
|
WriteControllerToken(const WriteControllerToken&) = delete; |
||||||
|
void operator=(const WriteControllerToken&) = delete; |
||||||
|
}; |
||||||
|
|
||||||
|
class StopWriteToken : public WriteControllerToken { |
||||||
|
public: |
||||||
|
explicit StopWriteToken(WriteController* controller) |
||||||
|
: WriteControllerToken(controller) {} |
||||||
|
~StopWriteToken(); |
||||||
|
}; |
||||||
|
|
||||||
|
class DelayWriteToken : public WriteControllerToken { |
||||||
|
public: |
||||||
|
DelayWriteToken(WriteController* controller, uint64_t delay_us) |
||||||
|
: WriteControllerToken(controller), delay_us_(delay_us) {} |
||||||
|
~DelayWriteToken(); |
||||||
|
|
||||||
|
private: |
||||||
|
uint64_t delay_us_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace rocksdb
|
@ -0,0 +1,40 @@ |
|||||||
|
// 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.
|
||||||
|
//
|
||||||
|
#include "db/write_controller.h" |
||||||
|
|
||||||
|
#include "util/testharness.h" |
||||||
|
|
||||||
|
namespace rocksdb { |
||||||
|
|
||||||
|
class WriteControllerTest {}; |
||||||
|
|
||||||
|
TEST(WriteControllerTest, SanityTest) { |
||||||
|
WriteController controller; |
||||||
|
auto stop_token_1 = controller.GetStopToken(); |
||||||
|
auto stop_token_2 = controller.GetStopToken(); |
||||||
|
|
||||||
|
ASSERT_EQ(true, controller.IsStopped()); |
||||||
|
stop_token_1.reset(); |
||||||
|
ASSERT_EQ(true, controller.IsStopped()); |
||||||
|
stop_token_2.reset(); |
||||||
|
ASSERT_EQ(false, controller.IsStopped()); |
||||||
|
|
||||||
|
auto delay_token_1 = controller.GetDelayToken(5); |
||||||
|
ASSERT_EQ(static_cast<uint64_t>(5), controller.GetDelay()); |
||||||
|
auto delay_token_2 = controller.GetDelayToken(8); |
||||||
|
ASSERT_EQ(static_cast<uint64_t>(13), controller.GetDelay()); |
||||||
|
|
||||||
|
delay_token_2.reset(); |
||||||
|
ASSERT_EQ(static_cast<uint64_t>(5), controller.GetDelay()); |
||||||
|
delay_token_1.reset(); |
||||||
|
ASSERT_EQ(static_cast<uint64_t>(0), controller.GetDelay()); |
||||||
|
delay_token_1.reset(); |
||||||
|
ASSERT_EQ(false, controller.IsStopped()); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace rocksdb
|
||||||
|
|
||||||
|
int main(int argc, char** argv) { return rocksdb::test::RunAllTests(); } |
Loading…
Reference in new issue