curve/test/client/copyset_client_test.cpp

4026 lines
162 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: 18-9-29
* Author: wudemiao
*/
#include <brpc/controller.h>
#include <brpc/channel.h>
#include <brpc/server.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <thread> //NOLINT
#include <chrono> // NOLINT
#include "src/client/copyset_client.h"
#include "test/client/mock/mock_meta_cache.h"
#include "src/common/concurrent/count_down_event.h"
#include "test/client/mock/mock_chunkservice.h"
#include "test/client/mock/mock_request_context.h"
#include "src/client/chunk_closure.h"
#include "src/common/timeutility.h"
#include "test/client/fake/fakeChunkserver.h"
#include "test/client/mock/mock_request_scheduler.h"
#include "src/client/request_closure.h"
#include "src/client/metacache.h"
namespace curve {
namespace client {
using curve::chunkserver::CHUNK_OP_STATUS;
using curve::chunkserver::ChunkRequest;
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::AnyNumber;
using ::testing::DoAll;
using ::testing::SetArgPointee;
using ::testing::SetArgReferee;
using ::testing::InSequence;
using ::testing::AtLeast;
using ::testing::SaveArgPointee;
using curve::client::MetaCache;
using curve::common::TimeUtility;
class CopysetClientTest : public testing::Test {
protected:
virtual void SetUp() {
listenAddr_ = "127.0.0.1:9109";
server_ = new brpc::Server();
}
virtual void TearDown() {
server_->Stop(0);
server_->Join();
delete server_;
server_ = nullptr;
}
public:
std::string listenAddr_;
brpc::Server *server_;
};
/* TODO(wudemiao) 当前 controller 错误不能通过 mock 返回 */
int gWriteCntlFailedCode = 0;
int gReadCntlFailedCode = 0;
static void WriteChunkFunc(::google::protobuf::RpcController *controller,
const ::curve::chunkserver::ChunkRequest *request,
::curve::chunkserver::ChunkResponse *response,
google::protobuf::Closure *done) {
/* return response */
brpc::ClosureGuard doneGuard(done);
if (0 != gWriteCntlFailedCode) {
if (gWriteCntlFailedCode == brpc::ERPCTIMEDOUT) {
std::this_thread::sleep_for(std::chrono::milliseconds(3500));
}
brpc::Controller *cntl = dynamic_cast<brpc::Controller *>(controller);
cntl->SetFailed(gWriteCntlFailedCode, "write controller error");
}
}
static void ReadChunkFunc(::google::protobuf::RpcController *controller,
const ::curve::chunkserver::ChunkRequest *request,
::curve::chunkserver::ChunkResponse *response,
google::protobuf::Closure *done) {
brpc::ClosureGuard doneGuard(done);
if (0 != gReadCntlFailedCode) {
if (gReadCntlFailedCode == brpc::ERPCTIMEDOUT) {
std::this_thread::sleep_for(std::chrono::milliseconds(4000));
}
brpc::Controller *cntl = dynamic_cast<brpc::Controller *>(controller);
cntl->SetFailed(gReadCntlFailedCode, "read controller error");
}
}
static void ReadChunkSnapshotFunc(::google::protobuf::RpcController *controller,
const ::curve::chunkserver::ChunkRequest *request, //NOLINT
::curve::chunkserver::ChunkResponse *response, //NOLINT
google::protobuf::Closure *done) {
brpc::ClosureGuard doneGuard(done);
if (0 != gReadCntlFailedCode) {
brpc::Controller *cntl = dynamic_cast<brpc::Controller *>(controller);
cntl->SetFailed(-1, "read snapshot controller error");
}
}
static void DeleteChunkSnapshotFunc(::google::protobuf::RpcController *controller, //NOLINT
const ::curve::chunkserver::ChunkRequest *request, //NOLINT
::curve::chunkserver::ChunkResponse *response,
google::protobuf::Closure *done) {
brpc::ClosureGuard doneGuard(done);
if (0 != gReadCntlFailedCode) {
brpc::Controller *cntl = dynamic_cast<brpc::Controller *>(controller);
cntl->SetFailed(-1, "delete snapshot controller error");
}
}
static void CreateCloneChunkFunc(
::google::protobuf::RpcController* controller,
const ::curve::chunkserver::ChunkRequest* request,
::curve::chunkserver::ChunkResponse* response,
google::protobuf::Closure* done) {
brpc::ClosureGuard doneGuard(done);
if (0 != gReadCntlFailedCode) {
brpc::Controller* cntl = dynamic_cast<brpc::Controller*>(controller);
cntl->SetFailed(-1, "create clone chunk controller error");
}
}
static void RecoverChunkFunc(::google::protobuf::RpcController *controller, //NOLINT
const ::curve::chunkserver::ChunkRequest *request, //NOLINT
::curve::chunkserver::ChunkResponse *response,
google::protobuf::Closure *done) {
brpc::ClosureGuard doneGuard(done);
if (0 != gReadCntlFailedCode) {
brpc::Controller *cntl = dynamic_cast<brpc::Controller *>(controller);
cntl->SetFailed(-1, "recover chunk controller error");
}
}
static void GetChunkInfoFunc(::google::protobuf::RpcController *controller,
const ::curve::chunkserver::GetChunkInfoRequest *request, //NOLINT
::curve::chunkserver::GetChunkInfoResponse *response, //NOLINT
google::protobuf::Closure *done) {
brpc::ClosureGuard doneGuard(done);
if (0 != gReadCntlFailedCode) {
brpc::Controller *cntl = dynamic_cast<brpc::Controller *>(controller);
cntl->SetFailed(-1, "get chunk info controller error");
}
}
TEST_F(CopysetClientTest, normal_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(server_->AddService(&mockChunkService,
brpc::SERVER_DOESNT_OWN_SERVICE), 0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 5000;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 3;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS = 500;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler, nullptr);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
uint64_t fileId = 1;
uint64_t epoch = 1;
uint64_t sn = 1;
size_t len = 8;
char buff1[8 + 1];
char buff2[8 + 1];
memset(buff1, 'a', 8);
memset(buff2, 'a', 8);
buff1[8] = '\0';
buff2[8] = '\0';
off_t offset = 0;
butil::IOBuf iobuf;
iobuf.append(buff1, sizeof(buff1) - 1);
ChunkServerID leaderId = 10000;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
iot.PrepareReadIOBuffers(1);
// write success
for (int i = 0; i < 10; ++i) {
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = i * 8;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = offset;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = offset;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(Return(-1))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
// read success
for (int i = 0; i < 10; ++i) {
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = i * 8;
reqCtx->rawlength_ = len;
reqCtx->subIoIndex_ = 0;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = offset;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = offset;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(Return(-1))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
}
/**
* write error testing
*/
TEST_F(CopysetClientTest, write_error_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(server_->AddService(&mockChunkService,
brpc::SERVER_DOESNT_OWN_SERVICE), 0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 1000;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 3;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS = 5000;
ioSenderOpt.failRequestOpt.chunkserverMaxRPCTimeoutMS = 3500;
ioSenderOpt.failRequestOpt.chunkserverMaxRetrySleepIntervalUS = 3500000;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
RequestScheduleOption reqopt;
reqopt.ioSenderOpt = ioSenderOpt;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
scheduler.Init(reqopt, &mockMetaCache);
scheduler.Run();
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler, nullptr);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
uint64_t fileId = 1;
uint64_t epoch = 1;
size_t len = 8;
char buff1[8 + 1];
char buff2[8 + 1];
memset(buff1, 'a', 8);
memset(buff2, 'a', 8);
buff1[8] = '\0';
buff2[8] = '\0';
off_t offset = 0;
butil::IOBuf iobuf;
iobuf.append(buff1, sizeof(buff1) - 1);
ChunkServerID leaderId = 10000;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
/* 非法参数 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST,
reqDone->GetErrorCode());
}
/* controller error */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
// 配置文件设置的重试睡眠时间为5000因为没有触发底层指数退避所以重试之间不会睡眠
uint64_t start = TimeUtility::GetTimeofDayUs();
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
gWriteCntlFailedCode = -1;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(3)
.WillRepeatedly(Invoke(WriteChunkFunc));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
uint64_t end = TimeUtility::GetTimeofDayUs();
ASSERT_GT(end - start, 10000);
gWriteCntlFailedCode = 0;
}
/* controller set timeout */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
// 配置文件设置的重试超时时间为5000因为chunkserver设置返回timeout
// 导致触发底层超时时间指数退避每次重试间隔增大。重试三次正常只需要睡眠3*1000
// 但是增加指数退避之后超时时间将增加到1000 + 2000 + 2000 = 5000
// 加上随机因子三次重试时间应该大于7000, 且小于8000
uint64_t start = TimeUtility::GetTimeofDayMs();
reqCtx->done_ = reqDone;
gWriteCntlFailedCode = brpc::ERPCTIMEDOUT;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(3))
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(3)
.WillRepeatedly(Invoke(WriteChunkFunc));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
uint64_t end = TimeUtility::GetTimeofDayMs();
ASSERT_GT(end - start, 3000);
ASSERT_LT(end - start, 6000);
std::this_thread::sleep_for(std::chrono::seconds(8));
gWriteCntlFailedCode = 0;
}
/* controller set timeout */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
// 配置文件设置的重试睡眠时间为5000因为chunkserver设置返回timeout
// 导致触发底层指数退避每次重试间隔增大。重试三次正常只需要睡眠3*5000
// 但是增加指数退避之后睡眠间隔将增加到10000 + 20000 = 30000
// 加上随机因子三次重试时间应该大于29000, 且小于50000
uint64_t start = TimeUtility::GetTimeofDayUs();
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_OVERLOAD);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_OVERLOAD,
reqDone->GetErrorCode());
uint64_t end = TimeUtility::GetTimeofDayUs();
ASSERT_GT(end - start, 28000);
ASSERT_LT(end - start, 2 * 50000);
gWriteCntlFailedCode = 0;
}
/* 其他错误 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN,
reqDone->GetErrorCode());
}
/* 不是 leader返回正确的 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(1)
.WillOnce(Return(0));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(WriteChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
ASSERT_EQ(1, fm.writeRPC.redirectQps.count.get_value());
}
/* 不是 leader没有返回 leader刷新 meta cache 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
// response1.set_redirect(leaderStr2);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(WriteChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader没有返回 leader刷新 meta cache 失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
// response1.set_redirect(leaderStr2);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(WriteChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但返回的是错误 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
FileMetric fm("test");
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _))
.Times(3)
.WillRepeatedly(Return(0));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(WriteChunkFunc)));
auto startTimeUs = curve::common::TimeUtility::GetTimeofDayUs();
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
auto elpased = curve::common::TimeUtility::GetTimeofDayUs()
- startTimeUs;
// chunkserverOPRetryIntervalUS = 5000
// 每次redirect睡眠500us共重试2次(chunkserverOPMaxRetry=3判断时大于等于就返回所以共只重试了两次)
// 所以总共耗费时间大于1000us
ASSERT_GE(elpased, 1000);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED,
reqDone->GetErrorCode());
ASSERT_EQ(3, fm.writeRPC.redirectQps.count.get_value());
}
/* copyset 不存在,更新 leader 依然失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
response2.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(WriteChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
// epoch too old
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_EPOCH_TOO_OLD);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(1)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_EPOCH_TOO_OLD,
reqDone->GetErrorCode());
}
scheduler.Fini();
}
/**
* write failed testing
*/
TEST_F(CopysetClientTest, write_failed_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(server_->AddService(&mockChunkService,
brpc::SERVER_DOESNT_OWN_SERVICE), 0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 500;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 50;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS = 5000;
ioSenderOpt.failRequestOpt.chunkserverMaxRPCTimeoutMS = 1000;
ioSenderOpt.failRequestOpt.chunkserverMaxRetrySleepIntervalUS = 100000;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
RequestScheduleOption reqopt;
reqopt.ioSenderOpt = ioSenderOpt;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
scheduler.Init(reqopt, &mockMetaCache);
scheduler.Run();
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler, nullptr);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
uint64_t fileId = 1;
uint64_t epoch = 1;
size_t len = 8;
char buff1[8 + 1];
char buff2[8 + 1];
memset(buff1, 'a', 8);
memset(buff2, 'a', 8);
buff1[8] = '\0';
buff2[8] = '\0';
off_t offset = 0;
butil::IOBuf iobuf;
iobuf.append(buff1, sizeof(buff1) - 1);
ChunkServerID leaderId = 10000;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
/* controller set timeout */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
// 配置文件设置的重试超时时间为500因为chunkserver设置返回timeout
// 导致触发底层超时时间指数退避每次重试间隔增大。重试50次正常只需要超时49*500
// 但是增加指数退避之后超时时间将增加到49*1000 = 49000
uint64_t start = TimeUtility::GetTimeofDayMs();
reqCtx->done_ = reqDone;
gWriteCntlFailedCode = brpc::ERPCTIMEDOUT;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(50))
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(50)
.WillRepeatedly(Invoke(WriteChunkFunc));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
uint64_t end = TimeUtility::GetTimeofDayMs();
ASSERT_GT(end - start, 25000);
ASSERT_LT(end - start, 55000);
std::this_thread::sleep_for(std::chrono::seconds(8));
gWriteCntlFailedCode = 0;
}
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
// 配置文件设置的重试睡眠时间为5000us因为chunkserver设置返回timeout
// 导致触发底层指数退避每次重试间隔增大。重试50次正常只需要睡眠49*5000us
// 但是增加指数退避之后,睡眠间隔将增加到
// 10000 + 20000 + 40000... ~= 4650000
uint64_t start = TimeUtility::GetTimeofDayUs();
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_OVERLOAD);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(50).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _)).Times(50)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(WriteChunkFunc)));
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_OVERLOAD,
reqDone->GetErrorCode());
uint64_t end = TimeUtility::GetTimeofDayUs();
ASSERT_GT(end - start, 250000);
ASSERT_LT(end - start, 4650000);
gWriteCntlFailedCode = 0;
}
scheduler.Fini();
}
/**
* read failed testing
*/
TEST_F(CopysetClientTest, read_failed_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(server_->AddService(&mockChunkService,
brpc::SERVER_DOESNT_OWN_SERVICE), 0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 500;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 50;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS = 5000;
ioSenderOpt.failRequestOpt.chunkserverMaxRPCTimeoutMS = 1000;
ioSenderOpt.failRequestOpt.chunkserverMaxRetrySleepIntervalUS = 100000;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
RequestScheduleOption reqopt;
reqopt.ioSenderOpt = ioSenderOpt;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
scheduler.Init(reqopt, &mockMetaCache);
scheduler.Run();
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler, nullptr);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
uint64_t sn = 1;
size_t len = 8;
char buff1[8 + 1];
char buff2[8 + 1];
memset(buff1, 'a', 8);
memset(buff2, 'a', 8);
buff1[8] = '\0';
buff2[8] = '\0';
off_t offset = 0;
ChunkServerID leaderId = 10000;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
iot.PrepareReadIOBuffers(1);
/* controller set timeout */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
// 配置文件设置的重试超时时间为500因为chunkserver设置返回timeout
// 导致触发底层超时时间指数退避每次重试间隔增大。重试50次正常只需要50*500
// 但是增加指数退避之后超时时间将增加到500 + 1000 + 2000... ~= 60000
uint64_t start = TimeUtility::GetTimeofDayMs();
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
gReadCntlFailedCode = brpc::ERPCTIMEDOUT;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(50))
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(50)
.WillRepeatedly(Invoke(ReadChunkFunc));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
uint64_t end = TimeUtility::GetTimeofDayMs();
ASSERT_GT(end - start, 25000);
ASSERT_LT(end - start, 60000);
std::this_thread::sleep_for(std::chrono::seconds(8));
gReadCntlFailedCode = 0;
}
/* 设置 overload */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
// 配置文件设置的重试睡眠时间为5000us因为chunkserver设置返回timeout
// 导致触发底层指数退避每次重试间隔增大。重试50次正常只需要睡眠49*5000
// 但是增加指数退避之后,睡眠间隔将增加到
// 10000 + 20000 + 40000 ... = 4650000
// 加上随机因子三次重试时间应该大于2900, 且小于5000
uint64_t start = TimeUtility::GetTimeofDayUs();
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_OVERLOAD);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(50).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(50)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_OVERLOAD,
reqDone->GetErrorCode());
uint64_t end = TimeUtility::GetTimeofDayUs();
ASSERT_GT(end - start, 250000);
ASSERT_LT(end - start, 4650000);
}
scheduler.Fini();
}
/**
* read error testing
*/
TEST_F(CopysetClientTest, read_error_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(server_->AddService(&mockChunkService,
brpc::SERVER_DOESNT_OWN_SERVICE), 0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 1000;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 3;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS = 500;
ioSenderOpt.failRequestOpt.chunkserverMaxRPCTimeoutMS = 3500;
ioSenderOpt.failRequestOpt.chunkserverMaxRetrySleepIntervalUS = 3500000;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
RequestScheduleOption reqopt;
reqopt.ioSenderOpt = ioSenderOpt;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
scheduler.Init(reqopt, &mockMetaCache);
scheduler.Run();
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
uint64_t sn = 1;
size_t len = 8;
char buff1[8 + 1];
char buff2[8 + 1];
memset(buff1, 'a', 8);
memset(buff2, 'a', 8);
buff1[8] = '\0';
buff2[8] = '\0';
off_t offset = 0;
ChunkServerID leaderId = 10000;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
iot.PrepareReadIOBuffers(1);
/* 非法参数 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST,
reqDone->GetErrorCode());
}
/* chunk not exist */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_CHUNK_NOTEXIST);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(0, reqDone->GetErrorCode());
}
/* controller error */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
// 配置文件设置的重试睡眠时间为5000因为没有触发底层指数退避所以重试之间不会睡眠
uint64_t start = TimeUtility::GetTimeofDayUs();
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
gReadCntlFailedCode = -1;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(3))
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(3)
.WillRepeatedly(Invoke(ReadChunkFunc));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
uint64_t end = TimeUtility::GetTimeofDayUs();
ASSERT_GT(end - start, 1000);
gReadCntlFailedCode = 0;
}
/* controller set timeout */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
// 配置文件设置的超时时间为1000因为chunkserver设置返回timeout
// 导致触发底层超时时间指数退避每次重试间隔增大。重试三次正常只需要睡眠3*1000
// 但是增加指数退避之后超时时间将增加到1000 + 2000 + 2000 = 5000
// 加上随机因子三次重试时间应该大于7000, 且小于8000
uint64_t start = TimeUtility::GetTimeofDayMs();
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
gReadCntlFailedCode = brpc::ERPCTIMEDOUT;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(3))
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(3)
.WillRepeatedly(Invoke(ReadChunkFunc));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
uint64_t end = TimeUtility::GetTimeofDayMs();
ASSERT_GT(end - start, 3000);
ASSERT_LT(end - start, 6000);
std::this_thread::sleep_for(std::chrono::seconds(8));
gReadCntlFailedCode = 0;
}
/* 设置 overload */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
// 配置文件设置的重试睡眠时间为500因为chunkserver设置返回timeout
// 导致触发底层指数退避每次重试间隔增大。重试三次正常只需要睡眠3*500
// 但是增加指数退避之后睡眠间隔将增加到1000 + 2000 = 3000
// 加上随机因子三次重试时间应该大于2900, 且小于5000
uint64_t start = TimeUtility::GetTimeofDayUs();
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_OVERLOAD);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_OVERLOAD,
reqDone->GetErrorCode());
uint64_t end = TimeUtility::GetTimeofDayUs();
ASSERT_GT(end - start, 2900);
ASSERT_LT(end - start, 3 * 5000);
}
/* 其他错误 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN,
reqDone->GetErrorCode());
}
/* 不是 leader返回正确的 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(1)
.WillOnce(Return(0));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(ReadChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
// response1.set_redirect(leaderStr2);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(ReadChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
// response1.set_redirect(leaderStr2);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(-1)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(ReadChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但返回的是错误 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(3)
.WillRepeatedly(Return(0));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 依然失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->subIoIndex_ = 0;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
response2.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(ReadChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(ReadChunkFunc)));
copysetClient.ReadChunk(reqCtx->idinfo_, sn,
offset, len, 0, {}, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
scheduler.Fini();
}
/**
* read snapshot error testing
*/
TEST_F(CopysetClientTest, read_snapshot_error_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(server_->AddService(&mockChunkService,
brpc::SERVER_DOESNT_OWN_SERVICE), 0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 5000;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 3;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS = 500;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
size_t len = 8;
int sn = 1;
char buff1[8 + 1];
char buff2[8 + 1];
memset(buff1, 'a', 8);
memset(buff2, 'a', 8);
buff1[8] = '\0';
buff2[8] = '\0';
off_t offset = 0;
ChunkServerID leaderId = 10000;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
/* 非法参数 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkSnapshotFunc)));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST,
reqDone->GetErrorCode());
}
/* chunk snapshot not exist */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_CHUNK_NOTEXIST);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkSnapshotFunc)));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_CHUNK_NOTEXIST,
reqDone->GetErrorCode());
}
/* controller error */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
gReadCntlFailedCode = -1;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(3)
.WillRepeatedly(Invoke(ReadChunkSnapshotFunc));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
gReadCntlFailedCode = 0;
}
/* 其他错误 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkSnapshotFunc)));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN,
reqDone->GetErrorCode());
}
/* 不是 leader返回正确的 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(1)
.WillOnce(Return(0));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(ReadChunkSnapshotFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(ReadChunkSnapshotFunc)));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(ReadChunkSnapshotFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(ReadChunkSnapshotFunc)));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(ReadChunkSnapshotFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(ReadChunkSnapshotFunc)));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但返回的是错误 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(3)
.WillRepeatedly(Return(0));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkSnapshotFunc)));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 依然失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkSnapshotFunc)));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
response2.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(ReadChunkSnapshotFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(ReadChunkSnapshotFunc)));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::READ_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(Return(-1))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, ReadChunkSnapshot(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(ReadChunkSnapshotFunc)));
copysetClient.ReadChunkSnapshot(reqCtx->idinfo_,
sn, offset, len, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
}
/**
* delete snapshot error testing
*/
TEST_F(CopysetClientTest, delete_snapshot_error_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(server_->AddService(&mockChunkService,
brpc::SERVER_DOESNT_OWN_SERVICE), 0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 5000;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 3;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS = 500;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
uint64_t sn = 1;
ChunkServerID leaderId = 10000;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
/* 非法参数 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, DeleteChunkSnapshotOrCorrectSn(_, _, _, _)) // NOLINT
.Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(DeleteChunkSnapshotFunc)));
copysetClient.DeleteChunkSnapshotOrCorrectSn(reqCtx->idinfo_,
sn, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST,
reqDone->GetErrorCode());
}
/* controller error */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
gReadCntlFailedCode = -1;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, DeleteChunkSnapshotOrCorrectSn(_, _, _, _)) // NOLINT
.Times(3)
.WillRepeatedly(Invoke(DeleteChunkSnapshotFunc));
copysetClient.DeleteChunkSnapshotOrCorrectSn(reqCtx->idinfo_,
sn, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
gReadCntlFailedCode = 0;
}
/* 其他错误 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, DeleteChunkSnapshotOrCorrectSn(_, _, _, _)) // NOLINT
.Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(DeleteChunkSnapshotFunc)));
copysetClient.DeleteChunkSnapshotOrCorrectSn(reqCtx->idinfo_,
sn, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN,
reqDone->GetErrorCode());
}
/* 不是 leader返回正确的 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(1)
.WillOnce(Return(0));
EXPECT_CALL(mockChunkService, DeleteChunkSnapshotOrCorrectSn(_, _, _, _)) // NOLINT
.Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(ReadChunkSnapshotFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(DeleteChunkSnapshotFunc)));
copysetClient.DeleteChunkSnapshotOrCorrectSn(reqCtx->idinfo_,
sn, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, DeleteChunkSnapshotOrCorrectSn(_, _, _, _)) // NOLINT
.Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(DeleteChunkSnapshotFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(DeleteChunkSnapshotFunc)));
copysetClient.DeleteChunkSnapshotOrCorrectSn(reqCtx->idinfo_,
sn, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(-1)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, DeleteChunkSnapshotOrCorrectSn(_, _, _, _)) // NOLINT
.Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(DeleteChunkSnapshotFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(DeleteChunkSnapshotFunc)));
copysetClient.DeleteChunkSnapshotOrCorrectSn(reqCtx->idinfo_,
sn, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但返回的是错误 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response.set_redirect(leaderStr);;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(3)
.WillRepeatedly(Return(0));
EXPECT_CALL(mockChunkService, DeleteChunkSnapshotOrCorrectSn(_, _, _, _)) // NOLINT
.Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(DeleteChunkSnapshotFunc)));
copysetClient.DeleteChunkSnapshotOrCorrectSn(reqCtx->idinfo_,
sn, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 依然失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, DeleteChunkSnapshotOrCorrectSn(_, _, _, _)) // NOLINT
.Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(DeleteChunkSnapshotFunc)));
copysetClient.DeleteChunkSnapshotOrCorrectSn(reqCtx->idinfo_,
sn, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
response2.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, DeleteChunkSnapshotOrCorrectSn(_, _, _, _)) // NOLINT
.Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(DeleteChunkSnapshotFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(DeleteChunkSnapshotFunc)));
copysetClient.DeleteChunkSnapshotOrCorrectSn(reqCtx->idinfo_,
sn, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(-1)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(-1)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, DeleteChunkSnapshotOrCorrectSn(_, _, _, _)) // NOLINT
.Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(DeleteChunkSnapshotFunc)));
copysetClient.DeleteChunkSnapshotOrCorrectSn(reqCtx->idinfo_,
sn, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
}
/**
* create clone chunk error testing
*/
TEST_F(CopysetClientTest, create_s3_clone_error_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(server_->AddService(&mockChunkService,
brpc::SERVER_DOESNT_OWN_SERVICE), 0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 5000;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 3;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS = 500;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
uint64_t sn = 1;
ChunkServerID leaderId = 10000;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
/* 非法参数 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::CREATE_CLONE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->seq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, CreateCloneChunk(_, _, _, _)).Times(1) // NOLINT
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(CreateCloneChunkFunc)));
copysetClient.CreateCloneChunk(reqCtx->idinfo_,
"destination", sn, 1, 1024, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST,
reqDone->GetErrorCode());
}
/* controller error */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
gReadCntlFailedCode = -1;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, CreateCloneChunk(_, _, _, _)).Times(3) // NOLINT
.WillRepeatedly(Invoke(CreateCloneChunkFunc));
copysetClient.CreateCloneChunk(reqCtx->idinfo_,
"destination", sn, 1, 1024, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
gReadCntlFailedCode = 0;
}
// /* 其他错误 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, CreateCloneChunk(_, _, _, _)).Times(3) // NOLINT
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(CreateCloneChunkFunc)));
copysetClient.CreateCloneChunk(reqCtx->idinfo_,
"destination", sn, 1, 1024, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN,
reqDone->GetErrorCode());
}
/* op success */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, CreateCloneChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(CreateCloneChunkFunc)));
copysetClient.CreateCloneChunk(reqCtx->idinfo_,
"destination", sn, 1, 1024, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, CreateCloneChunk(_, _, _, _)).Times(2) // NOLINT
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(CreateCloneChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(CreateCloneChunkFunc)));
copysetClient.CreateCloneChunk(reqCtx->idinfo_,
"destination", sn, 1, 1024, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, CreateCloneChunk(_, _, _, _)).Times(2) // NOLINT
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(CreateCloneChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(CreateCloneChunkFunc)));
copysetClient.CreateCloneChunk(reqCtx->idinfo_,
"destination", sn, 1, 1024, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但返回的是错误 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(3)
.WillRepeatedly(Return(0));
EXPECT_CALL(mockChunkService, CreateCloneChunk(_, _, _, _)).Times(3) // NOLINT
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(CreateCloneChunkFunc)));
copysetClient.CreateCloneChunk(reqCtx->idinfo_,
"destination", sn, 1, 1024, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 依然失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, CreateCloneChunk(_, _, _, _)).Times(3) // NOLINT
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(CreateCloneChunkFunc)));
copysetClient.CreateCloneChunk(reqCtx->idinfo_,
"destination", sn, 1, 1024, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
response2.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, CreateCloneChunk(_, _, _, _)).Times(2) // NOLINT
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(CreateCloneChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(CreateCloneChunkFunc)));
copysetClient.CreateCloneChunk(reqCtx->idinfo_,
"destination", sn, 1, 1024, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(Return(-1))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, CreateCloneChunk(_, _, _, _)).Times(1) // NOLINT
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(CreateCloneChunkFunc)));
copysetClient.CreateCloneChunk(reqCtx->idinfo_,
"destination", sn, 1, 1024, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
}
/**
* recover chunk error testing
*/
TEST_F(CopysetClientTest, recover_chunk_error_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(server_->AddService(&mockChunkService,
brpc::SERVER_DOESNT_OWN_SERVICE), 0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 5000;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 3;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS = 500;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
uint64_t sn = 1;
ChunkServerID leaderId = 10000;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
/* 非法参数 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, RecoverChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(RecoverChunkFunc)));
copysetClient.RecoverChunk(reqCtx->idinfo_, 0, 4096, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST,
reqDone->GetErrorCode());
}
/* controller error */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
gReadCntlFailedCode = -1;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, RecoverChunk(_, _, _, _)).Times(3)
.WillRepeatedly(Invoke(RecoverChunkFunc));
copysetClient.RecoverChunk(reqCtx->idinfo_, 0, 4096, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
gReadCntlFailedCode = 0;
}
/* 其他错误 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, RecoverChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(RecoverChunkFunc)));
copysetClient.RecoverChunk(reqCtx->idinfo_, 0, 4096, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN,
reqDone->GetErrorCode());
}
/* 不是 leader返回正确的 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, RecoverChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(RecoverChunkFunc)));
copysetClient.RecoverChunk(reqCtx->idinfo_, 0, 4096, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, RecoverChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(RecoverChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(RecoverChunkFunc)));
copysetClient.RecoverChunk(reqCtx->idinfo_, 0, 4096, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, RecoverChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(RecoverChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(RecoverChunkFunc)));
copysetClient.RecoverChunk(reqCtx->idinfo_, 0, 4096, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但返回的是错误 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(3)
.WillRepeatedly(Return(0));
EXPECT_CALL(mockChunkService, RecoverChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(RecoverChunkFunc)));
copysetClient.RecoverChunk(reqCtx->idinfo_, 0, 4096, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 依然失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, RecoverChunk(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(RecoverChunkFunc)));
copysetClient.RecoverChunk(reqCtx->idinfo_, 0, 4096, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
response2.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, RecoverChunk(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(RecoverChunkFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(RecoverChunkFunc)));
copysetClient.RecoverChunk(reqCtx->idinfo_, 0, 4096, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::DELETE_SNAP;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
reqCtx->correctedSeq_ = sn;
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(Return(-1))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, RecoverChunk(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(RecoverChunkFunc)));
copysetClient.RecoverChunk(reqCtx->idinfo_,
0, 4096, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
}
/**
* get chunk info error testing
*/
TEST_F(CopysetClientTest, get_chunk_info_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(server_->AddService(&mockChunkService,
brpc::SERVER_DOESNT_OWN_SERVICE), 0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 5000;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 3;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS = 500;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
ChunkServerID leaderId = 10000;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
/* 非法参数 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::GET_CHUNK_INFO;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
GetChunkInfoResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, GetChunkInfo(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(GetChunkInfoFunc)));
copysetClient.GetChunkInfo(reqCtx->idinfo_, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_INVALID_REQUEST,
reqDone->GetErrorCode());
}
/* controller error */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::GET_CHUNK_INFO;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
gReadCntlFailedCode = -1;
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, GetChunkInfo(_, _, _, _)).Times(3)
.WillRepeatedly(Invoke(GetChunkInfoFunc));
copysetClient.GetChunkInfo(reqCtx->idinfo_, reqDone);
cond.Wait();
ASSERT_NE(0, reqDone->GetErrorCode());
gReadCntlFailedCode = 0;
}
/* 其他错误 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::GET_CHUNK_INFO;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
GetChunkInfoResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3).WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, GetChunkInfo(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(GetChunkInfoFunc)));
copysetClient.GetChunkInfo(reqCtx->idinfo_, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_FAILURE_UNKNOWN,
reqDone->GetErrorCode());
}
/* 不是 leader返回正确的 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::GET_CHUNK_INFO;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
GetChunkInfoResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response1.set_redirect(leaderStr);
GetChunkInfoResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(1)
.WillOnce(Return(0));
EXPECT_CALL(mockChunkService, GetChunkInfo(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(GetChunkInfoFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(GetChunkInfoFunc)));
copysetClient.GetChunkInfo(reqCtx->idinfo_, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::GET_CHUNK_INFO;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
GetChunkInfoResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
GetChunkInfoResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, GetChunkInfo(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(GetChunkInfoFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(GetChunkInfoFunc)));
copysetClient.GetChunkInfo(reqCtx->idinfo_, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但是没有返回 leader刷新 meta cache 失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::GET_CHUNK_INFO;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
GetChunkInfoResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
GetChunkInfoResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, GetChunkInfo(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(GetChunkInfoFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(GetChunkInfoFunc)));
copysetClient.GetChunkInfo(reqCtx->idinfo_, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
/* 不是 leader但返回的是错误 leader */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::GET_CHUNK_INFO;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
GetChunkInfoResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(3)
.WillRepeatedly(Return(0));
EXPECT_CALL(mockChunkService, GetChunkInfo(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(GetChunkInfoFunc)));
copysetClient.GetChunkInfo(reqCtx->idinfo_, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 依然失败 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::GET_CHUNK_INFO;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
GetChunkInfoResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(6)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, GetChunkInfo(_, _, _, _)).Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(response),
Invoke(GetChunkInfoFunc)));
copysetClient.GetChunkInfo(reqCtx->idinfo_, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST,
reqDone->GetErrorCode());
}
/* copyset 不存在,更新 leader 成功 */
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::GET_CHUNK_INFO;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
GetChunkInfoResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_COPYSET_NOTEXIST);
response1.set_redirect(leaderStr);
GetChunkInfoResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
response2.set_redirect(leaderStr);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, GetChunkInfo(_, _, _, _)).Times(2)
.WillOnce(DoAll(SetArgPointee<2>(response1),
Invoke(GetChunkInfoFunc)))
.WillOnce(DoAll(SetArgPointee<2>(response2),
Invoke(GetChunkInfoFunc)));
copysetClient.GetChunkInfo(reqCtx->idinfo_, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
RequestContext *reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::GET_CHUNK_INFO;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
curve::common::CountDownEvent cond(1);
RequestClosure *reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
GetChunkInfoResponse response;
response.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _)).
Times(AtLeast(1))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(-1)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(-1)))
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr),
Return(0)));
EXPECT_CALL(mockChunkService, GetChunkInfo(_, _, _, _)).Times(1)
.WillOnce(DoAll(SetArgPointee<2>(response),
Invoke(GetChunkInfoFunc)));
copysetClient.GetChunkInfo(reqCtx->idinfo_, reqDone);
cond.Wait();
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
}
namespace {
bool gWriteSuccessFlag = false;
void WriteCallBack(CurveAioContext* aioctx) {
gWriteSuccessFlag = true;
delete aioctx;
}
void PrepareOpenFile(FakeCurveFSService *service,
OpenFileResponse *openresp,
FakeReturn *fakeReturn) {
openresp->set_statuscode(curve::mds::StatusCode::kOK);
auto *session = openresp->mutable_protosession();
session->set_sessionid("xxx");
session->set_leasetime(10000);
session->set_createtime(10000);
session->set_sessionstatus(curve::mds::SessionStatus::kSessionOK);
auto *fileinfo = openresp->mutable_fileinfo();
fileinfo->set_id(1);
fileinfo->set_filename("filename");
fileinfo->set_parentid(0);
fileinfo->set_length(10ULL * 1024 * 1024 * 1024);
fileinfo->set_blocksize(4096);
*fakeReturn = FakeReturn(nullptr, static_cast<void *>(openresp));
service->SetOpenFile(fakeReturn);
}
} // namespace
TEST(ChunkServerBackwardTest, ChunkServerBackwardTest) {
const std::string endpoint = "127.0.0.1:9102";
ClientConfig cc;
const std::string& configPath = "./conf/client.conf";
cc.Init(configPath.c_str());
FileInstance fileinstance;
UserInfo userinfo;
userinfo.owner = "userinfo";
std::shared_ptr<MDSClient> mdsclient = std::make_shared<MDSClient>();
// set mds addr
auto mdsopts = cc.GetFileServiceOption().metaServerOpt;
mdsopts.rpcRetryOpt.addrs.clear();
mdsopts.rpcRetryOpt.addrs.push_back(endpoint);
ASSERT_EQ(LIBCURVE_ERROR::OK, mdsclient->Initialize(mdsopts));
ASSERT_TRUE(fileinstance.Initialize(
"/test", mdsclient, userinfo, OpenFlags{}, cc.GetFileServiceOption()));
// create fake chunkserver service
FakeChunkServerService fakechunkservice;
// 设置cli服务
CliServiceFake fakeCliservice;
FakeCurveFSService curvefsService;
OpenFileResponse openresp;
FakeReturn fakeReturn;
PrepareOpenFile(&curvefsService, &openresp, &fakeReturn);
brpc::Server server;
ASSERT_EQ(0, server.AddService(&fakechunkservice,
brpc::SERVER_DOESNT_OWN_SERVICE)) << "Fail to add fakechunkservice";
ASSERT_EQ(0, server.AddService(&fakeCliservice,
brpc::SERVER_DOESNT_OWN_SERVICE)) << "Fail to add fakecliservice";
ASSERT_EQ(
0, server.AddService(&curvefsService, brpc::SERVER_DOESNT_OWN_SERVICE))
<< "Fail to add curvefsService";
ASSERT_EQ(0, server.Start(endpoint.c_str(), nullptr))
<< "Fail to start server at " << endpoint;
// fill metacache
curve::client::MetaCache* mc =
fileinstance.GetIOManager4File()->GetMetaCache();
curve::client::ChunkIDInfo_t chunkinfo(1, 2, 3);
mc->UpdateChunkInfoByIndex(0, chunkinfo);
curve::client::CopysetInfo<ChunkServerID> cpinfo;
curve::client::EndPoint ep;
butil::str2endpoint("127.0.0.1", 9102, &ep);
braft::PeerId pd(ep);
curve::client::PeerAddr addr = curve::client::PeerAddr(ep);
curve::client::CopysetPeerInfo<ChunkServerID> peer(1, addr, addr);
cpinfo.csinfos_.push_back(peer);
mc->UpdateCopysetInfo(2, 3, cpinfo);
fakeCliservice.SetPeerID(pd);
curve::chunkserver::ChunkResponse response;
response.set_status(
curve::chunkserver::CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
response.set_appliedindex(0);
FakeReturn writeFakeRet(nullptr, static_cast<void*>(&response));
fakechunkservice.SetFakeWriteReturn(&writeFakeRet);
const int kNewFileSn = 100;
const int kOldFileSn = 30;
ASSERT_EQ(LIBCURVE_ERROR::OK, fileinstance.Open());
// 设置文件版本号
fileinstance.GetIOManager4File()->SetLatestFileSn(kNewFileSn);
// 发送写请求并等待sec秒后检查io是否返回
auto startWriteAndCheckResult = [&fileinstance](int sec)-> bool { // NOLINT
CurveAioContext* aioctx = new CurveAioContext();
char buffer[4096];
aioctx->buf = buffer;
aioctx->offset = 0;
aioctx->length = sizeof(buffer);
aioctx->op = LIBCURVE_OP::LIBCURVE_OP_WRITE;
aioctx->cb = WriteCallBack;
// 下发写请求
fileinstance.AioWrite(aioctx, UserDataType::RawBuffer);
std::this_thread::sleep_for(std::chrono::seconds(sec));
return gWriteSuccessFlag;
};
// 第一次写成功并更新chunkserver端的文件版本号
ASSERT_TRUE(startWriteAndCheckResult(3));
// 设置一个旧的版本号去写
fileinstance.GetIOManager4File()->SetLatestFileSn(kOldFileSn);
gWriteSuccessFlag = false;
// chunkserver返回backward重新获取版本号后还是旧的版本
// IO hang
ASSERT_FALSE(startWriteAndCheckResult(3));
// 更新版本号为正常状态
fileinstance.GetIOManager4File()->SetLatestFileSn(kNewFileSn);
std::this_thread::sleep_for(std::chrono::seconds(1));
// 上次写请求成功
ASSERT_EQ(true, gWriteSuccessFlag);
server.Stop(0);
server.Join();
}
TEST_F(CopysetClientTest, retry_rpc_sleep_test) {
MockChunkServiceImpl mockChunkService;
ASSERT_EQ(
server_->AddService(&mockChunkService, brpc::SERVER_DOESNT_OWN_SERVICE),
0);
ASSERT_EQ(server_->Start(listenAddr_.c_str(), nullptr), 0);
const uint64_t sleepUsBeforeRetry = 5 * 1000 * 1000;
IOSenderOption ioSenderOpt;
ioSenderOpt.failRequestOpt.chunkserverRPCTimeoutMS = 1000;
ioSenderOpt.failRequestOpt.chunkserverOPMaxRetry = 3;
ioSenderOpt.failRequestOpt.chunkserverOPRetryIntervalUS =
sleepUsBeforeRetry;
ioSenderOpt.failRequestOpt.chunkserverMaxRPCTimeoutMS = 3500;
ioSenderOpt.failRequestOpt.chunkserverMaxRetrySleepIntervalUS = 3500000;
ioSenderOpt.chunkserverEnableAppliedIndexRead = 1;
RequestScheduleOption reqopt;
reqopt.ioSenderOpt = ioSenderOpt;
CopysetClient copysetClient;
MockMetaCache mockMetaCache;
mockMetaCache.DelegateToFake();
RequestScheduler scheduler;
scheduler.Init(reqopt, &mockMetaCache);
scheduler.Run();
copysetClient.Init(&mockMetaCache, ioSenderOpt, &scheduler, nullptr);
LogicPoolID logicPoolId = 1;
CopysetID copysetId = 100001;
ChunkID chunkId = 1;
uint64_t fileId = 1;
uint64_t epoch = 1;
size_t len = 8;
char buff1[8] = {0};
butil::IOBuf iobuf;
iobuf.append(buff1, sizeof(len));
off_t offset = 0;
ChunkServerID leaderId = 10000;
ChunkServerID leaderId2 = 10001;
butil::EndPoint leaderAddr;
std::string leaderStr = "127.0.0.1:9109";
butil::str2endpoint(leaderStr.c_str(), &leaderAddr);
FileMetric fm("test");
IOTracker iot(nullptr, nullptr, nullptr, &fm);
{
// redirect情况下, chunkserver返回新的leader
// 重试之前不会睡眠
RequestContext* reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
// reqCtx->writeBuffer_ = buff1;
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure* reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr), Return(0)))
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId2),
SetArgPointee<3>(leaderAddr), Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _))
.Times(1)
.WillOnce(Return(0));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _))
.Times(2)
.WillOnce(
DoAll(SetArgPointee<2>(response1), Invoke(WriteChunkFunc)))
.WillOnce(
DoAll(SetArgPointee<2>(response2), Invoke(WriteChunkFunc)));
auto startUs = curve::common::TimeUtility::GetTimeofDayUs();
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {},
reqDone);
cond.Wait();
auto endUs = curve::common::TimeUtility::GetTimeofDayUs();
// 返回新的leader id所以重试之前不会进行睡眠
ASSERT_LE(endUs - startUs, sleepUsBeforeRetry / 10);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
// redirect情况下,chunkserver返回旧leader
// 重试之前会睡眠
RequestContext* reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
// reqCtx->writeBuffer_ = buff1;
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure* reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
response1.set_redirect(leaderStr);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr), Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _))
.Times(1)
.WillOnce(Return(0));
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _))
.Times(2)
.WillOnce(
DoAll(SetArgPointee<2>(response1), Invoke(WriteChunkFunc)))
.WillOnce(
DoAll(SetArgPointee<2>(response2), Invoke(WriteChunkFunc)));
auto startUs = curve::common::TimeUtility::GetTimeofDayUs();
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {},
reqDone);
cond.Wait();
auto endUs = curve::common::TimeUtility::GetTimeofDayUs();
// 返回同样的leader id重试之前会进行睡眠
ASSERT_GE(endUs - startUs, sleepUsBeforeRetry / 10);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
// redirect情况下,chunkserver未返回leader
// 主动refresh获取到新leader
RequestContext* reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
// reqCtx->writeBuffer_ = buff1;
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure* reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3)
.WillOnce(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr), Return(0)))
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId2),
SetArgPointee<3>(leaderAddr), Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(0);
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _))
.Times(2)
.WillOnce(
DoAll(SetArgPointee<2>(response1), Invoke(WriteChunkFunc)))
.WillOnce(
DoAll(SetArgPointee<2>(response2), Invoke(WriteChunkFunc)));
auto startUs = curve::common::TimeUtility::GetTimeofDayUs();
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {},
reqDone);
cond.Wait();
auto endUs = curve::common::TimeUtility::GetTimeofDayUs();
// 返回新的leader id所以重试之前不会进行睡眠
ASSERT_LE(endUs - startUs, sleepUsBeforeRetry / 10);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
{
// redirect情况下,chunkserver未返回leader
// 主动refresh获取到旧leader
RequestContext* reqCtx = new FakeRequestContext();
reqCtx->optype_ = OpType::WRITE;
reqCtx->idinfo_ = ChunkIDInfo(chunkId, logicPoolId, copysetId);
// reqCtx->writeBuffer_ = buff1;
reqCtx->writeData_ = iobuf;
reqCtx->offset_ = 0;
reqCtx->rawlength_ = len;
curve::common::CountDownEvent cond(1);
RequestClosure* reqDone = new FakeRequestClosure(&cond, reqCtx);
reqDone->SetFileMetric(&fm);
reqDone->SetIOTracker(&iot);
reqCtx->done_ = reqDone;
ChunkResponse response1;
response1.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_REDIRECTED);
ChunkResponse response2;
response2.set_status(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS);
EXPECT_CALL(mockMetaCache, GetLeader(_, _, _, _, _, _))
.Times(3)
.WillRepeatedly(DoAll(SetArgPointee<2>(leaderId),
SetArgPointee<3>(leaderAddr), Return(0)));
EXPECT_CALL(mockMetaCache, UpdateLeader(_, _, _)).Times(0);
EXPECT_CALL(mockChunkService, WriteChunk(_, _, _, _))
.Times(2)
.WillOnce(
DoAll(SetArgPointee<2>(response1), Invoke(WriteChunkFunc)))
.WillOnce(
DoAll(SetArgPointee<2>(response2), Invoke(WriteChunkFunc)));
auto startUs = curve::common::TimeUtility::GetTimeofDayUs();
copysetClient.WriteChunk(reqCtx->idinfo_, fileId, epoch, 0,
iobuf, offset, len, {},
reqDone);
cond.Wait();
auto endUs = curve::common::TimeUtility::GetTimeofDayUs();
ASSERT_GE(endUs - startUs, sleepUsBeforeRetry / 10);
ASSERT_EQ(CHUNK_OP_STATUS::CHUNK_OP_STATUS_SUCCESS,
reqDone->GetErrorCode());
}
scheduler.Fini();
}
class TestRunnedRequestClosure : public RequestClosure {
public:
TestRunnedRequestClosure() : RequestClosure(nullptr) {}
void Run() override {
runned_ = true;
}
bool IsRunned() const {
return runned_;
}
private:
bool runned_ = false;
};
// 测试session失效后重试请求会被重新放入请求队列
TEST(CopysetClientBasicTest, TestReScheduleWhenSessionNotValid) {
MockRequestScheduler requestScheduler;
CopysetClient copysetClient;
IOSenderOption ioSenderOption;
MetaCache metaCache;
ASSERT_EQ(0, copysetClient.Init(&metaCache, ioSenderOption,
&requestScheduler, nullptr));
// 设置session not valid
copysetClient.StartRecycleRetryRPC();
{
EXPECT_CALL(requestScheduler, ReSchedule(_))
.Times(1);
TestRunnedRequestClosure closure;
copysetClient.ReadChunk({}, 0, 0, 0, 0, {}, &closure);
ASSERT_FALSE(closure.IsRunned());
}
{
EXPECT_CALL(requestScheduler, ReSchedule(_))
.Times(1);
TestRunnedRequestClosure closure;
copysetClient.WriteChunk({}, 1, 1, 0, {}, 0, 0, {}, &closure);
ASSERT_FALSE(closure.IsRunned());
}
}
} // namespace client
} // namespace curve