/Users/deen/code/yugabyte-db/src/yb/integration-tests/yb-ts-cli-itest.cc
Line | Count | Source |
1 | | // Copyright (c) YugaByte, Inc. |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
4 | | // in compliance with the License. You may obtain a copy of the License at |
5 | | // |
6 | | // http://www.apache.org/licenses/LICENSE-2.0 |
7 | | // |
8 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
9 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
10 | | // or implied. See the License for the specific language governing permissions and limitations |
11 | | // under the License. |
12 | | // |
13 | | |
14 | | #include <string> |
15 | | #include <unordered_map> |
16 | | #include <vector> |
17 | | |
18 | | #include <gtest/gtest.h> |
19 | | |
20 | | #include "yb/integration-tests/cluster_itest_util.h" |
21 | | #include "yb/integration-tests/external_mini_cluster.h" |
22 | | #include "yb/integration-tests/external_mini_cluster_fs_inspector.h" |
23 | | #include "yb/integration-tests/yb_table_test_base.h" |
24 | | |
25 | | #include "yb/tserver/tserver_service.proxy.h" |
26 | | |
27 | | #include "yb/util/path_util.h" |
28 | | #include "yb/util/status_log.h" |
29 | | #include "yb/util/subprocess.h" |
30 | | #include "yb/util/test_util.h" |
31 | | |
32 | | namespace yb { |
33 | | namespace integration_tests { |
34 | | namespace { |
35 | | |
36 | | const auto kDefaultTimeout = 30000ms; |
37 | | const auto kServerIndex = 0; |
38 | | |
39 | 1 | string GetTsCliToolPath() { |
40 | 1 | static const char* const kTsCliToolName = "yb-ts-cli"; |
41 | 1 | return GetToolPath(kTsCliToolName); |
42 | 1 | } |
43 | | |
44 | | } // anonymous namespace |
45 | | |
46 | | class YBTsCliITest : public YBTableTestBase { |
47 | | protected: |
48 | 2 | bool use_yb_admin_client() override { return true; } |
49 | | |
50 | 10 | bool use_external_mini_cluster() override { return true; } |
51 | | |
52 | 2 | int num_tablets() override { |
53 | 2 | return 12; |
54 | 2 | } |
55 | | |
56 | 1 | int num_drives() override { |
57 | 1 | return 2; |
58 | 1 | } |
59 | | |
60 | 1 | bool enable_ysql() override { |
61 | | // Do not create the transaction status table. |
62 | 1 | return false; |
63 | 1 | } |
64 | | |
65 | 1 | void WaitForTablet(const string& tablet_id) { |
66 | 1 | ExternalTabletServer* ts = external_mini_cluster()->tablet_server(kServerIndex); |
67 | 1 | tserver::TabletServerServiceProxy proxy(&external_mini_cluster()->proxy_cache(), |
68 | 1 | ts->bound_rpc_addr()); |
69 | 1 | ASSERT_OK(WaitFor([&]() -> Result<bool> { |
70 | 1 | rpc::RpcController rpc; |
71 | 1 | tserver::GetTabletStatusRequestPB req; |
72 | 1 | tserver::GetTabletStatusResponsePB resp; |
73 | 1 | rpc.set_timeout(kDefaultTimeout); |
74 | 1 | req.set_tablet_id(tablet_id); |
75 | 1 | RETURN_NOT_OK(proxy.GetTabletStatus(req, &resp, &rpc)); |
76 | 1 | if (resp.has_error() || resp.tablet_status().state() != tablet::RaftGroupStatePB::RUNNING) { |
77 | 1 | return false; |
78 | 1 | } |
79 | 1 | return true; |
80 | 1 | }, kDefaultTimeout * 2, "WaitForTablet")); |
81 | 1 | } |
82 | | }; |
83 | | |
84 | | // Test steps: |
85 | | // Setup a cluster with three drives on each ts, reduce drives on first ts |
86 | | // create table and increase drives on first ts to lead to bad distribution across drives |
87 | | // call rpc DeleteTablet and restart a ts to validate that tablet was moved |
88 | 1 | TEST_F(YBTsCliITest, MoveTablet) { |
89 | 1 | auto inspect = std::make_unique<itest::ExternalMiniClusterFsInspector>(external_mini_cluster()); |
90 | | |
91 | 1 | string tablet_id; |
92 | 1 | string root_dir; |
93 | | |
94 | 1 | ExternalTabletServer* ts = external_mini_cluster()->tablet_server(kServerIndex); |
95 | | |
96 | 1 | ts->Shutdown(); |
97 | 1 | ASSERT_OK(ts->SetNumDrives(3)); |
98 | 1 | ASSERT_OK(ts->Restart()); |
99 | | |
100 | 1 | size_t max_count = 0; |
101 | | // Look for TS with max number of tablets on one drive and get one tablet to try move it. |
102 | 2 | for (const auto& drive_and_tablets : inspect->DrivesOnTS(kServerIndex)) { |
103 | 2 | const vector<string>& tablets = drive_and_tablets.second; |
104 | 2 | if (tablets.size() > max_count) { |
105 | 1 | root_dir = drive_and_tablets.first; |
106 | 1 | max_count = tablets.size(); |
107 | 1 | tablet_id = tablets.front(); |
108 | 1 | } |
109 | 2 | } |
110 | | |
111 | | // Tablet id to be moved should not be empty. |
112 | 1 | ASSERT_FALSE(tablet_id.empty()); |
113 | 1 | WaitForTablet(tablet_id); |
114 | | |
115 | 1 | string exe_path = GetTsCliToolPath(); |
116 | 1 | vector<string> argv = {exe_path, "--server_address", AsString(ts->bound_rpc_addr()), |
117 | 1 | "delete_tablet", "-force", tablet_id, "Deleting for yb-ts-cli-itest"}; |
118 | 1 | ASSERT_OK(WaitFor([&]() -> Result<bool> { |
119 | 1 | Status s = Subprocess::Call(argv); |
120 | 1 | return s.ok(); |
121 | 1 | }, kDefaultTimeout * 2, "CallTsCli")); |
122 | | |
123 | 1 | ts->Shutdown(); |
124 | 1 | ASSERT_OK(ts->Restart()); |
125 | | |
126 | 1 | ASSERT_OK(external_mini_cluster()->WaitForTabletServerCount(num_tablet_servers(), |
127 | 1 | kDefaultTimeout)); |
128 | | |
129 | 1 | ASSERT_OK(WaitFor([&]() -> Result<bool> { |
130 | 1 | for (const auto& drive_and_tablets : inspect->DrivesOnTS(kServerIndex)) { |
131 | 1 | const vector<string>& tablets = drive_and_tablets.second; |
132 | 1 | if (find(tablets.begin(), tablets.end(), tablet_id) != tablets.end()) { |
133 | 1 | bool same = drive_and_tablets.first == root_dir; |
134 | 1 | if (same) { |
135 | 1 | LOG(INFO) << "TS still have tablet " << tablet_id << " at " << root_dir; |
136 | 1 | } |
137 | 1 | return !same; |
138 | 1 | } |
139 | 1 | } |
140 | 1 | return false; |
141 | 1 | }, kDefaultTimeout * 2, "WaitForTabletMovedFromTS")); |
142 | 1 | } |
143 | | |
144 | | } // namespace integration_tests |
145 | | } // namespace yb |