Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chunkserver: added clean chunk feature #322

Merged
merged 1 commit into from
Apr 29, 2021
Merged

chunkserver: added clean chunk feature #322

merged 1 commit into from
Apr 29, 2021

Conversation

Wine93
Copy link
Contributor

@Wine93 Wine93 commented Apr 20, 2021

What problem does this PR solve?

Issue Number: close #xxx

Problem Summary:

What is changed and how it works?

What's Changed:

How it Works:

Side effects(Breaking backward compatibility? Performance regression?):

Check List

  • Relevant documentation/comments is changed or added
  • I acknowledge that all my contributions will be made under the project's license

@@ -163,6 +163,10 @@ chunkfilepool.chunk_file_pool_dir=./0/
chunkfilepool.cpmeta_file_size=4096
# chunkfilepool get chunk最大重试次数
chunkfilepool.retry_times=5
# Enable clean chunk
chunkfilepool.clean.enable=true
# The throttle iops for cleaning chunk (4KB/IO)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IO size should be configurable

Copy link
Contributor Author

@Wine93 Wine93 Apr 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

In my origin design, i think the IO size is as small as better to reduce effect for user's IO request. if we want control the speed of cleaning, we can increase the limited iops.

LOG(ERROR) << "File format version incompatible."
<< "file version: "
<< static_cast<uint32_t>(version)
<< ", format version: "
<< static_cast<uint32_t>(FORMAT_VERSION);
<< ", format version: ["
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may be valid version is better, and the statis_cast is redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -189,7 +192,14 @@ CSErrorCode CSChunkFile::Open(bool createFile) {
char buf[pageSize_]; // NOLINT
memset(buf, 0, sizeof(buf));
metaPage_.encode(buf);
int rc = chunkFilePool_->GetFile(chunkFilePath, buf);

auto updateMetapageCb = [this](char* buffer) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can just update default version from FORMAT_VERSION to FORMAT_VERSION_V2 in ChunkFileMetaPage's constructor. And, remove this callback to update version.

Copy link
Contributor Author

@Wine93 Wine93 Apr 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't gurantee get clean chunk success in every operation, so if we get dirty chunk we should use FORMAT_VERSION in metapage.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't gurantee get clean chunk success in every operation, so if we get dirty chunk we should use FORMAT_VERSION in metapage.

I think you will get a clean chunk if you set needClean to true when calling GetChunk.
And after GetChunk returned with true, you got a cleaned chunk even if the chunk is from dirty pool, because you did fallocate it with FALLOC_FL_ZERO_RANGE. And for FALLOC_FL_ZERO_RANGE, it will mark the file as unwritten, so read from this file is just like a clean chunk file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::string chunkpath = currentdir_ + "/" + std::to_string(chunkid);
int ret = fsptr_->Open(chunkpath.c_str(), O_RDWR);
if (ret < 0) {
LOG(ERROR) << "Open file failed: " << chunkpath.c_str();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.c_str() is redundant

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


uint64_t chunklen = poolOpt_.fileSize + poolOpt_.metaPageSize;
if (onlyMarked) {
ret = fsptr_->Fallocate(fd, FALLOC_FL_ZERO_RANGE, 0, chunklen);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does FALLOC_FL_KEEP_SIZE flag is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The chunklen is fixed, I think we don't need this flag.

auto popBack = [this](std::vector<uint64_t>& chunks,
uint64_t& chunksLeft) -> uint64_t {
std::unique_lock<std::mutex> lk(mtx_);
if (0 == chunks.size()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check whether a vector is empty, just use .empty() member function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -35,6 +35,7 @@ namespace chunkserver {
using curve::common::Bitmap;

const uint8_t FORMAT_VERSION = 1;
const uint8_t FORMAT_VERSION_V2 = 2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some annotation to explain the meaning of each version

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

private:
// Protect tmpChunkvec_
// The suffix of clean chunk file (".0")
static const char* kCleanChunkSuffix_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just define as std::string, so static std::string GetCleanChunkSuffix() no need to convert from const char* to std::string.
And, I think .clean suffix is more meaningful.

Copy link
Contributor Author

@Wine93 Wine93 Apr 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YES, i think it's better to use .clean suffix.

}

uint64_t FilePool::GetChunk(bool needClean, bool* isCleaned) {
auto smartPop = [this](std::vector<uint64_t>& chunks1,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lambda a little bit complex. It better to define two member functions, e.g., Push and Pop, and each one takes one argument to determine whether push/pop from dirty pool or clean pool.

By the way, if the function going to change the argument, it should task a pointer instead of a non-const reference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

std::unique_lock<std::mutex> lk(mtx_);
if (tmpChunkvec_.empty()) {
LOG(ERROR) << "no avaliable chunk!";
chunkID = GetChunk(needClean, &isCleaned);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure whether exist a chunk which id is 0, so it's better to let chunkID as an argument and check by the return value(true/false).

Copy link
Contributor Author

@Wine93 Wine93 Apr 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

yeap, the valid chunk id start with 1

@Wine93
Copy link
Contributor Author

Wine93 commented Apr 23, 2021

All review suggestions had been implemented @wu-hanqing

@@ -131,6 +131,9 @@ chunkserver_rconcurrentapply_queuedepth: 1
chunkserver_chunkfilepool_chunk_file_pool_dir: ./0/
chunkserver_chunkfilepool_cpmeta_file_size: 4096
chunkserver_chunkfilepool_retry_times: 5
chunkserver_chunkfilepool_clean_enable: true
chunkserver_chunkfilepool_clean_bytes_per_write: 4096
chunkserver_chunkfilepool_clean_throttle_iops: 500
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in chunkserver.conf it is 2000, why here 500?

Copy link
Contributor Author

@Wine93 Wine93 Apr 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SeanHai I will unify them.

return true;
}

void FilePool::CleanWorker() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how to clean the dirty chunks already in chunkfilepool?

Copy link
Contributor Author

@Wine93 Wine93 Apr 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SeanHai The chunkserver will create a thread to clean dirty chunk when it start, see code

}
std::unique_lock<std::mutex> lk(mtx_);
tmpChunkvec_.push_back(newfilenum);
++currentState_.preallocatedChunksLeft;
dirtyChunks_.push_back(newfilenum);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the chunkserver crashed, will the recycled dirty chunks missed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SeanHai No, the recycled dirty chunks already in the chunkfile directory, the dirtyChunks_ vector is only hold the chunk's number in memory.

LOG_IF(FATAL, !conf->GetUInt32Value("chunkfilepool.clean.throttle_iops",
&chunkFilePoolOptions->iops4clean));

if (chunkFilePoolOptions->bytesPerWrite < 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have defined chunkFilePoolOptions->bytesPerWrite as unsigned int, so it cannot less than 0.
And, I think bytesPerWrite should be aligned to 4K.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

memset(buffer, 0, kBytesPerWrite_);
uint32_t bytesPerWrite = poolOpt_.bytesPerWrite;

char *buffer = new (std::nothrow) char[bytesPerWrite];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forget free this space, and I think buffer can be a member variable, so you don't have to frequently new/delete it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -202,7 +207,9 @@ class CURVE_CACHELINE_ALIGNMENT FilePool {
/**
* @brief: Return the suffix of clean chunk
*/
static std::string GetCleanChunkSuffix();
inline static std::string GetCleanChunkSuffix() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keyword inline is redundant because member functions defined in header files are implicit inline.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wu-hanqing done.

bool ret = pop(&cleanChunks_, &currentState_.cleanChunksLeft, true)
|| pop(&dirtyChunks_, &currentState_.dirtyChunksLeft, false);

if (false == *isCleaned && CleanChunk(*chunkid, true)) {
Copy link
Contributor

@wu-hanqing wu-hanqing Apr 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should also test ret is true or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wu-hanqing done.

@@ -189,7 +192,14 @@ CSErrorCode CSChunkFile::Open(bool createFile) {
char buf[pageSize_]; // NOLINT
memset(buf, 0, sizeof(buf));
metaPage_.encode(buf);
int rc = chunkFilePool_->GetFile(chunkFilePath, buf);

auto updateMetapageCb = [this](char* buffer) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't gurantee get clean chunk success in every operation, so if we get dirty chunk we should use FORMAT_VERSION in metapage.

I think you will get a clean chunk if you set needClean to true when calling GetChunk.
And after GetChunk returned with true, you got a cleaned chunk even if the chunk is from dirty pool, because you did fallocate it with FALLOC_FL_ZERO_RANGE. And for FALLOC_FL_ZERO_RANGE, it will mark the file as unwritten, so read from this file is just like a clean chunk file.

@@ -199,7 +204,9 @@ FilePool::FilePool(std::shared_ptr<LocalFileSystem> fsptr)
: currentmaxfilenum_(0) {
CHECK(fsptr != nullptr) << "fs ptr allocate failed!";
fsptr_ = fsptr;
tmpChunkvec_.clear();
cleanAlived_ = false;
dirtyChunks_.clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to clear it, it's empty by default.

Copy link
Contributor Author

@Wine93 Wine93 Apr 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wu-hanqing In some test case, it will call the UnInitialize() to do some clear work, and expect to clear these vectors.

InterruptibleSleeper cleanSleeper_;

// The buffer for write chunk file
std::shared_ptr<char> writeBuffer_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::unique_ptr<char[]> is more suitable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::unique_ptr<char[]> is more suitable.

done.

@@ -188,8 +187,10 @@ CSErrorCode CSChunkFile::Open(bool createFile) {
&& metaPage_.sn > 0) {
char buf[pageSize_]; // NOLINT
memset(buf, 0, sizeof(buf));
metaPage_.version = FORMAT_VERSION_V2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this version in MetaPage's constructor is better, but some unit tests may be affected.

Copy link
Contributor Author

@Wine93 Wine93 Apr 27, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change this version in MetaPage's constructor is better, but some unit tests may be affected.

When chunkserver start, it will scan all chunk files in the copyset directory, and these chunk files is not clean. so if we change the version in MetaPage's constructor, these dirty chunk files will cause confuse.

@ilixiaocui ilixiaocui merged commit 5ee6378 into opencurve:master Apr 29, 2021
ilixiaocui pushed a commit to ilixiaocui/curve that referenced this pull request Feb 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants