/Users/deen/code/yugabyte-db/src/yb/tools/ysck-test.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | |
33 | | #include <memory> |
34 | | #include <unordered_map> |
35 | | |
36 | | #include <boost/lexical_cast.hpp> |
37 | | #include <glog/logging.h> |
38 | | #include <gtest/gtest.h> |
39 | | |
40 | | #include "yb/gutil/callback.h" |
41 | | #include "yb/gutil/map-util.h" |
42 | | #include "yb/gutil/strings/substitute.h" |
43 | | |
44 | | #include "yb/tools/ysck.h" |
45 | | |
46 | | #include "yb/util/test_util.h" |
47 | | |
48 | | namespace yb { |
49 | | namespace tools { |
50 | | |
51 | | using std::shared_ptr; |
52 | | using std::static_pointer_cast; |
53 | | using std::string; |
54 | | using std::unordered_map; |
55 | | using std::vector; |
56 | | using client::YBTableName; |
57 | | |
58 | | static const YBTableName kTableName(YQL_DATABASE_CQL, "test"); |
59 | | |
60 | | class MockYsckTabletServer : public YsckTabletServer { |
61 | | public: |
62 | | explicit MockYsckTabletServer(const string& uuid) |
63 | | : YsckTabletServer(uuid), |
64 | | connect_status_(Status::OK()), |
65 | 0 | address_("<mock>") { |
66 | 0 | } |
67 | | |
68 | 0 | Status Connect() const override { |
69 | 0 | return connect_status_; |
70 | 0 | } |
71 | | |
72 | | virtual void RunTabletChecksumScanAsync( |
73 | | const std::string& tablet_id, |
74 | | const Schema& schema, |
75 | | const ChecksumOptions& options, |
76 | 0 | const ReportResultCallback& callback) override { |
77 | 0 | callback.Run(Status::OK(), 0); |
78 | 0 | } |
79 | | |
80 | 0 | Status CurrentHybridTime(uint64_t* hybrid_time) const override { |
81 | 0 | *hybrid_time = 0; |
82 | 0 | return Status::OK(); |
83 | 0 | } |
84 | | |
85 | 0 | const std::string& address() const override { |
86 | 0 | return address_; |
87 | 0 | } |
88 | | |
89 | | // Public because the unit tests mutate this variable directly. |
90 | | Status connect_status_; |
91 | | |
92 | | private: |
93 | | const string address_; |
94 | | }; |
95 | | |
96 | | class MockYsckMaster : public YsckMaster { |
97 | | public: |
98 | | MockYsckMaster() |
99 | 0 | : connect_status_(Status::OK()) { |
100 | 0 | } |
101 | | |
102 | 0 | Status Connect() const override { |
103 | 0 | return connect_status_; |
104 | 0 | } |
105 | | |
106 | 0 | Status RetrieveTabletServers(TSMap* tablet_servers) override { |
107 | 0 | *tablet_servers = tablet_servers_; |
108 | 0 | return Status::OK(); |
109 | 0 | } |
110 | | |
111 | 0 | Status RetrieveTablesList(vector<shared_ptr<YsckTable>>* tables) override { |
112 | 0 | tables->assign(tables_.begin(), tables_.end()); |
113 | 0 | return Status::OK(); |
114 | 0 | } |
115 | | |
116 | 0 | Status RetrieveTabletsList(const shared_ptr<YsckTable>& table) override { |
117 | 0 | return Status::OK(); |
118 | 0 | } |
119 | | |
120 | | // Public because the unit tests mutate these variables directly. |
121 | | Status connect_status_; |
122 | | TSMap tablet_servers_; |
123 | | vector<shared_ptr<YsckTable>> tables_; |
124 | | }; |
125 | | |
126 | | class YsckTest : public YBTest { |
127 | | public: |
128 | | YsckTest() |
129 | | : master_(new MockYsckMaster()), |
130 | | cluster_(new YsckCluster(static_pointer_cast<YsckMaster>(master_))), |
131 | 0 | ysck_(new Ysck(cluster_)) { |
132 | 0 | unordered_map<string, shared_ptr<YsckTabletServer>> tablet_servers; |
133 | 0 | for (int i = 0; i < 3; i++) { |
134 | 0 | string name = strings::Substitute("$0", i); |
135 | 0 | shared_ptr<MockYsckTabletServer> ts(new MockYsckTabletServer(name)); |
136 | 0 | InsertOrDie(&tablet_servers, ts->uuid(), ts); |
137 | 0 | } |
138 | 0 | master_->tablet_servers_.swap(tablet_servers); |
139 | 0 | } |
140 | | |
141 | | protected: |
142 | 0 | void CreateDefaultAssignmentPlan(int tablets_count) { |
143 | 0 | while (tablets_count > 0) { |
144 | 0 | for (const YsckMaster::TSMap::value_type& entry : master_->tablet_servers_) { |
145 | 0 | if (tablets_count-- == 0) return; |
146 | 0 | assignment_plan_.push_back(entry.second->uuid()); |
147 | 0 | } |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | 0 | void CreateOneTableOneTablet() { |
152 | 0 | CreateDefaultAssignmentPlan(1); |
153 | |
|
154 | 0 | shared_ptr<YsckTablet> tablet(new YsckTablet("1")); |
155 | 0 | CreateAndFillTablet(tablet, 1, true); |
156 | |
|
157 | 0 | CreateAndAddTable({tablet}, kTableName, 1); |
158 | 0 | } |
159 | | |
160 | 0 | void CreateOneSmallReplicatedTable() { |
161 | 0 | int num_replicas = 3; |
162 | 0 | int num_tablets = 3; |
163 | 0 | vector<shared_ptr<YsckTablet>> tablets; |
164 | 0 | CreateDefaultAssignmentPlan(num_replicas * num_tablets); |
165 | 0 | for (int i = 0; i < num_tablets; i++) { |
166 | 0 | shared_ptr<YsckTablet> tablet(new YsckTablet(boost::lexical_cast<string>(i))); |
167 | 0 | CreateAndFillTablet(tablet, num_replicas, true); |
168 | 0 | tablets.push_back(tablet); |
169 | 0 | } |
170 | |
|
171 | 0 | CreateAndAddTable(tablets, kTableName, num_replicas); |
172 | 0 | } |
173 | | |
174 | 0 | void CreateOneOneTabletReplicatedBrokenTable() { |
175 | | // We're placing only two tablets, the 3rd goes nowhere. |
176 | 0 | CreateDefaultAssignmentPlan(2); |
177 | |
|
178 | 0 | shared_ptr<YsckTablet> tablet(new YsckTablet("1")); |
179 | 0 | CreateAndFillTablet(tablet, 2, false); |
180 | |
|
181 | 0 | CreateAndAddTable({tablet}, kTableName, 3); |
182 | 0 | } |
183 | | |
184 | | void CreateAndAddTable(vector<shared_ptr<YsckTablet>> tablets, |
185 | 0 | const YBTableName& name, int num_replicas) { |
186 | 0 | shared_ptr<YsckTable> table(new YsckTable(/* id */ "", name, Schema(), num_replicas, |
187 | 0 | TableType::YQL_TABLE_TYPE)); |
188 | 0 | table->set_tablets(tablets); |
189 | |
|
190 | 0 | vector<shared_ptr<YsckTable>> tables = { table }; |
191 | 0 | master_->tables_.assign(tables.begin(), tables.end()); |
192 | 0 | } |
193 | | |
194 | | void CreateAndFillTablet(const shared_ptr<YsckTablet>& tablet, |
195 | | int num_replicas, |
196 | 0 | bool has_leader) { |
197 | 0 | vector<shared_ptr<YsckTabletReplica>> replicas; |
198 | 0 | if (has_leader) { |
199 | 0 | CreateReplicaAndAdd(&replicas, true); |
200 | 0 | num_replicas--; |
201 | 0 | } |
202 | 0 | for (int i = 0; i < num_replicas; i++) { |
203 | 0 | CreateReplicaAndAdd(&replicas, false); |
204 | 0 | } |
205 | 0 | tablet->set_replicas(replicas); |
206 | 0 | } |
207 | | |
208 | 0 | void CreateReplicaAndAdd(vector<shared_ptr<YsckTabletReplica>>* replicas, bool is_leader) { |
209 | 0 | shared_ptr<YsckTabletReplica> replica(new YsckTabletReplica(assignment_plan_.back(), |
210 | 0 | is_leader, !is_leader)); |
211 | 0 | assignment_plan_.pop_back(); |
212 | 0 | replicas->push_back(replica); |
213 | 0 | } |
214 | | |
215 | | shared_ptr<MockYsckMaster> master_; |
216 | | shared_ptr<YsckCluster> cluster_; |
217 | | shared_ptr<Ysck> ysck_; |
218 | | // This is used as a stack. First the unit test is responsible to create a plan to follow, that |
219 | | // is the order in which each replica of each tablet will be assigned, starting from the end. |
220 | | // So if you have 2 tablets with num_replicas=3 and 3 tablet servers, then to distribute evenly |
221 | | // you should have a list that looks like ts1,ts2,ts3,ts3,ts2,ts1 so that the two LEADERS, which |
222 | | // are assigned first, end up on ts1 and ts3. |
223 | | vector<string> assignment_plan_; |
224 | | }; |
225 | | |
226 | 0 | TEST_F(YsckTest, TestMasterOk) { |
227 | 0 | ASSERT_OK(ysck_->CheckMasterRunning()); |
228 | 0 | } |
229 | | |
230 | 0 | TEST_F(YsckTest, TestMasterUnavailable) { |
231 | 0 | Status error = STATUS(NetworkError, "Network failure"); |
232 | 0 | master_->connect_status_ = error; |
233 | 0 | ASSERT_TRUE(ysck_->CheckMasterRunning().IsNetworkError()); |
234 | 0 | } |
235 | | |
236 | 0 | TEST_F(YsckTest, TestTabletServersOk) { |
237 | 0 | ASSERT_OK(ysck_->CheckMasterRunning()); |
238 | 0 | ASSERT_OK(ysck_->FetchTableAndTabletInfo()); |
239 | 0 | ASSERT_OK(ysck_->CheckTabletServersRunning()); |
240 | 0 | } |
241 | | |
242 | 0 | TEST_F(YsckTest, TestBadTabletServer) { |
243 | 0 | ASSERT_OK(ysck_->CheckMasterRunning()); |
244 | 0 | Status error = STATUS(NetworkError, "Network failure"); |
245 | 0 | static_pointer_cast<MockYsckTabletServer>(master_->tablet_servers_.begin()->second) |
246 | 0 | ->connect_status_ = error; |
247 | 0 | ASSERT_OK(ysck_->FetchTableAndTabletInfo()); |
248 | 0 | Status s = ysck_->CheckTabletServersRunning(); |
249 | 0 | ASSERT_TRUE(s.IsNetworkError()) << "Status returned: " << s.ToString(); |
250 | 0 | } |
251 | | |
252 | 0 | TEST_F(YsckTest, TestZeroTableCheck) { |
253 | 0 | ASSERT_OK(ysck_->CheckMasterRunning()); |
254 | 0 | ASSERT_OK(ysck_->FetchTableAndTabletInfo()); |
255 | 0 | ASSERT_OK(ysck_->CheckTabletServersRunning()); |
256 | 0 | ASSERT_OK(ysck_->CheckTablesConsistency()); |
257 | 0 | } |
258 | | |
259 | 0 | TEST_F(YsckTest, TestOneTableCheck) { |
260 | 0 | CreateOneTableOneTablet(); |
261 | 0 | ASSERT_OK(ysck_->CheckMasterRunning()); |
262 | 0 | ASSERT_OK(ysck_->FetchTableAndTabletInfo()); |
263 | 0 | ASSERT_OK(ysck_->CheckTabletServersRunning()); |
264 | 0 | ASSERT_OK(ysck_->CheckTablesConsistency()); |
265 | 0 | } |
266 | | |
267 | 0 | TEST_F(YsckTest, TestOneSmallReplicatedTable) { |
268 | 0 | CreateOneSmallReplicatedTable(); |
269 | 0 | ASSERT_OK(ysck_->CheckMasterRunning()); |
270 | 0 | ASSERT_OK(ysck_->FetchTableAndTabletInfo()); |
271 | 0 | ASSERT_OK(ysck_->CheckTabletServersRunning()); |
272 | 0 | ASSERT_OK(ysck_->CheckTablesConsistency()); |
273 | 0 | } |
274 | | |
275 | 0 | TEST_F(YsckTest, TestOneOneTabletBrokenTable) { |
276 | 0 | CreateOneOneTabletReplicatedBrokenTable(); |
277 | 0 | ASSERT_OK(ysck_->CheckMasterRunning()); |
278 | 0 | ASSERT_OK(ysck_->FetchTableAndTabletInfo()); |
279 | 0 | ASSERT_OK(ysck_->CheckTabletServersRunning()); |
280 | 0 | ASSERT_TRUE(ysck_->CheckTablesConsistency().IsCorruption()); |
281 | 0 | } |
282 | | |
283 | | } // namespace tools |
284 | | } // namespace yb |