curve/test/chunkserver/clone/clone_core_test.cpp

789 lines
32 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020 NetEase Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Project: curve
* Created Date: Saturday March 30th 2019
* Author: yangyaokai
*/
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <glog/logging.h>
#include <google/protobuf/stubs/callback.h>
#include <tuple>
#include "src/chunkserver/clone_core.h"
#include "src/chunkserver/copyset_node.h"
#include "src/chunkserver/op_request.h"
#include "test/chunkserver/mock_copyset_node.h"
#include "test/chunkserver/clone/clone_test_util.h"
#include "test/chunkserver/clone/mock_clone_copyer.h"
#include "test/chunkserver/datastore/mock_datastore.h"
#include "src/fs/local_filesystem.h"
namespace curve {
namespace chunkserver {
using curve::chunkserver::CHUNK_OP_TYPE;
using curve::fs::FileSystemType;
using curve::fs::LocalFsFactory;
ACTION_TEMPLATE(SaveBraftTask, HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(value)) {
auto input = static_cast<braft::Task>(::testing::get<k>(args));
auto output = static_cast<braft::Task *>(value);
output->data->swap(*input.data);
output->done = input.done;
}
const LogicPoolID LOGICPOOL_ID = 1;
const CopysetID COPYSET_ID = 1;
const ChunkID CHUNK_ID = 1;
class CloneCoreTest
: public testing::TestWithParam<
std::tuple<ChunkSizeType, ChunkSizeType, PageSizeType>> {
public:
void SetUp() {
chunksize_ = std::get<0>(GetParam());
blocksize_ = std::get<1>(GetParam());
pagesize_ = std::get<1>(GetParam());
datastore_ = std::make_shared<MockDataStore>();
copyer_ = std::make_shared<MockChunkCopyer>();
node_ = std::make_shared<MockCopysetNode>();
FakeCopysetNode();
}
void TearDown() {
Mock::VerifyAndClearExpectations(datastore_.get());
Mock::VerifyAndClearExpectations(node_.get());
}
void FakeCopysetNode() {
EXPECT_CALL(*node_, IsLeaderTerm()).WillRepeatedly(Return(true));
EXPECT_CALL(*node_, GetDataStore()).WillRepeatedly(Return(datastore_));
EXPECT_CALL(*node_, GetConcurrentApplyModule())
.WillRepeatedly(Return(nullptr));
EXPECT_CALL(*node_, GetAppliedIndex())
.WillRepeatedly(Return(LAST_INDEX));
}
std::shared_ptr<ReadChunkRequest>
GenerateReadRequest(CHUNK_OP_TYPE optype, off_t offset, size_t length) {
ChunkRequest *readRequest = new ChunkRequest();
readRequest->set_logicpoolid(LOGICPOOL_ID);
readRequest->set_copysetid(COPYSET_ID);
readRequest->set_chunkid(CHUNK_ID);
readRequest->set_optype(optype);
readRequest->set_offset(offset);
readRequest->set_size(length);
brpc::Controller *cntl = new brpc::Controller();
ChunkResponse *response = new ChunkResponse();
FakeChunkClosure *closure = new FakeChunkClosure();
closure->SetCntl(cntl);
closure->SetRequest(readRequest);
closure->SetResponse(response);
std::shared_ptr<ReadChunkRequest> req =
std::make_shared<ReadChunkRequest>(node_, nullptr, cntl,
readRequest, response, closure);
return req;
}
void SetCloneParam(std::shared_ptr<ReadChunkRequest> readRequest) {
ChunkRequest *request =
const_cast<ChunkRequest *>(readRequest->GetChunkRequest());
request->set_clonefilesource("/test");
request->set_clonefileoffset(0);
}
void CheckTask(const braft::Task &task, off_t offset, size_t length,
char *buf) {
butil::IOBuf data;
ChunkRequest request;
auto req = ChunkOpRequest::Decode(*task.data, &request, &data, 0,
PeerId("127.0.0.1:8200:0"));
auto preq = dynamic_cast<PasteChunkInternalRequest *>(req.get());
ASSERT_TRUE(preq != nullptr);
ASSERT_EQ(LOGICPOOL_ID, request.logicpoolid());
ASSERT_EQ(COPYSET_ID, request.copysetid());
ASSERT_EQ(CHUNK_ID, request.chunkid());
ASSERT_EQ(offset, request.offset());
ASSERT_EQ(length, request.size());
ASSERT_EQ(memcmp(buf, data.to_string().c_str(), length), 0);
}
protected:
ChunkSizeType chunksize_;
ChunkSizeType blocksize_;
PageSizeType pagesize_;
std::shared_ptr<MockDataStore> datastore_;
std::shared_ptr<MockCopysetNode> node_;
std::shared_ptr<MockChunkCopyer> copyer_;
};
/**
* 测试CHUNK_OP_READ类型请求,请求读取的chunk不是clone chunk
* result:不会从远端拷贝数据,直接从本地读取数据,结果返回成功
*/
TEST_P(CloneCoreTest, ReadChunkTest1) {
off_t offset = 0;
size_t length = 5 * blocksize_;
std::shared_ptr<CloneCore> core
= std::make_shared<CloneCore>(SLICE_SIZE, true, copyer_);
std::shared_ptr<ReadChunkRequest> readRequest
= GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_READ, offset, length);
// 不会从源端拷贝数据
EXPECT_CALL(*copyer_, DownloadAsync(_)).Times(0);
// 获取chunk信息
CSChunkInfo info;
info.isClone = false;
info.metaPageSize = pagesize_;
info.chunkSize = chunksize_;
info.blockSize = blocksize_;
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.WillOnce(DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
// 读chunk文件
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, offset, length)).Times(1);
// 更新 applied index
EXPECT_CALL(*node_, UpdateAppliedIndex(_)).Times(1);
// 不会产生PasteChunkRequest
EXPECT_CALL(*node_, Propose(_)).Times(0);
ASSERT_EQ(0, core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
closure->resContent_.status);
}
/**
* 测试CHUNK_OP_READ类型请求,请求读取的chunk是clone chunk
* case1:请求读取的区域全部被写过
* result1:全部从本地chunk读取
* case2:请求读取的区域都未被写过
* result2:全部从源端读取产生paste请求
* case3:请求读取的区域有部分被写过,部分未被写过
* result3:写过区域从本地chunk读取未写过区域从源端读取产生paste请求
* case4:请求读取的区域部分被写过请求的偏移未与pagesize对齐
* result4:返回错误
*/
TEST_P(CloneCoreTest, ReadChunkTest2) {
off_t offset = 0;
size_t length = 5 * blocksize_;
CSChunkInfo info;
info.isClone = true;
info.metaPageSize = pagesize_;
info.chunkSize = chunksize_;
info.blockSize = blocksize_;
info.bitmap = std::make_shared<Bitmap>(chunksize_ / blocksize_);
std::shared_ptr<CloneCore> core
= std::make_shared<CloneCore>(SLICE_SIZE, true, copyer_);
// case1
{
info.bitmap->Set();
// 每次调HandleReadRequest后会被closure释放
std::shared_ptr<ReadChunkRequest> readRequest =
GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_READ, offset, length);
EXPECT_CALL(*copyer_, DownloadAsync(_)).Times(0);
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.WillOnce(
DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
// 读chunk文件
char *chunkData = new char[length];
memset(chunkData, 'a', length);
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, offset, length))
.WillOnce(DoAll(SetArrayArgument<2>(chunkData, chunkData + length),
Return(CSErrorCode::Success)));
// 更新 applied index
EXPECT_CALL(*node_, UpdateAppliedIndex(_)).Times(1);
// 不会产生PasteChunkRequest
EXPECT_CALL(*node_, Propose(_)).Times(0);
ASSERT_EQ(0,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
closure->resContent_.status);
ASSERT_EQ(
memcmp(
chunkData,
closure->resContent_.attachment.to_string().c_str(), // NOLINT
length),
0);
delete[] chunkData;
}
// case2
{
info.bitmap->Clear();
// 每次调HandleReadRequest后会被closure释放
std::shared_ptr<ReadChunkRequest> readRequest =
GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_READ, offset, length);
char *cloneData = new char[length];
memset(cloneData, 'b', length);
EXPECT_CALL(*copyer_, DownloadAsync(_))
.WillOnce(Invoke([&](DownloadClosure *closure) {
brpc::ClosureGuard guard(closure);
AsyncDownloadContext *context = closure->GetDownloadContext();
memcpy(context->buf, cloneData, length);
}));
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.Times(2)
.WillRepeatedly(
DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
// 读chunk文件
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, offset, length)).Times(0);
// 更新 applied index
EXPECT_CALL(*node_, UpdateAppliedIndex(_)).Times(1);
// 产生PasteChunkRequest
braft::Task task;
butil::IOBuf iobuf;
task.data = &iobuf;
EXPECT_CALL(*node_, Propose(_)).WillOnce(SaveBraftTask<0>(&task));
ASSERT_EQ(0,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
closure->resContent_.status);
CheckTask(task, offset, length, cloneData);
// 正常propose后会将closure交给并发层处理
// 由于这里node是mock的因此需要主动来执行task.done.Run来释放资源
ASSERT_NE(nullptr, task.done);
task.done->Run();
ASSERT_EQ(
memcmp(
cloneData,
closure->resContent_.attachment.to_string().c_str(), // NOLINT
length),
0);
delete[] cloneData;
}
// case3
{
info.bitmap->Clear();
info.bitmap->Set(0, 2);
// 每次调HandleReadRequest后会被closure释放
std::shared_ptr<ReadChunkRequest> readRequest =
GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_READ, offset, length);
char *cloneData = new char[length];
memset(cloneData, 'b', length);
EXPECT_CALL(*copyer_, DownloadAsync(_))
.WillOnce(Invoke([&](DownloadClosure *closure) {
brpc::ClosureGuard guard(closure);
AsyncDownloadContext *context = closure->GetDownloadContext();
memcpy(context->buf, cloneData, length);
}));
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.Times(2)
.WillRepeatedly(
DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
// 读chunk文件
char chunkData[pagesize_ + 2 * blocksize_]; // NOLINT(runtime/arrays)
memset(chunkData, 'a', pagesize_ + 2 * blocksize_);
EXPECT_CALL(*datastore_,
ReadChunk(_, _, _, 0, pagesize_ + 2 * blocksize_))
.WillOnce(
DoAll(SetArrayArgument<2>(
chunkData, chunkData + pagesize_ + 2 * blocksize_),
Return(CSErrorCode::Success)));
// 更新 applied index
EXPECT_CALL(*node_, UpdateAppliedIndex(_)).Times(1);
// 产生PasteChunkRequest
braft::Task task;
butil::IOBuf iobuf;
task.data = &iobuf;
EXPECT_CALL(*node_, Propose(_)).WillOnce(SaveBraftTask<0>(&task));
ASSERT_EQ(0,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
closure->resContent_.status);
CheckTask(task, offset, length, cloneData);
// 正常propose后会将closure交给并发层处理
// 由于这里node是mock的因此需要主动来执行task.done.Run来释放资源
ASSERT_NE(nullptr, task.done);
task.done->Run();
ASSERT_EQ(memcmp(chunkData,
closure->resContent_.attachment.to_string().c_str(), //NOLINT
3 * blocksize_), 0);
ASSERT_EQ(memcmp(cloneData,
closure->resContent_.attachment.to_string().c_str() + 3 * blocksize_, //NOLINT
2 * blocksize_), 0);
}
// case4
{
static unsigned int seed = time(nullptr);
offset = blocksize_ + (rand_r(&seed) & 1 ? 1 : -1);
length = 4 * blocksize_;
info.bitmap->Clear();
info.bitmap->Set(0, 2);
// 每次调HandleReadRequest后会被closure释放
std::shared_ptr<ReadChunkRequest> readRequest =
GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_READ, offset, length);
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.WillOnce(
DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
EXPECT_CALL(*copyer_, DownloadAsync(_)).Times(0);
// 读chunk文件
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, _, _)).Times(0);
// 更新 applied index
EXPECT_CALL(*node_, UpdateAppliedIndex(_)).Times(0);
// 不产生PasteChunkRequest
braft::Task task;
EXPECT_CALL(*node_, Propose(_)).Times(0);
ASSERT_EQ(-1,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST,
closure->resContent_.status);
}
}
/**
* 测试CHUNK_OP_READ类型请求,请求读取的chunk不存在但是请求中包含源端数据地址
* 预期结果从源端下载数据产生paste请求
*/
TEST_P(CloneCoreTest, ReadChunkTest3) {
off_t offset = 0;
size_t length = 5 * blocksize_;
CSChunkInfo info;
info.isClone = true;
info.metaPageSize = pagesize_;
info.chunkSize = chunksize_;
info.blockSize = blocksize_;
info.bitmap = std::make_shared<Bitmap>(chunksize_ / pagesize_);
std::shared_ptr<CloneCore> core
= std::make_shared<CloneCore>(SLICE_SIZE, true, copyer_);
// case1
{
info.bitmap->Clear();
// 每次调HandleReadRequest后会被closure释放
std::shared_ptr<ReadChunkRequest> readRequest =
GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_READ, offset, length);
SetCloneParam(readRequest);
char *cloneData = new char[length];
memset(cloneData, 'b', length);
EXPECT_CALL(*copyer_, DownloadAsync(_))
.WillOnce(Invoke([&](DownloadClosure *closure) {
brpc::ClosureGuard guard(closure);
AsyncDownloadContext *context = closure->GetDownloadContext();
memcpy(context->buf, cloneData, length);
}));
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.Times(2)
.WillRepeatedly(Return(CSErrorCode::ChunkNotExistError));
// 读chunk文件
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, offset, length)).Times(0);
// 更新 applied index
EXPECT_CALL(*node_, UpdateAppliedIndex(_)).Times(1);
// 产生PasteChunkRequest
braft::Task task;
butil::IOBuf iobuf;
task.data = &iobuf;
EXPECT_CALL(*node_, Propose(_)).WillOnce(SaveBraftTask<0>(&task));
ASSERT_EQ(0,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
closure->resContent_.status);
CheckTask(task, offset, length, cloneData);
// 正常propose后会将closure交给并发层处理
// 由于这里node是mock的因此需要主动来执行task.done.Run来释放资源
ASSERT_NE(nullptr, task.done);
task.done->Run();
ASSERT_EQ(
memcmp(
cloneData,
closure->resContent_.attachment.to_string().c_str(), // NOLINT
length),
0);
delete[] cloneData;
}
}
/**
* 执行HandleReadRequest过程中出现错误
* case1:GetChunkInfo时出错
* result1:返回-1response状态改为CHUNK_OP_STATUS_FAILURE_UNKNOWN
* case2:Download时出错
* result2:返回-1response状态改为CHUNK_OP_STATUS_FAILURE_UNKNOWN
* case3:ReadChunk时出错
* result3:返回-1response状态改为CHUNK_OP_STATUS_FAILURE_UNKNOWN
*/
TEST_P(CloneCoreTest, ReadChunkErrorTest) {
off_t offset = 0;
size_t length = 5 * blocksize_;
CSChunkInfo info;
info.isClone = true;
info.metaPageSize = pagesize_;
info.chunkSize = chunksize_;
info.blockSize = blocksize_;
info.bitmap = std::make_shared<Bitmap>(chunksize_ / blocksize_);
info.bitmap->Clear();
info.bitmap->Set(0, 2);
std::shared_ptr<CloneCore> core =
std::make_shared<CloneCore>(SLICE_SIZE, true, copyer_);
// case1
{
std::shared_ptr<ReadChunkRequest> readRequest =
GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_READ, offset, length);
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.WillOnce(Return(CSErrorCode::InternalError));
EXPECT_CALL(*copyer_, DownloadAsync(_)).Times(0);
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, _, _)).Times(0);
EXPECT_CALL(*node_, Propose(_)).Times(0);
ASSERT_EQ(-1,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN,
closure->resContent_.status);
}
// case2
{
std::shared_ptr<ReadChunkRequest> readRequest =
GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_READ, offset, length);
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.WillOnce(
DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
char *cloneData = new char[length];
memset(cloneData, 'b', length);
EXPECT_CALL(*copyer_, DownloadAsync(_))
.WillOnce(Invoke([&](DownloadClosure *closure) {
brpc::ClosureGuard guard(closure);
closure->SetFailed();
}));
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, _, _)).Times(0);
ASSERT_EQ(0,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN,
closure->resContent_.status);
delete[] cloneData;
}
// case3
{
std::shared_ptr<ReadChunkRequest> readRequest =
GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_READ, offset, length);
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.Times(2)
.WillRepeatedly(
DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
char *cloneData = new char[length];
memset(cloneData, 'b', length);
EXPECT_CALL(*copyer_, DownloadAsync(_))
.WillOnce(Invoke([&](DownloadClosure *closure) {
brpc::ClosureGuard guard(closure);
AsyncDownloadContext *context = closure->GetDownloadContext();
memcpy(context->buf, cloneData, length);
}));
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, _, _))
.WillOnce(Return(CSErrorCode::InternalError));
// 产生PasteChunkRequest
braft::Task task;
butil::IOBuf iobuf;
task.data = &iobuf;
EXPECT_CALL(*node_, Propose(_)).WillOnce(SaveBraftTask<0>(&task));
ASSERT_EQ(0,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN,
closure->resContent_.status);
CheckTask(task, offset, length, cloneData);
// 正常propose后会将closure交给并发层处理
// 由于这里node是mock的因此需要主动来执行task.done.Run来释放资源
ASSERT_NE(nullptr, task.done);
task.done->Run();
delete[] cloneData;
}
}
/**
* 测试CHUNK_OP_RECOVER类型请求,请求的chunk不是clone chunk
* result:不会从远端拷贝数据,也不会从本地读取数据,直接返回成功
*/
TEST_P(CloneCoreTest, RecoverChunkTest1) {
off_t offset = 0;
size_t length = 5 * pagesize_;
std::shared_ptr<CloneCore> core
= std::make_shared<CloneCore>(SLICE_SIZE, true, copyer_);
std::shared_ptr<ReadChunkRequest> readRequest
= GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_RECOVER, offset, length);
// 不会从源端拷贝数据
EXPECT_CALL(*copyer_, DownloadAsync(_)).Times(0);
// 获取chunk信息
CSChunkInfo info;
info.isClone = false;
info.metaPageSize = pagesize_;
info.chunkSize = chunksize_;
info.blockSize = blocksize_;
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.WillOnce(DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
// 读chunk文件
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, _, _)).Times(0);
// 不会产生PasteChunkRequest
EXPECT_CALL(*node_, Propose(_)).Times(0);
ASSERT_EQ(0, core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
closure->resContent_.status);
}
/**
* 测试CHUNK_OP_RECOVER类型请求,请求的chunk是clone chunk
* case1:请求恢复的区域全部被写过
* result1:不会拷贝数据,直接返回成功
* case2:请求恢复的区域全部或部分未被写过
* result2:从远端拷贝数据并产生paste请求
*/
TEST_P(CloneCoreTest, RecoverChunkTest2) {
off_t offset = 0;
size_t length = 5 * blocksize_;
CSChunkInfo info;
info.isClone = true;
info.metaPageSize = pagesize_;
info.chunkSize = chunksize_;
info.blockSize = blocksize_;
info.bitmap = std::make_shared<Bitmap>(chunksize_ / blocksize_);
std::shared_ptr<CloneCore> core
= std::make_shared<CloneCore>(SLICE_SIZE, true, copyer_);
// case1
{
info.bitmap->Set();
// 每次调HandleReadRequest后会被closure释放
std::shared_ptr<ReadChunkRequest> readRequest = GenerateReadRequest(
CHUNK_OP_TYPE::CHUNK_OP_RECOVER, offset, length); // NOLINT
EXPECT_CALL(*copyer_, DownloadAsync(_)).Times(0);
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.WillOnce(
DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
// 不会读chunk文件
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, _, _)).Times(0);
// 不会产生PasteChunkRequest
EXPECT_CALL(*node_, Propose(_)).Times(0);
ASSERT_EQ(0,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
closure->resContent_.status);
}
// case2
{
info.bitmap->Clear();
// 每次调HandleReadRequest后会被closure释放
std::shared_ptr<ReadChunkRequest> readRequest = GenerateReadRequest(
CHUNK_OP_TYPE::CHUNK_OP_RECOVER, offset, length); // NOLINT
char *cloneData = new char[length];
memset(cloneData, 'b', length);
EXPECT_CALL(*copyer_, DownloadAsync(_))
.WillOnce(Invoke([&](DownloadClosure *closure) {
brpc::ClosureGuard guard(closure);
AsyncDownloadContext *context = closure->GetDownloadContext();
memcpy(context->buf, cloneData, length);
}));
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.WillOnce(
DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
// 不会读chunk文件
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, offset, length)).Times(0);
// 产生PasteChunkRequest
braft::Task task;
butil::IOBuf iobuf;
task.data = &iobuf;
EXPECT_CALL(*node_, Propose(_)).WillOnce(SaveBraftTask<0>(&task));
ASSERT_EQ(0,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
// closure被转交给PasteRequest处理这里closure还未执行
ASSERT_FALSE(closure->isDone_);
CheckTask(task, offset, length, cloneData);
// 正常propose后会将closure交给并发层处理
// 由于这里node是mock的因此需要主动来执行task.done.Run来释放资源
ASSERT_NE(nullptr, task.done);
task.done->Run();
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(0, closure->resContent_.appliedindex);
ASSERT_EQ(0, closure->resContent_.status);
delete[] cloneData;
}
}
// case1: read chunk时从远端拷贝数据但是不会产生paste请求
// case2: recover chunk时从远端拷贝数据会产生paste请求
TEST_P(CloneCoreTest, DisablePasteTest) {
off_t offset = 0;
size_t length = 5 * blocksize_;
CSChunkInfo info;
info.isClone = true;
info.metaPageSize = pagesize_;
info.chunkSize = chunksize_;
info.blockSize = blocksize_;
info.bitmap = std::make_shared<Bitmap>(chunksize_ / blocksize_);
std::shared_ptr<CloneCore> core
= std::make_shared<CloneCore>(SLICE_SIZE, false, copyer_);
// case1
{
info.bitmap->Clear();
// 每次调HandleReadRequest后会被closure释放
std::shared_ptr<ReadChunkRequest> readRequest =
GenerateReadRequest(CHUNK_OP_TYPE::CHUNK_OP_READ, offset, length);
char *cloneData = new char[length];
memset(cloneData, 'b', length);
EXPECT_CALL(*copyer_, DownloadAsync(_))
.WillOnce(Invoke([&](DownloadClosure *closure) {
brpc::ClosureGuard guard(closure);
AsyncDownloadContext *context = closure->GetDownloadContext();
memcpy(context->buf, cloneData, length);
}));
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.Times(2)
.WillRepeatedly(
DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
// 读chunk文件
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, offset, length)).Times(0);
// 更新 applied index
EXPECT_CALL(*node_, UpdateAppliedIndex(_)).Times(1);
// 不会产生paste chunk请求
EXPECT_CALL(*node_, Propose(_)).Times(0);
ASSERT_EQ(0,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(LAST_INDEX, closure->resContent_.appliedindex);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
closure->resContent_.status);
delete[] cloneData;
}
// case2
{
info.bitmap->Clear();
// 每次调HandleReadRequest后会被closure释放
std::shared_ptr<ReadChunkRequest> readRequest = GenerateReadRequest(
CHUNK_OP_TYPE::CHUNK_OP_RECOVER, offset, length); // NOLINT
char *cloneData = new char[length];
memset(cloneData, 'b', length);
EXPECT_CALL(*copyer_, DownloadAsync(_))
.WillOnce(Invoke([&](DownloadClosure *closure) {
brpc::ClosureGuard guard(closure);
AsyncDownloadContext *context = closure->GetDownloadContext();
memcpy(context->buf, cloneData, length);
}));
EXPECT_CALL(*datastore_, GetChunkInfo(_, _))
.WillOnce(
DoAll(SetArgPointee<1>(info), Return(CSErrorCode::Success)));
// 不会读chunk文件
EXPECT_CALL(*datastore_, ReadChunk(_, _, _, offset, length)).Times(0);
// 产生PasteChunkRequest
braft::Task task;
butil::IOBuf iobuf;
task.data = &iobuf;
EXPECT_CALL(*node_, Propose(_)).WillOnce(SaveBraftTask<0>(&task));
ASSERT_EQ(0,
core->HandleReadRequest(readRequest, readRequest->Closure()));
FakeChunkClosure *closure =
reinterpret_cast<FakeChunkClosure *>(readRequest->Closure());
// closure被转交给PasteRequest处理这里closure还未执行
ASSERT_FALSE(closure->isDone_);
CheckTask(task, offset, length, cloneData);
// 正常propose后会将closure交给并发层处理
// 由于这里node是mock的因此需要主动来执行task.done.Run来释放资源
ASSERT_NE(nullptr, task.done);
task.done->Run();
ASSERT_TRUE(closure->isDone_);
ASSERT_EQ(0, closure->resContent_.appliedindex);
ASSERT_EQ(0, closure->resContent_.status);
delete[] cloneData;
}
}
INSTANTIATE_TEST_CASE_P(
CloneCoreTest,
CloneCoreTest,
::testing::Values(
// chunk size block size, metapagesize
std::make_tuple(16U * 1024 * 1024, 4096U, 4096U),
std::make_tuple(16U * 1024 * 1024, 4096U, 8192U),
std::make_tuple(16U * 1024 * 1024, 512U, 8192U),
std::make_tuple(16U * 1024 * 1024, 512U, 4096U * 4)));
} // namespace chunkserver
} // namespace curve