Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 8F5E5200BAB for ; Sat, 8 Oct 2016 00:39:32 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 8DDA6160AE9; Fri, 7 Oct 2016 22:39:32 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 35D2B160AE8 for ; Sat, 8 Oct 2016 00:39:31 +0200 (CEST) Received: (qmail 57019 invoked by uid 500); 7 Oct 2016 22:39:30 -0000 Mailing-List: contact commits-help@kudu.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@kudu.apache.org Delivered-To: mailing list commits@kudu.apache.org Received: (qmail 57009 invoked by uid 99); 7 Oct 2016 22:39:30 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 07 Oct 2016 22:39:30 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 4F152E00A4; Fri, 7 Oct 2016 22:39:30 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: alexey@apache.org To: commits@kudu.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: kudu git commit: [tools] Implement a manual leader_step_down for a tablet Date: Fri, 7 Oct 2016 22:39:30 +0000 (UTC) archived-at: Fri, 07 Oct 2016 22:39:32 -0000 Repository: kudu Updated Branches: refs/heads/master fc5e6a3e7 -> 833abea78 [tools] Implement a manual leader_step_down for a tablet This change introduces a leader_step_down functionality under 'kudu tablet'. This tool may be handy to recover from situations when a single tablet server is overloaded and we want to kick off a new election to balance the load across the clusters. Although it is not guaranteed that a different replica will be elected as the leader, this is an optimistic effort to elect a new tablet server as the leader for the given tablet in the cluster. Test: Ran 8000 iterations of leader_step_down test on dist_test. Also snuck in a small change to display host:port details with 'kudu table list --list_tablets' command. Change-Id: Ia046a28a2008f4f5d1e955f57752a32a1ddc5ab8 Reviewed-on: http://gerrit.cloudera.org:8080/4533 Tested-by: Kudu Jenkins Reviewed-by: Alexey Serbin Project: http://git-wip-us.apache.org/repos/asf/kudu/repo Commit: http://git-wip-us.apache.org/repos/asf/kudu/commit/833abea7 Tree: http://git-wip-us.apache.org/repos/asf/kudu/tree/833abea7 Diff: http://git-wip-us.apache.org/repos/asf/kudu/diff/833abea7 Branch: refs/heads/master Commit: 833abea787d3ac4bde3e5292c82b3408aad9ba92 Parents: fc5e6a3 Author: Dinesh Bhat Authored: Thu Sep 22 09:56:34 2016 -0700 Committer: Alexey Serbin Committed: Fri Oct 7 22:11:16 2016 +0000 ---------------------------------------------------------------------- src/kudu/tools/kudu-admin-test.cc | 91 +++++++++++++++++++++++ src/kudu/tools/kudu-tool-test.cc | 3 +- src/kudu/tools/tool_action_cluster.cc | 8 +- src/kudu/tools/tool_action_common.cc | 6 ++ src/kudu/tools/tool_action_common.h | 6 ++ src/kudu/tools/tool_action_local_replica.cc | 32 ++++---- src/kudu/tools/tool_action_remote_replica.cc | 10 +-- src/kudu/tools/tool_action_table.cc | 15 ++-- src/kudu/tools/tool_action_tablet.cc | 81 ++++++++++++++------ src/kudu/tools/tool_action_test.cc | 4 +- 10 files changed, 190 insertions(+), 66 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kudu/blob/833abea7/src/kudu/tools/kudu-admin-test.cc ---------------------------------------------------------------------- diff --git a/src/kudu/tools/kudu-admin-test.cc b/src/kudu/tools/kudu-admin-test.cc index b5aac2c..b5c5f83 100644 --- a/src/kudu/tools/kudu-admin-test.cc +++ b/src/kudu/tools/kudu-admin-test.cc @@ -37,6 +37,7 @@ using client::KuduClientBuilder; using client::KuduSchema; using client::KuduTableCreator; using client::sp::shared_ptr; +using consensus::ConsensusStatePB; using itest::TabletServerMap; using itest::TServerDetails; using std::string; @@ -153,6 +154,96 @@ TEST_F(AdminCliTest, TestChangeConfig) { MonoDelta::FromSeconds(10))); } +Status GetTermFromConsensus(const vector& tservers, + const string& tablet_id, + int64 *current_term) { + ConsensusStatePB cstate; + for (auto& ts : tservers) { + RETURN_NOT_OK( + itest::GetConsensusState(ts, tablet_id, + consensus::CONSENSUS_CONFIG_COMMITTED, + MonoDelta::FromSeconds(10), &cstate)); + if (cstate.has_leader_uuid() && cstate.has_current_term()) { + *current_term = cstate.current_term(); + return Status::OK(); + } + } + return Status::NotFound(Substitute( + "No leader replica found for tablet $0", tablet_id)); +} + +TEST_F(AdminCliTest, TestLeaderStepDown) { + FLAGS_num_tablet_servers = 3; + FLAGS_num_replicas = 3; + BuildAndStart({}, {}); + + vector tservers; + AppendValuesFromMap(tablet_servers_, &tservers); + ASSERT_EQ(FLAGS_num_tablet_servers, tservers.size()); + for (auto& ts : tservers) { + ASSERT_OK(itest::WaitUntilTabletRunning(ts, + tablet_id_, + MonoDelta::FromSeconds(10))); + } + + int64 current_term; + ASSERT_OK(GetTermFromConsensus(tservers, tablet_id_, + ¤t_term)); + + // The leader for the given tablet may change anytime, resulting in + // the command returning an error code. Hence checking for term advancement + // only if the leader_step_down succeeds. It is also unsafe to check + // the term advancement without honoring status of the command since + // there may not have been another election in the meanwhile. + string stderr; + Status s = Subprocess::Call({GetKuduCtlAbsolutePath(), + "tablet", "leader_step_down", + cluster_->master()->bound_rpc_addr().ToString(), + tablet_id_}, nullptr, &stderr); + bool not_currently_leader = stderr.find( + Status::IllegalState("").CodeAsString()) != string::npos; + ASSERT_TRUE(s.ok() || not_currently_leader); + if (s.ok()) { + int64 new_term; + AssertEventually([&]() { + ASSERT_OK(GetTermFromConsensus(tservers, tablet_id_, + &new_term)); + ASSERT_GT(new_term, current_term); + }); + } +} + +TEST_F(AdminCliTest, TestLeaderStepDownWhenNotPresent) { + FLAGS_num_tablet_servers = 3; + FLAGS_num_replicas = 3; + BuildAndStart( + { "--enable_leader_failure_detection=false" }, + { "--catalog_manager_wait_for_new_tablets_to_elect_leader=false" }); + vector tservers; + AppendValuesFromMap(tablet_servers_, &tservers); + ASSERT_EQ(FLAGS_num_tablet_servers, tservers.size()); + for (auto& ts : tservers) { + ASSERT_OK(itest::WaitUntilTabletRunning(ts, + tablet_id_, + MonoDelta::FromSeconds(10))); + } + + int64 current_term; + ASSERT_TRUE(GetTermFromConsensus(tservers, tablet_id_, + ¤t_term).IsNotFound()); + string stdout; + ASSERT_OK(Subprocess::Call({ + GetKuduCtlAbsolutePath(), + "tablet", + "leader_step_down", + cluster_->master()->bound_rpc_addr().ToString(), + tablet_id_ + }, &stdout)); + ASSERT_STR_CONTAINS(stdout, + Substitute("No leader replica found for tablet $0", + tablet_id_)); +} + TEST_F(AdminCliTest, TestDeleteTable) { FLAGS_num_tablet_servers = 1; FLAGS_num_replicas = 1; http://git-wip-us.apache.org/repos/asf/kudu/blob/833abea7/src/kudu/tools/kudu-tool-test.cc ---------------------------------------------------------------------- diff --git a/src/kudu/tools/kudu-tool-test.cc b/src/kudu/tools/kudu-tool-test.cc index 34e0a59..94c5160 100644 --- a/src/kudu/tools/kudu-tool-test.cc +++ b/src/kudu/tools/kudu-tool-test.cc @@ -282,7 +282,8 @@ TEST_F(ToolTest, TestModeHelp) { } { const vector kTabletModeRegexes = { - "change_config.*Change.*Raft configuration" + "change_config.*Change.*Raft configuration", + "leader_step_down.*Force the tablet's leader replica to step down" }; NO_FATALS(RunTestHelp("tablet", kTabletModeRegexes)); } http://git-wip-us.apache.org/repos/asf/kudu/blob/833abea7/src/kudu/tools/tool_action_cluster.cc ---------------------------------------------------------------------- diff --git a/src/kudu/tools/tool_action_cluster.cc b/src/kudu/tools/tool_action_cluster.cc index 513d75b..e5351f2 100644 --- a/src/kudu/tools/tool_action_cluster.cc +++ b/src/kudu/tools/tool_action_cluster.cc @@ -28,6 +28,7 @@ #include "kudu/gutil/strings/split.h" #include "kudu/tools/ksck.h" #include "kudu/tools/ksck_remote.h" +#include "kudu/tools/tool_action_common.h" #include "kudu/util/status.h" #define PUSH_PREPEND_NOT_OK(s, statuses, msg) do { \ @@ -62,8 +63,6 @@ using std::vector; namespace { -const char* const kMasterAddressesArg = "master_addresses"; - Status RunKsck(const RunnerContext& context) { const string& master_addresses_str = FindOrDie(context.required_args, kMasterAddressesArg); @@ -130,10 +129,7 @@ unique_ptr BuildClusterMode() { ActionBuilder("ksck", &RunKsck) .Description(desc) .ExtraDescription(extra_desc) - .AddRequiredParameter({ - kMasterAddressesArg, - "Comma-separated list of Kudu Master addressess where each address is " - "of form 'hostname:port'" }) + .AddRequiredParameter({ kMasterAddressesArg, kMasterAddressesArgDesc }) .AddOptionalParameter("checksum_scan") .AddOptionalParameter("checksum_scan_concurrency") .AddOptionalParameter("checksum_snapshot") http://git-wip-us.apache.org/repos/asf/kudu/blob/833abea7/src/kudu/tools/tool_action_common.cc ---------------------------------------------------------------------- diff --git a/src/kudu/tools/tool_action_common.cc b/src/kudu/tools/tool_action_common.cc index 867cc2a..bd0db19 100644 --- a/src/kudu/tools/tool_action_common.cc +++ b/src/kudu/tools/tool_action_common.cc @@ -97,6 +97,12 @@ using tserver::TabletServerAdminServiceProxy; using tserver::TabletServerServiceProxy; using tserver::WriteRequestPB; +const char* const kMasterAddressesArg = "master_addresses"; +const char* const kMasterAddressesArgDesc = "Comma-separated list of Kudu " + "Master addresses where each address is of form 'hostname:port'"; +const char* const kTabletIdArg = "tablet_id"; +const char* const kTabletIdArgDesc = "Tablet Identifier"; + namespace { enum PrintEntryType { http://git-wip-us.apache.org/repos/asf/kudu/blob/833abea7/src/kudu/tools/tool_action_common.h ---------------------------------------------------------------------- diff --git a/src/kudu/tools/tool_action_common.h b/src/kudu/tools/tool_action_common.h index d51f656..27284f1 100644 --- a/src/kudu/tools/tool_action_common.h +++ b/src/kudu/tools/tool_action_common.h @@ -35,6 +35,12 @@ class ServerStatusPB; namespace tools { +// Constants for parameters and descriptions. +extern const char* const kMasterAddressesArg; +extern const char* const kMasterAddressesArgDesc; +extern const char* const kTabletIdArg; +extern const char* const kTabletIdArgDesc; + // Utility methods used by multiple actions across different modes. // Builds a proxy to a Kudu server running at 'address', returning it in http://git-wip-us.apache.org/repos/asf/kudu/blob/833abea7/src/kudu/tools/tool_action_local_replica.cc ---------------------------------------------------------------------- diff --git a/src/kudu/tools/tool_action_local_replica.cc b/src/kudu/tools/tool_action_local_replica.cc index b0455a7..461235e 100644 --- a/src/kudu/tools/tool_action_local_replica.cc +++ b/src/kudu/tools/tool_action_local_replica.cc @@ -116,8 +116,8 @@ using tserver::WriteRequestPB; namespace { -static const char* const kSeparatorLine = - "----------------------------------------------------------------------\n"; +const char* const kSeparatorLine = + "----------------------------------------------------------------------\n"; string Indent(int indent) { return string(indent, ' '); @@ -172,7 +172,7 @@ Status ParsePeerString(const string& peer_str, Status PrintReplicaUuids(const RunnerContext& context) { unique_ptr fs_manager; RETURN_NOT_OK(FsInit(&fs_manager)); - const string& tablet_id = FindOrDie(context.required_args, "tablet_id"); + const string& tablet_id = FindOrDie(context.required_args, kTabletIdArg); // Load the cmeta file and print all peer uuids. unique_ptr cmeta; @@ -186,7 +186,7 @@ Status PrintReplicaUuids(const RunnerContext& context) { Status RewriteRaftConfig(const RunnerContext& context) { // Parse tablet ID argument. - const string& tablet_id = FindOrDie(context.required_args, "tablet_id"); + const string& tablet_id = FindOrDie(context.required_args, kTabletIdArg); if (tablet_id != master::SysCatalogTable::kSysCatalogTabletId) { LOG(WARNING) << "Master will not notice rewritten Raft config of regular " << "tablets. A regular Raft config change must occur."; @@ -238,7 +238,7 @@ Status RewriteRaftConfig(const RunnerContext& context) { Status CopyFromRemote(const RunnerContext& context) { // Parse the tablet ID and source arguments. - const string& tablet_id = FindOrDie(context.required_args, "tablet_id"); + const string& tablet_id = FindOrDie(context.required_args, kTabletIdArg); const string& rpc_address = FindOrDie(context.required_args, "source"); HostPort hp; @@ -259,7 +259,7 @@ Status CopyFromRemote(const RunnerContext& context) { Status DumpWals(const RunnerContext& context) { unique_ptr fs_manager; RETURN_NOT_OK(FsInit(&fs_manager)); - const string& tablet_id = FindOrDie(context.required_args, "tablet_id"); + const string& tablet_id = FindOrDie(context.required_args, kTabletIdArg); shared_ptr reader; RETURN_NOT_OK(LogReader::Open(fs_manager.get(), @@ -309,7 +309,7 @@ Status ListBlocksInRowSet(const Schema& schema, Status DumpBlockIdsForLocalReplica(const RunnerContext& context) { unique_ptr fs_manager; RETURN_NOT_OK(FsInit(&fs_manager)); - const string& tablet_id = FindOrDie(context.required_args, "tablet_id"); + const string& tablet_id = FindOrDie(context.required_args, kTabletIdArg); scoped_refptr meta; RETURN_NOT_OK(TabletMetadata::Load(fs_manager.get(), tablet_id, &meta)); @@ -567,7 +567,7 @@ Status DumpRowSetInternal(FsManager* fs_manager, Status DumpRowSet(const RunnerContext& context) { unique_ptr fs_manager; RETURN_NOT_OK(FsInit(&fs_manager)); - const string& tablet_id = FindOrDie(context.required_args, "tablet_id"); + const string& tablet_id = FindOrDie(context.required_args, kTabletIdArg); scoped_refptr meta; RETURN_NOT_OK(TabletMetadata::Load(fs_manager.get(), tablet_id, &meta)); @@ -607,7 +607,7 @@ Status DumpRowSet(const RunnerContext& context) { Status DumpMeta(const RunnerContext& context) { unique_ptr fs_manager; RETURN_NOT_OK(FsInit(&fs_manager)); - const string& tablet_id = FindOrDie(context.required_args, "tablet_id"); + const string& tablet_id = FindOrDie(context.required_args, kTabletIdArg); RETURN_NOT_OK(DumpTabletMeta(fs_manager.get(), tablet_id, 0)); return Status::OK(); } @@ -616,7 +616,7 @@ unique_ptr BuildDumpMode() { unique_ptr dump_block_ids = ActionBuilder("block_ids", &DumpBlockIdsForLocalReplica) .Description("Dump the IDs of all blocks belonging to a local replica") - .AddRequiredParameter({ "tablet_id", "tablet identifier" }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddOptionalParameter("fs_wal_dir") .AddOptionalParameter("fs_data_dirs") .Build(); @@ -624,7 +624,7 @@ unique_ptr BuildDumpMode() { unique_ptr dump_meta = ActionBuilder("meta", &DumpMeta) .Description("Dump the metadata of a local replica") - .AddRequiredParameter({ "tablet_id", "tablet identifier" }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddOptionalParameter("fs_wal_dir") .AddOptionalParameter("fs_data_dirs") .Build(); @@ -632,7 +632,7 @@ unique_ptr BuildDumpMode() { unique_ptr dump_rowset = ActionBuilder("rowset", &DumpRowSet) .Description("Dump the rowset contents of a local replica") - .AddRequiredParameter({ "tablet_id", "tablet identifier" }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddOptionalParameter("dump_data") .AddOptionalParameter("fs_wal_dir") .AddOptionalParameter("fs_data_dirs") @@ -645,7 +645,7 @@ unique_ptr BuildDumpMode() { ActionBuilder("wals", &DumpWals) .Description("Dump all WAL (write-ahead log) segments of " "a local replica") - .AddRequiredParameter({ "tablet_id", "Tablet identifier" }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddOptionalParameter("fs_wal_dir") .AddOptionalParameter("fs_data_dirs") .AddOptionalParameter("print_entries") @@ -669,7 +669,7 @@ unique_ptr BuildLocalReplicaMode() { ActionBuilder("print_replica_uuids", &PrintReplicaUuids) .Description("Print all replica UUIDs found in a " "tablet's Raft configuration") - .AddRequiredParameter({ "tablet_id", "Tablet identifier" }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddOptionalParameter("fs_wal_dir") .AddOptionalParameter("fs_data_dirs") .Build(); @@ -677,7 +677,7 @@ unique_ptr BuildLocalReplicaMode() { unique_ptr rewrite_raft_config = ActionBuilder("rewrite_raft_config", &RewriteRaftConfig) .Description("Rewrite a replica's Raft configuration") - .AddRequiredParameter({ "tablet_id", "Tablet identifier" }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddRequiredVariadicParameter({ "peers", "List of peers where each peer is of " "form 'uuid:hostname:port'" }) @@ -696,7 +696,7 @@ unique_ptr BuildLocalReplicaMode() { unique_ptr copy_from_remote = ActionBuilder("copy_from_remote", &CopyFromRemote) .Description("Copy a replica from a remote server") - .AddRequiredParameter({ "tablet_id", "Tablet identifier" }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddRequiredParameter({ "source", "Source RPC address of " "form hostname:port" }) .AddOptionalParameter("fs_wal_dir") http://git-wip-us.apache.org/repos/asf/kudu/blob/833abea7/src/kudu/tools/tool_action_remote_replica.cc ---------------------------------------------------------------------- diff --git a/src/kudu/tools/tool_action_remote_replica.cc b/src/kudu/tools/tool_action_remote_replica.cc index e305d4c..0e117a9 100644 --- a/src/kudu/tools/tool_action_remote_replica.cc +++ b/src/kudu/tools/tool_action_remote_replica.cc @@ -136,8 +136,6 @@ class ReplicaDumper { namespace { const char* const kReasonArg = "reason"; -const char* const kTabletArg = "tablet_id"; -const char* const kTabletDesc = "Tablet identifier"; const char* const kTServerAddressArg = "tserver_address"; const char* const kTServerAddressDesc = "Address of a Kudu Tablet Server of " "form 'hostname:port'. Port may be omitted if the Tablet Server is bound " @@ -189,7 +187,7 @@ Status CheckReplicas(const RunnerContext& context) { Status DeleteReplica(const RunnerContext& context) { const string& address = FindOrDie(context.required_args, kTServerAddressArg); - const string& tablet_id = FindOrDie(context.required_args, kTabletArg); + const string& tablet_id = FindOrDie(context.required_args, kTabletIdArg); const string& reason = FindOrDie(context.required_args, kReasonArg); ServerStatusPB status; @@ -220,7 +218,7 @@ Status DeleteReplica(const RunnerContext& context) { Status DumpReplica(const RunnerContext& context) { const string& address = FindOrDie(context.required_args, kTServerAddressArg); - const string& tablet_id = FindOrDie(context.required_args, kTabletArg); + const string& tablet_id = FindOrDie(context.required_args, kTabletIdArg); unique_ptr proxy; RETURN_NOT_OK(BuildProxy(address, tserver::TabletServer::kDefaultPort, @@ -295,7 +293,7 @@ unique_ptr BuildRemoteReplicaMode() { ActionBuilder("delete", &DeleteReplica) .Description("Delete a replica from a Kudu Tablet Server") .AddRequiredParameter({ kTServerAddressArg, kTServerAddressDesc }) - .AddRequiredParameter({ kTabletArg, kTabletDesc }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddRequiredParameter({ kReasonArg, "Reason for deleting the replica" }) .Build(); @@ -303,7 +301,7 @@ unique_ptr BuildRemoteReplicaMode() { ActionBuilder("dump", &DumpReplica) .Description("Dump the data of a replica on a Kudu Tablet Server") .AddRequiredParameter({ kTServerAddressArg, kTServerAddressDesc }) - .AddRequiredParameter({ kTabletArg, kTabletDesc }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .Build(); unique_ptr list = http://git-wip-us.apache.org/repos/asf/kudu/blob/833abea7/src/kudu/tools/tool_action_table.cc ---------------------------------------------------------------------- diff --git a/src/kudu/tools/tool_action_table.cc b/src/kudu/tools/tool_action_table.cc index c50e3bd..5296886 100644 --- a/src/kudu/tools/tool_action_table.cc +++ b/src/kudu/tools/tool_action_table.cc @@ -28,6 +28,7 @@ #include "kudu/gutil/map-util.h" #include "kudu/gutil/stl_util.h" #include "kudu/gutil/strings/split.h" +#include "kudu/tools/tool_action_common.h" #include "kudu/util/status.h" DEFINE_bool(list_tablets, false, @@ -49,7 +50,6 @@ using std::vector; namespace { -const char* const kMasterAddressesArg = "master_addresses"; const char* const kTableNameArg = "table_name"; Status DeleteTable(const RunnerContext& context) { @@ -93,7 +93,8 @@ Status ListTables(const RunnerContext& context) { cout << "T " << token->tablet().id() << "\t"; for (const auto* replica : token->tablet().replicas()) { cout << "P" << (replica->is_leader() ? "(L) " : " ") - << replica->ts().uuid() << " "; + << replica->ts().uuid() << "(" << replica->ts().hostname() + << ":" << replica->ts().port() << ")" << " "; } cout << endl; } @@ -108,20 +109,14 @@ unique_ptr BuildTableMode() { unique_ptr delete_table = ActionBuilder("delete", &DeleteTable) .Description("Delete a table") - .AddRequiredParameter({ - kMasterAddressesArg, - "Comma-separated list of Kudu Master addresses where each address is " - "of form 'hostname:port'" }) + .AddRequiredParameter({ kMasterAddressesArg, kMasterAddressesArgDesc }) .AddRequiredParameter({ kTableNameArg, "Name of the table to delete" }) .Build(); unique_ptr list_tables = ActionBuilder("list", &ListTables) .Description("List all tables") - .AddRequiredParameter({ - kMasterAddressesArg, - "Comma-separated list of Kudu Master addresses where each address is " - "of form 'hostname:port'" }) + .AddRequiredParameter({ kMasterAddressesArg, kMasterAddressesArgDesc }) .AddOptionalParameter("list_tablets") .Build(); http://git-wip-us.apache.org/repos/asf/kudu/blob/833abea7/src/kudu/tools/tool_action_tablet.cc ---------------------------------------------------------------------- diff --git a/src/kudu/tools/tool_action_tablet.cc b/src/kudu/tools/tool_action_tablet.cc index 0e57d08..0ebc710 100644 --- a/src/kudu/tools/tool_action_tablet.cc +++ b/src/kudu/tools/tool_action_tablet.cc @@ -18,6 +18,7 @@ #include "kudu/tools/tool_action.h" #include +#include #include #include #include @@ -31,6 +32,7 @@ #include "kudu/gutil/strings/split.h" #include "kudu/gutil/strings/substitute.h" #include "kudu/rpc/rpc_controller.h" +#include "kudu/server/server_base.pb.h" #include "kudu/tools/tool_action_common.h" #include "kudu/util/net/net_util.h" #include "kudu/util/status.h" @@ -47,6 +49,8 @@ using consensus::ChangeConfigType; using consensus::ConsensusServiceProxy; using consensus::RaftPeerPB; using rpc::RpcController; +using std::cout; +using std::endl; using std::string; using std::unique_ptr; using std::vector; @@ -54,10 +58,8 @@ using strings::Substitute; namespace { -const char* const kMasterAddressesArg = "master_addresses"; const char* const kReplicaTypeArg = "replica_type"; const char* const kReplicaUuidArg = "replica_uuid"; -const char* const kTabletIdArg = "tablet_id"; Status GetRpcAddressForTS(const client::sp::shared_ptr& client, const string& uuid, @@ -94,6 +96,7 @@ Status GetTabletLeader(const client::sp::shared_ptr& client, return Status::OK(); } } + return Status::NotFound(Substitute( "No leader replica found for tablet $0", tablet_id)); } @@ -134,13 +137,6 @@ Status ChangeConfig(const RunnerContext& context, ChangeConfigType cc_type) { string leader_uuid; HostPort leader_hp; RETURN_NOT_OK(GetTabletLeader(client, tablet_id, &leader_uuid, &leader_hp)); - vector leader_addrs; - RETURN_NOT_OK(leader_hp.ResolveAddresses(&leader_addrs)); - if (leader_addrs.empty()) { - return Status::NotFound( - "Unable to resolve IP address for tablet leader host", - leader_hp.ToString()); - } unique_ptr proxy; RETURN_NOT_OK(BuildProxy(leader_hp.host(), leader_hp.port(), &proxy)); @@ -172,17 +168,52 @@ Status RemoveReplica(const RunnerContext& context) { return ChangeConfig(context, consensus::REMOVE_SERVER); } +Status LeaderStepDown(const RunnerContext& context) { + const string& master_addresses_str = FindOrDie(context.required_args, + kMasterAddressesArg); + vector master_addresses = strings::Split(master_addresses_str, ","); + const string& tablet_id = FindOrDie(context.required_args, kTabletIdArg); + + client::sp::shared_ptr client; + RETURN_NOT_OK(KuduClientBuilder() + .master_server_addrs(master_addresses) + .Build(&client)); + + // If leader is not present, command can gracefully return. + string leader_uuid; + HostPort leader_hp; + Status s = GetTabletLeader(client, tablet_id, &leader_uuid, &leader_hp); + if (s.IsNotFound()) { + cout << s.ToString() << endl; + return Status::OK(); + } + RETURN_NOT_OK(s); + + unique_ptr proxy; + RETURN_NOT_OK(BuildProxy(leader_hp.host(), leader_hp.port(), &proxy)); + + consensus::LeaderStepDownRequestPB req; + consensus::LeaderStepDownResponsePB resp; + RpcController rpc; + rpc.set_timeout(client->default_admin_operation_timeout()); + req.set_dest_uuid(leader_uuid); + req.set_tablet_id(tablet_id); + + RETURN_NOT_OK(proxy->LeaderStepDown(req, &resp, &rpc)); + if (resp.has_error()) { + return StatusFromPB(resp.error().status()); + } + return Status::OK(); +} + } // anonymous namespace unique_ptr BuildTabletMode() { unique_ptr add_replica = ActionBuilder("add_replica", &AddReplica) .Description("Add a new replica to a tablet's Raft configuration") - .AddRequiredParameter({ - kMasterAddressesArg, - "Comma-separated list of Kudu Master addresses where each address is " - "of form 'hostname:port'" }) - .AddRequiredParameter({ kTabletIdArg, "Tablet Identifier" }) + .AddRequiredParameter({ kMasterAddressesArg, kMasterAddressesArgDesc }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddRequiredParameter({ kReplicaUuidArg, "New replica's UUID" }) .AddRequiredParameter( { kReplicaTypeArg, "New replica's type. Must be VOTER or NON-VOTER." @@ -193,11 +224,8 @@ unique_ptr BuildTabletMode() { ActionBuilder("change_replica_type", &ChangeReplicaType) .Description( "Change the type of an existing replica in a tablet's Raft configuration") - .AddRequiredParameter({ - kMasterAddressesArg, - "Comma-separated list of Kudu Master addresses where each address is " - "of 'form hostname:port'" }) - .AddRequiredParameter({ kTabletIdArg, "Tablet Identifier" }) + .AddRequiredParameter({ kMasterAddressesArg, kMasterAddressesArgDesc }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddRequiredParameter({ kReplicaUuidArg, "Existing replica's UUID" }) .AddRequiredParameter( { kReplicaTypeArg, "Existing replica's new type. Must be VOTER or NON-VOTER." @@ -207,14 +235,18 @@ unique_ptr BuildTabletMode() { unique_ptr remove_replica = ActionBuilder("remove_replica", &RemoveReplica) .Description("Remove an existing replica from a tablet's Raft configuration") - .AddRequiredParameter({ - kMasterAddressesArg, - "Comma-separated list of Kudu Master addresses where each address is " - "of form 'hostname:port'" }) - .AddRequiredParameter({ kTabletIdArg, "Tablet Identifier" }) + .AddRequiredParameter({ kMasterAddressesArg, kMasterAddressesArgDesc }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) .AddRequiredParameter({ kReplicaUuidArg, "Existing replica's UUID" }) .Build(); + unique_ptr leader_step_down = + ActionBuilder("leader_step_down", &LeaderStepDown) + .Description("Force the tablet's leader replica to step down") + .AddRequiredParameter({ kMasterAddressesArg, kMasterAddressesArgDesc }) + .AddRequiredParameter({ kTabletIdArg, kTabletIdArgDesc }) + .Build(); + unique_ptr change_config = ModeBuilder("change_config") .Description("Change a tablet's Raft configuration") @@ -226,6 +258,7 @@ unique_ptr BuildTabletMode() { return ModeBuilder("tablet") .Description("Operate on remote Kudu tablets") .AddMode(std::move(change_config)) + .AddAction(std::move(leader_step_down)) .Build(); } http://git-wip-us.apache.org/repos/asf/kudu/blob/833abea7/src/kudu/tools/tool_action_test.cc ---------------------------------------------------------------------- diff --git a/src/kudu/tools/tool_action_test.cc b/src/kudu/tools/tool_action_test.cc index 29b8950..5166025 100644 --- a/src/kudu/tools/tool_action_test.cc +++ b/src/kudu/tools/tool_action_test.cc @@ -105,6 +105,7 @@ #include "kudu/common/types.h" #include "kudu/gutil/strings/split.h" #include "kudu/gutil/strings/substitute.h" +#include "kudu/tools/tool_action_common.h" #include "kudu/util/oid_generator.h" #include "kudu/util/random.h" #include "kudu/util/stopwatch.h" @@ -204,11 +205,8 @@ DEFINE_bool(use_random, false, namespace kudu { namespace tools { - namespace { -const char* const kMasterAddressesArg = "master_addresses"; - class Generator { public: enum Mode {