[consensus] update UnsafeChangeConfig() signature While testing various scenarios involving kudu CLI's 'remote_replica unsafe_change_config' sub-command, it turned out that tserver might end up trying to respond with invalid value of TabletServerErrorPB::Code. In that case, a tserver built in DEBUG configuration would crash with the following stack trace:
../nptl/sysdeps/unix/sysv/linux/raise.c:64 assertion=0x7fb4eab7c4f8 "::kudu::tserver::TabletServerErrorPB_Code_IsValid(value)", file=0x7fb4eab7c531 "src/kudu/tserver/tserver.pb.h", line=<value optimized out>, function=<value optimized out>) at assert.c:96 assertion=0x7fb4eab7c4f8 "::kudu::tserver::TabletServerErrorPB_Code_IsValid(value)", file=0x7fb4eab7c531 "src/kudu/tserver/tserver.pb.h", line=2623, function=0x7fb4eab7c586 "void kudu::tserver::TabletServerErrorPB::set_code( ::kudu::tserver::TabletServerErrorPB_Code)") at assert.c:105 kudu::tserver::TabletServerErrorPB_Code) () at src/kudu/tserver/tserver.pb.h:2623 kudu::tserver::TabletServerErrorPB*, kudu::Status const&, kudu::tserver::TabletServerErrorPB_Code, kudu::rpc::RpcContext*) () at src/kudu/tserver/tablet_service.cc:370 kudu::consensus::UnsafeChangeConfigRequestPB const*, kudu::consensus::UnsafeChangeConfigResponsePB*, kudu::rpc::RpcContext*) () at src/kudu/tserver/tablet_service.cc:1060 This changelist fixes the issue, 'unifying' the signatures of the RaftConsensus::{ChangeConfig,BulkChangeConfig,UpdateChangeConfig}() methods. Change-Id: Id266dd72ef5e44e9b4e2ce9df10694bce8cfe2fb Reviewed-on: http://gerrit.cloudera.org:8080/10141 Tested-by: Alexey Serbin <aser...@cloudera.com> Reviewed-by: Mike Percy <mpe...@apache.org> Project: http://git-wip-us.apache.org/repos/asf/kudu/repo Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/f1e652b2 Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/f1e652b2 Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/f1e652b2 Branch: refs/heads/master Commit: f1e652b23c2bd76033868fe6a30484cb30098649 Parents: 009cfe2 Author: Alexey Serbin <aser...@cloudera.com> Authored: Fri Apr 20 18:37:25 2018 -0700 Committer: Alexey Serbin <aser...@cloudera.com> Committed: Fri Jun 22 18:05:06 2018 +0000 ---------------------------------------------------------------------- src/kudu/consensus/raft_consensus.cc | 22 ++++++++++------------ src/kudu/consensus/raft_consensus.h | 2 +- src/kudu/tserver/tablet_service.cc | 10 ++++++---- 3 files changed, 17 insertions(+), 17 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kudu/blob/f1e652b2/src/kudu/consensus/raft_consensus.cc ---------------------------------------------------------------------- diff --git a/src/kudu/consensus/raft_consensus.cc b/src/kudu/consensus/raft_consensus.cc index 68f1859..b4ef789 100644 --- a/src/kudu/consensus/raft_consensus.cc +++ b/src/kudu/consensus/raft_consensus.cc @@ -1886,8 +1886,9 @@ Status RaftConsensus::BulkChangeConfig(const BulkChangeConfigRequestPB& req, return Status::OK(); } -Status RaftConsensus::UnsafeChangeConfig(const UnsafeChangeConfigRequestPB& req, - TabletServerErrorPB::Code* error_code) { +Status RaftConsensus::UnsafeChangeConfig( + const UnsafeChangeConfigRequestPB& req, + boost::optional<tserver::TabletServerErrorPB::Code>* error_code) { if (PREDICT_FALSE(!req.has_new_config())) { *error_code = TabletServerErrorPB::INVALID_CONFIG; return Status::InvalidArgument("Request must contain 'new_config' argument " @@ -1960,6 +1961,7 @@ Status RaftConsensus::UnsafeChangeConfig(const UnsafeChangeConfigRequestPB& req, // in the committed config, it is rare and a replica without itself // in the latest config is definitely not caught up with the latest leader's log. if (!IsRaftConfigVoter(peer_uuid(), new_config)) { + *error_code = TabletServerErrorPB::INVALID_CONFIG; return Status::InvalidArgument(Substitute("Local replica uuid $0 is not " "a VOTER in the new config, " "rejecting the unsafe config " @@ -1985,13 +1987,12 @@ Status RaftConsensus::UnsafeChangeConfig(const UnsafeChangeConfigRequestPB& req, // Prepare the consensus request as if the request is being generated // from a different leader. ConsensusRequestPB consensus_req; - ConsensusResponsePB consensus_resp; consensus_req.set_caller_uuid(req.caller_id()); // Bumping up the term for the consensus request being generated. // This makes this request appear to come from a new leader that // the local replica doesn't know about yet. If the local replica // happens to be the leader, this will cause it to step down. - int64_t new_term = current_term + 1; + const int64_t new_term = current_term + 1; consensus_req.set_caller_term(new_term); consensus_req.mutable_preceding_id()->CopyFrom(preceding_opid); consensus_req.set_committed_index(last_committed_index); @@ -2018,14 +2019,11 @@ Status RaftConsensus::UnsafeChangeConfig(const UnsafeChangeConfigRequestPB& req, << "COMMITTED CONFIG: " << SecureShortDebugString(committed_config) << "NEW CONFIG: " << SecureShortDebugString(new_config); - s = Update(&consensus_req, &consensus_resp); - if (!s.ok() || consensus_resp.has_error()) { - *error_code = TabletServerErrorPB::UNKNOWN_ERROR; - } - if (s.ok() && consensus_resp.has_error()) { - s = StatusFromPB(consensus_resp.error().status()); - } - return s; + ConsensusResponsePB consensus_resp; + return Update(&consensus_req, &consensus_resp).AndThen([&consensus_resp]{ + return consensus_resp.has_error() + ? StatusFromPB(consensus_resp.error().status()) : Status::OK(); + }); } void RaftConsensus::Stop() { http://git-wip-us.apache.org/repos/asf/kudu/blob/f1e652b2/src/kudu/consensus/raft_consensus.h ---------------------------------------------------------------------- diff --git a/src/kudu/consensus/raft_consensus.h b/src/kudu/consensus/raft_consensus.h index 33e8196..f84a71e 100644 --- a/src/kudu/consensus/raft_consensus.h +++ b/src/kudu/consensus/raft_consensus.h @@ -274,7 +274,7 @@ class RaftConsensus : public std::enable_shared_from_this<RaftConsensus>, // Implement an UnsafeChangeConfig() request. Status UnsafeChangeConfig(const UnsafeChangeConfigRequestPB& req, - tserver::TabletServerErrorPB::Code* error_code); + boost::optional<tserver::TabletServerErrorPB::Code>* error_code); // Returns the last OpId (either received or committed, depending on the // 'type' argument) that the Consensus implementation knows about. http://git-wip-us.apache.org/repos/asf/kudu/blob/f1e652b2/src/kudu/tserver/tablet_service.cc ---------------------------------------------------------------------- diff --git a/src/kudu/tserver/tablet_service.cc b/src/kudu/tserver/tablet_service.cc index 89dbf95..4cf88ea 100644 --- a/src/kudu/tserver/tablet_service.cc +++ b/src/kudu/tserver/tablet_service.cc @@ -1086,11 +1086,13 @@ void ConsensusServiceImpl::UnsafeChangeConfig(const UnsafeChangeConfigRequestPB* } shared_ptr<RaftConsensus> consensus; - if (!GetConsensusOrRespond(replica, resp, context, &consensus)) return; - TabletServerErrorPB::Code error_code; - Status s = consensus->UnsafeChangeConfig(*req, &error_code); + if (!GetConsensusOrRespond(replica, resp, context, &consensus)) { + return; + } + boost::optional<TabletServerErrorPB::Code> error_code; + const Status s = consensus->UnsafeChangeConfig(*req, &error_code); if (PREDICT_FALSE(!s.ok())) { - SetupErrorAndRespond(resp->mutable_error(), s, error_code, context); + HandleErrorResponse(req, resp, context, error_code, s); return; } context->RespondSuccess();